1import type { ClientSettingsPatch, DesktopSnapShotState, SnapShotSound } from "@t3tools/contracts";
2import {
3 captureSetupBackend,
4 captureSetupDesktopName,
5 captureSetupAccessReady,
6 captureSetupMacPermissionsReady,
7} from "./SnapShotSetupDialog.logic";
8
9export function snapShotStatus(state: DesktopSnapShotState | null, enabled: boolean): string {
10 if (!state) return "Checking snapshots…";
11 if (state.mode === "unavailable") return state.message ?? "Not supported on this platform.";
12 if (!enabled) return "Turn this on to set up snapshots.";
13 return snapShotSetupSummary(state, enabled);
14}
15
16export function snapShotSetupSummary(state: DesktopSnapShotState, enabled: boolean): string {
17 if (state.message) return "Capture needs attention";
18 if (state.linuxBackend === "hyprland" && state.hyprlandHelper?.status !== "ready")
19 return state.hyprlandHelper?.status === "error"
20 ? "Check capture access in setup"
21 : "Install the capture helper to continue";
22 if (captureSetupBackend(state) === "gnome" && state.gnomeExtension?.status !== "enabled")
23 return "Set up active-window snapshots";
24 if (captureSetupBackend(state) === "kde" && state.kdeHelper?.status !== "ready")
25 return state.kdeHelper?.status === "error"
26 ? "Check capture access in setup"
27 : "Install the capture helper to continue";
28 if (captureSetupBackend(state) === "picker")
29 return "Manual capture only — you'll choose a window each time";
30 if (!enabled) return "Enable capture to continue";
31 if (state.shortcutPending)
32 return state.linuxBackend === "hyprland"
33 ? "Connecting your shortcut…"
34 : "Waiting for shortcut permission";
35 if (state.shortcutVerified) return "Ready to capture";
36 if (state.linuxBackend === "niri" && state.shortcutBinding)
37 return "Use your shortcut from another app";
38 if (state.linuxBackend === "hyprland" && state.shortcutActionRegistered)
39 return "Use your shortcut from another app";
40 if (state.shortcutRegistered) return state.shortcutLabel ? "Ready to capture" : "Shortcut saved";
41 return "Finish shortcut setup";
42}
43
44export function snapShotShortcutStatus(state: DesktopSnapShotState | null): string | null {
45 if (!state) return null;
46 if (state.linuxBackend === "hyprland") return state.shortcutMessage;
47 if (state.shortcutPending) return "Approve the shortcut permission prompt to continue.";
48 if (state.shortcutRegistered) return state.mode === "portal" ? null : "Shortcut saved.";
49 return state.shortcutMessage;
50}
51
52export function snapShotSetupButtonLabel(state: DesktopSnapShotState | null): string {
53 if (!state) return "Continue setup";
54 if (captureSetupAccessReady(state)) return "Manage capture";
55 const desktop = captureSetupDesktopName(state);
56 return desktop ? `Set up ${desktop} capture` : "Continue setup";
57}
58
59// Windows needs no permissions or setup: turning capture on is enough. macOS setup
60// has nothing left to manage once permissions and the shortcut are in place; the
61// shortcut row stays editable inline. Revoking a permission brings the button back
62// as "Continue setup" through the state message.
63export function snapShotSetupComplete(
64 state: DesktopSnapShotState | null,
65 includeAccessibility: boolean,
66): boolean {
67 if (state?.windows) return true;
68 return (
69 state?.macPermissions !== undefined &&
70 captureSetupAccessReady(state) &&
71 captureSetupMacPermissionsReady(state, includeAccessibility) &&
72 state.shortcutRegistered
73 );
74}
75
76export type SnapShotSoundSelection = SnapShotSound | "off";
77
78export function snapShotFeedbackUnavailableMessage(
79 state: DesktopSnapShotState | null,
80): string | undefined {
81 if (state?.mode !== "portal" || state.linuxFeedbackAvailable) return undefined;
82 if (state.linuxBackend === "hyprland")
83 return state.hyprlandHelper?.status === "ready"
84 ? "Capture effects aren't available on this desktop."
85 : "Install or update the capture helper to enable effects.";
86 if (state.linuxBackend === "niri") return "Capture effects aren't available on Niri.";
87 if (state.linuxBackend === "kde")
88 return state.kdeHelper?.status === "ready"
89 ? "Capture effects aren't available on this desktop."
90 : "Install or update the capture helper to enable effects.";
91 return state.linuxBackend === "gnome-extension"
92 ? "Update the GNOME extension, then sign out and back in to enable effects."
93 : captureSetupBackend(state) === "gnome"
94 ? "Finish extension setup to enable effects."
95 : "Capture effects aren't available on this desktop.";
96}
97
98export function snapShotDescription(state: DesktopSnapShotState | null): string {
99 return state?.mode === "portal" && captureSetupBackend(state) === "picker"
100 ? "Automatic capture isn't available here. Choose a window instead."
101 : "Capture a window and attach it to your current draft.";
102}
103
104export function snapShotAccessibilityUnavailableMessage(
105 state: DesktopSnapShotState | null,
106): string | undefined {
107 if (state?.mode !== "portal") return undefined;
108 if (state.linuxBackend === "picker" || state.linuxBackend === "screenshot-portal")
109 return "This desktop only provides a screenshot.";
110 return undefined;
111}
112
113export function snapShotUnavailableMessage(hasBridge: boolean): string | undefined {
114 if (hasBridge) return undefined;
115 return typeof window !== "undefined" && window.desktopBridge
116 ? "Update the desktop app to use snapshots."
117 : "Only available in the desktop app.";
118}
119
120export function snapShotSoundPatch(sound: SnapShotSoundSelection): ClientSettingsPatch {
121 return sound === "off"
122 ? { snapShotPlaySound: false }
123 : { snapShotPlaySound: true, snapShotSound: sound };
124}
125
126export function createRecordingRequestTracker() {
127 let currentRequest: symbol | null = null;
128
129 return {
130 tryBegin() {
131 if (currentRequest) return null;
132 currentRequest = Symbol();
133 return currentRequest;
134 },
135 clear() {
136 currentRequest = null;
137 },
138 owns(request: symbol) {
139 return currentRequest === request;
140 },
141 };
142}