import { Component, Show, createMemo, createResource } from "solid-js" import { createMediaQuery } from "@solid-primitives/media" 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" import { useDialog } from "@opencode-ai/ui/context/dialog" import { useLanguage } from "@/context/language" import { usePlatform } from "@/context/platform" import { useUpdaterAction } from "../updater-action" import { useSettings } from "@/context/settings" import { ExternalLink } from "../external-link" import { SettingsListV2 } from "./parts/list" import { SettingsRowV2 } from "./parts/row" import { LayoutRetirementNotice, LayoutTransitionToggle } from "./interface-transition" import { createAppearanceSettingsController, createPermissionScopeController, createShellOptions, createShellSettingsController, createSoundSettingsController, soundOptions, type AppearanceSettingsController, type PermissionScopeController, type ShellSettingsController, type SoundSettingsController, } from "./general-controllers" import "./settings-v2.css" const schemeOptions: ("system" | "light" | "dark")[] = ["system", "light", "dark"] const fontSettings = { ui: { action: "settings-ui-font", title: "settings.general.row.uiFont.title", description: "settings.general.row.uiFont.description", font: "ui", input: "setUI", }, code: { action: "settings-code-font", title: "settings.general.row.font.title", description: "settings.general.row.font.description", font: "code", input: "setCode", }, terminal: { action: "settings-terminal-font", title: "settings.general.row.terminalFont.title", description: "settings.general.row.terminalFont.description", font: "terminal", input: "setTerminal", }, } as const const soundSettings = { agent: { action: "settings-sounds-agent", title: "settings.general.sounds.agent.title", description: "settings.general.sounds.agent.description", }, permissions: { action: "settings-sounds-permissions", title: "settings.general.sounds.permissions.title", description: "settings.general.sounds.permissions.description", }, errors: { action: "settings-sounds-errors", title: "settings.general.sounds.errors.title", description: "settings.general.sounds.errors.description", }, } as const const PermissionScopeSetting: Component<{ controller: PermissionScopeController }> = (props) => { const language = useLanguage() return (
) } const ShellSetting: Component<{ controller: ShellSettingsController }> = (props) => { const language = useLanguage() const options = createMemo(() => createShellOptions({ shells: props.controller.shells(), current: props.controller.current(), }), ) return ( option.value === props.controller.current()) ?? options()[0]} placement="bottom-end" gutter={6} value={(option) => option.id} label={(option) => { if (option.id === "auto") return language.t("settings.general.row.shell.autoDefault") if (!option.terminalOnly) return option.name return `${option.name} (${language.t("settings.general.row.shell.terminalOnly")})` }} onSelect={(option) => option && props.controller.select(option.value)} /> ) } const AppearanceSection: Component<{ controller: AppearanceSettingsController }> = (props) => { const language = useLanguage() return (

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

option === props.controller.scheme.current())} placement="bottom-end" gutter={6} label={(option) => { if (option === "system") return language.t("theme.scheme.system") if (option === "light") return language.t("theme.scheme.light") return language.t("theme.scheme.dark") }} onSelect={(option) => option && props.controller.scheme.select(option)} /> {language.t("settings.general.row.theme.description")}{" "} {language.t("common.learnMore")} } > option.id} label={(option) => option.name} onSelect={props.controller.theme.select} />
) } const FontSetting: Component<{ kind: "ui" | "code" | "terminal" fonts: AppearanceSettingsController["fonts"] }> = (props) => { const language = useLanguage() const config = () => fontSettings[props.kind] return (
props.fonts[config().input](event.currentTarget.value)} placeholder={props.fonts[config().font]().placeholder} spellcheck={false} autocorrect="off" autocomplete="off" autocapitalize="off" aria-label={language.t(config().title)} style={{ "font-family": props.fonts[config().font]().family }} />
) } const SoundsSection: Component<{ controller: SoundSettingsController }> = (props) => { const language = useLanguage() return (

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

) } const SoundSetting: Component<{ kind: "agent" | "permissions" | "errors" channel: SoundSettingsController["agent"] }> = (props) => { const language = useLanguage() const config = () => soundSettings[props.kind] return ( option.id} label={(option) => language.t(option.label)} onHighlight={props.channel.highlight} onSelect={props.channel.select} placement="bottom-end" gutter={6} /> ) } const LanguageSetting = () => { const language = useLanguage() const options = createMemo(() => language.locales.map((locale) => ({ value: locale, label: language.label(locale), })), ) return ( option.value === language.locale())} value={(option) => option.value} label={(option) => option.label} onSelect={(option) => option && language.setLocale(option.value)} /> ) } export const SettingsGeneralV2: Component<{ sessionID?: string }> = (props) => { const language = useLanguage() const platform = usePlatform() const dialog = useDialog() const settings = useSettings() const mobile = createMediaQuery("(max-width: 767px)") const updater = useUpdaterAction() const permissionScope = createPermissionScopeController(() => props.sessionID) const shell = createShellSettingsController() const appearance = createAppearanceSettingsController() const sounds = createSoundSettingsController() const desktop = createMemo(() => platform.platform === "desktop") const [pinchZoom, { mutate: setPinchZoom }] = createResource( () => desktop() && "getPinchZoomEnabled" in platform, () => Promise.resolve(platform.getPinchZoomEnabled?.() ?? false).catch(() => false), { initialValue: false }, ) const onPinchZoomChange = (checked: boolean) => { setPinchZoom(checked) const update = platform.setPinchZoomEnabled?.(checked) if (!update) return void update.catch(() => setPinchZoom(!checked)) } const InterfaceSection = () => ( { settings.general.setNewLayoutDesigns(checked) if (checked) return void import("@/components/dialog-settings").then((module) => { void dialog.show(() => ) }) }} /> ) const InterfaceNoticeSection = () => ( settings.general.dismissNewInterfaceNotice()} /> ) const GeneralSection = () => (
settings.general.setShowReasoningSummaries(checked)} />
settings.general.setShellToolPartsExpanded(checked)} />
settings.general.setEditToolPartsExpanded(checked)} />
settings.general.setMobileTitlebarPosition(checked ? "bottom" : "top")} />
) const AdvancedSection = () => (

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

settings.general.setShowFileTree(checked)} />
settings.general.setShowSearch(checked)} />
settings.general.setShowStatus(checked)} />
settings.general.setShowCustomAgents(checked)} />
) const NotificationsSection = () => (

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

settings.notifications.setAgent(checked)} />
settings.notifications.setPermissions(checked)} />
settings.notifications.setErrors(checked)} />
) const UpdatesSection = () => (

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

settings.general.setReleaseNotes(checked)} />
updater.run()}> {language.t(updater.action().label)}
) // We can probably remove this, right? const DisplaySection = () => (

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

) return ( <>

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

) }