import {
isModifierPairShortcut,
type DesktopSnapShotSetupAction,
type DesktopSnapShotState,
} from "@t3tools/contracts";
import { CircleCheckIcon } from "lucide-react";
import { useEffect, useId, useState, type ReactNode } from "react";
import { CaptureShortcutConfig } from "./CaptureShortcutConfig";
import { Button } from "../ui/button";
import { Dialog, DialogDescription } from "../ui/dialog";
import { WizardSteps, WizardPopup, WizardHeader, WizardPanel, WizardFooter } from "../ui/wizard";
import {
captureSetupAccessReady,
captureSetupBackend,
captureSetupCheckMessage,
captureSetupDesktopName,
captureSetupInitialStep,
captureSetupMacPermissionsReady,
captureSetupShortcutReady,
type CaptureSetupStep,
} from "./SnapShotSetupDialog.logic";
const SETUP_STEPS = [
{ id: "access", label: "Access" },
{ id: "shortcut", label: "Shortcut" },
] as const;
const GNOME_ACCESS_COPY = {
"not-installed": {
title: "Install the extension",
description:
"The T3 Code GNOME extension lets you capture other windows and bring them into your draft. Sign out once after installing.",
},
"restart-required": {
title: "Extension installed",
description: "Save your work, then sign out and back in. Your setup will be waiting here.",
},
"update-required": {
title: "Update the extension",
description: "Install the update, then sign out and back in.",
},
"extensions-disabled": {
title: "Allow GNOME extensions",
description: "Open GNOME Extensions and turn on extensions, then check again.",
},
disabled: {
title: "Enable the extension",
description: "Enable T3 Code SnapShots to start capturing windows.",
},
enabled: {
title: "Capture is ready",
description: "Next, choose your shortcut.",
},
unsupported: {
title: "Automatic capture isn't available",
description: "Use Take snapshot from the command palette to choose a window.",
},
error: {
title: "Couldn't set up the extension",
description: "Check T3 Code SnapShots in GNOME Extensions, then try again.",
},
};
function ScreenRecordingIcon() {
const gradientId = useId();
return (
);
}
function AccessibilityPermissionIcon() {
const gradientId = useId();
return (
);
}
function MacPermissionRow({
icon,
title,
description,
granted,
busy,
onAllow,
}: {
icon: ReactNode;
title: string;
description: string;
granted: boolean;
busy: boolean;
onAllow: () => void;
}) {
return (
{icon}
{title}
{description}
{granted ? (
Allowed
) : (
)}
);
}
export function SnapShotSetupDialog({
state,
initialStep,
wasEnabled,
includeAccessibility,
busy: actionBusy,
error,
shortcutInput,
shortcutStatus,
shortcutChanged,
canSaveShortcut,
onSaveShortcut,
onEnable,
onAction,
onRefresh,
onClose,
onLeaveStep,
}: {
state: DesktopSnapShotState;
initialStep: CaptureSetupStep;
wasEnabled: boolean;
includeAccessibility: boolean;
busy: boolean;
error: string | null;
shortcutInput: ReactNode;
shortcutStatus: string | null | undefined;
shortcutChanged: boolean;
canSaveShortcut: boolean;
onSaveShortcut: () => Promise;
onEnable: () => Promise;
onAction: (action: DesktopSnapShotSetupAction) => Promise;
onRefresh: () => Promise;
onClose: (completed: boolean) => Promise;
onLeaveStep: () => void;
}) {
const [step, setStep] = useState(() => captureSetupInitialStep(state, initialStep));
const [checking, setChecking] = useState(false);
const [checked, setChecked] = useState(false);
const [configBusy, setConfigBusy] = useState(false);
const busy = actionBusy || checking || configBusy;
const backend = captureSetupBackend(state);
const configShortcut = backend === "niri" || backend === "hyprland";
const desktop = captureSetupDesktopName(state);
const extension = state.gnomeExtension;
const helper = backend === "hyprland" ? state.hyprlandHelper : state.kdeHelper;
const helperBackend = backend === "kde" || backend === "hyprland";
const installHelper = backend === "hyprland" ? "install-hyprland-helper" : "install-kde-helper";
const removeHelper = backend === "hyprland" ? "remove-hyprland-helper" : "remove-kde-helper";
const accessReady = captureSetupAccessReady(state);
const macPermissions = state.macPermissions;
const macPermissionsReady = captureSetupMacPermissionsReady(state, includeAccessibility);
const shortcutReady = captureSetupShortcutReady(state, shortcutChanged);
const install = extension?.status === "not-installed" || extension?.status === "update-required";
const enable = extension?.status === "disabled";
useEffect(() => {
if (!macPermissions || step !== "access") return;
const refresh = () => void onRefresh();
window.addEventListener("focus", refresh);
return () => window.removeEventListener("focus", refresh);
}, [macPermissions, onRefresh, step]);
const changeStep = (next: CaptureSetupStep) => {
onLeaveStep();
setChecked(false);
setStep(next);
};
const checkAgain = async () => {
if (busy) return;
setChecking(true);
setChecked(false);
try {
setChecked((await onRefresh()) !== undefined);
} finally {
setChecking(false);
}
};
const accessCopy =
state.message && !macPermissions
? {
title: "Let's try that again",
description: "Couldn't check snapshots. Try again to continue.",
}
: backend === "gnome" && extension
? extension.status === "enabled" && !accessReady
? {
title: "Check capture access",
description: "The extension isn't ready yet. Try again in a moment.",
}
: GNOME_ACCESS_COPY[extension.status]
: helperBackend
? helper?.status === "ready"
? {
title: "Capture is ready",
description: "Next, choose your shortcut.",
}
: helper?.status === "error"
? {
title: "Let's fix capture access",
description: "Try reinstalling the capture helper, then check again.",
}
: {
title:
helper?.status === "update-required"
? "Update the capture helper"
: "Allow snapshots",
description:
"T3 Code's capture helper lets you capture other apps and return to your draft. It's included with T3 Code.",
}
: backend === "niri"
? {
title: "Capture is ready",
description: "Next, choose your shortcut.",
}
: backend === "picker"
? {
title: "Choose a window each time",
description:
"Your desktop doesn't support automatic capture. You'll choose the window to capture instead.",
}
: {
title: "Allow snapshots",
description:
backend === "portal"
? "Your desktop may ask for permission when you first capture."
: macPermissions
? macPermissionsReady
? "Test a snapshot of the current window. If macOS asks to bypass its window picker, choose Allow. The test image is discarded."
: "Allow each permission, then continue."
: "Allow access when prompted to start capturing windows.",
};
const title = step === "access" ? accessCopy.title : "Choose your shortcut";
const description =
step === "access"
? accessCopy.description
: configShortcut
? "Click the shortcut, then press the keys you want."
: state.mode === "portal"
? "Choose your keys, then approve the permission prompt if asked."
: "Use both Shift keys, or record a different shortcut.";
const stepIndex = SETUP_STEPS.findIndex(({ id }) => id === step);
const details = [
...new Set(
[
error,
...(step === "access"
? [
state.message,
backend === "gnome" &&
(extension?.status === "error" || extension?.status === "unsupported")
? extension.message
: null,
helperBackend && helper?.status === "error" ? helper.message : null,
]
: []),
].filter((detail) => detail !== null),
),
];
return (
);
}