From d3aee774d99d2f83d0a43e6453e76b7f7073ee81 Mon Sep 17 00:00:00 2001 From: LukeParkerDev <10430890+Hona@users.noreply.github.com> Date: Fri, 7 Aug 2026 17:04:21 +1000 Subject: [PATCH] fix(app): scope redesigned settings by server --- .../components/dialog-connect-provider.tsx | 2 +- .../src/components/server/server-row-menu.tsx | 9 +- .../src/components/settings-server-picker.tsx | 106 ++++++++ .../src/components/settings-v2/appearance.tsx | 16 +- .../settings-v2/dialog-settings-v2.tsx | 25 +- .../src/components/settings-v2/extensions.tsx | 253 +++++++----------- .../app/src/components/settings-v2/index.tsx | 8 - .../app/src/components/settings-v2/models.tsx | 11 +- .../components/settings-v2/notifications.tsx | 13 - .../settings-v2/parts/server-select.tsx | 67 +---- .../src/components/settings-v2/projects.tsx | 37 +-- .../src/components/settings-v2/providers.tsx | 31 +-- .../components/settings-v2/settings-v2.css | 42 +-- packages/app/src/i18n/ar.ts | 20 -- packages/app/src/i18n/br.ts | 20 -- packages/app/src/i18n/bs.ts | 20 -- packages/app/src/i18n/en.ts | 1 - packages/app/src/i18n/es.ts | 20 -- packages/app/src/i18n/no.ts | 20 -- packages/app/src/i18n/parity.test.ts | 25 +- packages/app/src/i18n/pl.ts | 20 -- packages/app/src/i18n/ru.ts | 20 -- packages/app/src/i18n/th.ts | 20 -- packages/app/src/i18n/tr.ts | 20 -- packages/app/src/i18n/uk.ts | 20 -- packages/app/src/i18n/zh.ts | 20 -- packages/app/src/i18n/zht.ts | 20 -- packages/app/src/wsl/settings.tsx | 12 +- packages/ui/src/v2/components/tabs-v2.css | 16 +- 29 files changed, 326 insertions(+), 588 deletions(-) create mode 100644 packages/app/src/components/settings-server-picker.tsx diff --git a/packages/app/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx index 8291bfc9474..82205dc72d1 100644 --- a/packages/app/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -389,7 +389,7 @@ function ProviderConnection(props: { const settings = useSettings() const newLayout = settings.general.newLayoutDesigns const providers = useProviders(() => props.directory?.()) - const directory = () => props.directory?.() ?? decode64(params.dir) + const directory = () => (props.directory ? props.directory() : decode64(params.dir)) const location = () => { const value = directory() return value ? { directory: value } : undefined diff --git a/packages/app/src/components/server/server-row-menu.tsx b/packages/app/src/components/server/server-row-menu.tsx index 3c6a6299457..ee4a46db070 100644 --- a/packages/app/src/components/server/server-row-menu.tsx +++ b/packages/app/src/components/server/server-row-menu.tsx @@ -80,10 +80,11 @@ export const ServerRowMenuView: Component<{ > {props.labels.edit} - - - {props.labels.default} - + + {props.labels.default} + + + {props.labels.defaultRemove} diff --git a/packages/app/src/components/settings-server-picker.tsx b/packages/app/src/components/settings-server-picker.tsx new file mode 100644 index 00000000000..d9fd4d40c07 --- /dev/null +++ b/packages/app/src/components/settings-server-picker.tsx @@ -0,0 +1,106 @@ +import { Button } from "@opencode-ai/ui/button" +import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" +import { Icon } from "@opencode-ai/ui/icon" +import { QueryClientProvider } from "@tanstack/solid-query" +import { createMemo, For, type ParentProps, Show } from "solid-js" +import { ServerHealthIndicator, ServerRow } from "@/components/server/server-row" +import { ModelsProvider } from "@/context/models" +import { ServerConnection } from "@/context/server" +import { ServerSDKProvider } from "@/context/server-sdk" +import { ServerSyncProvider } from "@/context/server-sync" +import { useGlobal } from "@/context/global" +import { useSettings } from "@/context/settings" + +export function SettingsServerScope(props: ParentProps) { + const global = useGlobal() + const settings = useSettings() + + return ( + + + {(server) => {props.children}} + + + ) +} + +function SettingsServerDataProviders(props: ParentProps<{ server: ServerConnection.Any }>) { + const global = useGlobal() + const serverCtx = () => global.ensureServerCtx(props.server) + + return ( + + props.server}> + props.server}> + {props.children} + + + + ) +} + +export function SettingsServerPicker() { + const global = useGlobal() + const settings = useSettings() + const selected = createMemo(() => + settings.general.newLayoutDesigns() ? global.settings.server.selected() : undefined, + ) + + return ( + + {(conn) => ( + + + + + + + + + { + if (typeof key === "string") global.settings.server.set(ServerConnection.Key.make(key)) + }} + > + + {(item) => { + const key = ServerConnection.key(item) + const blocked = () => global.servers.health[key]?.healthy === false + return ( + + + + + + + + ) + }} + + + + + + )} + + ) +} diff --git a/packages/app/src/components/settings-v2/appearance.tsx b/packages/app/src/components/settings-v2/appearance.tsx index 2ae41e5e208..91fd36597d9 100644 --- a/packages/app/src/components/settings-v2/appearance.tsx +++ b/packages/app/src/components/settings-v2/appearance.tsx @@ -1,5 +1,4 @@ import { Component, Show, createMemo, createResource } from "solid-js" -import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2" import { SelectV2 } from "@opencode-ai/ui/v2/select-v2" import { Switch } from "@opencode-ai/ui/v2/switch-v2" import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2" @@ -8,10 +7,7 @@ import { usePlatform } from "@/context/platform" import { ExternalLink } from "../external-link" import { SettingsListV2 } from "./parts/list" import { SettingsRowV2 } from "./parts/row" -import { - createAppearanceSettingsController, - type AppearanceSettingsController, -} from "./general-controllers" +import { createAppearanceSettingsController, type AppearanceSettingsController } from "./general-controllers" import "./settings-v2.css" const schemeOptions: ("system" | "light" | "dark")[] = ["system", "light", "dark"] @@ -86,21 +82,11 @@ export const SettingsAppearanceV2: Component = () => { void update.catch(() => setPinchZoom(!checked)) } - const restoreDefaults = () => { - appearance.scheme.select("system") - appearance.fonts.setUI("") - appearance.fonts.setCode("") - appearance.fonts.setTerminal("") - } - return ( <>

{language.t("settings.general.section.appearance")}

- - {language.t("common.reset")} -
diff --git a/packages/app/src/components/settings-v2/dialog-settings-v2.tsx b/packages/app/src/components/settings-v2/dialog-settings-v2.tsx index f60a803c86b..4dad7fe061c 100644 --- a/packages/app/src/components/settings-v2/dialog-settings-v2.tsx +++ b/packages/app/src/components/settings-v2/dialog-settings-v2.tsx @@ -13,10 +13,12 @@ import { SettingsModelsV2 } from "./models" import { SettingsServersV2 } from "./servers" import { SettingsProjectsV2 } from "./projects" import { SettingsExtensionsV2 } from "./extensions" +import { SettingsServerScope } from "../settings-server-picker" import { useDialog } from "@opencode-ai/ui/context/dialog" import { useLayout } from "@/context/layout" import { useTabs } from "@/context/tabs" import { useServerSync } from "@/context/server-sync" +import { useGlobal } from "@/context/global" import "./settings-v2.css" export const DialogSettings: Component<{ @@ -29,8 +31,11 @@ export const DialogSettings: Component<{ const layout = useLayout() const tabs = useTabs() const serverSync = useServerSync() + const global = useGlobal() const [tab, setTab] = createSignal(props.defaultValue ?? "general") const directory = createMemo(() => { + const server = global.settings.server.selected() + if (!server || serverSync() !== global.ensureServerCtx(server).sync) return const route = layout.route() if (route.type === "dir-new-sesssion") return route.dir if (route.type === "draft") { @@ -131,15 +136,17 @@ export const DialogSettings: Component<{ - - - - - - - - - + + + + + + + + + + + ) diff --git a/packages/app/src/components/settings-v2/extensions.tsx b/packages/app/src/components/settings-v2/extensions.tsx index 02ed8096892..baa04484427 100644 --- a/packages/app/src/components/settings-v2/extensions.tsx +++ b/packages/app/src/components/settings-v2/extensions.tsx @@ -1,71 +1,44 @@ -import { Component, For, Show, createMemo, createSignal } from "solid-js" +import { Component, For, createMemo, createResource } from "solid-js" import { Icon } from "@opencode-ai/ui/icon" import { Switch } from "@opencode-ai/ui/v2/switch-v2" +import { TabsV2 } from "@opencode-ai/ui/v2/tabs-v2" import { useLanguage } from "@/context/language" -import { ServerConnection, useServer } from "@/context/server" +import { useServerSDK } from "@/context/server-sdk" import { useServerSync } from "@/context/server-sync" import { ExternalLink } from "../external-link" import { InlineServerSelect } from "./parts/server-select" import "./settings-v2.css" -type ExtensionSubTab = "mcps" | "plugins" | "skills" - interface McpRowItem { name: string - status?: string + enabled: boolean } interface PluginRowItem { name: string } -interface SkillRowItem { - name: string -} - export const SettingsExtensionsV2: Component = () => { const language = useLanguage() - const server = useServer() + const serverSdk = useServerSDK() const serverSync = useServerSync() - const [selectedServer, setSelectedServer] = createSignal(server.key) - const [activeSubTab, setActiveSubTab] = createSignal("mcps") - - const [mcpOverrides, setMcpOverrides] = createSignal>({}) const mcps = createMemo(() => { - const configMcp = (serverSync().data.config as { mcp?: Record })?.mcp ?? {} - const configEntries = Object.entries(configMcp).map(([name, conf]) => ({ + const configMcp = serverSync().data.config.mcp ?? {} + return Object.entries(configMcp).map(([name, config]) => ({ name, - status: conf.disabled ? "disabled" : "connected", + enabled: config.enabled !== false, })) - - const firstProject = server.projects.list()[0]?.worktree - const childMcp = firstProject ? serverSync().child(firstProject, { mcp: true })[0].mcp : {} - const childEntries = Object.entries(childMcp ?? {}).map(([name, stat]) => ({ - name, - status: stat?.status, - })) - - const map = new Map() - for (const item of configEntries) map.set(item.name, item) - for (const item of childEntries) { - const existing = map.get(item.name) - map.set(item.name, { ...existing, ...item }) - } - - return Array.from(map.values()).sort((a, b) => a.name.localeCompare(b.name)) }) - const isMcpEnabled = (item: McpRowItem) => { - if (mcpOverrides()[item.name] !== undefined) return mcpOverrides()[item.name] - return item.status === "connected" - } - const handleMcpToggle = (item: McpRowItem, checked: boolean) => { - setMcpOverrides((prev) => ({ ...prev, [item.name]: checked })) - const firstProject = server.projects.list()[0]?.worktree - if (firstProject) { - void serverSync().mcp.toggle(firstProject, item.name) - } + const before = serverSync().data.config.mcp ?? {} + const config = before[item.name] + if (!config) return + const next = { ...before, [item.name]: { ...config, enabled: checked } } + serverSync().set("config", "mcp", next) + void serverSync() + .updateConfig({ mcp: next }) + .catch(() => serverSync().set("config", "mcp", before)) } const plugins = createMemo(() => { @@ -76,10 +49,14 @@ export const SettingsExtensionsV2: Component = () => { }) }) - const skills = createMemo(() => { - const configSkills = (serverSync().data.config as { skills?: string[] })?.skills ?? [] - return configSkills.map((name) => ({ name })) - }) + const [skills] = createResource( + () => serverSdk(), + async (sdk) => { + const result = await sdk.client.v2.skill.list(undefined, { throwOnError: true }) + return result.data.data + }, + { initialValue: [] }, + ) return ( <> @@ -89,125 +66,95 @@ export const SettingsExtensionsV2: Component = () => {

{language.t("settings.tab.extensions")}

{language.t("settings.mcps.description")} - +
- {/* Sub-Tabs Pill Strip */} -
- - - -
+ + + {language.t("status.popover.tab.mcp")} + {language.t("status.popover.tab.plugins")} + {language.t("settings.permissions.tool.skill.title")} + - {/* MCPs Sub-Tab */} - -
-
- - {language.t("settings.extensions.availableAll")} - - {language.t("settings.extensions.manageConfig")} -
-
- - {(item) => ( -
-
- - {item.name} + +
+
+ + {language.t("settings.extensions.availableAll")} + + {language.t("settings.extensions.manageConfig")} +
+
+ + {(item) => ( +
+
+ + {item.name} +
+ handleMcpToggle(item, checked)} hideLabel> + {item.name} +
- handleMcpToggle(item, checked)} - hideLabel - > - {item.name} - -
- )} - + )} + +
-
- + - {/* Plugins Sub-Tab */} - -
-
- - {language.t("settings.extensions.availableAll")} - - {language.t("settings.extensions.manageConfig")} -
-
- - {(plugin) => ( -
-
- - {plugin.name} + +
+
+ + {language.t("settings.extensions.availableAll")} + + {language.t("settings.extensions.manageConfig")} +
+
+ + {(plugin) => ( +
+
+ + {plugin.name} +
-
- )} - + )} + +
-
- + - {/* Skills Sub-Tab */} - -
-
- - {language.t("settings.extensions.availableAll")} - - - {language.t("settings.extensions.addSkills")} - -
-
- - {(skill) => ( -
-
- - {skill.name} + +
+
+ + {language.t("settings.extensions.availableAll")} + + + {language.t("settings.extensions.addSkills")} + +
+
+ + {(skill) => ( +
+
+ + {skill.name} +
-
- )} - + )} + +
-
- + +
) diff --git a/packages/app/src/components/settings-v2/index.tsx b/packages/app/src/components/settings-v2/index.tsx index cbece20af6e..8ff58843798 100644 --- a/packages/app/src/components/settings-v2/index.tsx +++ b/packages/app/src/components/settings-v2/index.tsx @@ -1,9 +1 @@ export { DialogSettings } from "./dialog-settings-v2" -export { SettingsGeneralV2 } from "./general" -export { SettingsAppearanceV2 } from "./appearance" -export { SettingsNotificationsV2 } from "./notifications" -export { SettingsProvidersV2 } from "./providers" -export { SettingsModelsV2 } from "./models" -export { SettingsServersV2 } from "./servers" -export { SettingsProjectsV2 } from "./projects" -export { SettingsExtensionsV2 } from "./extensions" diff --git a/packages/app/src/components/settings-v2/models.tsx b/packages/app/src/components/settings-v2/models.tsx index b43ff6ef345..deb5b97c126 100644 --- a/packages/app/src/components/settings-v2/models.tsx +++ b/packages/app/src/components/settings-v2/models.tsx @@ -4,12 +4,11 @@ import { Switch } from "@opencode-ai/ui/v2/switch-v2" import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon" import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2" import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2" -import { type Component, For, Show, createSignal } from "solid-js" +import { type Component, For, Show } from "solid-js" import { createStore } from "solid-js/store" import { useLanguage } from "@/context/language" import { useModels } from "@/context/models" import { useServerSDK } from "@/context/server-sdk" -import { ServerConnection, useServer } from "@/context/server" import { popularProviders } from "@/hooks/use-providers" import { Persist, persisted } from "@/utils/persist" import { InlineServerSelect } from "./parts/server-select" @@ -24,9 +23,7 @@ const PROVIDER_ICON_SIZE = 16 export const SettingsModelsV2: Component = () => { const language = useLanguage() const models = useModels() - const server = useServer() const serverSdk = useServerSDK() - const [selectedServer, setSelectedServer] = createSignal(server.key) const [store, setStore] = persisted( Persist.serverGlobal(serverSdk().scope, "settings-v2.models.providers"), createStore({ collapsed: {} as Record }), @@ -59,11 +56,7 @@ export const SettingsModelsV2: Component = () => {

{language.t("settings.models.title")}

- +
- +
@@ -57,7 +52,7 @@ export const SettingsProjectsV2: Component = () => { > {(project) => { - const name = () => project.name || project.worktree.split(/[/\\]/).pop() || project.worktree + const name = () => displayName(project) const color = () => getProjectAvatarVariant(project.icon?.color) return ( @@ -66,11 +61,7 @@ export const SettingsProjectsV2: Component = () => { onClick={() => openProjectSettings(project)} >
- + {name()}
diff --git a/packages/app/src/components/settings-v2/providers.tsx b/packages/app/src/components/settings-v2/providers.tsx index 604c9976ee5..1bc9ec85036 100644 --- a/packages/app/src/components/settings-v2/providers.tsx +++ b/packages/app/src/components/settings-v2/providers.tsx @@ -4,13 +4,13 @@ import { useDialog } from "@opencode-ai/ui/context/dialog" import { ProviderIcon } from "@opencode-ai/ui/provider-icon" import { showToast } from "@/utils/toast" import { popularProviders, useProviders } from "@/hooks/use-providers" -import { createMemo, createSignal, type Accessor, type Component, For, Show } from "solid-js" +import { createMemo, type Accessor, type Component, For, Show } from "solid-js" import { useLanguage } from "@/context/language" import { useServerSDK } from "@/context/server-sdk" import { useServerSync } from "@/context/server-sync" -import { ServerConnection, useServer } from "@/context/server" import { DialogConnectProvider, useProviderConnectController } from "../dialog-connect-provider" import { DialogCustomProvider } from "../dialog-custom-provider" +import { SettingsServerScope } from "../settings-server-picker" import { InlineServerSelect } from "./parts/server-select" import { SettingsListV2 } from "./parts/list" import "./settings-v2.css" @@ -42,12 +42,13 @@ export const SettingsProvidersV2: Component<{ const providers = useProviders(props.directory) const providerConnect = useProviderConnectController({ onBack: props.onBack }) - const server = useServer() - const [selectedServer, setSelectedServer] = createSignal(server.key) - const connect = (provider?: string) => { providerConnect.select(provider) - void dialog.show(() => ) + void dialog.show(() => ( + + + + )) } const connected = createMemo(() => { @@ -101,9 +102,9 @@ export const SettingsProvidersV2: Component<{ return const before = serverSync().data.config.disabled_providers ?? [] const next = before.includes(providerID) ? before : [...before, providerID] - serverSync().set("config", "disabled_providers", next) + sync.set("config", "disabled_providers", next) - await serverSync() + await sync .updateConfig({ disabled_providers: next }) .then(() => { showToast({ @@ -114,7 +115,7 @@ export const SettingsProvidersV2: Component<{ }) }) .catch((err: unknown) => { - serverSync().set("config", "disabled_providers", before) + sync.set("config", "disabled_providers", before) const message = err instanceof Error ? err.message : String(err) showToast({ title: language.t("common.requestFailed"), description: message }) }) @@ -150,11 +151,7 @@ export const SettingsProvidersV2: Component<{

{language.t("settings.providers.title")}

- +
@@ -258,7 +255,11 @@ export const SettingsProvidersV2: Component<{ variant="neutral" icon="plus" onClick={() => { - dialog.show(() => ) + dialog.show(() => ( + + + + )) }} > {language.t("common.connect")} diff --git a/packages/app/src/components/settings-v2/settings-v2.css b/packages/app/src/components/settings-v2/settings-v2.css index d1f9479a9a5..6612fff1f8a 100644 --- a/packages/app/src/components/settings-v2/settings-v2.css +++ b/packages/app/src/components/settings-v2/settings-v2.css @@ -182,13 +182,13 @@ max-width: 100%; } -[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] [data-slot="tabs-v2-list"] { +[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] > [data-slot="tabs-v2-list"] { background-color: var(--v2-background-bg-layer-01); } @media (max-width: 639px) { .settings-v2[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] - [data-slot="tabs-v2-list"] { + > [data-slot="tabs-v2-list"] { width: 144px; min-width: 144px; padding-inline: 8px; @@ -736,39 +736,19 @@ color: var(--v2-state-fg-danger); } -.settings-v2-extensions-tabs { - display: flex; - align-items: flex-start; - gap: 6px; +.settings-v2-extensions-tabs [data-slot="tabs-v2-list"] { width: 280px; - height: 32px; - overflow: visible; + padding-inline: 0; } -.settings-v2-extensions-tabs > button { - display: flex; - align-items: center; - justify-content: center; - flex: none; - height: 24px; - padding: 0 8px; - border: 0.5px solid transparent; +.settings-v2-extensions-tabs [data-slot="tabs-v2-list"]::before { + display: none; +} + +.settings-v2-extensions-tabs [data-slot="tabs-v2-trigger-wrapper"] { border-radius: 6px; - background: transparent; - color: var(--v2-text-text-muted); - font-size: 13px; - font-weight: 440; - line-height: 1; - letter-spacing: -0.04px; } -.settings-v2-extensions-tabs > button[data-selected] { - border-color: var(--v2-border-border-muted); - background: var(--v2-background-bg-layer-03); - color: var(--v2-text-text-base); -} - -.settings-v2-extensions-tabs > button:focus-visible { - outline: 2px solid var(--v2-border-border-focus); - outline-offset: 2px; +.settings-v2-extensions-tabs [data-slot="tabs-v2-trigger"] { + padding-inline: 8px; } diff --git a/packages/app/src/i18n/ar.ts b/packages/app/src/i18n/ar.ts index 7f513e9fd7e..320738a6b02 100644 --- a/packages/app/src/i18n/ar.ts +++ b/packages/app/src/i18n/ar.ts @@ -1005,24 +1005,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "فشل إنشاء أيقونة المشروع الدائمة", "error.childStore.storeCreateFailed": "فشل إنشاء المخزن", "terminal.connectionLost.abnormalClose": "تم إغلاق WebSocket بشكل غير طبيعي: {{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } diff --git a/packages/app/src/i18n/br.ts b/packages/app/src/i18n/br.ts index 88de2090e86..9d6765ec94e 100644 --- a/packages/app/src/i18n/br.ts +++ b/packages/app/src/i18n/br.ts @@ -1021,24 +1021,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "Falha ao criar ícone de projeto persistente", "error.childStore.storeCreateFailed": "Falha ao criar armazenamento", "terminal.connectionLost.abnormalClose": "WebSocket fechado anormalmente: {{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } diff --git a/packages/app/src/i18n/bs.ts b/packages/app/src/i18n/bs.ts index f8804c6c736..915313c3e2b 100644 --- a/packages/app/src/i18n/bs.ts +++ b/packages/app/src/i18n/bs.ts @@ -1097,24 +1097,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "Nije uspjelo kreiranje trajne ikone projekta", "error.childStore.storeCreateFailed": "Nije uspjelo kreiranje skladišta", "terminal.connectionLost.abnormalClose": "WebSocket zatvoren nenormalno: {{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index ff4b6dd6ce8..e9ecfc83acc 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -849,7 +849,6 @@ export const dict = { "settings.tab.notifications": "Notifications", "settings.tab.projects": "Projects", "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", "settings.projects.title": "Projects", "settings.projects.description": "View and configure projects on this server", "settings.projects.empty": "No projects found", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index fcfb3470b57..00dca6704b2 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -1105,24 +1105,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "Error al crear icono de proyecto persistente", "error.childStore.storeCreateFailed": "Error al crear almacén", "terminal.connectionLost.abnormalClose": "WebSocket cerrado anormalmente: {{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } diff --git a/packages/app/src/i18n/no.ts b/packages/app/src/i18n/no.ts index cdc9bbc8072..a4e8efb79d2 100644 --- a/packages/app/src/i18n/no.ts +++ b/packages/app/src/i18n/no.ts @@ -1108,24 +1108,4 @@ export const dict = { "settings.general.row.pinchZoom.description": "Tillat knipebevegelser på styreflaten og Ctrl-rulling for å zoome", "settings.updates.action.downloading": "Laster ned...", "settings.updates.action.installing": "Installerer...", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } satisfies Partial> diff --git a/packages/app/src/i18n/parity.test.ts b/packages/app/src/i18n/parity.test.ts index 566be37e647..2e79ec44cff 100644 --- a/packages/app/src/i18n/parity.test.ts +++ b/packages/app/src/i18n/parity.test.ts @@ -20,6 +20,27 @@ const appLocales = [ "zht", ] as const const desktopLocales = appLocales.filter((locale) => locale !== "th" && locale !== "tr") +const appFallbackKeys = new Set([ + "command.session.export", + "command.session.export.description", + "context.export.session", + "toast.session.export.success.title", + "toast.session.export.success.description", + "toast.session.export.failed.title", + "toast.session.export.failed.description", + "common.export", + "settings.tab.preferences", + "settings.tab.notifications", + "settings.tab.projects", + "settings.tab.extensions", + "settings.projects.title", + "settings.projects.description", + "settings.projects.empty", + "settings.mcps.description", + "settings.extensions.availableAll", + "settings.extensions.manageConfig", + "settings.extensions.addSkills", +]) const domains = [ { @@ -48,7 +69,9 @@ describe.skipIf(!!process.env.CI)("i18n parity", () => { const source = await dictionary(domain.source) for (const locale of domain.locales) { const target = await dictionary(domain.target(locale)) - const missing = Object.keys(source).filter((key) => !Object.hasOwn(target, key)) + const missing = Object.keys(source).filter( + (key) => !Object.hasOwn(target, key) && (domain.name !== "app" || !appFallbackKeys.has(key)), + ) const extra = Object.keys(target).filter((key) => !Object.hasOwn(source, key)) expect({ domain: domain.name, locale, missing, extra }).toEqual({ domain: domain.name, diff --git a/packages/app/src/i18n/pl.ts b/packages/app/src/i18n/pl.ts index 0b1c60c85f4..f2e32ffeb87 100644 --- a/packages/app/src/i18n/pl.ts +++ b/packages/app/src/i18n/pl.ts @@ -1021,24 +1021,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "Nie udało się utworzyć trwałej ikony projektu", "error.childStore.storeCreateFailed": "Nie udało się utworzyć magazynu", "terminal.connectionLost.abnormalClose": "WebSocket zamknięty nieprawidłowo: {{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } diff --git a/packages/app/src/i18n/ru.ts b/packages/app/src/i18n/ru.ts index c323f0e6a5c..fa6fb04d04e 100644 --- a/packages/app/src/i18n/ru.ts +++ b/packages/app/src/i18n/ru.ts @@ -1100,24 +1100,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "Не удалось создать постоянный значок проекта", "error.childStore.storeCreateFailed": "Не удалось создать хранилище", "terminal.connectionLost.abnormalClose": "WebSocket закрыт аварийно: {{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } diff --git a/packages/app/src/i18n/th.ts b/packages/app/src/i18n/th.ts index d3814932903..0ae67ee34eb 100644 --- a/packages/app/src/i18n/th.ts +++ b/packages/app/src/i18n/th.ts @@ -1084,24 +1084,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "ไม่สามารถสร้างไอคอนโปรเจกต์ถาวร", "error.childStore.storeCreateFailed": "ไม่สามารถสร้างที่เก็บ", "terminal.connectionLost.abnormalClose": "WebSocket ปิดอย่างผิดปกติ: {{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } diff --git a/packages/app/src/i18n/tr.ts b/packages/app/src/i18n/tr.ts index 06d6c1a31b0..d401519af9c 100644 --- a/packages/app/src/i18n/tr.ts +++ b/packages/app/src/i18n/tr.ts @@ -1103,24 +1103,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "Kalıcı proje simgesi oluşturulamadı", "error.childStore.storeCreateFailed": "Depo oluşturulamadı", "terminal.connectionLost.abnormalClose": "WebSocket anormal şekilde kapandı: {{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } satisfies Partial> diff --git a/packages/app/src/i18n/uk.ts b/packages/app/src/i18n/uk.ts index 5524c0a3bcd..c949f41de2d 100644 --- a/packages/app/src/i18n/uk.ts +++ b/packages/app/src/i18n/uk.ts @@ -1102,24 +1102,4 @@ export const dict = { "workspace.reset.archived.one": "1 сесію буде заархівовано.", "workspace.reset.archived.many": "{{count}} сесій буде заархівовано.", "workspace.reset.note": "Це скине робочу область, щоб вона відповідала гілці за замовчуванням.", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } diff --git a/packages/app/src/i18n/zh.ts b/packages/app/src/i18n/zh.ts index d957ed5dae9..82d73ac3a6a 100644 --- a/packages/app/src/i18n/zh.ts +++ b/packages/app/src/i18n/zh.ts @@ -1075,24 +1075,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "创建持久化项目图标失败", "error.childStore.storeCreateFailed": "创建存储失败", "terminal.connectionLost.abnormalClose": "WebSocket 异常关闭:{{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } satisfies Partial> diff --git a/packages/app/src/i18n/zht.ts b/packages/app/src/i18n/zht.ts index 8fe9855cc1b..fbe80d95eef 100644 --- a/packages/app/src/i18n/zht.ts +++ b/packages/app/src/i18n/zht.ts @@ -1071,24 +1071,4 @@ export const dict = { "error.childStore.persistedProjectIconCreateFailed": "建立持續性專案圖示失敗", "error.childStore.storeCreateFailed": "建立儲存區失敗", "terminal.connectionLost.abnormalClose": "WebSocket 異常關閉:{{code}}", - "command.session.export": "Export session", - "command.session.export.description": "Export the full session transcript as JSON", - "context.export.session": "Export session", - "toast.session.export.success.title": "Session exported", - "toast.session.export.success.description": "Saved session to {{filename}}", - "toast.session.export.failed.title": "Failed to export session", - "toast.session.export.failed.description": "An error occurred while exporting the session", - "common.export": "Export", - "settings.tab.preferences": "Preferences", - "settings.tab.projects": "Projects", - "settings.tab.extensions": "Extensions", - "settings.server.all": "All servers", - "settings.projects.title": "Projects", - "settings.projects.description": "View and configure projects on this server", - "settings.projects.empty": "No projects found", - "settings.mcps.description": "Manage Model Context Protocol (MCP) servers and tools", - "settings.extensions.availableAll": "Available to all projects", - "settings.extensions.manageConfig": "Manage in opencode.json", - "settings.extensions.addSkills": "How to add skills", - "settings.tab.notifications": "Notifications", } satisfies Partial> diff --git a/packages/app/src/wsl/settings.tsx b/packages/app/src/wsl/settings.tsx index 8659ad00b6b..82616c6dcf7 100644 --- a/packages/app/src/wsl/settings.tsx +++ b/packages/app/src/wsl/settings.tsx @@ -145,14 +145,16 @@ export function WslServerSettings(props: { {language.t("wsl.server.retryStart")} - - props.controller.setDefault(key)} - > + + props.controller.setDefault(key)}> {language.t("dialog.server.menu.default")} + + props.controller.setDefault(null)}> + {language.t("dialog.server.menu.defaultRemove")} + + remove(key)}> {language.t("dialog.server.menu.delete")} diff --git a/packages/ui/src/v2/components/tabs-v2.css b/packages/ui/src/v2/components/tabs-v2.css index 70abf78606a..ec468580098 100644 --- a/packages/ui/src/v2/components/tabs-v2.css +++ b/packages/ui/src/v2/components/tabs-v2.css @@ -175,7 +175,7 @@ border: 0.5px solid var(--v2-border-border-muted); } -[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] [data-slot="tabs-v2-list"] { +[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] > [data-slot="tabs-v2-list"] { flex-direction: column; width: 240px; min-width: 200px; @@ -186,7 +186,9 @@ border-right: 1px solid var(--v2-border-border-base); } -[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] [data-slot="tabs-v2-section-title"] { +[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] + > [data-slot="tabs-v2-list"] + [data-slot="tabs-v2-section-title"] { width: 100%; padding-left: 4px; color: var(--v2-text-text-muted); @@ -195,7 +197,9 @@ user-select: none; } -[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] [data-slot="tabs-v2-trigger-wrapper"] { +[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] + > [data-slot="tabs-v2-list"] + [data-slot="tabs-v2-trigger-wrapper"] { width: 100%; height: 28px; border-radius: 4px; @@ -204,7 +208,9 @@ color: var(--v2-text-text-muted); } -[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] [data-slot="tabs-v2-trigger"] { +[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] + > [data-slot="tabs-v2-list"] + [data-slot="tabs-v2-trigger"] { width: 100%; height: 100%; justify-content: flex-start; @@ -213,12 +219,14 @@ } [data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] + > [data-slot="tabs-v2-list"] [data-slot="tabs-v2-trigger-wrapper"]:hover:not(:disabled):not(:has([data-selected])) { background-color: var(--v2-background-bg-layer-03); color: var(--v2-text-text-base); } [data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] + > [data-slot="tabs-v2-list"] [data-slot="tabs-v2-trigger-wrapper"]:has([data-selected]) { background-color: var(--v2-background-bg-layer-03); color: var(--v2-text-text-base);