feat(ui): centralize interaction preferences
Expose interaction defaults in Settings and reuse the same registry for command palette actions.
This commit is contained in:
@@ -45,7 +45,6 @@ export const SettingsScreen: Component = () => {
|
||||
<div class="settings-screen-frame">
|
||||
<Dialog.Content class="modal-surface settings-screen-shell">
|
||||
<Dialog.Title class="sr-only">{t("settings.title")}</Dialog.Title>
|
||||
<Dialog.Description class="sr-only">{t("settings.description")}</Dialog.Description>
|
||||
|
||||
<aside class="settings-screen-nav">
|
||||
<div class="settings-screen-nav-header">
|
||||
@@ -55,7 +54,6 @@ export const SettingsScreen: Component = () => {
|
||||
</span>
|
||||
<div>
|
||||
<h2 class="settings-screen-title">{t("settings.title")}</h2>
|
||||
<p class="settings-screen-subtitle">{t("settings.description")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { Component } from "solid-js"
|
||||
import { Check, Laptop, Moon, Sun } from "lucide-solid"
|
||||
import { Select } from "@kobalte/core/select"
|
||||
import { createEffect, createMemo, createSignal, For, type Component } from "solid-js"
|
||||
import { Check, ChevronDown, Laptop, Moon, Sun } from "lucide-solid"
|
||||
import { useI18n } from "../../lib/i18n"
|
||||
import { useTheme, type ThemeMode } from "../../lib/theme"
|
||||
import { useConfig } from "../../stores/preferences"
|
||||
import { getBehaviorSettings, type BehaviorSetting } from "../../lib/settings/behavior-registry"
|
||||
|
||||
const themeModeOptions: Array<{ value: ThemeMode; icon: typeof Laptop }> = [
|
||||
{ value: "system", icon: Laptop },
|
||||
@@ -12,6 +15,200 @@ const themeModeOptions: Array<{ value: ThemeMode; icon: typeof Laptop }> = [
|
||||
export const AppearanceSettingsSection: Component = () => {
|
||||
const { t } = useI18n()
|
||||
const { themeMode, setThemeMode } = useTheme()
|
||||
const {
|
||||
preferences,
|
||||
updatePreferences,
|
||||
toggleShowThinkingBlocks,
|
||||
toggleKeyboardShortcutHints,
|
||||
toggleShowTimelineTools,
|
||||
toggleUsageMetrics,
|
||||
toggleAutoCleanupBlankSessions,
|
||||
togglePromptSubmitOnEnter,
|
||||
setDiffViewMode,
|
||||
setToolOutputExpansion,
|
||||
setDiagnosticsExpansion,
|
||||
setThinkingBlocksExpansion,
|
||||
setToolInputsVisibility,
|
||||
} = useConfig()
|
||||
|
||||
const behaviorSettings = createMemo(() =>
|
||||
getBehaviorSettings({
|
||||
preferences,
|
||||
updatePreferences,
|
||||
toggleShowThinkingBlocks,
|
||||
toggleKeyboardShortcutHints,
|
||||
toggleShowTimelineTools,
|
||||
toggleUsageMetrics,
|
||||
toggleAutoCleanupBlankSessions,
|
||||
togglePromptSubmitOnEnter,
|
||||
setDiffViewMode,
|
||||
setToolOutputExpansion,
|
||||
setDiagnosticsExpansion,
|
||||
setThinkingBlocksExpansion,
|
||||
setToolInputsVisibility,
|
||||
}),
|
||||
)
|
||||
|
||||
const [overrides, setOverrides] = createSignal<Map<string, unknown>>(new Map())
|
||||
|
||||
const setOverride = (id: string, value: unknown) => {
|
||||
setOverrides((prev) => {
|
||||
const next = new Map(prev)
|
||||
next.set(id, value)
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
const current = overrides()
|
||||
if (current.size === 0) return
|
||||
|
||||
const prefs = preferences()
|
||||
const settings = behaviorSettings()
|
||||
|
||||
let changed = false
|
||||
const next = new Map(current)
|
||||
for (const setting of settings) {
|
||||
if (!next.has(setting.id)) continue
|
||||
const overrideValue = next.get(setting.id)
|
||||
const actualValue = setting.get(prefs)
|
||||
if (Object.is(actualValue, overrideValue)) {
|
||||
next.delete(setting.id)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
setOverrides(next)
|
||||
}
|
||||
})
|
||||
|
||||
const readSettingValue = (setting: BehaviorSetting) => {
|
||||
const current = overrides()
|
||||
if (current.has(setting.id)) return current.get(setting.id)
|
||||
return setting.get(preferences())
|
||||
}
|
||||
|
||||
type SelectOption = { value: string; label: string }
|
||||
|
||||
const BehaviorRow: Component<{ setting: BehaviorSetting }> = (props) => {
|
||||
const setting = props.setting
|
||||
const disabled = createMemo(() => (setting.disabled ? Boolean(setting.disabled()) : false))
|
||||
|
||||
if (setting.kind === "toggle") {
|
||||
const options = createMemo<SelectOption[]>(() => [
|
||||
{ value: "true", label: t("settings.common.enabled") },
|
||||
{ value: "false", label: t("settings.common.disabled") },
|
||||
])
|
||||
const currentValue = createMemo(() => String(Boolean(readSettingValue(setting))))
|
||||
const selectedOption = createMemo(() => options().find((opt) => opt.value === currentValue()))
|
||||
|
||||
return (
|
||||
<div class={`settings-toggle-row ${disabled() ? "opacity-60" : ""}`}>
|
||||
<div>
|
||||
<div class="settings-toggle-title">{t(setting.titleKey)}</div>
|
||||
<div class="settings-toggle-caption">{t(setting.subtitleKey)}</div>
|
||||
</div>
|
||||
<Select<SelectOption>
|
||||
value={selectedOption()}
|
||||
onChange={(opt) => {
|
||||
if (!opt) return
|
||||
const next = opt.value === "true"
|
||||
setOverride(setting.id, next)
|
||||
setting.set(next)
|
||||
}}
|
||||
options={options()}
|
||||
optionValue="value"
|
||||
optionTextValue="label"
|
||||
disabled={disabled()}
|
||||
itemComponent={(itemProps) => (
|
||||
<Select.Item item={itemProps.item} class="selector-option">
|
||||
<Select.ItemLabel class="selector-option-label">{itemProps.item.rawValue.label}</Select.ItemLabel>
|
||||
</Select.Item>
|
||||
)}
|
||||
>
|
||||
<Select.Trigger class="selector-trigger" aria-label={t(setting.titleKey)}>
|
||||
<div class="flex-1 min-w-0">
|
||||
<Select.Value<SelectOption>>
|
||||
{(state) => (
|
||||
<span class="selector-trigger-primary selector-trigger-primary--align-left">
|
||||
{state.selectedOption()?.label}
|
||||
</span>
|
||||
)}
|
||||
</Select.Value>
|
||||
</div>
|
||||
<Select.Icon class="selector-trigger-icon">
|
||||
<ChevronDown class="w-3 h-3" />
|
||||
</Select.Icon>
|
||||
</Select.Trigger>
|
||||
|
||||
<Select.Portal>
|
||||
<Select.Content class="selector-popover">
|
||||
<Select.Listbox class="selector-listbox" />
|
||||
</Select.Content>
|
||||
</Select.Portal>
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const enumSetting = setting as Extract<BehaviorSetting, { kind: "enum" }>
|
||||
const options = createMemo<SelectOption[]>(() =>
|
||||
enumSetting.options.map((opt: { value: string; labelKey: string }) => ({
|
||||
value: String(opt.value),
|
||||
label: t(opt.labelKey),
|
||||
})),
|
||||
)
|
||||
const currentValue = createMemo(() => String(readSettingValue(setting) ?? ""))
|
||||
const selectedOption = createMemo(() => options().find((opt) => opt.value === currentValue()))
|
||||
|
||||
return (
|
||||
<div class={`settings-toggle-row ${disabled() ? "opacity-60" : ""}`}>
|
||||
<div>
|
||||
<div class="settings-toggle-title">{t(setting.titleKey)}</div>
|
||||
<div class="settings-toggle-caption">{t(setting.subtitleKey)}</div>
|
||||
</div>
|
||||
<Select<SelectOption>
|
||||
value={selectedOption()}
|
||||
onChange={(opt) => {
|
||||
if (!opt) return
|
||||
setOverride(setting.id, opt.value)
|
||||
enumSetting.set(opt.value as any)
|
||||
}}
|
||||
options={options()}
|
||||
optionValue="value"
|
||||
optionTextValue="label"
|
||||
disabled={disabled()}
|
||||
itemComponent={(itemProps) => (
|
||||
<Select.Item item={itemProps.item} class="selector-option">
|
||||
<Select.ItemLabel class="selector-option-label">{itemProps.item.rawValue.label}</Select.ItemLabel>
|
||||
</Select.Item>
|
||||
)}
|
||||
>
|
||||
<Select.Trigger class="selector-trigger" aria-label={t(setting.titleKey)}>
|
||||
<div class="flex-1 min-w-0">
|
||||
<Select.Value<SelectOption>>
|
||||
{(state) => (
|
||||
<span class="selector-trigger-primary selector-trigger-primary--align-left">
|
||||
{state.selectedOption()?.label}
|
||||
</span>
|
||||
)}
|
||||
</Select.Value>
|
||||
</div>
|
||||
<Select.Icon class="selector-trigger-icon">
|
||||
<ChevronDown class="w-3 h-3" />
|
||||
</Select.Icon>
|
||||
</Select.Trigger>
|
||||
|
||||
<Select.Portal>
|
||||
<Select.Content class="selector-popover">
|
||||
<Select.Listbox class="selector-listbox" />
|
||||
</Select.Content>
|
||||
</Select.Portal>
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const modeLabel = (mode: ThemeMode) => {
|
||||
if (mode === "system") return t("theme.mode.system")
|
||||
@@ -54,6 +251,20 @@ export const AppearanceSettingsSection: Component = () => {
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<div class="settings-card-header">
|
||||
<div>
|
||||
<h3 class="settings-card-title">{t("settings.appearance.behavior.title")}</h3>
|
||||
<p class="settings-card-subtitle">{t("settings.appearance.behavior.subtitle")}</p>
|
||||
</div>
|
||||
<span class="settings-scope-badge">{t("settings.scope.device")}</span>
|
||||
</div>
|
||||
|
||||
<div class="settings-stack">
|
||||
<For each={behaviorSettings()}>{(setting) => <BehaviorRow setting={setting} />}</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { getLogger } from "../logger"
|
||||
import { requestData } from "../opencode-api"
|
||||
import { emitSessionSidebarRequest } from "../session-sidebar-events"
|
||||
import { tGlobal } from "../i18n"
|
||||
import { runtimeEnv } from "../runtime-env"
|
||||
import { registerBehaviorCommands } from "../settings/behavior-registry"
|
||||
|
||||
const log = getLogger("actions")
|
||||
|
||||
@@ -427,178 +427,19 @@ export function useCommands(options: UseCommandsOptions) {
|
||||
},
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "prompt-submit-shortcut",
|
||||
label: () =>
|
||||
options.preferences().promptSubmitOnEnter
|
||||
? tGlobal("commands.promptSubmitShortcut.label.swapped")
|
||||
: tGlobal("commands.promptSubmitShortcut.label.default"),
|
||||
description: () => tGlobal("commands.promptSubmitShortcut.description"),
|
||||
category: "Input & Focus",
|
||||
keywords: () => splitKeywords("commands.promptSubmitShortcut.keywords"),
|
||||
action: options.togglePromptSubmitOnEnter,
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "thinking",
|
||||
label: () => tGlobal(options.preferences().showThinkingBlocks ? "commands.thinkingBlocks.label.hide" : "commands.thinkingBlocks.label.show"),
|
||||
description: () => tGlobal("commands.thinkingBlocks.description"),
|
||||
category: "System",
|
||||
keywords: () => ["/thinking", ...splitKeywords("commands.thinkingBlocks.keywords")],
|
||||
action: options.toggleShowThinkingBlocks,
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "timeline-tools",
|
||||
label: () => tGlobal(options.preferences().showTimelineTools ? "commands.timelineToolCalls.label.hide" : "commands.timelineToolCalls.label.show"),
|
||||
description: () => tGlobal("commands.timelineToolCalls.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.timelineToolCalls.keywords"),
|
||||
action: options.toggleShowTimelineTools,
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "keyboard-shortcut-hints",
|
||||
label: () =>
|
||||
tGlobal(
|
||||
options.preferences().showKeyboardShortcutHints
|
||||
? "commands.keyboardShortcutHints.label.hide"
|
||||
: "commands.keyboardShortcutHints.label.show",
|
||||
),
|
||||
description: () =>
|
||||
tGlobal(
|
||||
runtimeEnv.host === "web"
|
||||
? "commands.keyboardShortcutHints.description.disabledWeb"
|
||||
: "commands.keyboardShortcutHints.description",
|
||||
),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.keyboardShortcutHints.keywords"),
|
||||
disabled: () => runtimeEnv.host === "web",
|
||||
action: options.toggleKeyboardShortcutHints,
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "thinking-default-visibility",
|
||||
label: () => {
|
||||
const mode = options.preferences().thinkingBlocksExpansion ?? "expanded"
|
||||
const state = mode === "expanded" ? tGlobal("commands.common.expanded") : tGlobal("commands.common.collapsed")
|
||||
return tGlobal("commands.thinkingBlocksDefault.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.thinkingBlocksDefault.description"),
|
||||
category: "System",
|
||||
keywords: () => ["/thinking", ...splitKeywords("commands.thinkingBlocksDefault.keywords")],
|
||||
action: () => {
|
||||
const mode = options.preferences().thinkingBlocksExpansion ?? "expanded"
|
||||
const next: ExpansionPreference = mode === "expanded" ? "collapsed" : "expanded"
|
||||
options.setThinkingBlocksExpansion(next)
|
||||
},
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "diff-view-split",
|
||||
label: () => {
|
||||
const prefix = (options.preferences().diffViewMode || "split") === "split" ? "✓ " : ""
|
||||
return `${prefix}${tGlobal("commands.diffViewSplit.label")}`
|
||||
},
|
||||
description: () => tGlobal("commands.diffViewSplit.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.diffViewSplit.keywords"),
|
||||
action: () => options.setDiffViewMode("split"),
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "diff-view-unified",
|
||||
label: () => {
|
||||
const prefix = (options.preferences().diffViewMode || "split") === "unified" ? "✓ " : ""
|
||||
return `${prefix}${tGlobal("commands.diffViewUnified.label")}`
|
||||
},
|
||||
description: () => tGlobal("commands.diffViewUnified.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.diffViewUnified.keywords"),
|
||||
action: () => options.setDiffViewMode("unified"),
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "tool-output-default-visibility",
|
||||
label: () => {
|
||||
const mode = options.preferences().toolOutputExpansion || "expanded"
|
||||
const state = mode === "expanded" ? tGlobal("commands.common.expanded") : tGlobal("commands.common.collapsed")
|
||||
return tGlobal("commands.toolOutputsDefault.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.toolOutputsDefault.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.toolOutputsDefault.keywords"),
|
||||
action: () => {
|
||||
const mode = options.preferences().toolOutputExpansion || "expanded"
|
||||
const next: ExpansionPreference = mode === "expanded" ? "collapsed" : "expanded"
|
||||
options.setToolOutputExpansion(next)
|
||||
},
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "diagnostics-default-visibility",
|
||||
label: () => {
|
||||
const mode = options.preferences().diagnosticsExpansion || "expanded"
|
||||
const state = mode === "expanded" ? tGlobal("commands.common.expanded") : tGlobal("commands.common.collapsed")
|
||||
return tGlobal("commands.diagnosticsDefault.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.diagnosticsDefault.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.diagnosticsDefault.keywords"),
|
||||
action: () => {
|
||||
const mode = options.preferences().diagnosticsExpansion || "expanded"
|
||||
const next: ExpansionPreference = mode === "expanded" ? "collapsed" : "expanded"
|
||||
options.setDiagnosticsExpansion(next)
|
||||
},
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "tool-inputs-visibility",
|
||||
label: () => {
|
||||
const mode = options.preferences().toolInputsVisibility || "hidden"
|
||||
const state =
|
||||
mode === "expanded"
|
||||
? tGlobal("commands.common.expanded")
|
||||
: mode === "collapsed"
|
||||
? tGlobal("commands.common.collapsed")
|
||||
: tGlobal("commands.common.hidden")
|
||||
return tGlobal("commands.toolInputsVisibility.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.toolInputsVisibility.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.toolInputsVisibility.keywords"),
|
||||
action: () => {
|
||||
const mode = options.preferences().toolInputsVisibility || "hidden"
|
||||
const next: ToolInputsVisibilityPreference =
|
||||
mode === "hidden" ? "collapsed" : mode === "collapsed" ? "expanded" : "hidden"
|
||||
options.setToolInputsVisibility(next)
|
||||
},
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "token-usage-visibility",
|
||||
label: () => {
|
||||
const visible = options.preferences().showUsageMetrics ?? true
|
||||
const state = visible ? tGlobal("commands.common.visible") : tGlobal("commands.common.hidden")
|
||||
return tGlobal("commands.tokenUsageDisplay.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.tokenUsageDisplay.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.tokenUsageDisplay.keywords"),
|
||||
action: options.toggleUsageMetrics,
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
id: "auto-cleanup-blank-sessions",
|
||||
label: () => {
|
||||
const enabled = options.preferences().autoCleanupBlankSessions
|
||||
const state = enabled ? tGlobal("commands.common.enabled") : tGlobal("commands.common.disabled")
|
||||
return tGlobal("commands.autoCleanupBlankSessions.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.autoCleanupBlankSessions.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.autoCleanupBlankSessions.keywords"),
|
||||
action: options.toggleAutoCleanupBlankSessions,
|
||||
registerBehaviorCommands((command) => commandRegistry.register(command), {
|
||||
preferences: options.preferences,
|
||||
toggleShowThinkingBlocks: options.toggleShowThinkingBlocks,
|
||||
toggleKeyboardShortcutHints: options.toggleKeyboardShortcutHints,
|
||||
toggleShowTimelineTools: options.toggleShowTimelineTools,
|
||||
toggleUsageMetrics: options.toggleUsageMetrics,
|
||||
toggleAutoCleanupBlankSessions: options.toggleAutoCleanupBlankSessions,
|
||||
togglePromptSubmitOnEnter: options.togglePromptSubmitOnEnter,
|
||||
setDiffViewMode: options.setDiffViewMode,
|
||||
setToolOutputExpansion: options.setToolOutputExpansion,
|
||||
setDiagnosticsExpansion: options.setDiagnosticsExpansion,
|
||||
setThinkingBlocksExpansion: options.setThinkingBlocksExpansion,
|
||||
setToolInputsVisibility: options.setToolInputsVisibility,
|
||||
})
|
||||
|
||||
commandRegistry.register({
|
||||
|
||||
@@ -57,7 +57,6 @@ export const settingsMessages = {
|
||||
"contextUsagePanel.unavailable": "--",
|
||||
|
||||
"settings.title": "Settings",
|
||||
"settings.description": "Manage appearance, notifications, remote access, and OpenCode runtime options.",
|
||||
"settings.navigationAriaLabel": "Settings sections",
|
||||
"settings.close": "Close settings",
|
||||
"settings.content.eyebrow": "Workspace preferences",
|
||||
@@ -70,6 +69,7 @@ export const settingsMessages = {
|
||||
"settings.scope.device": "This device",
|
||||
"settings.scope.server": "Server setting",
|
||||
"settings.common.enabled": "Enabled",
|
||||
"settings.common.disabled": "Disabled",
|
||||
"settings.section.appearance.title": "Appearance",
|
||||
"settings.section.appearance.subtitle": "Adjust how the app looks on this device.",
|
||||
"settings.appearance.theme.title": "Theme",
|
||||
@@ -112,4 +112,31 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
|
||||
"settings.appearance.behavior.title": "Interaction",
|
||||
"settings.appearance.behavior.subtitle": "Message, diff, and input defaults.",
|
||||
"settings.behavior.keyboardHints.title": "Keyboard shortcut hints",
|
||||
"settings.behavior.keyboardHints.subtitle": "Show keyboard shortcut hints across the UI.",
|
||||
"settings.behavior.thinking.title": "Thinking sections",
|
||||
"settings.behavior.thinking.subtitle": "Show or hide AI thinking sections in messages.",
|
||||
"settings.behavior.thinkingDefault.title": "Thinking default",
|
||||
"settings.behavior.thinkingDefault.subtitle": "Choose whether thinking sections start expanded or collapsed.",
|
||||
"settings.behavior.timelineTools.title": "Timeline tool calls",
|
||||
"settings.behavior.timelineTools.subtitle": "Show or hide tool call entries in the message timeline.",
|
||||
"settings.behavior.diffView.title": "Diff view",
|
||||
"settings.behavior.diffView.subtitle": "Choose how tool-call diffs are displayed.",
|
||||
"settings.behavior.diffView.option.split": "Split",
|
||||
"settings.behavior.diffView.option.unified": "Unified",
|
||||
"settings.behavior.toolOutputsDefault.title": "Tool outputs default",
|
||||
"settings.behavior.toolOutputsDefault.subtitle": "Choose whether tool outputs start expanded or collapsed.",
|
||||
"settings.behavior.diagnosticsDefault.title": "Diagnostics default",
|
||||
"settings.behavior.diagnosticsDefault.subtitle": "Choose whether diagnostics output starts expanded or collapsed.",
|
||||
"settings.behavior.toolInputsVisibility.title": "Tool inputs visibility",
|
||||
"settings.behavior.toolInputsVisibility.subtitle": "Set default visibility for tool call input arguments.",
|
||||
"settings.behavior.usageMetrics.title": "Token usage metrics",
|
||||
"settings.behavior.usageMetrics.subtitle": "Show or hide token and cost stats for assistant messages.",
|
||||
"settings.behavior.autoCleanup.title": "Auto-cleanup blank sessions",
|
||||
"settings.behavior.autoCleanup.subtitle": "Automatically clean up blank sessions when creating new ones.",
|
||||
"settings.behavior.promptSubmit.title": "Enter to submit",
|
||||
"settings.behavior.promptSubmit.subtitle": "Use Enter to submit prompts; Cmd/Ctrl+Enter inserts a new line.",
|
||||
} as const
|
||||
|
||||
@@ -57,7 +57,6 @@ export const settingsMessages = {
|
||||
"contextUsagePanel.unavailable": "--",
|
||||
|
||||
"settings.title": "Settings",
|
||||
"settings.description": "Manage appearance, notifications, remote access, and OpenCode runtime options.",
|
||||
"settings.navigationAriaLabel": "Settings sections",
|
||||
"settings.close": "Close settings",
|
||||
"settings.content.eyebrow": "Workspace preferences",
|
||||
@@ -70,6 +69,7 @@ export const settingsMessages = {
|
||||
"settings.scope.device": "This device",
|
||||
"settings.scope.server": "Server setting",
|
||||
"settings.common.enabled": "Enabled",
|
||||
"settings.common.disabled": "Desactivado",
|
||||
"settings.section.appearance.title": "Appearance",
|
||||
"settings.section.appearance.subtitle": "Adjust how the app looks on this device.",
|
||||
"settings.appearance.theme.title": "Theme",
|
||||
@@ -112,4 +112,31 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
|
||||
"settings.appearance.behavior.title": "Interaccion",
|
||||
"settings.appearance.behavior.subtitle": "Valores predeterminados de mensajes, diffs y entrada.",
|
||||
"settings.behavior.keyboardHints.title": "Sugerencias de atajos de teclado",
|
||||
"settings.behavior.keyboardHints.subtitle": "Muestra sugerencias de atajos de teclado en toda la interfaz.",
|
||||
"settings.behavior.thinking.title": "Secciones de pensamiento",
|
||||
"settings.behavior.thinking.subtitle": "Muestra u oculta las secciones de pensamiento de la IA en los mensajes.",
|
||||
"settings.behavior.thinkingDefault.title": "Pensamiento por defecto",
|
||||
"settings.behavior.thinkingDefault.subtitle": "Elige si las secciones de pensamiento comienzan expandidas o contraidas.",
|
||||
"settings.behavior.timelineTools.title": "Llamadas de herramientas en la linea de tiempo",
|
||||
"settings.behavior.timelineTools.subtitle": "Muestra u oculta entradas de llamadas de herramientas en la linea de tiempo de mensajes.",
|
||||
"settings.behavior.diffView.title": "Vista de diferencias",
|
||||
"settings.behavior.diffView.subtitle": "Elige como se muestran los diffs de llamadas de herramientas.",
|
||||
"settings.behavior.diffView.option.split": "Dividida",
|
||||
"settings.behavior.diffView.option.unified": "Unificada",
|
||||
"settings.behavior.toolOutputsDefault.title": "Salidas de herramientas por defecto",
|
||||
"settings.behavior.toolOutputsDefault.subtitle": "Elige si las salidas de herramientas comienzan expandidas o contraidas.",
|
||||
"settings.behavior.diagnosticsDefault.title": "Diagnosticos por defecto",
|
||||
"settings.behavior.diagnosticsDefault.subtitle": "Elige si la salida de diagnosticos comienza expandida o contraida.",
|
||||
"settings.behavior.toolInputsVisibility.title": "Visibilidad de entradas de herramientas",
|
||||
"settings.behavior.toolInputsVisibility.subtitle": "Establece la visibilidad por defecto de los argumentos de entrada de las llamadas de herramientas.",
|
||||
"settings.behavior.usageMetrics.title": "Metricas de uso de tokens",
|
||||
"settings.behavior.usageMetrics.subtitle": "Muestra u oculta estadisticas de tokens y costo en mensajes del asistente.",
|
||||
"settings.behavior.autoCleanup.title": "Limpieza automatica de sesiones en blanco",
|
||||
"settings.behavior.autoCleanup.subtitle": "Limpia automaticamente las sesiones en blanco al crear nuevas.",
|
||||
"settings.behavior.promptSubmit.title": "Enter para enviar",
|
||||
"settings.behavior.promptSubmit.subtitle": "Usa Enter para enviar; Cmd/Ctrl+Enter inserta una nueva linea.",
|
||||
} as const
|
||||
|
||||
@@ -57,7 +57,6 @@ export const settingsMessages = {
|
||||
"contextUsagePanel.unavailable": "--",
|
||||
|
||||
"settings.title": "Settings",
|
||||
"settings.description": "Manage appearance, notifications, remote access, and OpenCode runtime options.",
|
||||
"settings.navigationAriaLabel": "Settings sections",
|
||||
"settings.close": "Close settings",
|
||||
"settings.content.eyebrow": "Workspace preferences",
|
||||
@@ -70,6 +69,7 @@ export const settingsMessages = {
|
||||
"settings.scope.device": "This device",
|
||||
"settings.scope.server": "Server setting",
|
||||
"settings.common.enabled": "Enabled",
|
||||
"settings.common.disabled": "Desactive",
|
||||
"settings.section.appearance.title": "Appearance",
|
||||
"settings.section.appearance.subtitle": "Adjust how the app looks on this device.",
|
||||
"settings.appearance.theme.title": "Theme",
|
||||
@@ -112,4 +112,31 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
|
||||
"settings.appearance.behavior.title": "Interaction",
|
||||
"settings.appearance.behavior.subtitle": "Parametres par defaut pour les messages, les diffs et la saisie.",
|
||||
"settings.behavior.keyboardHints.title": "Indications de raccourcis clavier",
|
||||
"settings.behavior.keyboardHints.subtitle": "Afficher des indications de raccourcis clavier dans toute l'interface.",
|
||||
"settings.behavior.thinking.title": "Sections de reflexion",
|
||||
"settings.behavior.thinking.subtitle": "Afficher ou masquer les sections de reflexion de l'IA dans les messages.",
|
||||
"settings.behavior.thinkingDefault.title": "Etat initial de la reflexion",
|
||||
"settings.behavior.thinkingDefault.subtitle": "Choisir si les sections de reflexion commencent developpees ou reduites.",
|
||||
"settings.behavior.timelineTools.title": "Appels d'outils dans la chronologie",
|
||||
"settings.behavior.timelineTools.subtitle": "Afficher ou masquer les entrees d'appels d'outils dans la chronologie des messages.",
|
||||
"settings.behavior.diffView.title": "Vue du diff",
|
||||
"settings.behavior.diffView.subtitle": "Choisir comment les diffs des appels d'outils sont affiches.",
|
||||
"settings.behavior.diffView.option.split": "Scinde",
|
||||
"settings.behavior.diffView.option.unified": "Unifie",
|
||||
"settings.behavior.toolOutputsDefault.title": "Etat initial des sorties d'outils",
|
||||
"settings.behavior.toolOutputsDefault.subtitle": "Choisir si les sorties d'outils commencent developpees ou reduites.",
|
||||
"settings.behavior.diagnosticsDefault.title": "Etat initial des diagnostics",
|
||||
"settings.behavior.diagnosticsDefault.subtitle": "Choisir si la sortie des diagnostics commence developpee ou reduite.",
|
||||
"settings.behavior.toolInputsVisibility.title": "Visibilite des entrees d'outils",
|
||||
"settings.behavior.toolInputsVisibility.subtitle": "Definir la visibilite par defaut des arguments d'entree des appels d'outils.",
|
||||
"settings.behavior.usageMetrics.title": "Metriques d'utilisation des tokens",
|
||||
"settings.behavior.usageMetrics.subtitle": "Afficher ou masquer les stats de tokens et de cout pour les messages de l'assistant.",
|
||||
"settings.behavior.autoCleanup.title": "Nettoyage auto des sessions vides",
|
||||
"settings.behavior.autoCleanup.subtitle": "Nettoyer automatiquement les sessions vides lors de la creation de nouvelles.",
|
||||
"settings.behavior.promptSubmit.title": "Entrer pour envoyer",
|
||||
"settings.behavior.promptSubmit.subtitle": "Utiliser Entrer pour envoyer; Cmd/Ctrl+Entrer insere une nouvelle ligne.",
|
||||
} as const
|
||||
|
||||
@@ -57,7 +57,6 @@ export const settingsMessages = {
|
||||
"contextUsagePanel.unavailable": "--",
|
||||
|
||||
"settings.title": "Settings",
|
||||
"settings.description": "Manage appearance, notifications, remote access, and OpenCode runtime options.",
|
||||
"settings.navigationAriaLabel": "Settings sections",
|
||||
"settings.close": "Close settings",
|
||||
"settings.content.eyebrow": "Workspace preferences",
|
||||
@@ -70,6 +69,7 @@ export const settingsMessages = {
|
||||
"settings.scope.device": "This device",
|
||||
"settings.scope.server": "Server setting",
|
||||
"settings.common.enabled": "Enabled",
|
||||
"settings.common.disabled": "無効",
|
||||
"settings.section.appearance.title": "Appearance",
|
||||
"settings.section.appearance.subtitle": "Adjust how the app looks on this device.",
|
||||
"settings.appearance.theme.title": "Theme",
|
||||
@@ -112,4 +112,31 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
|
||||
"settings.appearance.behavior.title": "操作",
|
||||
"settings.appearance.behavior.subtitle": "メッセージ、差分、入力の既定値。",
|
||||
"settings.behavior.keyboardHints.title": "キーボードショートカットのヒント",
|
||||
"settings.behavior.keyboardHints.subtitle": "UI全体でキーボードショートカットのヒントを表示します。",
|
||||
"settings.behavior.thinking.title": "思考セクション",
|
||||
"settings.behavior.thinking.subtitle": "メッセージ内のAIの思考セクションを表示/非表示にします。",
|
||||
"settings.behavior.thinkingDefault.title": "思考の既定",
|
||||
"settings.behavior.thinkingDefault.subtitle": "思考セクションを最初に展開/折りたたみのどちらで表示するかを選びます。",
|
||||
"settings.behavior.timelineTools.title": "タイムラインのツール呼び出し",
|
||||
"settings.behavior.timelineTools.subtitle": "メッセージタイムラインでツール呼び出しを表示/非表示にします。",
|
||||
"settings.behavior.diffView.title": "差分表示",
|
||||
"settings.behavior.diffView.subtitle": "ツール呼び出しの差分の表示方法を選びます。",
|
||||
"settings.behavior.diffView.option.split": "分割",
|
||||
"settings.behavior.diffView.option.unified": "統合",
|
||||
"settings.behavior.toolOutputsDefault.title": "ツール出力の既定",
|
||||
"settings.behavior.toolOutputsDefault.subtitle": "ツール出力を最初に展開/折りたたみのどちらで表示するかを選びます。",
|
||||
"settings.behavior.diagnosticsDefault.title": "診断の既定",
|
||||
"settings.behavior.diagnosticsDefault.subtitle": "診断出力を最初に展開/折りたたみのどちらで表示するかを選びます。",
|
||||
"settings.behavior.toolInputsVisibility.title": "ツール入力の表示",
|
||||
"settings.behavior.toolInputsVisibility.subtitle": "ツール呼び出しの入力引数の既定の表示状態を設定します。",
|
||||
"settings.behavior.usageMetrics.title": "トークン使用量メトリクス",
|
||||
"settings.behavior.usageMetrics.subtitle": "アシスタントのメッセージにトークン数とコストの統計を表示/非表示にします。",
|
||||
"settings.behavior.autoCleanup.title": "空のセッションを自動クリーンアップ",
|
||||
"settings.behavior.autoCleanup.subtitle": "新しいセッション作成時に空のセッションを自動的にクリーンアップします。",
|
||||
"settings.behavior.promptSubmit.title": "Enterで送信",
|
||||
"settings.behavior.promptSubmit.subtitle": "Enterで送信し、Cmd/Ctrl+Enterで改行します。",
|
||||
} as const
|
||||
|
||||
@@ -57,7 +57,6 @@ export const settingsMessages = {
|
||||
"contextUsagePanel.unavailable": "--",
|
||||
|
||||
"settings.title": "Settings",
|
||||
"settings.description": "Manage appearance, notifications, remote access, and OpenCode runtime options.",
|
||||
"settings.navigationAriaLabel": "Settings sections",
|
||||
"settings.close": "Close settings",
|
||||
"settings.content.eyebrow": "Workspace preferences",
|
||||
@@ -70,6 +69,7 @@ export const settingsMessages = {
|
||||
"settings.scope.device": "This device",
|
||||
"settings.scope.server": "Server setting",
|
||||
"settings.common.enabled": "Enabled",
|
||||
"settings.common.disabled": "Отключено",
|
||||
"settings.section.appearance.title": "Appearance",
|
||||
"settings.section.appearance.subtitle": "Adjust how the app looks on this device.",
|
||||
"settings.appearance.theme.title": "Theme",
|
||||
@@ -112,4 +112,31 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
|
||||
"settings.appearance.behavior.title": "Взаимодействие",
|
||||
"settings.appearance.behavior.subtitle": "Значения по умолчанию для сообщений, диффов и ввода.",
|
||||
"settings.behavior.keyboardHints.title": "Подсказки сочетаний клавиш",
|
||||
"settings.behavior.keyboardHints.subtitle": "Показывать подсказки сочетаний клавиш по всему интерфейсу.",
|
||||
"settings.behavior.thinking.title": "Разделы размышлений",
|
||||
"settings.behavior.thinking.subtitle": "Показывать или скрывать разделы размышлений ИИ в сообщениях.",
|
||||
"settings.behavior.thinkingDefault.title": "Размышления по умолчанию",
|
||||
"settings.behavior.thinkingDefault.subtitle": "Выберите, начинать ли разделы размышлений развернутыми или свернутыми.",
|
||||
"settings.behavior.timelineTools.title": "Вызовы инструментов в таймлайне",
|
||||
"settings.behavior.timelineTools.subtitle": "Показывать или скрывать записи вызовов инструментов в таймлайне сообщений.",
|
||||
"settings.behavior.diffView.title": "Вид диффа",
|
||||
"settings.behavior.diffView.subtitle": "Выберите, как отображаются диффы вызовов инструментов.",
|
||||
"settings.behavior.diffView.option.split": "Раздельный",
|
||||
"settings.behavior.diffView.option.unified": "Единый",
|
||||
"settings.behavior.toolOutputsDefault.title": "Выводы инструментов по умолчанию",
|
||||
"settings.behavior.toolOutputsDefault.subtitle": "Выберите, начинать ли выводы инструментов развернутыми или свернутыми.",
|
||||
"settings.behavior.diagnosticsDefault.title": "Диагностика по умолчанию",
|
||||
"settings.behavior.diagnosticsDefault.subtitle": "Выберите, начинать ли вывод диагностики развернутым или свернутым.",
|
||||
"settings.behavior.toolInputsVisibility.title": "Видимость входных данных инструмента",
|
||||
"settings.behavior.toolInputsVisibility.subtitle": "Задайте видимость по умолчанию для входных аргументов вызовов инструментов.",
|
||||
"settings.behavior.usageMetrics.title": "Метрики использования токенов",
|
||||
"settings.behavior.usageMetrics.subtitle": "Показывать или скрывать статистику токенов и стоимости в сообщениях ассистента.",
|
||||
"settings.behavior.autoCleanup.title": "Автоочистка пустых сессий",
|
||||
"settings.behavior.autoCleanup.subtitle": "Автоматически очищать пустые сессии при создании новых.",
|
||||
"settings.behavior.promptSubmit.title": "Enter для отправки",
|
||||
"settings.behavior.promptSubmit.subtitle": "Enter отправляет; Cmd/Ctrl+Enter вставляет новую строку.",
|
||||
} as const
|
||||
|
||||
@@ -57,7 +57,6 @@ export const settingsMessages = {
|
||||
"contextUsagePanel.unavailable": "--",
|
||||
|
||||
"settings.title": "Settings",
|
||||
"settings.description": "Manage appearance, notifications, remote access, and OpenCode runtime options.",
|
||||
"settings.navigationAriaLabel": "Settings sections",
|
||||
"settings.close": "Close settings",
|
||||
"settings.content.eyebrow": "Workspace preferences",
|
||||
@@ -70,6 +69,7 @@ export const settingsMessages = {
|
||||
"settings.scope.device": "This device",
|
||||
"settings.scope.server": "Server setting",
|
||||
"settings.common.enabled": "Enabled",
|
||||
"settings.common.disabled": "已禁用",
|
||||
"settings.section.appearance.title": "Appearance",
|
||||
"settings.section.appearance.subtitle": "Adjust how the app looks on this device.",
|
||||
"settings.appearance.theme.title": "Theme",
|
||||
@@ -112,4 +112,31 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
|
||||
"settings.appearance.behavior.title": "交互",
|
||||
"settings.appearance.behavior.subtitle": "消息、差异与输入的默认值。",
|
||||
"settings.behavior.keyboardHints.title": "键盘快捷键提示",
|
||||
"settings.behavior.keyboardHints.subtitle": "在整个界面中显示键盘快捷键提示。",
|
||||
"settings.behavior.thinking.title": "思考区块",
|
||||
"settings.behavior.thinking.subtitle": "在消息中显示或隐藏AI的思考区块。",
|
||||
"settings.behavior.thinkingDefault.title": "思考默认状态",
|
||||
"settings.behavior.thinkingDefault.subtitle": "选择思考区块默认是展开还是折叠。",
|
||||
"settings.behavior.timelineTools.title": "时间线工具调用",
|
||||
"settings.behavior.timelineTools.subtitle": "在消息时间线中显示或隐藏工具调用条目。",
|
||||
"settings.behavior.diffView.title": "差异视图",
|
||||
"settings.behavior.diffView.subtitle": "选择工具调用差异的显示方式。",
|
||||
"settings.behavior.diffView.option.split": "分栏",
|
||||
"settings.behavior.diffView.option.unified": "统一",
|
||||
"settings.behavior.toolOutputsDefault.title": "工具输出默认状态",
|
||||
"settings.behavior.toolOutputsDefault.subtitle": "选择工具输出默认是展开还是折叠。",
|
||||
"settings.behavior.diagnosticsDefault.title": "诊断默认状态",
|
||||
"settings.behavior.diagnosticsDefault.subtitle": "选择诊断输出默认是展开还是折叠。",
|
||||
"settings.behavior.toolInputsVisibility.title": "工具输入可见性",
|
||||
"settings.behavior.toolInputsVisibility.subtitle": "设置工具调用输入参数的默认可见性。",
|
||||
"settings.behavior.usageMetrics.title": "令牌用量指标",
|
||||
"settings.behavior.usageMetrics.subtitle": "显示或隐藏助手消息的令牌与成本统计。",
|
||||
"settings.behavior.autoCleanup.title": "自动清理空会话",
|
||||
"settings.behavior.autoCleanup.subtitle": "创建新会话时自动清理空会话。",
|
||||
"settings.behavior.promptSubmit.title": "回车发送",
|
||||
"settings.behavior.promptSubmit.subtitle": "使用回车发送;Cmd/Ctrl+回车插入新行。",
|
||||
} as const
|
||||
|
||||
452
packages/ui/src/lib/settings/behavior-registry.ts
Normal file
452
packages/ui/src/lib/settings/behavior-registry.ts
Normal file
@@ -0,0 +1,452 @@
|
||||
import type { Accessor } from "solid-js"
|
||||
import type {
|
||||
Preferences,
|
||||
ExpansionPreference,
|
||||
ToolInputsVisibilityPreference,
|
||||
} from "../../stores/preferences"
|
||||
import type { Command } from "../commands"
|
||||
import { tGlobal } from "../i18n"
|
||||
import { runtimeEnv } from "../runtime-env"
|
||||
|
||||
export type BehaviorSettingKind = "toggle" | "enum"
|
||||
|
||||
export type BehaviorToggleSetting = {
|
||||
kind: "toggle"
|
||||
id: string
|
||||
titleKey: string
|
||||
subtitleKey: string
|
||||
get: (preferences: Preferences) => boolean
|
||||
set: (next: boolean) => void
|
||||
disabled?: () => boolean
|
||||
}
|
||||
|
||||
export type BehaviorEnumSetting<T extends string = string> = {
|
||||
kind: "enum"
|
||||
id: string
|
||||
titleKey: string
|
||||
subtitleKey: string
|
||||
get: (preferences: Preferences) => T
|
||||
set: (next: T) => void
|
||||
options: Array<{ value: T; labelKey: string }>
|
||||
disabled?: () => boolean
|
||||
}
|
||||
|
||||
export type BehaviorSetting = BehaviorToggleSetting | BehaviorEnumSetting
|
||||
|
||||
export type BehaviorRegistryActions = {
|
||||
preferences: Accessor<Preferences>
|
||||
updatePreferences?: (updates: Partial<Preferences>) => void
|
||||
toggleShowThinkingBlocks: () => void
|
||||
toggleKeyboardShortcutHints: () => void
|
||||
toggleShowTimelineTools: () => void
|
||||
toggleUsageMetrics: () => void
|
||||
toggleAutoCleanupBlankSessions: () => void
|
||||
togglePromptSubmitOnEnter: () => void
|
||||
setDiffViewMode: (mode: "split" | "unified") => void
|
||||
setToolOutputExpansion: (mode: ExpansionPreference) => void
|
||||
setDiagnosticsExpansion: (mode: ExpansionPreference) => void
|
||||
setThinkingBlocksExpansion: (mode: ExpansionPreference) => void
|
||||
setToolInputsVisibility: (mode: ToolInputsVisibilityPreference) => void
|
||||
}
|
||||
|
||||
function splitKeywords(key: string): string[] {
|
||||
return tGlobal(key)
|
||||
.split(",")
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function setBooleanByToggle(getCurrent: () => boolean, toggle: () => void, next: boolean) {
|
||||
if (getCurrent() === next) return
|
||||
toggle()
|
||||
}
|
||||
|
||||
export function getBehaviorSettings(actions: BehaviorRegistryActions): BehaviorSetting[] {
|
||||
const prefs = actions.preferences
|
||||
const updatePreferences = actions.updatePreferences
|
||||
|
||||
return [
|
||||
{
|
||||
kind: "toggle",
|
||||
id: "behavior.keyboardShortcutHints",
|
||||
titleKey: "settings.behavior.keyboardHints.title",
|
||||
subtitleKey: "settings.behavior.keyboardHints.subtitle",
|
||||
get: (p) => Boolean(p.showKeyboardShortcutHints ?? true),
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ showKeyboardShortcutHints: next })
|
||||
return
|
||||
}
|
||||
setBooleanByToggle(
|
||||
() => Boolean(prefs().showKeyboardShortcutHints ?? true),
|
||||
actions.toggleKeyboardShortcutHints,
|
||||
next,
|
||||
)
|
||||
},
|
||||
disabled: () => runtimeEnv.host === "web",
|
||||
},
|
||||
{
|
||||
kind: "toggle",
|
||||
id: "behavior.thinkingBlocks",
|
||||
titleKey: "settings.behavior.thinking.title",
|
||||
subtitleKey: "settings.behavior.thinking.subtitle",
|
||||
get: (p) => Boolean(p.showThinkingBlocks),
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ showThinkingBlocks: next })
|
||||
return
|
||||
}
|
||||
setBooleanByToggle(
|
||||
() => Boolean(prefs().showThinkingBlocks),
|
||||
actions.toggleShowThinkingBlocks,
|
||||
next,
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "enum",
|
||||
id: "behavior.thinkingBlocksDefault",
|
||||
titleKey: "settings.behavior.thinkingDefault.title",
|
||||
subtitleKey: "settings.behavior.thinkingDefault.subtitle",
|
||||
get: (p) => (p.thinkingBlocksExpansion ?? "expanded") as ExpansionPreference,
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ thinkingBlocksExpansion: next as ExpansionPreference })
|
||||
return
|
||||
}
|
||||
actions.setThinkingBlocksExpansion(next as ExpansionPreference)
|
||||
},
|
||||
options: [
|
||||
{ value: "expanded", labelKey: "commands.common.expanded" },
|
||||
{ value: "collapsed", labelKey: "commands.common.collapsed" },
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "toggle",
|
||||
id: "behavior.timelineToolCalls",
|
||||
titleKey: "settings.behavior.timelineTools.title",
|
||||
subtitleKey: "settings.behavior.timelineTools.subtitle",
|
||||
get: (p) => Boolean(p.showTimelineTools),
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ showTimelineTools: next })
|
||||
return
|
||||
}
|
||||
setBooleanByToggle(
|
||||
() => Boolean(prefs().showTimelineTools),
|
||||
actions.toggleShowTimelineTools,
|
||||
next,
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "enum",
|
||||
id: "behavior.diffViewMode",
|
||||
titleKey: "settings.behavior.diffView.title",
|
||||
subtitleKey: "settings.behavior.diffView.subtitle",
|
||||
get: (p) => (p.diffViewMode ?? "split") as "split" | "unified",
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ diffViewMode: next as "split" | "unified" })
|
||||
return
|
||||
}
|
||||
actions.setDiffViewMode(next as "split" | "unified")
|
||||
},
|
||||
options: [
|
||||
{ value: "split", labelKey: "settings.behavior.diffView.option.split" },
|
||||
{ value: "unified", labelKey: "settings.behavior.diffView.option.unified" },
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "enum",
|
||||
id: "behavior.toolOutputsDefault",
|
||||
titleKey: "settings.behavior.toolOutputsDefault.title",
|
||||
subtitleKey: "settings.behavior.toolOutputsDefault.subtitle",
|
||||
get: (p) => (p.toolOutputExpansion ?? "expanded") as ExpansionPreference,
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ toolOutputExpansion: next as ExpansionPreference })
|
||||
return
|
||||
}
|
||||
actions.setToolOutputExpansion(next as ExpansionPreference)
|
||||
},
|
||||
options: [
|
||||
{ value: "expanded", labelKey: "commands.common.expanded" },
|
||||
{ value: "collapsed", labelKey: "commands.common.collapsed" },
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "enum",
|
||||
id: "behavior.diagnosticsDefault",
|
||||
titleKey: "settings.behavior.diagnosticsDefault.title",
|
||||
subtitleKey: "settings.behavior.diagnosticsDefault.subtitle",
|
||||
get: (p) => (p.diagnosticsExpansion ?? "expanded") as ExpansionPreference,
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ diagnosticsExpansion: next as ExpansionPreference })
|
||||
return
|
||||
}
|
||||
actions.setDiagnosticsExpansion(next as ExpansionPreference)
|
||||
},
|
||||
options: [
|
||||
{ value: "expanded", labelKey: "commands.common.expanded" },
|
||||
{ value: "collapsed", labelKey: "commands.common.collapsed" },
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "enum",
|
||||
id: "behavior.toolInputsVisibility",
|
||||
titleKey: "settings.behavior.toolInputsVisibility.title",
|
||||
subtitleKey: "settings.behavior.toolInputsVisibility.subtitle",
|
||||
get: (p) => (p.toolInputsVisibility ?? "hidden") as ToolInputsVisibilityPreference,
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ toolInputsVisibility: next as ToolInputsVisibilityPreference })
|
||||
return
|
||||
}
|
||||
actions.setToolInputsVisibility(next as ToolInputsVisibilityPreference)
|
||||
},
|
||||
options: [
|
||||
{ value: "hidden", labelKey: "commands.common.hidden" },
|
||||
{ value: "collapsed", labelKey: "commands.common.collapsed" },
|
||||
{ value: "expanded", labelKey: "commands.common.expanded" },
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "toggle",
|
||||
id: "behavior.usageMetrics",
|
||||
titleKey: "settings.behavior.usageMetrics.title",
|
||||
subtitleKey: "settings.behavior.usageMetrics.subtitle",
|
||||
get: (p) => Boolean(p.showUsageMetrics ?? true),
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ showUsageMetrics: next })
|
||||
return
|
||||
}
|
||||
setBooleanByToggle(
|
||||
() => Boolean(prefs().showUsageMetrics ?? true),
|
||||
actions.toggleUsageMetrics,
|
||||
next,
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "toggle",
|
||||
id: "behavior.autoCleanupBlankSessions",
|
||||
titleKey: "settings.behavior.autoCleanup.title",
|
||||
subtitleKey: "settings.behavior.autoCleanup.subtitle",
|
||||
get: (p) => Boolean(p.autoCleanupBlankSessions),
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ autoCleanupBlankSessions: next })
|
||||
return
|
||||
}
|
||||
setBooleanByToggle(
|
||||
() => Boolean(prefs().autoCleanupBlankSessions),
|
||||
actions.toggleAutoCleanupBlankSessions,
|
||||
next,
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "toggle",
|
||||
id: "behavior.promptSubmitOnEnter",
|
||||
titleKey: "settings.behavior.promptSubmit.title",
|
||||
subtitleKey: "settings.behavior.promptSubmit.subtitle",
|
||||
get: (p) => Boolean(p.promptSubmitOnEnter),
|
||||
set: (next) => {
|
||||
if (updatePreferences) {
|
||||
updatePreferences({ promptSubmitOnEnter: next })
|
||||
return
|
||||
}
|
||||
setBooleanByToggle(
|
||||
() => Boolean(prefs().promptSubmitOnEnter),
|
||||
actions.togglePromptSubmitOnEnter,
|
||||
next,
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export function getBehaviorCommands(actions: BehaviorRegistryActions): Command[] {
|
||||
return [
|
||||
{
|
||||
id: "prompt-submit-shortcut",
|
||||
label: () =>
|
||||
actions.preferences().promptSubmitOnEnter
|
||||
? tGlobal("commands.promptSubmitShortcut.label.swapped")
|
||||
: tGlobal("commands.promptSubmitShortcut.label.default"),
|
||||
description: () => tGlobal("commands.promptSubmitShortcut.description"),
|
||||
category: "Input & Focus",
|
||||
keywords: () => splitKeywords("commands.promptSubmitShortcut.keywords"),
|
||||
action: actions.togglePromptSubmitOnEnter,
|
||||
},
|
||||
{
|
||||
id: "thinking",
|
||||
label: () =>
|
||||
tGlobal(
|
||||
actions.preferences().showThinkingBlocks
|
||||
? "commands.thinkingBlocks.label.hide"
|
||||
: "commands.thinkingBlocks.label.show",
|
||||
),
|
||||
description: () => tGlobal("commands.thinkingBlocks.description"),
|
||||
category: "System",
|
||||
keywords: () => ["/thinking", ...splitKeywords("commands.thinkingBlocks.keywords")],
|
||||
action: actions.toggleShowThinkingBlocks,
|
||||
},
|
||||
{
|
||||
id: "timeline-tools",
|
||||
label: () =>
|
||||
tGlobal(
|
||||
actions.preferences().showTimelineTools
|
||||
? "commands.timelineToolCalls.label.hide"
|
||||
: "commands.timelineToolCalls.label.show",
|
||||
),
|
||||
description: () => tGlobal("commands.timelineToolCalls.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.timelineToolCalls.keywords"),
|
||||
action: actions.toggleShowTimelineTools,
|
||||
},
|
||||
{
|
||||
id: "keyboard-shortcut-hints",
|
||||
label: () =>
|
||||
tGlobal(
|
||||
actions.preferences().showKeyboardShortcutHints
|
||||
? "commands.keyboardShortcutHints.label.hide"
|
||||
: "commands.keyboardShortcutHints.label.show",
|
||||
),
|
||||
description: () =>
|
||||
tGlobal(
|
||||
runtimeEnv.host === "web"
|
||||
? "commands.keyboardShortcutHints.description.disabledWeb"
|
||||
: "commands.keyboardShortcutHints.description",
|
||||
),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.keyboardShortcutHints.keywords"),
|
||||
disabled: () => runtimeEnv.host === "web",
|
||||
action: actions.toggleKeyboardShortcutHints,
|
||||
},
|
||||
{
|
||||
id: "thinking-default-visibility",
|
||||
label: () => {
|
||||
const mode = actions.preferences().thinkingBlocksExpansion ?? "expanded"
|
||||
const state = mode === "expanded" ? tGlobal("commands.common.expanded") : tGlobal("commands.common.collapsed")
|
||||
return tGlobal("commands.thinkingBlocksDefault.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.thinkingBlocksDefault.description"),
|
||||
category: "System",
|
||||
keywords: () => ["/thinking", ...splitKeywords("commands.thinkingBlocksDefault.keywords")],
|
||||
action: () => {
|
||||
const mode = actions.preferences().thinkingBlocksExpansion ?? "expanded"
|
||||
const next: ExpansionPreference = mode === "expanded" ? "collapsed" : "expanded"
|
||||
actions.setThinkingBlocksExpansion(next)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "diff-view-split",
|
||||
label: () => {
|
||||
const prefix = (actions.preferences().diffViewMode || "split") === "split" ? "✓ " : ""
|
||||
return `${prefix}${tGlobal("commands.diffViewSplit.label")}`
|
||||
},
|
||||
description: () => tGlobal("commands.diffViewSplit.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.diffViewSplit.keywords"),
|
||||
action: () => actions.setDiffViewMode("split"),
|
||||
},
|
||||
{
|
||||
id: "diff-view-unified",
|
||||
label: () => {
|
||||
const prefix = (actions.preferences().diffViewMode || "split") === "unified" ? "✓ " : ""
|
||||
return `${prefix}${tGlobal("commands.diffViewUnified.label")}`
|
||||
},
|
||||
description: () => tGlobal("commands.diffViewUnified.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.diffViewUnified.keywords"),
|
||||
action: () => actions.setDiffViewMode("unified"),
|
||||
},
|
||||
{
|
||||
id: "tool-output-default-visibility",
|
||||
label: () => {
|
||||
const mode = actions.preferences().toolOutputExpansion || "expanded"
|
||||
const state = mode === "expanded" ? tGlobal("commands.common.expanded") : tGlobal("commands.common.collapsed")
|
||||
return tGlobal("commands.toolOutputsDefault.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.toolOutputsDefault.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.toolOutputsDefault.keywords"),
|
||||
action: () => {
|
||||
const mode = actions.preferences().toolOutputExpansion || "expanded"
|
||||
const next: ExpansionPreference = mode === "expanded" ? "collapsed" : "expanded"
|
||||
actions.setToolOutputExpansion(next)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "diagnostics-default-visibility",
|
||||
label: () => {
|
||||
const mode = actions.preferences().diagnosticsExpansion || "expanded"
|
||||
const state = mode === "expanded" ? tGlobal("commands.common.expanded") : tGlobal("commands.common.collapsed")
|
||||
return tGlobal("commands.diagnosticsDefault.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.diagnosticsDefault.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.diagnosticsDefault.keywords"),
|
||||
action: () => {
|
||||
const mode = actions.preferences().diagnosticsExpansion || "expanded"
|
||||
const next: ExpansionPreference = mode === "expanded" ? "collapsed" : "expanded"
|
||||
actions.setDiagnosticsExpansion(next)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "tool-inputs-visibility",
|
||||
label: () => {
|
||||
const mode = actions.preferences().toolInputsVisibility || "hidden"
|
||||
const state =
|
||||
mode === "expanded"
|
||||
? tGlobal("commands.common.expanded")
|
||||
: mode === "collapsed"
|
||||
? tGlobal("commands.common.collapsed")
|
||||
: tGlobal("commands.common.hidden")
|
||||
return tGlobal("commands.toolInputsVisibility.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.toolInputsVisibility.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.toolInputsVisibility.keywords"),
|
||||
action: () => {
|
||||
const mode = actions.preferences().toolInputsVisibility || "hidden"
|
||||
const next: ToolInputsVisibilityPreference =
|
||||
mode === "hidden" ? "collapsed" : mode === "collapsed" ? "expanded" : "hidden"
|
||||
actions.setToolInputsVisibility(next)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "token-usage-visibility",
|
||||
label: () => {
|
||||
const visible = actions.preferences().showUsageMetrics ?? true
|
||||
const state = visible ? tGlobal("commands.common.visible") : tGlobal("commands.common.hidden")
|
||||
return tGlobal("commands.tokenUsageDisplay.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.tokenUsageDisplay.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.tokenUsageDisplay.keywords"),
|
||||
action: actions.toggleUsageMetrics,
|
||||
},
|
||||
{
|
||||
id: "auto-cleanup-blank-sessions",
|
||||
label: () => {
|
||||
const enabled = actions.preferences().autoCleanupBlankSessions
|
||||
const state = enabled ? tGlobal("commands.common.enabled") : tGlobal("commands.common.disabled")
|
||||
return tGlobal("commands.autoCleanupBlankSessions.label", { state })
|
||||
},
|
||||
description: () => tGlobal("commands.autoCleanupBlankSessions.description"),
|
||||
category: "System",
|
||||
keywords: () => splitKeywords("commands.autoCleanupBlankSessions.keywords"),
|
||||
action: actions.toggleAutoCleanupBlankSessions,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export function registerBehaviorCommands(register: (command: Command) => void, actions: BehaviorRegistryActions) {
|
||||
const commands = getBehaviorCommands(actions)
|
||||
commands.forEach((command) => register(command))
|
||||
}
|
||||
@@ -15,6 +15,9 @@
|
||||
ring-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.selector-trigger:disabled,
|
||||
.selector-trigger[aria-disabled="true"],
|
||||
.selector-trigger[data-disabled],
|
||||
.selector-trigger-disabled {
|
||||
@apply opacity-50 cursor-not-allowed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user