/** * @file AutostartSettings — Settings → About's "Start automatically" switch: * whether the dashboard starts at boot/login through the per-user service * (systemd `--user` unit, or a LaunchAgent on macOS), over * `GET/PUT /api/settings/autostart`. Off by default; a change never starts or * stops the running server. Beside it, **Quit AgentHub** (issue #65, 9router's * tray Quit): `POST /api/settings/quit` closes every workspace terminal and * stops the server, which stays stopped until `agenthub start` — or the next * boot/login while the switch is on. Between them, the status-area icon switch * (`GET/PUT /api/settings/tray`, on by default — server/lib/tray.js). Last, * **Uninstall AgentHub**: `POST /api/settings/uninstall` removes AgentHub from * the machine, database included (scripts/uninstall.js). * @author Nguyễn Ngọc Trí Vĩ */ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { api, type AutostartStatus, type TrayStatus } from "../../lib/api"; import { toast } from "../../lib/toast"; import { confirmDialog } from "../../lib/confirm"; import { SettingsRow, SettingsSection, SettingsSwitchRow } from "./SettingsRow"; export function AutostartSettings() { const { t } = useTranslation("settings"); const [status, setStatus] = useState(null); const [saving, setSaving] = useState(false); useEffect(() => { api.settings.autostart .get() .then(setStatus) .catch(() => {}); }, []); const change = async (enabled: boolean) => { setSaving(true); try { setStatus(await api.settings.autostart.set(enabled)); } catch (err) { toast.error(err instanceof Error ? err.message : String(err)); } finally { setSaving(false); } }; const [tray, setTray] = useState(null); useEffect(() => { api.settings.tray .get() .then(setTray) .catch(() => {}); }, []); const changeTray = async (enabled: boolean) => { try { setTray(await api.settings.tray.set(enabled)); } catch (err) { toast.error(err instanceof Error ? err.message : String(err)); } }; const [quitting, setQuitting] = useState(false); const quit = async () => { const ok = await confirmDialog({ title: t("quit.confirmTitle"), message: t(status?.enabled ? "quit.confirmMessageAutostart" : "quit.confirmMessage"), confirmLabel: t("quit.button"), }); if (!ok) return; setQuitting(true); try { await api.settings.quit(); toast.success(t("quit.done")); } catch (err) { setQuitting(false); toast.error(err instanceof Error ? err.message : String(err)); } }; const [uninstalling, setUninstalling] = useState(false); const uninstall = async () => { const ok = await confirmDialog({ title: t("uninstall.confirmTitle"), message: t("uninstall.confirmMessage"), confirmLabel: t("uninstall.button"), }); if (!ok) return; setUninstalling(true); try { const { log } = await api.settings.uninstall(); toast.success(t("uninstall.done", { log })); } catch (err) { setUninstalling(false); toast.error(err instanceof Error ? err.message : String(err)); } }; const note = !status ? undefined : !status.available ? t("autostart.unavailable") : status.foreign ? t("autostart.foreign") : t(`autostart.desc_${status.kind}`); return ( void change(v)} /> void changeTray(v)} /> void quit()} className="btn-ghost text-xs text-status-danger disabled:opacity-40" > {t("quit.button")} } /> void uninstall()} className="btn-ghost text-xs text-status-danger disabled:opacity-40" > {t("uninstall.button")} } /> ); }