From 5b47b6594fea80dcbb5c4c34a2d4146461d06abd Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 26 Apr 2026 08:15:28 +0700 Subject: [PATCH] refactor: replace settings buttons with switches and simplify provider sheet Swap daemon management and keep-running-after-quit buttons for Switch toggles. Remove the restart/start daemon control. Simplify the provider diagnostic sheet to reuse shared settings styles and SettingsSection. Drop the unused "sm" size variant from the Switch component. Remove trailing periods from hint text for consistency. --- .../components/provider-diagnostic-sheet.tsx | 344 +++++++----------- packages/app/src/components/ui/switch.tsx | 13 +- .../desktop-updates-section.test.tsx | 61 +++- .../components/desktop-updates-section.tsx | 240 +----------- .../components/integrations-section.tsx | 6 +- .../app/src/screens/settings/host-page.tsx | 20 +- .../screens/settings/providers-section.tsx | 1 - 7 files changed, 204 insertions(+), 481 deletions(-) diff --git a/packages/app/src/components/provider-diagnostic-sheet.tsx b/packages/app/src/components/provider-diagnostic-sheet.tsx index 6aefebf19..bb2c2b411 100644 --- a/packages/app/src/components/provider-diagnostic-sheet.tsx +++ b/packages/app/src/components/provider-diagnostic-sheet.tsx @@ -1,4 +1,4 @@ -import { AlertCircle, Plus, RotateCw, Search, Trash2 } from "lucide-react-native"; +import { AlertCircle, RotateCw, Search, Trash2 } from "lucide-react-native"; import { useCallback, useEffect, useMemo, useState } from "react"; import { ActivityIndicator, @@ -10,12 +10,15 @@ import { } from "react-native"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { AdaptiveModalSheet, AdaptiveTextInput } from "@/components/adaptive-modal-sheet"; +import { Button } from "@/components/ui/button"; import { LoadingSpinner } from "@/components/ui/loading-spinner"; import { isWeb } from "@/constants/platform"; import { Fonts } from "@/constants/theme"; import { useDaemonConfig } from "@/hooks/use-daemon-config"; import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot"; import { useHostRuntimeClient } from "@/runtime/host-runtime"; +import { SettingsSection } from "@/screens/settings/settings-section"; +import { settingsStyles } from "@/styles/settings"; import { resolveProviderLabel } from "@/utils/provider-definitions"; import { formatTimeAgo } from "@/utils/time"; import type { AgentModelDefinition, AgentProvider } from "@server/server/agent/agent-sdk-types"; @@ -28,35 +31,28 @@ interface ProviderDiagnosticSheetProps { serverId: string; } -function ModelRow({ model, isFirst }: { model: AgentModelDefinition; isFirst: boolean }) { - const rowStyle = useMemo( - () => [sheetStyles.modelRow, !isFirst && sheetStyles.modelRowBorder], - [isFirst], - ); +function ModelRow({ model }: { model: AgentModelDefinition }) { return ( - - - {model.label} - - - {model.id} - + + + + {model.label} + + + {model.id} + + ); } -function AdditionalModelRow(props: { +function CustomModelRow(props: { model: ProviderProfileModel; - isFirst: boolean; deleting: boolean; onDelete: (modelId: string) => void; }) { const { theme } = useUnistyles(); - const { model, isFirst, deleting, onDelete } = props; - const rowStyle = useMemo( - () => [sheetStyles.additionalModelRow, !isFirst && sheetStyles.modelRowBorder], - [isFirst], - ); + const { model, deleting, onDelete } = props; const handleDelete = useCallback(() => onDelete(model.id), [model.id, onDelete]); const deleteButtonStyle = useCallback( ({ hovered, pressed }: PressableStateCallbackType & { hovered?: boolean }) => [ @@ -68,12 +64,12 @@ function AdditionalModelRow(props: { ); return ( - - - + + + {model.label} - + {model.id} @@ -91,7 +87,7 @@ function AdditionalModelRow(props: { ); } -function AdditionalModelsEditor(props: { +function CustomModelsSection(props: { provider: string; serverId: string; refresh: (providers?: AgentProvider[]) => Promise; @@ -162,20 +158,10 @@ function AdditionalModelsEditor(props: { [additionalModels, patchAdditionalModels], ); - const addButtonStyle = useCallback( - ({ hovered, pressed }: PressableStateCallbackType & { hovered?: boolean }) => [ - sheetStyles.addModelButton, - (Boolean(hovered) || pressed) && sheetStyles.addModelButtonHovered, - !canAdd || saving ? sheetStyles.disabled : null, - ], - [canAdd, saving], - ); - return ( - - Additional Models - - + + + + - - {saving ? ( - - ) : ( - - )} - + {additionalModels.map((model) => ( + + ))} {error ? {error} : null} - {additionalModels.length > 0 ? ( - - {additionalModels.map((model, index) => ( - - ))} - - ) : null} - + ); } @@ -251,7 +228,7 @@ function DiagnosticCodeBlock(props: { } return ( - No diagnostic available. + No diagnostic available ); } @@ -288,7 +265,6 @@ export function ProviderDiagnosticSheet({ }, [visible]); const fetchedAtLabel = useMemo(() => { if (!providerEntry?.fetchedAt) return null; - // clockTick is referenced so the label recomputes each timer tick. void clockTick; return formatTimeAgo(new Date(providerEntry.fetchedAt)); }, [providerEntry?.fetchedAt, clockTick]); @@ -375,10 +351,27 @@ export function ProviderDiagnosticSheet({ } }, [visible, fetchDiagnostic]); + const modelsTrailing = useMemo(() => { + if (models.length === 0 && !fetchedAtLabel) return undefined; + return ( + + {models.length > 0 ? ( + {models.length} + ) : null} + {models.length > 0 && fetchedAtLabel ? ( + · + ) : null} + {fetchedAtLabel ? ( + Updated {fetchedAtLabel} + ) : null} + + ); + }, [models.length, fetchedAtLabel]); + function renderModelsBody() { if (models.length === 0 && providerSnapshotRefreshing) { return ( - + Loading models… @@ -386,7 +379,7 @@ export function ProviderDiagnosticSheet({ } if (models.length === 0 && providerErrorMessage) { return ( - + {providerErrorMessage} @@ -394,21 +387,21 @@ export function ProviderDiagnosticSheet({ } if (models.length === 0) { return ( - - No models detected. + + No models detected ); } if (filteredModels.length === 0) { return ( - + No models match your search ); } - return filteredModels.map((model: AgentModelDefinition, index) => ( - + return filteredModels.map((model: AgentModelDefinition) => ( + )); } @@ -421,35 +414,26 @@ export function ProviderDiagnosticSheet({ scrollable={false} headerActions={headerActions} > - - Diagnostic - + + - + + + - - - Models - - {models.length} - {fetchedAtLabel ? ( - <> - · - Updated {fetchedAtLabel} - - ) : null} - + Models + {modelsTrailing} - {models.length > 0 ? ( - - + + + - ) : null} - - {renderModelsBody()} - + + {renderModelsBody()} + + ); } const sheetStyles = StyleSheet.create((theme) => ({ - section: { - gap: theme.spacing[2], - }, - sectionTitle: { - color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.xs, - fontWeight: theme.fontWeight.normal, - }, mutedText: { fontSize: theme.fontSize.sm, color: theme.colors.foregroundMuted, }, + monoHint: { + fontFamily: Fonts.mono, + fontSize: theme.fontSize.xs, + color: theme.colors.foregroundMuted, + marginTop: theme.spacing[1], + }, errorText: { fontSize: theme.fontSize.xs, color: theme.colors.destructive, + marginTop: theme.spacing[2], + marginLeft: theme.spacing[1], }, iconButton: { width: 30, @@ -505,20 +489,24 @@ const sheetStyles = StyleSheet.create((theme) => ({ disabled: { opacity: 0.5, }, - codeBlock: { - borderWidth: 1, - borderColor: theme.colors.border, - borderRadius: theme.borderRadius.base, - backgroundColor: theme.colors.surface2, - overflow: "hidden", - maxHeight: 180, + inlineRow: { + flexDirection: "row", + alignItems: "center", + gap: theme.spacing[2], + }, + inlineInput: { + flex: 1, + minWidth: 0, + paddingVertical: 0, + color: theme.colors.foreground, + fontSize: theme.fontSize.sm, }, codeScroll: { - maxHeight: 180, + maxHeight: 200, }, codeContent: { paddingVertical: theme.spacing[3], - paddingHorizontal: theme.spacing[3], + paddingHorizontal: theme.spacing[4], }, codeText: { fontFamily: Fonts.mono, @@ -528,7 +516,7 @@ const sheetStyles = StyleSheet.create((theme) => ({ }, codeBlockLoading: { paddingVertical: theme.spacing[4], - paddingHorizontal: theme.spacing[3], + paddingHorizontal: theme.spacing[4], flexDirection: "row", alignItems: "center", gap: theme.spacing[2], @@ -536,118 +524,46 @@ const sheetStyles = StyleSheet.create((theme) => ({ modelsSection: { flex: 1, minHeight: 0, - gap: theme.spacing[2], }, modelsHeader: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", + gap: theme.spacing[2], + marginBottom: theme.spacing[3], + marginLeft: theme.spacing[1], }, - modelsHeaderMeta: { + flexCard: { + flex: 1, + minHeight: 0, + }, + modelsTrailing: { flexDirection: "row", alignItems: "center", gap: theme.spacing[1], }, - metaDot: { - fontSize: theme.fontSize.xs, - color: theme.colors.foregroundMuted, - }, - countText: { - fontSize: theme.fontSize.xs, - color: theme.colors.foregroundMuted, - }, - searchContainer: { - flexDirection: "row", - alignItems: "center", - gap: theme.spacing[2], - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.md, - paddingHorizontal: theme.spacing[3], - }, - addModelRow: { - flexDirection: "row", - alignItems: "center", - gap: theme.spacing[2], - }, - addModelInputContainer: { - flex: 1, - minWidth: 0, - backgroundColor: theme.colors.surface2, - borderRadius: theme.borderRadius.md, - paddingHorizontal: theme.spacing[3], - }, - addModelInput: { - paddingVertical: theme.spacing[2], - color: theme.colors.foreground, - fontSize: theme.fontSize.sm, - }, - addModelButton: { - width: 34, - height: 34, - borderRadius: theme.borderRadius.full, - alignItems: "center", - justifyContent: "center", - backgroundColor: theme.colors.accent, - }, - addModelButtonHovered: { - backgroundColor: theme.colors.accentBright, - }, - additionalModelsList: { - borderTopWidth: 1, - borderTopColor: theme.colors.border, - }, - additionalModelRow: { - minHeight: 48, - paddingVertical: theme.spacing[2], - flexDirection: "row", - alignItems: "center", - justifyContent: "space-between", - gap: theme.spacing[2], - }, - additionalModelText: { - flex: 1, - minWidth: 0, - }, - searchInput: { - flex: 1, - paddingVertical: theme.spacing[2], - color: theme.colors.foreground, - fontSize: theme.fontSize.sm, - }, modelsScroll: { flex: 1, minHeight: 0, }, modelsScrollContent: { - paddingBottom: theme.spacing[2], + paddingBottom: 0, }, - modelRow: { - paddingVertical: theme.spacing[3], - }, - modelRowBorder: { + emptyRow: { + paddingVertical: theme.spacing[6], + paddingHorizontal: theme.spacing[4], + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + gap: theme.spacing[2], borderTopWidth: 1, borderTopColor: theme.colors.border, }, - modelLabel: { - fontSize: theme.fontSize.sm, - color: theme.colors.foreground, - }, - modelId: { - fontSize: theme.fontSize.xs, - color: theme.colors.foregroundMuted, - fontFamily: Fonts.mono, - marginTop: 2, - }, - emptyState: { - paddingVertical: theme.spacing[6], - alignItems: "center", - gap: theme.spacing[2], - }, })); const DIAGNOSTIC_SHEET_SNAP_POINTS = ["50%", "85%"]; -const DIAGNOSTIC_SEARCH_INPUT_STYLE = [sheetStyles.searchInput, isWeb && { outlineStyle: "none" }]; -const DIAGNOSTIC_ADD_MODEL_INPUT_STYLE = [ - sheetStyles.addModelInput, - isWeb && { outlineStyle: "none" }, -]; +const DIAGNOSTIC_SEARCH_INPUT_STYLE = [sheetStyles.inlineInput, isWeb && { outlineStyle: "none" }]; +const DIAGNOSTIC_INLINE_INPUT_STYLE = [sheetStyles.inlineInput, isWeb && { outlineStyle: "none" }]; +const MODEL_ROW_STYLE = [settingsStyles.row, settingsStyles.rowBorder]; +const INLINE_ROW_STYLE = [settingsStyles.row, sheetStyles.inlineRow]; +const MODELS_CARD_STYLE = [settingsStyles.card, sheetStyles.flexCard]; diff --git a/packages/app/src/components/ui/switch.tsx b/packages/app/src/components/ui/switch.tsx index 79ee066dd..66e0738d2 100644 --- a/packages/app/src/components/ui/switch.tsx +++ b/packages/app/src/components/ui/switch.tsx @@ -14,22 +14,17 @@ import Animated, { } from "react-native-reanimated"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; -type SwitchSize = "sm" | "md"; - interface SwitchProps { value: boolean; onValueChange?: (value: boolean) => void; disabled?: boolean; - size?: SwitchSize; accessibilityLabel?: string; testID?: string; style?: StyleProp; } -const SIZES: Record = { - md: { track: { width: 44, height: 26 }, thumb: 22 }, - sm: { track: { width: 34, height: 20 }, thumb: 16 }, -}; +const TRACK = { width: 34, height: 20 }; +const THUMB = 16; const TIMING = { duration: 180, easing: Easing.inOut(Easing.ease) }; @@ -37,13 +32,13 @@ export function Switch({ value, onValueChange, disabled = false, - size = "md", accessibilityLabel, testID, style, }: SwitchProps) { const { theme } = useUnistyles(); - const { track, thumb } = SIZES[size]; + const track = TRACK; + const thumb = THUMB; const padding = (track.height - thumb) / 2; const thumbTravel = track.width - thumb - padding * 2; diff --git a/packages/app/src/desktop/components/desktop-updates-section.test.tsx b/packages/app/src/desktop/components/desktop-updates-section.test.tsx index 1d2c1fe4b..12830a72e 100644 --- a/packages/app/src/desktop/components/desktop-updates-section.test.tsx +++ b/packages/app/src/desktop/components/desktop-updates-section.test.tsx @@ -98,9 +98,6 @@ vi.mock("lucide-react-native", () => { ArrowUpRight: icon("ArrowUpRight"), Copy: icon("Copy"), FileText: icon("FileText"), - Pause: icon("Pause"), - Play: icon("Play"), - RotateCw: icon("RotateCw"), }; }); @@ -163,6 +160,28 @@ vi.mock("@/components/ui/button", () => ({ ), })); +vi.mock("@/components/ui/switch", () => ({ + Switch: ({ + value, + onValueChange, + disabled, + accessibilityLabel, + }: { + value: boolean; + onValueChange?: (next: boolean) => void; + disabled?: boolean; + accessibilityLabel?: string; + }) => + React.createElement("button", { + type: "button", + role: "switch", + "aria-label": accessibilityLabel, + "aria-checked": value, + disabled, + onClick: () => onValueChange?.(!value), + }), +})); + vi.mock("@/desktop/settings/desktop-settings", () => ({ useDesktopSettings: () => ({ ...settingsState, @@ -189,16 +208,12 @@ vi.mock("@/desktop/updates/desktop-updates", () => ({ const daemonCommandMocks = vi.hoisted(() => ({ getCliDaemonStatusMock: vi.fn(), - restartDesktopDaemonMock: vi.fn(), - startDesktopDaemonMock: vi.fn(), stopDesktopDaemonMock: vi.fn(), })); vi.mock("@/desktop/daemon/desktop-daemon", () => ({ getCliDaemonStatus: daemonCommandMocks.getCliDaemonStatusMock, - restartDesktopDaemon: daemonCommandMocks.restartDesktopDaemonMock, shouldUseDesktopDaemon: vi.fn(() => true), - startDesktopDaemon: daemonCommandMocks.startDesktopDaemonMock, stopDesktopDaemon: daemonCommandMocks.stopDesktopDaemonMock, })); @@ -221,32 +236,40 @@ describe("LocalDaemonSection", () => { daemonStatusState.data.status.status = "running"; daemonStatusState.setStatus.mockReset(); daemonStatusState.refetch.mockReset(); - daemonCommandMocks.startDesktopDaemonMock.mockReset(); daemonCommandMocks.stopDesktopDaemonMock.mockReset(); daemonCommandMocks.stopDesktopDaemonMock.mockResolvedValue({ ...daemonStatusState.data.status, status: "stopped", }); - daemonCommandMocks.restartDesktopDaemonMock.mockReset(); daemonCommandMocks.getCliDaemonStatusMock.mockReset(); }); - it("shows the keep-running-after-quit control enabled by default", () => { + it("renders the daemon toggles as switches reflecting current settings", () => { const screen = render(); expect(screen.getByText("Keep daemon running after quit")).toBeTruthy(); - expect( - screen.getByText( - "Enabled. The built-in daemon keeps running after you close the desktop app.", - ), - ).toBeTruthy(); - expect(screen.getByRole("button", { name: "Disable" })).toBeTruthy(); + expect(screen.getByText("Manage built-in daemon")).toBeTruthy(); + + const keepRunningSwitch = screen.getByRole("switch", { + name: "Keep daemon running after quit", + }); + expect(keepRunningSwitch.getAttribute("aria-checked")).toBe("true"); + + const manageSwitch = screen.getByRole("switch", { name: "Manage built-in daemon" }); + expect(manageSwitch.getAttribute("aria-checked")).toBe("true"); + }); + + it("does not render a duplicate restart-daemon control", () => { + const screen = render(); + + expect(screen.queryByText("Restart daemon")).toBeNull(); + expect(screen.queryByText("Start daemon")).toBeNull(); }); it("updates keep-running-after-quit without changing daemon lifecycle", async () => { const screen = render(); - fireEvent.click(screen.getByRole("button", { name: "Disable" })); + fireEvent.click(screen.getByRole("switch", { name: "Keep daemon running after quit" })); await waitFor(() => { expect(settingsState.updateSettings).toHaveBeenCalledWith({ @@ -256,16 +279,14 @@ describe("LocalDaemonSection", () => { }); }); expect(confirmDialogMock).not.toHaveBeenCalled(); - expect(daemonCommandMocks.startDesktopDaemonMock).not.toHaveBeenCalled(); expect(daemonCommandMocks.stopDesktopDaemonMock).not.toHaveBeenCalled(); - expect(daemonCommandMocks.restartDesktopDaemonMock).not.toHaveBeenCalled(); }); it("pauses built-in daemon management and persists the setting through desktop settings", async () => { confirmDialogMock.mockResolvedValue(true); const screen = render(); - fireEvent.click(screen.getByRole("button", { name: "Pause" })); + fireEvent.click(screen.getByRole("switch", { name: "Manage built-in daemon" })); await waitFor(() => { expect(daemonCommandMocks.stopDesktopDaemonMock).toHaveBeenCalledTimes(1); diff --git a/packages/app/src/desktop/components/desktop-updates-section.tsx b/packages/app/src/desktop/components/desktop-updates-section.tsx index 7a08ae0a1..5991afc97 100644 --- a/packages/app/src/desktop/components/desktop-updates-section.tsx +++ b/packages/app/src/desktop/components/desktop-updates-section.tsx @@ -4,17 +4,16 @@ import * as Clipboard from "expo-clipboard"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { settingsStyles } from "@/styles/settings"; import { SettingsSection } from "@/screens/settings/settings-section"; -import { ArrowUpRight, Play, Pause, RotateCw, Copy, FileText, Activity } from "lucide-react-native"; +import { ArrowUpRight, Copy, FileText, Activity } from "lucide-react-native"; import { AdaptiveModalSheet } from "@/components/adaptive-modal-sheet"; import { Button } from "@/components/ui/button"; +import { Switch } from "@/components/ui/switch"; import { confirmDialog } from "@/utils/confirm-dialog"; import { openExternalUrl } from "@/utils/open-external-url"; import { isVersionMismatch } from "@/desktop/updates/desktop-updates"; import { getCliDaemonStatus, - restartDesktopDaemon, shouldUseDesktopDaemon, - startDesktopDaemon, stopDesktopDaemon, } from "@/desktop/daemon/desktop-daemon"; import { useDaemonStatus } from "@/desktop/hooks/use-daemon-status"; @@ -24,101 +23,14 @@ import { resolveAppVersion } from "@/utils/app-version"; type DesktopDaemonSettings = DesktopSettings["daemon"]; -function managementLabel(isUpdating: boolean, isPaused: boolean): string { - if (isUpdating) return isPaused ? "Resuming..." : "Pausing..."; - return isPaused ? "Resume" : "Pause"; -} - -function keepRunningLabel(isUpdating: boolean, isEnabled: boolean): string { - if (isUpdating) return isEnabled ? "Disabling..." : "Enabling..."; - return isEnabled ? "Disable" : "Enable"; -} - -function restartLabel(isRestarting: boolean, isRunning: boolean, idleLabel: string): string { - if (!isRestarting) return idleLabel; - return isRunning ? "Restarting..." : "Starting..."; -} - -function useDaemonRestartAction(args: { - showSection: boolean; - daemonStatus: DesktopDaemonStatus | null; - daemonActionLabel: string; - setStatus: (status: DesktopDaemonStatus) => void; - refetch: () => void; -}) { - const { showSection, daemonStatus, daemonActionLabel, setStatus, refetch } = args; - const [isRestartingDaemon, setIsRestartingDaemon] = useState(false); - const [statusMessage, setStatusMessage] = useState(null); - - const handleUpdateLocalDaemon = useCallback(() => { - if (!showSection || isRestartingDaemon) { - return; - } - - void confirmDialog({ - title: daemonActionLabel, - message: - daemonStatus?.status === "running" - ? "This will restart the built-in daemon. The app will reconnect automatically." - : "This will start the built-in daemon.", - confirmLabel: daemonActionLabel, - cancelLabel: "Cancel", - }) - .then((confirmed) => { - if (!confirmed) { - return; - } - - setIsRestartingDaemon(true); - setStatusMessage(null); - - const action = - daemonStatus?.status === "running" ? restartDesktopDaemon : startDesktopDaemon; - - void action() - .then((newStatus) => { - setStatus(newStatus); - setStatusMessage( - daemonStatus?.status === "running" ? "Daemon restarted." : "Daemon started.", - ); - refetch(); - return; - }) - .catch((error) => { - console.error("[Settings] Failed to change desktop daemon state", error); - const message = error instanceof Error ? error.message : String(error); - setStatusMessage(`${daemonActionLabel} failed: ${message}`); - }) - .finally(() => { - setIsRestartingDaemon(false); - }); - return; - }) - .catch((error) => { - console.error("[Settings] Failed to open desktop daemon action confirmation", error); - Alert.alert("Error", "Unable to open the daemon confirmation dialog."); - }); - }, [ - daemonActionLabel, - daemonStatus?.status, - isRestartingDaemon, - refetch, - setStatus, - showSection, - ]); - - return { isRestartingDaemon, statusMessage, setStatusMessage, handleUpdateLocalDaemon }; -} - function useDaemonManagementToggle(args: { daemonStatus: DesktopDaemonStatus | null; settings: DesktopDaemonSettings; updateSettings: (next: Partial) => Promise; setStatus: (status: DesktopDaemonStatus) => void; - setStatusMessage: (message: string | null) => void; refetch: () => void; }) { - const { daemonStatus, settings, updateSettings, setStatus, setStatusMessage, refetch } = args; + const { daemonStatus, settings, updateSettings, setStatus, refetch } = args; const [isUpdatingDaemonManagement, setIsUpdatingDaemonManagement] = useState(false); const handleToggleDaemonManagement = useCallback(() => { @@ -128,12 +40,7 @@ function useDaemonManagementToggle(args: { if (!settings.manageBuiltInDaemon) { setIsUpdatingDaemonManagement(true); - setStatusMessage(null); void updateSettings({ manageBuiltInDaemon: true }) - .then(() => { - setStatusMessage("Built-in daemon management resumed."); - return; - }) .catch((error) => { console.error("[Settings] Failed to update built-in daemon management", error); Alert.alert("Error", "Unable to update built-in daemon management."); @@ -158,7 +65,6 @@ function useDaemonManagementToggle(args: { } setIsUpdatingDaemonManagement(true); - setStatusMessage(null); const stopPromise = daemonStatus?.status === "running" @@ -174,7 +80,6 @@ function useDaemonManagementToggle(args: { }) .then(() => { refetch(); - setStatusMessage("Built-in daemon paused and stopped."); return; }) .catch((error) => { @@ -195,7 +100,6 @@ function useDaemonManagementToggle(args: { isUpdatingDaemonManagement, refetch, setStatus, - setStatusMessage, settings.manageBuiltInDaemon, updateSettings, ]); @@ -318,7 +222,7 @@ function DaemonLogsModal({ visible, onClose, daemonLogs }: DaemonLogsModalProps) snapPoints={LOGS_MODAL_SNAP_POINTS} > - {daemonLogs?.logPath ?? "Log path unavailable."} + {daemonLogs?.logPath ?? "Log path unavailable"} {daemonLogs?.contents?.length ? daemonLogs.contents : "(log file is empty)"} @@ -369,9 +273,6 @@ interface DaemonInfoCardProps { daemonStatusStateText: string; daemonStatusDetailText: string; isDaemonManagementPaused: boolean; - playIcon: ReactElement; - pauseIcon: ReactElement; - rotateIcon: ReactElement; copyIcon: ReactElement; fileTextIcon: ReactElement; activityIcon: ReactElement; @@ -380,12 +281,6 @@ interface DaemonInfoCardProps { keepRunningAfterQuit: boolean; handleToggleKeepRunningAfterQuit: () => void; isUpdatingKeepRunningAfterQuit: boolean; - daemonActionLabel: string; - daemonActionMessage: string; - statusMessage: string | null; - handleUpdateLocalDaemon: () => void; - isRestartingDaemon: boolean; - daemonStatus: DesktopDaemonStatus | null; daemonLogs: { logPath?: string } | null; handleCopyLogPath: () => void; handleOpenLogs: () => void; @@ -398,9 +293,6 @@ function DaemonInfoCard(props: DaemonInfoCardProps) { daemonStatusStateText, daemonStatusDetailText, isDaemonManagementPaused, - playIcon, - pauseIcon, - rotateIcon, copyIcon, fileTextIcon, activityIcon, @@ -409,12 +301,6 @@ function DaemonInfoCard(props: DaemonInfoCardProps) { keepRunningAfterQuit, handleToggleKeepRunningAfterQuit, isUpdatingKeepRunningAfterQuit, - daemonActionLabel, - daemonActionMessage, - statusMessage, - handleUpdateLocalDaemon, - isRestartingDaemon, - daemonStatus, daemonLogs, handleCopyLogPath, handleOpenLogs, @@ -422,28 +308,12 @@ function DaemonInfoCard(props: DaemonInfoCardProps) { isLoadingCliStatus, } = props; - const managementButtonLabel = managementLabel( - isUpdatingDaemonManagement, - isDaemonManagementPaused, - ); - const keepRunningButtonLabel = keepRunningLabel( - isUpdatingKeepRunningAfterQuit, - keepRunningAfterQuit, - ); - const restartButtonLabel = restartLabel( - isRestartingDaemon, - daemonStatus?.status === "running", - daemonActionLabel, - ); - return ( Status - - Only the built-in desktop daemon is shown here. - + Only the built-in desktop daemon is shown here {daemonStatusStateText} @@ -452,62 +322,33 @@ function DaemonInfoCard(props: DaemonInfoCardProps) { - Daemon management - - {isDaemonManagementPaused - ? "Paused. The built-in daemon stays stopped until you start it again." - : "Enabled. Paseo can manage the built-in daemon from the desktop app."} - + Manage built-in daemon + Let Paseo start and stop the built-in daemon - + accessibilityLabel="Manage built-in daemon" + /> Keep daemon running after quit - - {keepRunningAfterQuit - ? "Enabled. The built-in daemon keeps running after you close the desktop app." - : "Disabled. Quitting the desktop app stops the built-in daemon if desktop management is enabled."} - + Daemon keeps running when you quit Paseo - - - - - {daemonActionLabel} - {daemonActionMessage} - {statusMessage ? {statusMessage} : null} - - + accessibilityLabel="Keep daemon running after quit" + /> Log file - {daemonLogs?.logPath ?? "Log path unavailable."} + {daemonLogs?.logPath ?? "Log path unavailable"} @@ -531,7 +372,7 @@ function DaemonInfoCard(props: DaemonInfoCardProps) { Full status - Runs `paseo daemon status` and shows the output. + Runs `paseo daemon status` and shows the output