Skip to content

Commit a2c3aef

Browse files
committed
fix(app): align settings with v2 APIs
1 parent 80d7766 commit a2c3aef

6 files changed

Lines changed: 99 additions & 52 deletions

File tree

packages/app/src/components/project-settings-extensions.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { useSDK } from "@/context/sdk"
88
import { useServerSDK } from "@/context/server-sdk"
99
import { useServerSync } from "@/context/server-sync"
1010
import { useSync } from "@/context/sync"
11-
import { ExternalLink } from "./external-link"
11+
import { Link } from "./link"
1212

1313
type SkillItem = {
1414
name: string
@@ -111,9 +111,7 @@ export const ProjectSettingsExtensions: Component = () => {
111111
const [serverSkills] = createResource(
112112
serverSDK,
113113
(sdk): Promise<SkillItem[]> =>
114-
sdk.api.skill
115-
.list()
116-
.then((result) => result.data.map((item) => ({ name: item.name, location: item.location }))),
114+
sdk.api.skill.list().then((result) => result.data.map((item) => ({ name: item.name, location: item.location }))),
117115
{ initialValue: [] },
118116
)
119117
const [directorySkills] = createResource(
@@ -200,9 +198,9 @@ export const ProjectSettingsExtensions: Component = () => {
200198
<div class="project-settings-extension-section">
201199
<div class="project-settings-extension-section-header">
202200
<span>{language.t("project.settings.extensions.added")}</span>
203-
<ExternalLink class="project-settings-extension-link" href="https://opencode.ai/docs/skills/">
201+
<Link class="project-settings-extension-link" href="https://opencode.ai/docs/skills/">
204202
{language.t("settings.extensions.addSkills")}
205-
</ExternalLink>
203+
</Link>
206204
</div>
207205
<Show when={projectSkills().length > 0}>
208206
<ExtensionCard>{skillRows(projectSkills())}</ExtensionCard>

packages/app/src/components/settings-v2/appearance.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Component, createMemo } from "solid-js"
22
import { SelectV2 } from "@opencode-ai/ui/v2/select-v2"
33
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
44
import { useLanguage } from "@/context/language"
5-
import { ExternalLink } from "../external-link"
5+
import { Link } from "../link"
66
import { SettingsListV2 } from "./parts/list"
77
import { SettingsRowV2 } from "./parts/row"
88
import { createAppearanceSettingsController, type AppearanceSettingsController } from "./general-controllers"
@@ -104,9 +104,9 @@ export const SettingsAppearanceV2: Component = () => {
104104
description={
105105
<>
106106
{language.t("settings.general.row.theme.description")}{" "}
107-
<ExternalLink class="settings-v2-link" href="https://opencode.ai/docs/themes/">
107+
<Link class="settings-v2-link" href="https://opencode.ai/docs/themes/">
108108
{language.t("common.learnMore")}
109-
</ExternalLink>
109+
</Link>
110110
</>
111111
}
112112
>

packages/app/src/components/settings-v2/extensions.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { TabsV2 } from "@opencode-ai/ui/v2/tabs-v2"
55
import { useLanguage } from "@/context/language"
66
import { useServerSDK } from "@/context/server-sdk"
77
import { useServerSync } from "@/context/server-sync"
8-
import { ExternalLink } from "../external-link"
8+
import { Link } from "../link"
99
import { InlineServerSelect } from "./parts/server-select"
1010
import "./settings-v2.css"
1111

@@ -26,14 +26,14 @@ export const SettingsExtensionsV2: Component = () => {
2626
const configMcp = serverSync().data.config.mcp ?? {}
2727
return Object.entries(configMcp).map(([name, config]) => ({
2828
name,
29-
enabled: config.enabled !== false,
29+
enabled: typeof config !== "object" || config === null || !("enabled" in config) || config.enabled !== false,
3030
}))
3131
})
3232

3333
const handleMcpToggle = (item: McpRowItem, checked: boolean) => {
3434
const before = serverSync().data.config.mcp ?? {}
3535
const config = before[item.name]
36-
if (!config) return
36+
if (typeof config !== "object" || config === null) return
3737
const next = { ...before, [item.name]: { ...config, enabled: checked } }
3838
serverSync().set("config", "mcp", next)
3939
void serverSync()
@@ -49,11 +49,9 @@ export const SettingsExtensionsV2: Component = () => {
4949
})
5050
})
5151

52-
const [skills] = createResource(
53-
serverSdk,
54-
(sdk) => sdk.api.skill.list().then((result) => result.data),
55-
{ initialValue: [] },
56-
)
52+
const [skills] = createResource(serverSdk, (sdk) => sdk.api.skill.list().then((result) => result.data), {
53+
initialValue: [],
54+
})
5755

5856
return (
5957
<>
@@ -130,12 +128,12 @@ export const SettingsExtensionsV2: Component = () => {
130128
<span class="text-13-medium text-v2-text-text-base">
131129
{language.t("settings.extensions.availableAll")}
132130
</span>
133-
<ExternalLink
131+
<Link
134132
class="text-13-regular text-v2-text-accent hover:underline"
135133
href="https://opencode.ai/docs/skills/"
136134
>
137135
{language.t("settings.extensions.addSkills")}
138-
</ExternalLink>
136+
</Link>
139137
</div>
140138
<div class="bg-[var(--v2-background-bg-base)] border-[0.5px] border-[var(--v2-border-border-base)] rounded-[8px] pl-4 pr-3 overflow-hidden">
141139
<For each={skills()}>

packages/app/src/components/settings-v2/general-controllers.ts

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createMemo, onCleanup, onMount, type Accessor } from "solid-js"
1+
import { createMemo, createResource, onCleanup, onMount, type Accessor } from "solid-js"
22
import type { ColorScheme } from "@opencode-ai/ui/theme/context"
33
import { useTheme } from "@opencode-ai/ui/theme/context"
44
import {
@@ -14,6 +14,57 @@ import {
1414
useSettings,
1515
} from "@/context/settings"
1616
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
17+
import { useServerSync } from "@/context/server-sync"
18+
19+
type ShellOption = {
20+
path: string
21+
name: string
22+
acceptable: boolean
23+
}
24+
25+
type ShellSelectOption = {
26+
id: string
27+
value: string
28+
name: string
29+
terminalOnly: boolean
30+
}
31+
32+
export function createShellOptions(input: { shells: ShellOption[]; current: string | undefined }) {
33+
const counts = input.shells.reduce((result, shell) => {
34+
result.set(shell.name, (result.get(shell.name) ?? 0) + 1)
35+
return result
36+
}, new Map<string, number>())
37+
const options: ShellSelectOption[] = [
38+
{ id: "auto", value: "", name: "", terminalOnly: false },
39+
...input.shells.map((shell) => {
40+
const ambiguous = (counts.get(shell.name) ?? 0) > 1
41+
return {
42+
id: shell.path,
43+
value: ambiguous ? shell.path : shell.name,
44+
name: ambiguous ? shell.path : shell.name,
45+
terminalOnly: !shell.acceptable,
46+
}
47+
}),
48+
]
49+
if (input.current && !options.some((option) => option.value === input.current)) {
50+
options.push({ id: input.current, value: input.current, name: input.current, terminalOnly: false })
51+
}
52+
return options
53+
}
54+
55+
export function createShellSettingsController() {
56+
const serverSync = useServerSync()
57+
const [shells] = createResource(async () => [] as ShellOption[], { initialValue: [] as ShellOption[] })
58+
const current = createMemo(() => serverSync().data.config.shell ?? "")
59+
return {
60+
shells: () => shells.latest,
61+
current,
62+
select: (value: string) => {
63+
if (value === current()) return
64+
void serverSync().updateConfig({ shell: value })
65+
},
66+
}
67+
}
1768

1869
export function createAppearanceSettingsController() {
1970
const settings = useSettings()
@@ -86,19 +137,33 @@ export function createSoundSettingsController() {
86137
},
87138
})
88139
return {
89-
agent: channel(settings.sounds.agentEnabled, settings.sounds.agent, settings.sounds.setAgentEnabled, settings.sounds.setAgent),
140+
agent: channel(
141+
settings.sounds.agentEnabled,
142+
settings.sounds.agent,
143+
settings.sounds.setAgentEnabled,
144+
settings.sounds.setAgent,
145+
),
90146
permissions: channel(
91147
settings.sounds.permissionsEnabled,
92148
settings.sounds.permissions,
93149
settings.sounds.setPermissionsEnabled,
94150
settings.sounds.setPermissions,
95151
),
96-
errors: channel(settings.sounds.errorsEnabled, settings.sounds.errors, settings.sounds.setErrorsEnabled, settings.sounds.setErrors),
152+
errors: channel(
153+
settings.sounds.errorsEnabled,
154+
settings.sounds.errors,
155+
settings.sounds.setErrorsEnabled,
156+
settings.sounds.setErrors,
157+
),
97158
}
98159
}
99160

100161
function soundPreview() {
101-
const state = { cleanup: undefined as (() => void) | undefined, timeout: undefined as NodeJS.Timeout | undefined, run: 0 }
162+
const state = {
163+
cleanup: undefined as (() => void) | undefined,
164+
timeout: undefined as NodeJS.Timeout | undefined,
165+
run: 0,
166+
}
102167
const stop = () => {
103168
state.run += 1
104169
state.cleanup?.()

packages/app/src/components/settings-v2/providers.tsx

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,15 @@ export const SettingsProvidersV2: Component<{
5252
}
5353

5454
const connected = createMemo(() => {
55-
return providers
56-
.connected()
57-
.filter((p) => p.id !== "opencode" || Object.values(p.models).find((m) => m.cost?.input))
55+
return providers.connected().filter(
56+
(provider) =>
57+
provider.id !== "opencode" ||
58+
Object.values(provider.models).some((model) => {
59+
if (typeof model !== "object" || model === null || !("cost" in model)) return false
60+
const cost = model.cost
61+
return typeof cost === "object" && cost !== null && "input" in cost
62+
}),
63+
)
5864
})
5965

6066
const popular = createMemo(() => {
@@ -98,29 +104,6 @@ export const SettingsProvidersV2: Component<{
98104
return true
99105
}
100106

101-
const disableProvider = async (providerID: string, name: string) => {
102-
return
103-
const before = serverSync().data.config.disabled_providers ?? []
104-
const next = before.includes(providerID) ? before : [...before, providerID]
105-
sync.set("config", "disabled_providers", next)
106-
107-
await sync
108-
.updateConfig({ disabled_providers: next })
109-
.then(() => {
110-
showToast({
111-
variant: "success",
112-
icon: "circle-check",
113-
title: language.t("provider.disconnect.toast.disconnected.title", { provider: name }),
114-
description: language.t("provider.disconnect.toast.disconnected.description", { provider: name }),
115-
})
116-
})
117-
.catch((err: unknown) => {
118-
sync.set("config", "disabled_providers", before)
119-
const message = err instanceof Error ? err.message : String(err)
120-
showToast({ title: language.t("common.requestFailed"), description: message })
121-
})
122-
}
123-
124107
const disconnect = async (providerID: string, name: string) => {
125108
const location = props.directory() ? { directory: props.directory() } : undefined
126109
await serverSdk()
@@ -129,9 +112,7 @@ export const SettingsProvidersV2: Component<{
129112
const credentials = integration.data?.connections.filter((item) => item.type === "credential") ?? []
130113
if (credentials.length === 0) throw new Error(`No removable credentials found for ${name}`)
131114
await Promise.all(
132-
credentials.map((credential) =>
133-
serverSdk().api.credential.remove({ credentialID: credential.id, location }),
134-
),
115+
credentials.map((credential) => serverSdk().api.credential.remove({ credentialID: credential.id, location })),
135116
)
136117
showToast({
137118
variant: "success",

packages/app/src/i18n/parity.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ const appLocales = [
2121
] as const
2222
const desktopLocales = appLocales.filter((locale) => locale !== "th" && locale !== "tr")
2323
const appFallbackKeys = new Set([
24+
"dialog.provider.custom.label",
25+
"dialog.model.unpaid.viewMoreProviders",
26+
"session.header.reveal.finder",
27+
"session.header.reveal.fileExplorer",
28+
"session.header.reveal.containingFolder",
2429
"command.session.export",
2530
"command.session.export.description",
2631
"context.export.session",

0 commit comments

Comments
 (0)