Manage the built-in daemon from one place in settings (#1938)
* feat(settings): consolidate daemon management into the host page The app-level Daemon section and the localhost host page rendered the same daemon card in two places. The host page is now the single home: the Host sidebar group leads with Overview, and a desktop-only "Enable built-in daemon" option in the host switcher recovers a removed localhost connection. Old settings/daemon deep links redirect (COMPAT-tagged). * fix(settings): hide daemon recovery option when the server id probe fails Review feedback: !query.isLoading treated an errored IPC probe as resolved, so the recovery affordance could show without knowing the local server id. Replace the resolved-boolean hook with a single discriminated state hook (loading | error | resolved) — errors hide the affordance and fall back to the settings root in the redirect. Also collapses the two query subscriptions per caller into one. * fix(settings): keep daemon recovery reachable when no hosts remain Review feedback: removing localhost as the only host takes the settings sidebar down its no-host branch, which never mounts the host picker — so the "Enable built-in daemon" option was unreachable in the most common recovery case. The sidebar now owns the single hook call, passes it to the picker, and renders the same action as a sidebar row next to "Add host" when no hosts exist. * test(e2e): update settings host header to Overview Test drift: the host section's sidebar/header label was intentionally renamed to "Overview"; the spec still asserted the old "Host" title. Also assert the retired app-level Daemon row stays absent.
This commit is contained in:
@@ -72,7 +72,6 @@ import { SegmentedControl } from "@/components/ui/segmented-control";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||
import { DesktopPermissionsSection } from "@/desktop/components/desktop-permissions-section";
|
||||
import { IntegrationsSection } from "@/desktop/components/integrations-section";
|
||||
import { LocalDaemonSection } from "@/desktop/components/desktop-updates-section";
|
||||
import { isElectronRuntime } from "@/desktop/host";
|
||||
import { useDesktopAppUpdater } from "@/desktop/updates/use-desktop-app-updater";
|
||||
import { formatVersionWithPrefix } from "@/desktop/updates/desktop-updates";
|
||||
@@ -100,6 +99,10 @@ import ProjectsScreen from "@/screens/projects-screen";
|
||||
import ProjectSettingsScreen from "@/screens/project-settings-screen";
|
||||
import { useIsCompactFormFactor } from "@/constants/layout";
|
||||
import { useLocalDaemonServerId } from "@/hooks/use-is-local-daemon";
|
||||
import {
|
||||
type EnableBuiltInDaemonOption,
|
||||
useEnableBuiltInDaemonOption,
|
||||
} from "@/desktop/hooks/use-enable-built-in-daemon-option";
|
||||
import { useWebScrollbarStyle } from "@/hooks/use-web-scrollbar-style";
|
||||
import {
|
||||
buildOpenProjectRoute,
|
||||
@@ -131,7 +134,6 @@ interface SidebarSectionItem {
|
||||
|
||||
const SIDEBAR_SECTION_ITEMS: SidebarSectionItem[] = [
|
||||
{ id: "general", labelKey: "settings.sections.general", icon: Settings },
|
||||
{ id: "daemon", labelKey: "settings.sections.daemon", icon: Server, desktopOnly: true },
|
||||
{ id: "appearance", labelKey: "settings.sections.appearance", icon: Palette },
|
||||
{ id: "shortcuts", labelKey: "settings.sections.shortcuts", icon: Keyboard, desktopOnly: true },
|
||||
{
|
||||
@@ -157,13 +159,13 @@ interface HostSectionItem {
|
||||
}
|
||||
|
||||
const HOST_SECTION_ITEMS: HostSectionItem[] = [
|
||||
{ id: "host", labelKey: "settings.hostSections.host", icon: Server },
|
||||
{ id: "connections", labelKey: "settings.hostSections.connections", icon: Network },
|
||||
{ id: "agents", labelKey: "settings.hostSections.agents", icon: Bot },
|
||||
{ id: "workspaces", labelKey: "settings.hostSections.workspaces", icon: FolderGit2 },
|
||||
{ id: "providers", labelKey: "settings.hostSections.providers", icon: Boxes },
|
||||
{ id: "usage", labelKey: "settings.hostSections.usage", icon: Gauge },
|
||||
{ id: "terminals", labelKey: "settings.hostSections.terminals", icon: SquareTerminal },
|
||||
{ id: "host", labelKey: "settings.hostSections.host", icon: Server },
|
||||
];
|
||||
|
||||
function renderHostSettingsContent(
|
||||
@@ -893,6 +895,7 @@ interface HostPickerProps {
|
||||
sortedHosts: HostProfile[];
|
||||
onSelectHost: (serverId: string) => void;
|
||||
onAddHost: () => void;
|
||||
enableBuiltInDaemonOption: EnableBuiltInDaemonOption;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -902,7 +905,13 @@ interface HostPickerProps {
|
||||
* is using right now; an "Add host" row is always reachable from the list —
|
||||
* even with a single host.
|
||||
*/
|
||||
function HostPicker({ activeServerId, sortedHosts, onSelectHost, onAddHost }: HostPickerProps) {
|
||||
function HostPicker({
|
||||
activeServerId,
|
||||
sortedHosts,
|
||||
onSelectHost,
|
||||
onAddHost,
|
||||
enableBuiltInDaemonOption,
|
||||
}: HostPickerProps) {
|
||||
const { t } = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const triggerRef = useRef<View | null>(null);
|
||||
@@ -932,6 +941,8 @@ function HostPicker({ activeServerId, sortedHosts, onSelectHost, onAddHost }: Ho
|
||||
anchorRef={triggerRef}
|
||||
includeAddHost
|
||||
onAddHost={onAddHost}
|
||||
includeEnableBuiltInDaemon={enableBuiltInDaemonOption.visible}
|
||||
onEnableBuiltInDaemon={enableBuiltInDaemonOption.onPress}
|
||||
showActiveConnection
|
||||
searchable={false}
|
||||
title={t("settings.hostPicker.switchHost")}
|
||||
@@ -990,6 +1001,7 @@ function SettingsSidebar({
|
||||
const localServerId = useLocalDaemonServerId();
|
||||
const sortedHosts = useSortedHosts(hosts, localServerId);
|
||||
const hasHosts = sortedHosts.length > 0;
|
||||
const enableBuiltInDaemonOption = useEnableBuiltInDaemonOption();
|
||||
const isDesktopApp = isElectronRuntime();
|
||||
const items = SIDEBAR_SECTION_ITEMS.filter((item) => !item.desktopOnly || isDesktopApp);
|
||||
const insets = useSafeAreaInsets();
|
||||
@@ -1036,6 +1048,7 @@ function SettingsSidebar({
|
||||
sortedHosts={sortedHosts}
|
||||
onSelectHost={onSelectHost}
|
||||
onAddHost={onAddHost}
|
||||
enableBuiltInDaemonOption={enableBuiltInDaemonOption}
|
||||
/>
|
||||
{HOST_SECTION_ITEMS.map((item) => (
|
||||
<SidebarHostSectionButton
|
||||
@@ -1062,6 +1075,20 @@ function SettingsSidebar({
|
||||
{t("settings.addHost")}
|
||||
</Text>
|
||||
</Pressable>
|
||||
{enableBuiltInDaemonOption.visible ? (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t("settings.enableBuiltInDaemon")}
|
||||
onPress={enableBuiltInDaemonOption.onPress}
|
||||
testID="settings-enable-built-in-daemon"
|
||||
style={sidebarItemStyle}
|
||||
>
|
||||
<Server size={theme.iconSize.md} color={theme.colors.foregroundMuted} />
|
||||
<Text style={sidebarStyles.label} numberOfLines={1}>
|
||||
{t("settings.enableBuiltInDaemon")}
|
||||
</Text>
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
@@ -1387,8 +1414,6 @@ export default function SettingsScreen({ view, openAddHostIntent = null }: Setti
|
||||
handleTerminalScrollbackLinesChange={handleTerminalScrollbackLinesChange}
|
||||
/>
|
||||
);
|
||||
case "daemon":
|
||||
return <LocalDaemonSection />;
|
||||
case "appearance":
|
||||
return <AppearanceSection />;
|
||||
case "shortcuts":
|
||||
|
||||
Reference in New Issue
Block a user