1import { useState } from "react";
2
3import { isLocalEnvironmentDisabled } from "../../localEnvironment";
4import {
5 AlertDialog,
6 AlertDialogClose,
7 AlertDialogDescription,
8 AlertDialogFooter,
9 AlertDialogHeader,
10 AlertDialogPopup,
11 AlertDialogTitle,
12} from "../ui/alert-dialog";
13import { Button } from "../ui/button";
14import { Spinner } from "../ui/spinner";
15import { Switch } from "../ui/switch";
16import { SettingsRow } from "./settingsLayout";
17import { searchableSetting } from "./settingsSearch";
18
19// Toggling relaunches the desktop app, so the switch only reflects the value
20// this process started with; there is no live state to keep in sync.
21export function LocalEnvironmentSetting() {
22 const setEnabled = window.desktopBridge?.setLocalEnvironmentEnabled;
23 const [enabled] = useState(() => !isLocalEnvironmentDisabled());
24 const [confirmOpen, setConfirmOpen] = useState(false);
25 const [isUpdating, setIsUpdating] = useState(false);
26 const [error, setError] = useState<string | null>(null);
27 if (!setEnabled) return null;
28
29 const applyChange = async () => {
30 setIsUpdating(true);
31 setError(null);
32 try {
33 await setEnabled(!enabled);
34 } catch (cause) {
35 setError(cause instanceof Error ? cause.message : "Couldn't change this setting.");
36 setIsUpdating(false);
37 }
38 };
39
40 return (
41 <>
42 <SettingsRow
43 {...searchableSetting("local-environment")}
44 description={
45 enabled
46 ? "Run agents on this computer. Turn off to use The Knife only with remote environments."
47 : "Turned off. Agents only run in remote environments."
48 }
49 control={
50 <Switch
51 checked={enabled}
52 disabled={isUpdating}
53 onCheckedChange={() => setConfirmOpen(true)}
54 aria-label="Local environment"
55 />
56 }
57 />
58 <AlertDialog
59 open={confirmOpen}
60 onOpenChange={(open) => {
61 if (isUpdating) return;
62 setConfirmOpen(open);
63 if (!open) setError(null);
64 }}
65 >
66 <AlertDialogPopup>
67 <AlertDialogHeader>
68 <AlertDialogTitle>
69 {enabled ? "Turn off local environment?" : "Turn on local environment?"}
70 </AlertDialogTitle>
71 <AlertDialogDescription>
72 {enabled
73 ? "The Knife will restart without running a server on this computer. Any agents and terminals running here will stop, and other devices will no longer be able to connect to this computer. Your projects, history, and remote environments are unaffected."
74 : "The Knife will restart and start running a server on this computer again."}
75 </AlertDialogDescription>
76 </AlertDialogHeader>
77 {error ? <p className="px-6 pb-4 text-sm text-destructive">{error}</p> : null}
78 <AlertDialogFooter>
79 <AlertDialogClose disabled={isUpdating} render={<Button variant="outline" />}>
80 Cancel
81 </AlertDialogClose>
82 <Button
83 variant={enabled ? "destructive" : "default"}
84 disabled={isUpdating}
85 onClick={() => void applyChange()}
86 >
87 {isUpdating ? (
88 <>
89 <Spinner size="sm" />
90 Restarting…
91 </>
92 ) : enabled ? (
93 "Restart and turn off"
94 ) : (
95 "Restart and turn on"
96 )}
97 </Button>
98 </AlertDialogFooter>
99 </AlertDialogPopup>
100 </AlertDialog>
101 </>
102 );
103}