diff --git a/packages/ui/src/components/settings-screen.tsx b/packages/ui/src/components/settings-screen.tsx index 48fe45d1..f3776cbd 100644 --- a/packages/ui/src/components/settings-screen.tsx +++ b/packages/ui/src/components/settings-screen.tsx @@ -45,7 +45,6 @@ export const SettingsScreen: Component = () => {
{t("settings.title")} - {t("settings.description")}
diff --git a/packages/ui/src/components/settings/appearance-settings-section.tsx b/packages/ui/src/components/settings/appearance-settings-section.tsx index e8e5df25..7a634160 100644 --- a/packages/ui/src/components/settings/appearance-settings-section.tsx +++ b/packages/ui/src/components/settings/appearance-settings-section.tsx @@ -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>(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(() => [ + { 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 ( +
+
+
{t(setting.titleKey)}
+
{t(setting.subtitleKey)}
+
+ + 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) => ( + + {itemProps.item.rawValue.label} + + )} + > + +
+ > + {(state) => ( + + {state.selectedOption()?.label} + + )} + +
+ + + +
+ + + + + + + +
+ ) + } + + const enumSetting = setting as Extract + const options = createMemo(() => + 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 ( +
+
+
{t(setting.titleKey)}
+
{t(setting.subtitleKey)}
+
+ + 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) => ( + + {itemProps.item.rawValue.label} + + )} + > + +
+ > + {(state) => ( + + {state.selectedOption()?.label} + + )} + +
+ + + +
+ + + + + + + +
+ ) + } const modeLabel = (mode: ThemeMode) => { if (mode === "system") return t("theme.mode.system") @@ -54,6 +251,20 @@ export const AppearanceSettingsSection: Component = () => { })} + +
+
+
+

{t("settings.appearance.behavior.title")}

+

{t("settings.appearance.behavior.subtitle")}

+
+ {t("settings.scope.device")} +
+ +
+ {(setting) => } +
+
) } diff --git a/packages/ui/src/lib/hooks/use-commands.ts b/packages/ui/src/lib/hooks/use-commands.ts index 4105e274..a74fc840 100644 --- a/packages/ui/src/lib/hooks/use-commands.ts +++ b/packages/ui/src/lib/hooks/use-commands.ts @@ -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({ diff --git a/packages/ui/src/lib/i18n/messages/en/settings.ts b/packages/ui/src/lib/i18n/messages/en/settings.ts index 47c7d990..cbf95000 100644 --- a/packages/ui/src/lib/i18n/messages/en/settings.ts +++ b/packages/ui/src/lib/i18n/messages/en/settings.ts @@ -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 diff --git a/packages/ui/src/lib/i18n/messages/es/settings.ts b/packages/ui/src/lib/i18n/messages/es/settings.ts index 750eff3e..8a9cbe6c 100644 --- a/packages/ui/src/lib/i18n/messages/es/settings.ts +++ b/packages/ui/src/lib/i18n/messages/es/settings.ts @@ -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 diff --git a/packages/ui/src/lib/i18n/messages/fr/settings.ts b/packages/ui/src/lib/i18n/messages/fr/settings.ts index 7139acaa..9a7009ba 100644 --- a/packages/ui/src/lib/i18n/messages/fr/settings.ts +++ b/packages/ui/src/lib/i18n/messages/fr/settings.ts @@ -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 diff --git a/packages/ui/src/lib/i18n/messages/ja/settings.ts b/packages/ui/src/lib/i18n/messages/ja/settings.ts index 6cf894c0..6bc70aab 100644 --- a/packages/ui/src/lib/i18n/messages/ja/settings.ts +++ b/packages/ui/src/lib/i18n/messages/ja/settings.ts @@ -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 diff --git a/packages/ui/src/lib/i18n/messages/ru/settings.ts b/packages/ui/src/lib/i18n/messages/ru/settings.ts index cdd028a0..ce52f835 100644 --- a/packages/ui/src/lib/i18n/messages/ru/settings.ts +++ b/packages/ui/src/lib/i18n/messages/ru/settings.ts @@ -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 diff --git a/packages/ui/src/lib/i18n/messages/zh-Hans/settings.ts b/packages/ui/src/lib/i18n/messages/zh-Hans/settings.ts index fecd0a81..8451aeae 100644 --- a/packages/ui/src/lib/i18n/messages/zh-Hans/settings.ts +++ b/packages/ui/src/lib/i18n/messages/zh-Hans/settings.ts @@ -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 diff --git a/packages/ui/src/lib/settings/behavior-registry.ts b/packages/ui/src/lib/settings/behavior-registry.ts new file mode 100644 index 00000000..b7ab1b98 --- /dev/null +++ b/packages/ui/src/lib/settings/behavior-registry.ts @@ -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 = { + 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 + updatePreferences?: (updates: Partial) => 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)) +} diff --git a/packages/ui/src/styles/components/selector.css b/packages/ui/src/styles/components/selector.css index a65cce76..bcecda00 100644 --- a/packages/ui/src/styles/components/selector.css +++ b/packages/ui/src/styles/components/selector.css @@ -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; }