mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-07 21:13:26 +00:00
fix(app): align settings with v2 APIs
This commit is contained in:
parent
80d7766348
commit
a2c3aefcaf
6 changed files with 99 additions and 52 deletions
|
|
@ -8,7 +8,7 @@ import { useSDK } from "@/context/sdk"
|
|||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { ExternalLink } from "./external-link"
|
||||
import { Link } from "./link"
|
||||
|
||||
type SkillItem = {
|
||||
name: string
|
||||
|
|
@ -111,9 +111,7 @@ export const ProjectSettingsExtensions: Component = () => {
|
|||
const [serverSkills] = createResource(
|
||||
serverSDK,
|
||||
(sdk): Promise<SkillItem[]> =>
|
||||
sdk.api.skill
|
||||
.list()
|
||||
.then((result) => result.data.map((item) => ({ name: item.name, location: item.location }))),
|
||||
sdk.api.skill.list().then((result) => result.data.map((item) => ({ name: item.name, location: item.location }))),
|
||||
{ initialValue: [] },
|
||||
)
|
||||
const [directorySkills] = createResource(
|
||||
|
|
@ -200,9 +198,9 @@ export const ProjectSettingsExtensions: Component = () => {
|
|||
<div class="project-settings-extension-section">
|
||||
<div class="project-settings-extension-section-header">
|
||||
<span>{language.t("project.settings.extensions.added")}</span>
|
||||
<ExternalLink class="project-settings-extension-link" href="https://opencode.ai/docs/skills/">
|
||||
<Link class="project-settings-extension-link" href="https://opencode.ai/docs/skills/">
|
||||
{language.t("settings.extensions.addSkills")}
|
||||
</ExternalLink>
|
||||
</Link>
|
||||
</div>
|
||||
<Show when={projectSkills().length > 0}>
|
||||
<ExtensionCard>{skillRows(projectSkills())}</ExtensionCard>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Component, createMemo } from "solid-js"
|
|||
import { SelectV2 } from "@opencode-ai/ui/v2/select-v2"
|
||||
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { ExternalLink } from "../external-link"
|
||||
import { Link } from "../link"
|
||||
import { SettingsListV2 } from "./parts/list"
|
||||
import { SettingsRowV2 } from "./parts/row"
|
||||
import { createAppearanceSettingsController, type AppearanceSettingsController } from "./general-controllers"
|
||||
|
|
@ -104,9 +104,9 @@ export const SettingsAppearanceV2: Component = () => {
|
|||
description={
|
||||
<>
|
||||
{language.t("settings.general.row.theme.description")}{" "}
|
||||
<ExternalLink class="settings-v2-link" href="https://opencode.ai/docs/themes/">
|
||||
<Link class="settings-v2-link" href="https://opencode.ai/docs/themes/">
|
||||
{language.t("common.learnMore")}
|
||||
</ExternalLink>
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { TabsV2 } from "@opencode-ai/ui/v2/tabs-v2"
|
|||
import { useLanguage } from "@/context/language"
|
||||
import { useServerSDK } from "@/context/server-sdk"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
import { ExternalLink } from "../external-link"
|
||||
import { Link } from "../link"
|
||||
import { InlineServerSelect } from "./parts/server-select"
|
||||
import "./settings-v2.css"
|
||||
|
||||
|
|
@ -26,14 +26,14 @@ export const SettingsExtensionsV2: Component = () => {
|
|||
const configMcp = serverSync().data.config.mcp ?? {}
|
||||
return Object.entries(configMcp).map(([name, config]) => ({
|
||||
name,
|
||||
enabled: config.enabled !== false,
|
||||
enabled: typeof config !== "object" || config === null || !("enabled" in config) || config.enabled !== false,
|
||||
}))
|
||||
})
|
||||
|
||||
const handleMcpToggle = (item: McpRowItem, checked: boolean) => {
|
||||
const before = serverSync().data.config.mcp ?? {}
|
||||
const config = before[item.name]
|
||||
if (!config) return
|
||||
if (typeof config !== "object" || config === null) return
|
||||
const next = { ...before, [item.name]: { ...config, enabled: checked } }
|
||||
serverSync().set("config", "mcp", next)
|
||||
void serverSync()
|
||||
|
|
@ -49,11 +49,9 @@ export const SettingsExtensionsV2: Component = () => {
|
|||
})
|
||||
})
|
||||
|
||||
const [skills] = createResource(
|
||||
serverSdk,
|
||||
(sdk) => sdk.api.skill.list().then((result) => result.data),
|
||||
{ initialValue: [] },
|
||||
)
|
||||
const [skills] = createResource(serverSdk, (sdk) => sdk.api.skill.list().then((result) => result.data), {
|
||||
initialValue: [],
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -130,12 +128,12 @@ export const SettingsExtensionsV2: Component = () => {
|
|||
<span class="text-13-medium text-v2-text-text-base">
|
||||
{language.t("settings.extensions.availableAll")}
|
||||
</span>
|
||||
<ExternalLink
|
||||
<Link
|
||||
class="text-13-regular text-v2-text-accent hover:underline"
|
||||
href="https://opencode.ai/docs/skills/"
|
||||
>
|
||||
{language.t("settings.extensions.addSkills")}
|
||||
</ExternalLink>
|
||||
</Link>
|
||||
</div>
|
||||
<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">
|
||||
<For each={skills()}>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { createMemo, onCleanup, onMount, type Accessor } from "solid-js"
|
||||
import { createMemo, createResource, onCleanup, onMount, type Accessor } from "solid-js"
|
||||
import type { ColorScheme } from "@opencode-ai/ui/theme/context"
|
||||
import { useTheme } from "@opencode-ai/ui/theme/context"
|
||||
import {
|
||||
|
|
@ -14,6 +14,57 @@ import {
|
|||
useSettings,
|
||||
} from "@/context/settings"
|
||||
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
|
||||
import { useServerSync } from "@/context/server-sync"
|
||||
|
||||
type ShellOption = {
|
||||
path: string
|
||||
name: string
|
||||
acceptable: boolean
|
||||
}
|
||||
|
||||
type ShellSelectOption = {
|
||||
id: string
|
||||
value: string
|
||||
name: string
|
||||
terminalOnly: boolean
|
||||
}
|
||||
|
||||
export function createShellOptions(input: { shells: ShellOption[]; current: string | undefined }) {
|
||||
const counts = input.shells.reduce((result, shell) => {
|
||||
result.set(shell.name, (result.get(shell.name) ?? 0) + 1)
|
||||
return result
|
||||
}, new Map<string, number>())
|
||||
const options: ShellSelectOption[] = [
|
||||
{ id: "auto", value: "", name: "", terminalOnly: false },
|
||||
...input.shells.map((shell) => {
|
||||
const ambiguous = (counts.get(shell.name) ?? 0) > 1
|
||||
return {
|
||||
id: shell.path,
|
||||
value: ambiguous ? shell.path : shell.name,
|
||||
name: ambiguous ? shell.path : shell.name,
|
||||
terminalOnly: !shell.acceptable,
|
||||
}
|
||||
}),
|
||||
]
|
||||
if (input.current && !options.some((option) => option.value === input.current)) {
|
||||
options.push({ id: input.current, value: input.current, name: input.current, terminalOnly: false })
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
export function createShellSettingsController() {
|
||||
const serverSync = useServerSync()
|
||||
const [shells] = createResource(async () => [] as ShellOption[], { initialValue: [] as ShellOption[] })
|
||||
const current = createMemo(() => serverSync().data.config.shell ?? "")
|
||||
return {
|
||||
shells: () => shells.latest,
|
||||
current,
|
||||
select: (value: string) => {
|
||||
if (value === current()) return
|
||||
void serverSync().updateConfig({ shell: value })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function createAppearanceSettingsController() {
|
||||
const settings = useSettings()
|
||||
|
|
@ -86,19 +137,33 @@ export function createSoundSettingsController() {
|
|||
},
|
||||
})
|
||||
return {
|
||||
agent: channel(settings.sounds.agentEnabled, settings.sounds.agent, settings.sounds.setAgentEnabled, settings.sounds.setAgent),
|
||||
agent: channel(
|
||||
settings.sounds.agentEnabled,
|
||||
settings.sounds.agent,
|
||||
settings.sounds.setAgentEnabled,
|
||||
settings.sounds.setAgent,
|
||||
),
|
||||
permissions: channel(
|
||||
settings.sounds.permissionsEnabled,
|
||||
settings.sounds.permissions,
|
||||
settings.sounds.setPermissionsEnabled,
|
||||
settings.sounds.setPermissions,
|
||||
),
|
||||
errors: channel(settings.sounds.errorsEnabled, settings.sounds.errors, settings.sounds.setErrorsEnabled, settings.sounds.setErrors),
|
||||
errors: channel(
|
||||
settings.sounds.errorsEnabled,
|
||||
settings.sounds.errors,
|
||||
settings.sounds.setErrorsEnabled,
|
||||
settings.sounds.setErrors,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
function soundPreview() {
|
||||
const state = { cleanup: undefined as (() => void) | undefined, timeout: undefined as NodeJS.Timeout | undefined, run: 0 }
|
||||
const state = {
|
||||
cleanup: undefined as (() => void) | undefined,
|
||||
timeout: undefined as NodeJS.Timeout | undefined,
|
||||
run: 0,
|
||||
}
|
||||
const stop = () => {
|
||||
state.run += 1
|
||||
state.cleanup?.()
|
||||
|
|
|
|||
|
|
@ -52,9 +52,15 @@ export const SettingsProvidersV2: Component<{
|
|||
}
|
||||
|
||||
const connected = createMemo(() => {
|
||||
return providers
|
||||
.connected()
|
||||
.filter((p) => p.id !== "opencode" || Object.values(p.models).find((m) => m.cost?.input))
|
||||
return providers.connected().filter(
|
||||
(provider) =>
|
||||
provider.id !== "opencode" ||
|
||||
Object.values(provider.models).some((model) => {
|
||||
if (typeof model !== "object" || model === null || !("cost" in model)) return false
|
||||
const cost = model.cost
|
||||
return typeof cost === "object" && cost !== null && "input" in cost
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
const popular = createMemo(() => {
|
||||
|
|
@ -98,29 +104,6 @@ export const SettingsProvidersV2: Component<{
|
|||
return true
|
||||
}
|
||||
|
||||
const disableProvider = async (providerID: string, name: string) => {
|
||||
return
|
||||
const before = serverSync().data.config.disabled_providers ?? []
|
||||
const next = before.includes(providerID) ? before : [...before, providerID]
|
||||
sync.set("config", "disabled_providers", next)
|
||||
|
||||
await sync
|
||||
.updateConfig({ disabled_providers: next })
|
||||
.then(() => {
|
||||
showToast({
|
||||
variant: "success",
|
||||
icon: "circle-check",
|
||||
title: language.t("provider.disconnect.toast.disconnected.title", { provider: name }),
|
||||
description: language.t("provider.disconnect.toast.disconnected.description", { provider: name }),
|
||||
})
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
sync.set("config", "disabled_providers", before)
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
showToast({ title: language.t("common.requestFailed"), description: message })
|
||||
})
|
||||
}
|
||||
|
||||
const disconnect = async (providerID: string, name: string) => {
|
||||
const location = props.directory() ? { directory: props.directory() } : undefined
|
||||
await serverSdk()
|
||||
|
|
@ -129,9 +112,7 @@ export const SettingsProvidersV2: Component<{
|
|||
const credentials = integration.data?.connections.filter((item) => item.type === "credential") ?? []
|
||||
if (credentials.length === 0) throw new Error(`No removable credentials found for ${name}`)
|
||||
await Promise.all(
|
||||
credentials.map((credential) =>
|
||||
serverSdk().api.credential.remove({ credentialID: credential.id, location }),
|
||||
),
|
||||
credentials.map((credential) => serverSdk().api.credential.remove({ credentialID: credential.id, location })),
|
||||
)
|
||||
showToast({
|
||||
variant: "success",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@ const appLocales = [
|
|||
] as const
|
||||
const desktopLocales = appLocales.filter((locale) => locale !== "th" && locale !== "tr")
|
||||
const appFallbackKeys = new Set([
|
||||
"dialog.provider.custom.label",
|
||||
"dialog.model.unpaid.viewMoreProviders",
|
||||
"session.header.reveal.finder",
|
||||
"session.header.reveal.fileExplorer",
|
||||
"session.header.reveal.containingFolder",
|
||||
"command.session.export",
|
||||
"command.session.export.description",
|
||||
"context.export.session",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue