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:
@@ -371,6 +371,7 @@ export async function expectRetiredSidebarSectionsAbsent(page: Page): Promise<vo
|
|||||||
await expect(sidebar.getByRole("button", { name: "General", exact: true })).toBeVisible();
|
await expect(sidebar.getByRole("button", { name: "General", exact: true })).toBeVisible();
|
||||||
await expect(sidebar.getByRole("button", { name: "Diagnostics", exact: true })).toBeVisible();
|
await expect(sidebar.getByRole("button", { name: "Diagnostics", exact: true })).toBeVisible();
|
||||||
await expect(sidebar.getByRole("button", { name: "About", exact: true })).toBeVisible();
|
await expect(sidebar.getByRole("button", { name: "About", exact: true })).toBeVisible();
|
||||||
|
await expect(sidebar.getByRole("button", { name: "Daemon", exact: true })).toHaveCount(0);
|
||||||
|
|
||||||
// Host group rows are now flat top-level sections (no drill-in).
|
// Host group rows are now flat top-level sections (no drill-in).
|
||||||
await expect(sidebar.getByTestId("settings-host-section-connections")).toBeVisible();
|
await expect(sidebar.getByTestId("settings-host-section-connections")).toBeVisible();
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ test.describe("Settings host page", () => {
|
|||||||
await openSettingsHost(page, serverId);
|
await openSettingsHost(page, serverId);
|
||||||
|
|
||||||
await openHostSection(page, serverId, "host");
|
await openHostSection(page, serverId, "host");
|
||||||
await expectSettingsHeader(page, "Host");
|
await expectSettingsHeader(page, "Overview");
|
||||||
await expectHostLabelDisplayed(page);
|
await expectHostLabelDisplayed(page);
|
||||||
await expectHostActionCards(page, serverId);
|
await expectHostActionCards(page, serverId);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,38 @@
|
|||||||
import { useLocalSearchParams } from "expo-router";
|
import { Redirect, useLocalSearchParams } from "expo-router";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
import { useHostRuntimeBootstrapState } from "@/app/_layout";
|
||||||
|
import { HostRouteBootstrapBoundary } from "@/components/host-route-bootstrap-boundary";
|
||||||
|
import { useLocalDaemonServerIdState } from "@/hooks/use-is-local-daemon";
|
||||||
|
import { useHosts } from "@/runtime/host-runtime";
|
||||||
import SettingsScreen from "@/screens/settings-screen";
|
import SettingsScreen from "@/screens/settings-screen";
|
||||||
import { isSettingsSectionSlug, type SettingsSectionSlug } from "@/utils/host-routes";
|
import { StartupSplashScreen } from "@/screens/startup-splash-screen";
|
||||||
|
import {
|
||||||
|
buildSettingsHostSectionRoute,
|
||||||
|
buildSettingsRoute,
|
||||||
|
isSettingsSectionSlug,
|
||||||
|
type SettingsSectionSlug,
|
||||||
|
} from "@/utils/host-routes";
|
||||||
|
|
||||||
|
// COMPAT(settingsDaemonRedirect): added 2026-07-08, remove after 2027-01-08.
|
||||||
|
function SettingsDaemonRedirect() {
|
||||||
|
const hosts = useHosts();
|
||||||
|
const localDaemon = useLocalDaemonServerIdState();
|
||||||
|
const bootstrapState = useHostRuntimeBootstrapState();
|
||||||
|
|
||||||
|
if (localDaemon.status === "loading") {
|
||||||
|
return <StartupSplashScreen bootstrapState={bootstrapState} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
localDaemon.status === "resolved" &&
|
||||||
|
localDaemon.serverId !== null &&
|
||||||
|
hosts.some((host) => host.serverId === localDaemon.serverId)
|
||||||
|
) {
|
||||||
|
return <Redirect href={buildSettingsHostSectionRoute(localDaemon.serverId, "host")} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Redirect href={buildSettingsRoute()} />;
|
||||||
|
}
|
||||||
|
|
||||||
export default function SettingsSectionRoute() {
|
export default function SettingsSectionRoute() {
|
||||||
const params = useLocalSearchParams<{ section?: string; addHost?: string }>();
|
const params = useLocalSearchParams<{ section?: string; addHost?: string }>();
|
||||||
@@ -10,5 +41,14 @@ export default function SettingsSectionRoute() {
|
|||||||
const openAddHostIntent = typeof params.addHost === "string" ? params.addHost : null;
|
const openAddHostIntent = typeof params.addHost === "string" ? params.addHost : null;
|
||||||
const view = useMemo(() => ({ kind: "section" as const, section }), [section]);
|
const view = useMemo(() => ({ kind: "section" as const, section }), [section]);
|
||||||
|
|
||||||
|
// COMPAT(settingsDaemonRedirect): added 2026-07-08, remove after 2027-01-08.
|
||||||
|
if (rawSection === "daemon") {
|
||||||
|
return (
|
||||||
|
<HostRouteBootstrapBoundary>
|
||||||
|
<SettingsDaemonRedirect />
|
||||||
|
</HostRouteBootstrapBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return <SettingsScreen view={view} openAddHostIntent={openAddHostIntent} />;
|
return <SettingsScreen view={view} openAddHostIntent={openAddHostIntent} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export const ADD_HOST_OPTION_ID = "__add_host__";
|
export const ADD_HOST_OPTION_ID = "__add_host__";
|
||||||
export const ALL_HOSTS_OPTION_ID = "__all_hosts__";
|
export const ALL_HOSTS_OPTION_ID = "__all_hosts__";
|
||||||
|
export const ENABLE_BUILT_IN_DAEMON_OPTION_ID = "__enable_built_in_daemon__";
|
||||||
|
|
||||||
export function getHostPickerLabel(
|
export function getHostPickerLabel(
|
||||||
hosts: Array<{ label: string; serverId: string }>,
|
hosts: Array<{ label: string; serverId: string }>,
|
||||||
|
|||||||
@@ -11,10 +11,16 @@ import { orderHostsLocalFirst } from "@/types/host-connection";
|
|||||||
import {
|
import {
|
||||||
ADD_HOST_OPTION_ID,
|
ADD_HOST_OPTION_ID,
|
||||||
ALL_HOSTS_OPTION_ID,
|
ALL_HOSTS_OPTION_ID,
|
||||||
|
ENABLE_BUILT_IN_DAEMON_OPTION_ID,
|
||||||
getHostPickerLabel,
|
getHostPickerLabel,
|
||||||
} from "./host-picker-constants";
|
} from "./host-picker-constants";
|
||||||
|
|
||||||
export { ADD_HOST_OPTION_ID, ALL_HOSTS_OPTION_ID, getHostPickerLabel };
|
export {
|
||||||
|
ADD_HOST_OPTION_ID,
|
||||||
|
ALL_HOSTS_OPTION_ID,
|
||||||
|
ENABLE_BUILT_IN_DAEMON_OPTION_ID,
|
||||||
|
getHostPickerLabel,
|
||||||
|
};
|
||||||
|
|
||||||
const SEARCHABLE_THRESHOLD = 10;
|
const SEARCHABLE_THRESHOLD = 10;
|
||||||
type RenderHostOption = NonNullable<ComboboxProps["renderOption"]>;
|
type RenderHostOption = NonNullable<ComboboxProps["renderOption"]>;
|
||||||
@@ -115,6 +121,12 @@ export function HostPickerOption({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SYSTEM_HOST_PICKER_OPTION_LABELS: Record<"add" | "all" | "enableBuiltInDaemon", string> = {
|
||||||
|
add: "Add host",
|
||||||
|
all: "All hosts",
|
||||||
|
enableBuiltInDaemon: "Enable built-in daemon",
|
||||||
|
};
|
||||||
|
|
||||||
function SystemHostPickerOption({
|
function SystemHostPickerOption({
|
||||||
active,
|
active,
|
||||||
selected,
|
selected,
|
||||||
@@ -125,12 +137,12 @@ function SystemHostPickerOption({
|
|||||||
active: boolean;
|
active: boolean;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
kind: "add" | "all";
|
kind: "add" | "all" | "enableBuiltInDaemon";
|
||||||
testID?: string;
|
testID?: string;
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const Icon = kind === "add" ? Plus : Server;
|
const Icon = kind === "add" ? Plus : Server;
|
||||||
const label = kind === "add" ? "Add host" : "All hosts";
|
const label = SYSTEM_HOST_PICKER_OPTION_LABELS[kind];
|
||||||
const leadingSlot = useMemo(
|
const leadingSlot = useMemo(
|
||||||
() => <Icon size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />,
|
() => <Icon size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />,
|
||||||
[Icon, theme.colors.foregroundMuted, theme.iconSize.sm],
|
[Icon, theme.colors.foregroundMuted, theme.iconSize.sm],
|
||||||
@@ -158,6 +170,8 @@ export interface HostPickerProps {
|
|||||||
includeAllHost?: boolean;
|
includeAllHost?: boolean;
|
||||||
includeAddHost?: boolean;
|
includeAddHost?: boolean;
|
||||||
onAddHost?: () => void;
|
onAddHost?: () => void;
|
||||||
|
includeEnableBuiltInDaemon?: boolean;
|
||||||
|
onEnableBuiltInDaemon?: () => void;
|
||||||
showActiveConnection?: boolean;
|
showActiveConnection?: boolean;
|
||||||
onOpenHostSettings?: (serverId: string) => void;
|
onOpenHostSettings?: (serverId: string) => void;
|
||||||
searchable?: boolean;
|
searchable?: boolean;
|
||||||
@@ -179,6 +193,8 @@ export function HostPicker({
|
|||||||
includeAllHost,
|
includeAllHost,
|
||||||
includeAddHost,
|
includeAddHost,
|
||||||
onAddHost,
|
onAddHost,
|
||||||
|
includeEnableBuiltInDaemon,
|
||||||
|
onEnableBuiltInDaemon,
|
||||||
showActiveConnection,
|
showActiveConnection,
|
||||||
onOpenHostSettings,
|
onOpenHostSettings,
|
||||||
searchable,
|
searchable,
|
||||||
@@ -199,8 +215,13 @@ export function HostPicker({
|
|||||||
const hostOptions = orderedHosts.map((host) => ({ id: host.serverId, label: host.label }));
|
const hostOptions = orderedHosts.map((host) => ({ id: host.serverId, label: host.label }));
|
||||||
if (includeAllHost) hostOptions.unshift({ id: ALL_HOSTS_OPTION_ID, label: "All hosts" });
|
if (includeAllHost) hostOptions.unshift({ id: ALL_HOSTS_OPTION_ID, label: "All hosts" });
|
||||||
if (includeAddHost) hostOptions.push({ id: ADD_HOST_OPTION_ID, label: "Add host" });
|
if (includeAddHost) hostOptions.push({ id: ADD_HOST_OPTION_ID, label: "Add host" });
|
||||||
|
if (includeEnableBuiltInDaemon)
|
||||||
|
hostOptions.push({
|
||||||
|
id: ENABLE_BUILT_IN_DAEMON_OPTION_ID,
|
||||||
|
label: "Enable built-in daemon",
|
||||||
|
});
|
||||||
return hostOptions;
|
return hostOptions;
|
||||||
}, [orderedHosts, includeAllHost, includeAddHost]);
|
}, [orderedHosts, includeAllHost, includeAddHost, includeEnableBuiltInDaemon]);
|
||||||
|
|
||||||
const isSearchable = searchable === true && orderedHosts.length > SEARCHABLE_THRESHOLD;
|
const isSearchable = searchable === true && orderedHosts.length > SEARCHABLE_THRESHOLD;
|
||||||
|
|
||||||
@@ -208,12 +229,14 @@ export function HostPicker({
|
|||||||
(id: string) => {
|
(id: string) => {
|
||||||
if (id === ADD_HOST_OPTION_ID) {
|
if (id === ADD_HOST_OPTION_ID) {
|
||||||
onAddHost?.();
|
onAddHost?.();
|
||||||
|
} else if (id === ENABLE_BUILT_IN_DAEMON_OPTION_ID) {
|
||||||
|
onEnableBuiltInDaemon?.();
|
||||||
} else {
|
} else {
|
||||||
onSelect(id);
|
onSelect(id);
|
||||||
}
|
}
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
},
|
},
|
||||||
[onAddHost, onOpenChange, onSelect],
|
[onAddHost, onEnableBuiltInDaemon, onOpenChange, onSelect],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOpenHostSettings = useCallback(
|
const handleOpenHostSettings = useCallback(
|
||||||
@@ -246,6 +269,11 @@ export function HostPicker({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (option.id === ENABLE_BUILT_IN_DAEMON_OPTION_ID) {
|
||||||
|
return (
|
||||||
|
<SystemHostPickerOption kind="enableBuiltInDaemon" active={active} onPress={onPress} />
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<HostPickerOption
|
<HostPickerOption
|
||||||
serverId={option.id}
|
serverId={option.id}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ interface UseBuiltInDaemonManagementInput {
|
|||||||
interface UseBuiltInDaemonManagementResult {
|
interface UseBuiltInDaemonManagementResult {
|
||||||
isUpdating: boolean;
|
isUpdating: boolean;
|
||||||
toggle: () => void;
|
toggle: () => void;
|
||||||
|
enable: () => Promise<DaemonManagementToggleResult | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useBuiltInDaemonManagement(
|
export function useBuiltInDaemonManagement(
|
||||||
@@ -42,12 +43,16 @@ export function useBuiltInDaemonManagement(
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { daemonStatus, settings, updateSettings, setStatus, refreshStatus } = input;
|
const { daemonStatus, settings, updateSettings, setStatus, refreshStatus } = input;
|
||||||
const reportError = useDesktopIpcErrorReporter();
|
const reportError = useDesktopIpcErrorReporter();
|
||||||
const { mutate: toggleDaemonManagement, isPending: isUpdating } = useMutation<
|
const {
|
||||||
DaemonManagementToggleResult,
|
mutate: toggleDaemonManagement,
|
||||||
Error
|
mutateAsync: toggleDaemonManagementAsync,
|
||||||
>({
|
isPending: isUpdating,
|
||||||
mutationFn: async () => {
|
} = useMutation<DaemonManagementToggleResult, Error, { forceEnable: boolean }>({
|
||||||
const wasManagingDaemon = settings.manageBuiltInDaemon;
|
mutationFn: async ({ forceEnable }) => {
|
||||||
|
// forceEnable takes the enable branch regardless of the persisted
|
||||||
|
// setting — the recovery affordance must never reach the stop/confirm
|
||||||
|
// path even if manageBuiltInDaemon was left true.
|
||||||
|
const wasManagingDaemon = forceEnable ? false : settings.manageBuiltInDaemon;
|
||||||
try {
|
try {
|
||||||
const result = await executeDaemonManagementToggle(wasManagingDaemon, daemonStatus, {
|
const result = await executeDaemonManagementToggle(wasManagingDaemon, daemonStatus, {
|
||||||
confirm: () =>
|
confirm: () =>
|
||||||
@@ -109,8 +114,21 @@ export function useBuiltInDaemonManagement(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleDaemonManagement();
|
toggleDaemonManagement({ forceEnable: false });
|
||||||
}, [isUpdating, toggleDaemonManagement]);
|
}, [isUpdating, toggleDaemonManagement]);
|
||||||
|
|
||||||
return { isUpdating, toggle };
|
const enable = useCallback(async () => {
|
||||||
|
if (isUpdating) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await toggleDaemonManagementAsync({ forceEnable: true });
|
||||||
|
} catch {
|
||||||
|
// onError has already surfaced the failure; callers only act on success.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, [isUpdating, toggleDaemonManagementAsync]);
|
||||||
|
|
||||||
|
return { isUpdating, toggle, enable };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
import { getIsElectron } from "@/constants/platform";
|
||||||
|
import { useLocalDaemonServerIdState } from "@/hooks/use-is-local-daemon";
|
||||||
|
import { useHosts } from "@/runtime/host-runtime";
|
||||||
|
import { useDesktopSettings } from "@/desktop/settings/desktop-settings";
|
||||||
|
import { useDaemonStatus } from "@/desktop/hooks/use-daemon-status";
|
||||||
|
import { useBuiltInDaemonManagement } from "@/desktop/hooks/use-built-in-daemon-management";
|
||||||
|
import { buildSettingsHostSectionRoute } from "@/utils/host-routes";
|
||||||
|
|
||||||
|
export interface EnableBuiltInDaemonOption {
|
||||||
|
visible: boolean;
|
||||||
|
onPress: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useEnableBuiltInDaemonOption(): EnableBuiltInDaemonOption {
|
||||||
|
const isElectron = getIsElectron();
|
||||||
|
const localDaemon = useLocalDaemonServerIdState();
|
||||||
|
const hosts = useHosts();
|
||||||
|
const { settings, updateSettings } = useDesktopSettings();
|
||||||
|
const { data, setStatus, refetch } = useDaemonStatus();
|
||||||
|
const { enable } = useBuiltInDaemonManagement({
|
||||||
|
daemonStatus: data?.status ?? null,
|
||||||
|
settings: settings.daemon,
|
||||||
|
updateSettings: (updates) => updateSettings({ daemon: updates }),
|
||||||
|
setStatus,
|
||||||
|
refreshStatus: refetch,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isLocalhostConfigured =
|
||||||
|
localDaemon.status === "resolved" &&
|
||||||
|
localDaemon.serverId !== null &&
|
||||||
|
hosts.some((host) => host.serverId === localDaemon.serverId);
|
||||||
|
const visible = isElectron && localDaemon.status === "resolved" && !isLocalhostConfigured;
|
||||||
|
|
||||||
|
const onPress = useCallback(() => {
|
||||||
|
void (async () => {
|
||||||
|
const result = await enable();
|
||||||
|
if (result?.kind === "enabled") {
|
||||||
|
router.push(buildSettingsHostSectionRoute(result.newStatus.serverId, "host"));
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [enable]);
|
||||||
|
|
||||||
|
return { visible, onPress };
|
||||||
|
}
|
||||||
@@ -15,10 +15,10 @@ async function loadDesktopDaemonServerId(): Promise<DesktopDaemonServerIdResult>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useLocalDaemonServerId(): string | null {
|
function useLocalDaemonServerIdQuery() {
|
||||||
const isDesktopApp = shouldUseDesktopDaemon();
|
const isDesktopApp = shouldUseDesktopDaemon();
|
||||||
|
|
||||||
const query = useQuery({
|
return useQuery({
|
||||||
queryKey: DESKTOP_DAEMON_SERVER_ID_QUERY_KEY,
|
queryKey: DESKTOP_DAEMON_SERVER_ID_QUERY_KEY,
|
||||||
queryFn: loadDesktopDaemonServerId,
|
queryFn: loadDesktopDaemonServerId,
|
||||||
enabled: isDesktopApp,
|
enabled: isDesktopApp,
|
||||||
@@ -30,6 +30,11 @@ export function useLocalDaemonServerId(): string | null {
|
|||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
retry: false,
|
retry: false,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLocalDaemonServerId(): string | null {
|
||||||
|
const isDesktopApp = shouldUseDesktopDaemon();
|
||||||
|
const query = useLocalDaemonServerIdQuery();
|
||||||
|
|
||||||
if (!isDesktopApp) {
|
if (!isDesktopApp) {
|
||||||
return null;
|
return null;
|
||||||
@@ -38,6 +43,27 @@ export function useLocalDaemonServerId(): string | null {
|
|||||||
return query.data?.serverId ?? null;
|
return query.data?.serverId ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LocalDaemonServerIdState =
|
||||||
|
| { status: "loading" }
|
||||||
|
| { status: "error" }
|
||||||
|
| { status: "resolved"; serverId: string | null };
|
||||||
|
|
||||||
|
export function useLocalDaemonServerIdState(): LocalDaemonServerIdState {
|
||||||
|
const isDesktopApp = shouldUseDesktopDaemon();
|
||||||
|
const query = useLocalDaemonServerIdQuery();
|
||||||
|
|
||||||
|
if (!isDesktopApp) {
|
||||||
|
return { status: "resolved", serverId: null };
|
||||||
|
}
|
||||||
|
if (query.isError) {
|
||||||
|
return { status: "error" };
|
||||||
|
}
|
||||||
|
if (query.isSuccess) {
|
||||||
|
return { status: "resolved", serverId: query.data.serverId };
|
||||||
|
}
|
||||||
|
return { status: "loading" };
|
||||||
|
}
|
||||||
|
|
||||||
export function useIsLocalDaemon(serverId: string): boolean {
|
export function useIsLocalDaemon(serverId: string): boolean {
|
||||||
const normalizedServerId = serverId.trim();
|
const normalizedServerId = serverId.trim();
|
||||||
const localServerId = useLocalDaemonServerId();
|
const localServerId = useLocalDaemonServerId();
|
||||||
|
|||||||
@@ -1404,6 +1404,7 @@ export const ar: TranslationResources = {
|
|||||||
},
|
},
|
||||||
backToWorkspace: "خلف",
|
backToWorkspace: "خلف",
|
||||||
addHost: "أضف مضيفًا",
|
addHost: "أضف مضيفًا",
|
||||||
|
enableBuiltInDaemon: "تفعيل البرنامج الخفي المدمج",
|
||||||
projects: "المشاريع",
|
projects: "المشاريع",
|
||||||
projectList: {
|
projectList: {
|
||||||
hostLoadFailed: "تعذر تحميل المشاريع من المضيف{{hostName}}:{{message}}",
|
hostLoadFailed: "تعذر تحميل المشاريع من المضيف{{hostName}}:{{message}}",
|
||||||
@@ -1412,7 +1413,6 @@ export const ar: TranslationResources = {
|
|||||||
groupInfo: "حول{{title}}",
|
groupInfo: "حول{{title}}",
|
||||||
sections: {
|
sections: {
|
||||||
general: "عام",
|
general: "عام",
|
||||||
daemon: "Daemon",
|
|
||||||
appearance: "مظهر",
|
appearance: "مظهر",
|
||||||
shortcuts: "الاختصارات",
|
shortcuts: "الاختصارات",
|
||||||
integrations: "التكامل",
|
integrations: "التكامل",
|
||||||
@@ -1427,7 +1427,7 @@ export const ar: TranslationResources = {
|
|||||||
providers: "مقدمي الخدمات",
|
providers: "مقدمي الخدمات",
|
||||||
usage: "الاستخدام",
|
usage: "الاستخدام",
|
||||||
terminals: "Terminals",
|
terminals: "Terminals",
|
||||||
host: "Host",
|
host: "نظرة عامة",
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: "عام",
|
title: "عام",
|
||||||
|
|||||||
@@ -1412,6 +1412,7 @@ export const en = {
|
|||||||
},
|
},
|
||||||
backToWorkspace: "Back",
|
backToWorkspace: "Back",
|
||||||
addHost: "Add host",
|
addHost: "Add host",
|
||||||
|
enableBuiltInDaemon: "Enable built-in daemon",
|
||||||
projects: "Projects",
|
projects: "Projects",
|
||||||
projectList: {
|
projectList: {
|
||||||
hostLoadFailed: "Couldn't load projects from host {{hostName}}: {{message}}",
|
hostLoadFailed: "Couldn't load projects from host {{hostName}}: {{message}}",
|
||||||
@@ -1420,7 +1421,6 @@ export const en = {
|
|||||||
groupInfo: "About {{title}}",
|
groupInfo: "About {{title}}",
|
||||||
sections: {
|
sections: {
|
||||||
general: "General",
|
general: "General",
|
||||||
daemon: "Daemon",
|
|
||||||
appearance: "Appearance",
|
appearance: "Appearance",
|
||||||
shortcuts: "Shortcuts",
|
shortcuts: "Shortcuts",
|
||||||
integrations: "Integrations",
|
integrations: "Integrations",
|
||||||
@@ -1435,7 +1435,7 @@ export const en = {
|
|||||||
providers: "Providers",
|
providers: "Providers",
|
||||||
usage: "Usage",
|
usage: "Usage",
|
||||||
terminals: "Terminals",
|
terminals: "Terminals",
|
||||||
host: "Host",
|
host: "Overview",
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: "General",
|
title: "General",
|
||||||
|
|||||||
@@ -1443,6 +1443,7 @@ export const es: TranslationResources = {
|
|||||||
},
|
},
|
||||||
backToWorkspace: "Atrás",
|
backToWorkspace: "Atrás",
|
||||||
addHost: "Agregar anfitrión",
|
addHost: "Agregar anfitrión",
|
||||||
|
enableBuiltInDaemon: "Activar el demonio integrado",
|
||||||
projects: "Proyectos",
|
projects: "Proyectos",
|
||||||
projectList: {
|
projectList: {
|
||||||
hostLoadFailed: "No se pudieron cargar proyectos desde el host{{hostName}}:{{message}}",
|
hostLoadFailed: "No se pudieron cargar proyectos desde el host{{hostName}}:{{message}}",
|
||||||
@@ -1451,7 +1452,6 @@ export const es: TranslationResources = {
|
|||||||
groupInfo: "Acerca de{{title}}",
|
groupInfo: "Acerca de{{title}}",
|
||||||
sections: {
|
sections: {
|
||||||
general: "General",
|
general: "General",
|
||||||
daemon: "Daemon",
|
|
||||||
appearance: "Apariencia",
|
appearance: "Apariencia",
|
||||||
shortcuts: "Atajos",
|
shortcuts: "Atajos",
|
||||||
integrations: "Integraciones",
|
integrations: "Integraciones",
|
||||||
@@ -1466,7 +1466,7 @@ export const es: TranslationResources = {
|
|||||||
providers: "Proveedores",
|
providers: "Proveedores",
|
||||||
usage: "Uso",
|
usage: "Uso",
|
||||||
terminals: "Terminals",
|
terminals: "Terminals",
|
||||||
host: "Host",
|
host: "Resumen",
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: "General",
|
title: "General",
|
||||||
|
|||||||
@@ -1446,6 +1446,7 @@ export const fr: TranslationResources = {
|
|||||||
},
|
},
|
||||||
backToWorkspace: "Dos",
|
backToWorkspace: "Dos",
|
||||||
addHost: "Ajouter un hôte",
|
addHost: "Ajouter un hôte",
|
||||||
|
enableBuiltInDaemon: "Activer le démon intégré",
|
||||||
projects: "Projets",
|
projects: "Projets",
|
||||||
projectList: {
|
projectList: {
|
||||||
hostLoadFailed: "Impossible de charger les projets depuis l'hôte{{hostName}}:{{message}}",
|
hostLoadFailed: "Impossible de charger les projets depuis l'hôte{{hostName}}:{{message}}",
|
||||||
@@ -1454,7 +1455,6 @@ export const fr: TranslationResources = {
|
|||||||
groupInfo: "À propos de{{title}}",
|
groupInfo: "À propos de{{title}}",
|
||||||
sections: {
|
sections: {
|
||||||
general: "Général",
|
general: "Général",
|
||||||
daemon: "Daemon",
|
|
||||||
appearance: "Apparence",
|
appearance: "Apparence",
|
||||||
shortcuts: "Raccourcis",
|
shortcuts: "Raccourcis",
|
||||||
integrations: "Intégrations",
|
integrations: "Intégrations",
|
||||||
@@ -1469,7 +1469,7 @@ export const fr: TranslationResources = {
|
|||||||
providers: "Fournisseurs",
|
providers: "Fournisseurs",
|
||||||
usage: "Utilisation",
|
usage: "Utilisation",
|
||||||
terminals: "Terminals",
|
terminals: "Terminals",
|
||||||
host: "Host",
|
host: "Aperçu",
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: "Général",
|
title: "Général",
|
||||||
|
|||||||
@@ -1421,6 +1421,7 @@ export const ja: TranslationResources = {
|
|||||||
},
|
},
|
||||||
backToWorkspace: "戻る",
|
backToWorkspace: "戻る",
|
||||||
addHost: "ホストを追加",
|
addHost: "ホストを追加",
|
||||||
|
enableBuiltInDaemon: "組み込みデーモンを有効にする",
|
||||||
projects: "プロジェクト",
|
projects: "プロジェクト",
|
||||||
projectList: {
|
projectList: {
|
||||||
hostLoadFailed: "ホスト{{hostName}}からプロジェクトを読み込めませんでした: {{message}}",
|
hostLoadFailed: "ホスト{{hostName}}からプロジェクトを読み込めませんでした: {{message}}",
|
||||||
@@ -1429,7 +1430,6 @@ export const ja: TranslationResources = {
|
|||||||
groupInfo: "{{title}}について",
|
groupInfo: "{{title}}について",
|
||||||
sections: {
|
sections: {
|
||||||
general: "一般",
|
general: "一般",
|
||||||
daemon: "デーモン",
|
|
||||||
appearance: "外観",
|
appearance: "外観",
|
||||||
shortcuts: "ショートカット",
|
shortcuts: "ショートカット",
|
||||||
integrations: "連携",
|
integrations: "連携",
|
||||||
@@ -1444,7 +1444,7 @@ export const ja: TranslationResources = {
|
|||||||
providers: "プロバイダー",
|
providers: "プロバイダー",
|
||||||
usage: "使用状況",
|
usage: "使用状況",
|
||||||
terminals: "ターミナル",
|
terminals: "ターミナル",
|
||||||
host: "ホスト",
|
host: "概要",
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: "一般",
|
title: "一般",
|
||||||
|
|||||||
@@ -1429,6 +1429,7 @@ export const ptBR: TranslationResources = {
|
|||||||
},
|
},
|
||||||
backToWorkspace: "Voltar",
|
backToWorkspace: "Voltar",
|
||||||
addHost: "Adicionar host",
|
addHost: "Adicionar host",
|
||||||
|
enableBuiltInDaemon: "Ativar o daemon integrado",
|
||||||
projects: "Projetos",
|
projects: "Projetos",
|
||||||
projectList: {
|
projectList: {
|
||||||
hostLoadFailed: "Não foi possível carregar projetos do host {{hostName}}: {{message}}",
|
hostLoadFailed: "Não foi possível carregar projetos do host {{hostName}}: {{message}}",
|
||||||
@@ -1437,7 +1438,6 @@ export const ptBR: TranslationResources = {
|
|||||||
groupInfo: "Sobre {{title}}",
|
groupInfo: "Sobre {{title}}",
|
||||||
sections: {
|
sections: {
|
||||||
general: "Geral",
|
general: "Geral",
|
||||||
daemon: "Daemon",
|
|
||||||
appearance: "Aparência",
|
appearance: "Aparência",
|
||||||
shortcuts: "Atalhos",
|
shortcuts: "Atalhos",
|
||||||
integrations: "Integrações",
|
integrations: "Integrações",
|
||||||
@@ -1452,7 +1452,7 @@ export const ptBR: TranslationResources = {
|
|||||||
providers: "Provedores",
|
providers: "Provedores",
|
||||||
usage: "Uso",
|
usage: "Uso",
|
||||||
terminals: "Terminais",
|
terminals: "Terminais",
|
||||||
host: "Host",
|
host: "Visão geral",
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: "Geral",
|
title: "Geral",
|
||||||
|
|||||||
@@ -1435,6 +1435,7 @@ export const ru: TranslationResources = {
|
|||||||
},
|
},
|
||||||
backToWorkspace: "Назад",
|
backToWorkspace: "Назад",
|
||||||
addHost: "Добавить хост",
|
addHost: "Добавить хост",
|
||||||
|
enableBuiltInDaemon: "Включить встроенный демон",
|
||||||
projects: "Проекты",
|
projects: "Проекты",
|
||||||
projectList: {
|
projectList: {
|
||||||
hostLoadFailed: "Не удалось загрузить проекты с хоста{{hostName}}:{{message}}.",
|
hostLoadFailed: "Не удалось загрузить проекты с хоста{{hostName}}:{{message}}.",
|
||||||
@@ -1443,7 +1444,6 @@ export const ru: TranslationResources = {
|
|||||||
groupInfo: "О{{title}}",
|
groupInfo: "О{{title}}",
|
||||||
sections: {
|
sections: {
|
||||||
general: "Общий",
|
general: "Общий",
|
||||||
daemon: "Daemon",
|
|
||||||
appearance: "Появление",
|
appearance: "Появление",
|
||||||
shortcuts: "Ярлыки",
|
shortcuts: "Ярлыки",
|
||||||
integrations: "Интеграции",
|
integrations: "Интеграции",
|
||||||
@@ -1458,7 +1458,7 @@ export const ru: TranslationResources = {
|
|||||||
providers: "Провайдеры",
|
providers: "Провайдеры",
|
||||||
usage: "Использование",
|
usage: "Использование",
|
||||||
terminals: "Terminals",
|
terminals: "Terminals",
|
||||||
host: "Host",
|
host: "Обзор",
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: "Общий",
|
title: "Общий",
|
||||||
|
|||||||
@@ -1387,6 +1387,7 @@ export const zhCN: TranslationResources = {
|
|||||||
},
|
},
|
||||||
backToWorkspace: "返回",
|
backToWorkspace: "返回",
|
||||||
addHost: "添加主机",
|
addHost: "添加主机",
|
||||||
|
enableBuiltInDaemon: "启用内置 daemon",
|
||||||
projects: "项目",
|
projects: "项目",
|
||||||
projectList: {
|
projectList: {
|
||||||
hostLoadFailed: "无法从 Host {{hostName}} 加载 projects:{{message}}",
|
hostLoadFailed: "无法从 Host {{hostName}} 加载 projects:{{message}}",
|
||||||
@@ -1395,7 +1396,6 @@ export const zhCN: TranslationResources = {
|
|||||||
groupInfo: "关于 {{title}}",
|
groupInfo: "关于 {{title}}",
|
||||||
sections: {
|
sections: {
|
||||||
general: "通用",
|
general: "通用",
|
||||||
daemon: "Daemon",
|
|
||||||
appearance: "外观",
|
appearance: "外观",
|
||||||
shortcuts: "快捷键",
|
shortcuts: "快捷键",
|
||||||
integrations: "集成",
|
integrations: "集成",
|
||||||
@@ -1410,7 +1410,7 @@ export const zhCN: TranslationResources = {
|
|||||||
providers: "Providers",
|
providers: "Providers",
|
||||||
usage: "使用情况",
|
usage: "使用情况",
|
||||||
terminals: "Terminals",
|
terminals: "Terminals",
|
||||||
host: "Host",
|
host: "概览",
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: "通用",
|
title: "通用",
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ import { SegmentedControl } from "@/components/ui/segmented-control";
|
|||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||||
import { DesktopPermissionsSection } from "@/desktop/components/desktop-permissions-section";
|
import { DesktopPermissionsSection } from "@/desktop/components/desktop-permissions-section";
|
||||||
import { IntegrationsSection } from "@/desktop/components/integrations-section";
|
import { IntegrationsSection } from "@/desktop/components/integrations-section";
|
||||||
import { LocalDaemonSection } from "@/desktop/components/desktop-updates-section";
|
|
||||||
import { isElectronRuntime } from "@/desktop/host";
|
import { isElectronRuntime } from "@/desktop/host";
|
||||||
import { useDesktopAppUpdater } from "@/desktop/updates/use-desktop-app-updater";
|
import { useDesktopAppUpdater } from "@/desktop/updates/use-desktop-app-updater";
|
||||||
import { formatVersionWithPrefix } from "@/desktop/updates/desktop-updates";
|
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 ProjectSettingsScreen from "@/screens/project-settings-screen";
|
||||||
import { useIsCompactFormFactor } from "@/constants/layout";
|
import { useIsCompactFormFactor } from "@/constants/layout";
|
||||||
import { useLocalDaemonServerId } from "@/hooks/use-is-local-daemon";
|
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 { useWebScrollbarStyle } from "@/hooks/use-web-scrollbar-style";
|
||||||
import {
|
import {
|
||||||
buildOpenProjectRoute,
|
buildOpenProjectRoute,
|
||||||
@@ -131,7 +134,6 @@ interface SidebarSectionItem {
|
|||||||
|
|
||||||
const SIDEBAR_SECTION_ITEMS: SidebarSectionItem[] = [
|
const SIDEBAR_SECTION_ITEMS: SidebarSectionItem[] = [
|
||||||
{ id: "general", labelKey: "settings.sections.general", icon: Settings },
|
{ 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: "appearance", labelKey: "settings.sections.appearance", icon: Palette },
|
||||||
{ id: "shortcuts", labelKey: "settings.sections.shortcuts", icon: Keyboard, desktopOnly: true },
|
{ id: "shortcuts", labelKey: "settings.sections.shortcuts", icon: Keyboard, desktopOnly: true },
|
||||||
{
|
{
|
||||||
@@ -157,13 +159,13 @@ interface HostSectionItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const HOST_SECTION_ITEMS: HostSectionItem[] = [
|
const HOST_SECTION_ITEMS: HostSectionItem[] = [
|
||||||
|
{ id: "host", labelKey: "settings.hostSections.host", icon: Server },
|
||||||
{ id: "connections", labelKey: "settings.hostSections.connections", icon: Network },
|
{ id: "connections", labelKey: "settings.hostSections.connections", icon: Network },
|
||||||
{ id: "agents", labelKey: "settings.hostSections.agents", icon: Bot },
|
{ id: "agents", labelKey: "settings.hostSections.agents", icon: Bot },
|
||||||
{ id: "workspaces", labelKey: "settings.hostSections.workspaces", icon: FolderGit2 },
|
{ id: "workspaces", labelKey: "settings.hostSections.workspaces", icon: FolderGit2 },
|
||||||
{ id: "providers", labelKey: "settings.hostSections.providers", icon: Boxes },
|
{ id: "providers", labelKey: "settings.hostSections.providers", icon: Boxes },
|
||||||
{ id: "usage", labelKey: "settings.hostSections.usage", icon: Gauge },
|
{ id: "usage", labelKey: "settings.hostSections.usage", icon: Gauge },
|
||||||
{ id: "terminals", labelKey: "settings.hostSections.terminals", icon: SquareTerminal },
|
{ id: "terminals", labelKey: "settings.hostSections.terminals", icon: SquareTerminal },
|
||||||
{ id: "host", labelKey: "settings.hostSections.host", icon: Server },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function renderHostSettingsContent(
|
function renderHostSettingsContent(
|
||||||
@@ -893,6 +895,7 @@ interface HostPickerProps {
|
|||||||
sortedHosts: HostProfile[];
|
sortedHosts: HostProfile[];
|
||||||
onSelectHost: (serverId: string) => void;
|
onSelectHost: (serverId: string) => void;
|
||||||
onAddHost: () => 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 —
|
* is using right now; an "Add host" row is always reachable from the list —
|
||||||
* even with a single host.
|
* even with a single host.
|
||||||
*/
|
*/
|
||||||
function HostPicker({ activeServerId, sortedHosts, onSelectHost, onAddHost }: HostPickerProps) {
|
function HostPicker({
|
||||||
|
activeServerId,
|
||||||
|
sortedHosts,
|
||||||
|
onSelectHost,
|
||||||
|
onAddHost,
|
||||||
|
enableBuiltInDaemonOption,
|
||||||
|
}: HostPickerProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const triggerRef = useRef<View | null>(null);
|
const triggerRef = useRef<View | null>(null);
|
||||||
@@ -932,6 +941,8 @@ function HostPicker({ activeServerId, sortedHosts, onSelectHost, onAddHost }: Ho
|
|||||||
anchorRef={triggerRef}
|
anchorRef={triggerRef}
|
||||||
includeAddHost
|
includeAddHost
|
||||||
onAddHost={onAddHost}
|
onAddHost={onAddHost}
|
||||||
|
includeEnableBuiltInDaemon={enableBuiltInDaemonOption.visible}
|
||||||
|
onEnableBuiltInDaemon={enableBuiltInDaemonOption.onPress}
|
||||||
showActiveConnection
|
showActiveConnection
|
||||||
searchable={false}
|
searchable={false}
|
||||||
title={t("settings.hostPicker.switchHost")}
|
title={t("settings.hostPicker.switchHost")}
|
||||||
@@ -990,6 +1001,7 @@ function SettingsSidebar({
|
|||||||
const localServerId = useLocalDaemonServerId();
|
const localServerId = useLocalDaemonServerId();
|
||||||
const sortedHosts = useSortedHosts(hosts, localServerId);
|
const sortedHosts = useSortedHosts(hosts, localServerId);
|
||||||
const hasHosts = sortedHosts.length > 0;
|
const hasHosts = sortedHosts.length > 0;
|
||||||
|
const enableBuiltInDaemonOption = useEnableBuiltInDaemonOption();
|
||||||
const isDesktopApp = isElectronRuntime();
|
const isDesktopApp = isElectronRuntime();
|
||||||
const items = SIDEBAR_SECTION_ITEMS.filter((item) => !item.desktopOnly || isDesktopApp);
|
const items = SIDEBAR_SECTION_ITEMS.filter((item) => !item.desktopOnly || isDesktopApp);
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
@@ -1036,6 +1048,7 @@ function SettingsSidebar({
|
|||||||
sortedHosts={sortedHosts}
|
sortedHosts={sortedHosts}
|
||||||
onSelectHost={onSelectHost}
|
onSelectHost={onSelectHost}
|
||||||
onAddHost={onAddHost}
|
onAddHost={onAddHost}
|
||||||
|
enableBuiltInDaemonOption={enableBuiltInDaemonOption}
|
||||||
/>
|
/>
|
||||||
{HOST_SECTION_ITEMS.map((item) => (
|
{HOST_SECTION_ITEMS.map((item) => (
|
||||||
<SidebarHostSectionButton
|
<SidebarHostSectionButton
|
||||||
@@ -1062,6 +1075,20 @@ function SettingsSidebar({
|
|||||||
{t("settings.addHost")}
|
{t("settings.addHost")}
|
||||||
</Text>
|
</Text>
|
||||||
</Pressable>
|
</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>
|
</View>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -1387,8 +1414,6 @@ export default function SettingsScreen({ view, openAddHostIntent = null }: Setti
|
|||||||
handleTerminalScrollbackLinesChange={handleTerminalScrollbackLinesChange}
|
handleTerminalScrollbackLinesChange={handleTerminalScrollbackLinesChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "daemon":
|
|
||||||
return <LocalDaemonSection />;
|
|
||||||
case "appearance":
|
case "appearance":
|
||||||
return <AppearanceSection />;
|
return <AppearanceSection />;
|
||||||
case "shortcuts":
|
case "shortcuts":
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
decodeWorkspaceIdFromPathSegment,
|
decodeWorkspaceIdFromPathSegment,
|
||||||
encodeFilePathForPathSegment,
|
encodeFilePathForPathSegment,
|
||||||
encodeWorkspaceIdForPathSegment,
|
encodeWorkspaceIdForPathSegment,
|
||||||
|
isSettingsSectionSlug,
|
||||||
normalizeHostSectionSlug,
|
normalizeHostSectionSlug,
|
||||||
parseHostAgentRouteFromPathname,
|
parseHostAgentRouteFromPathname,
|
||||||
parseHostWorkspaceOpenIntentFromPathname,
|
parseHostWorkspaceOpenIntentFromPathname,
|
||||||
@@ -266,6 +267,12 @@ describe("host settings section slugs", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("settings section slugs", () => {
|
||||||
|
it("no longer treats daemon as a valid app-level settings section", () => {
|
||||||
|
expect(isSettingsSectionSlug("daemon")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("resolveKnownHostRoute", () => {
|
describe("resolveKnownHostRoute", () => {
|
||||||
it("renders when the route host is still saved", () => {
|
it("renders when the route host is still saved", () => {
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -482,7 +482,6 @@ export function resolveKnownHostRoute(input: {
|
|||||||
|
|
||||||
export const SETTINGS_SECTION_SLUGS = [
|
export const SETTINGS_SECTION_SLUGS = [
|
||||||
"general",
|
"general",
|
||||||
"daemon",
|
|
||||||
"appearance",
|
"appearance",
|
||||||
"shortcuts",
|
"shortcuts",
|
||||||
"integrations",
|
"integrations",
|
||||||
|
|||||||
Reference in New Issue
Block a user