diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index b02861c1..5ad213a6 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -72,6 +72,7 @@ const App: Component = () => { setToolOutputExpansion, setDiagnosticsExpansion, setThinkingBlocksExpansion, + setToolInputsVisibility, } = useConfig() const [escapeInDebounce, setEscapeInDebounce] = createSignal(false) interface LaunchErrorState { @@ -402,6 +403,7 @@ const App: Component = () => { setToolOutputExpansion, setDiagnosticsExpansion, setThinkingBlocksExpansion, + setToolInputsVisibility, handleNewInstanceRequest, handleCloseInstance, handleNewSession, diff --git a/packages/ui/src/components/tool-call.tsx b/packages/ui/src/components/tool-call.tsx index 458de294..445a1250 100644 --- a/packages/ui/src/components/tool-call.tsx +++ b/packages/ui/src/components/tool-call.tsx @@ -84,7 +84,7 @@ interface ToolCallProps { export default function ToolCall(props: ToolCallProps) { - const { preferences, setDiffViewMode } = useConfig() + const { preferences, setDiffViewMode, setToolInputsVisibility } = useConfig() const { isDark } = useTheme() const { t } = useI18n() const toolCallMemo = createMemo(() => props.toolCall) @@ -172,9 +172,21 @@ export default function ToolCall(props: ToolCallProps) { }) const [userExpanded, setUserExpanded] = createSignal(null) - const [inputVisible, setInputVisible] = createSignal(false) - const [inputSectionExpanded, setInputSectionExpanded] = createSignal(false) - const [outputSectionExpanded, setOutputSectionExpanded] = createSignal(true) + const toolInputsVisibility = createMemo(() => preferences().toolInputsVisibility || "hidden") + const isToolInputVisible = createMemo(() => toolInputsVisibility() !== "hidden") + const inputDefaultExpanded = createMemo(() => toolInputsVisibility() === "expanded") + const [inputSectionOverride, setInputSectionOverride] = createSignal(null) + const [outputSectionOverride, setOutputSectionOverride] = createSignal(null) + const inputSectionExpanded = () => { + const override = inputSectionOverride() + if (override !== null) return override + return inputDefaultExpanded() + } + const outputSectionExpanded = () => { + const override = outputSectionOverride() + if (override !== null) return override + return true + } const isPermissionActive = createMemo(() => { const pending = pendingPermission() @@ -591,22 +603,24 @@ export default function ToolCall(props: ToolCallProps) { }) } + createEffect(() => { + // When global preference changes, reset per-tool-call overrides so palette changes apply. + toolInputsVisibility() + setInputSectionOverride(null) + setOutputSectionOverride(null) + }) + const handleToggleInputVisibility = (event: MouseEvent) => { event.preventDefault() event.stopPropagation() if (!expanded()) { toggle() } - setInputVisible((prev) => { - const next = !prev - if (!next) { - setInputSectionExpanded(false) - } else { - setInputSectionExpanded(true) - setOutputSectionExpanded(true) - } - return next - }) + if (isToolInputVisible()) { + setToolInputsVisibility("hidden") + return + } + setToolInputsVisibility("expanded") } const renderer = createMemo(() => resolveToolRenderer(toolName())) @@ -855,13 +869,13 @@ export default function ToolCall(props: ToolCallProps) { type="button" class="tool-call-header-input" onClick={handleToggleInputVisibility} - aria-pressed={inputVisible()} + aria-pressed={isToolInputVisible()} aria-label={ - inputVisible() + isToolInputVisible() ? t("toolCall.header.hideInputAriaLabel") : t("toolCall.header.showInputAriaLabel") } - title={inputVisible() ? t("toolCall.header.hideInputTitle") : t("toolCall.header.showInputTitle")} + title={isToolInputVisible() ? t("toolCall.header.hideInputTitle") : t("toolCall.header.showInputTitle")} > @@ -885,7 +899,7 @@ export default function ToolCall(props: ToolCallProps) { {expanded() && (
{renderToolBody()} @@ -906,7 +920,10 @@ export default function ToolCall(props: ToolCallProps) { type="button" class="tool-call-io-toggle" aria-expanded={inputSectionExpanded()} - onClick={() => setInputSectionExpanded((prev) => !prev)} + onClick={() => setInputSectionOverride((prev) => { + const current = prev === null ? inputSectionExpanded() : prev + return !current + })} > {t("toolCall.io.input")} @@ -927,7 +944,10 @@ export default function ToolCall(props: ToolCallProps) { type="button" class="tool-call-io-toggle" aria-expanded={outputSectionExpanded()} - onClick={() => setOutputSectionExpanded((prev) => !prev)} + onClick={() => setOutputSectionOverride((prev) => { + const current = prev === null ? outputSectionExpanded() : prev + return !current + })} > {t("toolCall.io.output")} diff --git a/packages/ui/src/lib/hooks/use-commands.ts b/packages/ui/src/lib/hooks/use-commands.ts index c89a95d4..4105e274 100644 --- a/packages/ui/src/lib/hooks/use-commands.ts +++ b/packages/ui/src/lib/hooks/use-commands.ts @@ -1,6 +1,6 @@ import { createSignal, onMount } from "solid-js" import type { Accessor } from "solid-js" -import type { Preferences, ExpansionPreference } from "../../stores/preferences" +import type { Preferences, ExpansionPreference, ToolInputsVisibilityPreference } from "../../stores/preferences" import { createCommandRegistry, type Command } from "../commands" import { instances, activeInstanceId, setActiveInstanceId } from "../../stores/instances" import type { ClientPart, MessageInfo } from "../../types/message" @@ -38,6 +38,7 @@ export interface UseCommandsOptions { setToolOutputExpansion: (mode: ExpansionPreference) => void setDiagnosticsExpansion: (mode: ExpansionPreference) => void setThinkingBlocksExpansion: (mode: ExpansionPreference) => void + setToolInputsVisibility: (mode: ToolInputsVisibilityPreference) => void handleNewInstanceRequest: () => void handleCloseInstance: (instanceId: string) => Promise handleNewSession: (instanceId: string) => Promise @@ -551,6 +552,29 @@ export function useCommands(options: UseCommandsOptions) { }, }) + 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: () => { diff --git a/packages/ui/src/lib/i18n/messages/en/commands.ts b/packages/ui/src/lib/i18n/messages/en/commands.ts index dd0d12f7..b396200c 100644 --- a/packages/ui/src/lib/i18n/messages/en/commands.ts +++ b/packages/ui/src/lib/i18n/messages/en/commands.ts @@ -130,6 +130,10 @@ export const commandMessages = { "commands.diagnosticsDefault.description": "Toggle default expansion for diagnostics output", "commands.diagnosticsDefault.keywords": "diagnostics, expand, collapse", + "commands.toolInputsVisibility.label": "Tool Inputs Visibility · {state}", + "commands.toolInputsVisibility.description": "Set default visibility for tool call input arguments", + "commands.toolInputsVisibility.keywords": "tool, inputs, arguments, visibility, hide, show, expand, collapse", + "commands.tokenUsageDisplay.label": "Token Usage Display · {state}", "commands.tokenUsageDisplay.description": "Show or hide token and cost stats for assistant messages", "commands.tokenUsageDisplay.keywords": "token, usage, cost, stats", diff --git a/packages/ui/src/lib/i18n/messages/es/commands.ts b/packages/ui/src/lib/i18n/messages/es/commands.ts index c6a75e7e..43e1fbb1 100644 --- a/packages/ui/src/lib/i18n/messages/es/commands.ts +++ b/packages/ui/src/lib/i18n/messages/es/commands.ts @@ -130,6 +130,10 @@ export const commandMessages = { "commands.diagnosticsDefault.description": "Alternar la expansión por defecto de la salida de diagnósticos", "commands.diagnosticsDefault.keywords": "diagnósticos, expandir, colapsar", + "commands.toolInputsVisibility.label": "Visibilidad de entradas de herramientas · {state}", + "commands.toolInputsVisibility.description": "Configurar la visibilidad por defecto de los argumentos de entrada de llamadas de herramienta", + "commands.toolInputsVisibility.keywords": "herramienta, entradas, argumentos, visibilidad, ocultar, mostrar, expandir, colapsar", + "commands.tokenUsageDisplay.label": "Mostrar uso de tokens · {state}", "commands.tokenUsageDisplay.description": "Mostrar u ocultar estadísticas de tokens y costo en los mensajes del asistente", "commands.tokenUsageDisplay.keywords": "token, uso, costo, estadísticas", diff --git a/packages/ui/src/lib/i18n/messages/fr/commands.ts b/packages/ui/src/lib/i18n/messages/fr/commands.ts index 63e7c666..505baa19 100644 --- a/packages/ui/src/lib/i18n/messages/fr/commands.ts +++ b/packages/ui/src/lib/i18n/messages/fr/commands.ts @@ -130,6 +130,10 @@ export const commandMessages = { "commands.diagnosticsDefault.description": "Choisir l'ouverture par défaut de la sortie des diagnostics", "commands.diagnosticsDefault.keywords": "diagnostics, développer, réduire", + "commands.toolInputsVisibility.label": "Visibilité des entrées d'outil · {state}", + "commands.toolInputsVisibility.description": "Définir la visibilité par défaut des arguments d'entrée des appels d'outil", + "commands.toolInputsVisibility.keywords": "outil, entrées, arguments, visibilité, masquer, afficher, développer, réduire", + "commands.tokenUsageDisplay.label": "Affichage de l'usage des tokens · {state}", "commands.tokenUsageDisplay.description": "Afficher ou masquer les stats de tokens et de coût pour les messages de l'assistant", "commands.tokenUsageDisplay.keywords": "token, usage, coût, stats", diff --git a/packages/ui/src/lib/i18n/messages/ja/commands.ts b/packages/ui/src/lib/i18n/messages/ja/commands.ts index 75c1c5f3..de21f94c 100644 --- a/packages/ui/src/lib/i18n/messages/ja/commands.ts +++ b/packages/ui/src/lib/i18n/messages/ja/commands.ts @@ -130,6 +130,10 @@ export const commandMessages = { "commands.diagnosticsDefault.description": "診断出力を既定で展開するか切り替え", "commands.diagnosticsDefault.keywords": "診断, 展開, 折りたたみ, diagnostics, expand, collapse", + "commands.toolInputsVisibility.label": "ツール入力の表示 · {state}", + "commands.toolInputsVisibility.description": "ツール呼び出しの入力引数の既定の表示状態を設定します", + "commands.toolInputsVisibility.keywords": "ツール, 入力, 引数, 表示, 非表示, 展開, 折りたたみ, tool, inputs, arguments, visibility, hide, show, expand, collapse", + "commands.tokenUsageDisplay.label": "トークン使用量表示 · {state}", "commands.tokenUsageDisplay.description": "アシスタントメッセージのトークン/コスト統計を表示/非表示", "commands.tokenUsageDisplay.keywords": "トークン, 使用量, コスト, 統計, token, usage, cost, stats", diff --git a/packages/ui/src/lib/i18n/messages/ru/commands.ts b/packages/ui/src/lib/i18n/messages/ru/commands.ts index 068f020d..55d2a791 100644 --- a/packages/ui/src/lib/i18n/messages/ru/commands.ts +++ b/packages/ui/src/lib/i18n/messages/ru/commands.ts @@ -130,6 +130,10 @@ export const commandMessages = { "commands.diagnosticsDefault.description": "Переключить, разворачивать ли вывод диагностики по умолчанию", "commands.diagnosticsDefault.keywords": "diagnostics, развернуть, свернуть", + "commands.toolInputsVisibility.label": "Видимость входных данных инструмента · {state}", + "commands.toolInputsVisibility.description": "Установить видимость аргументов входа вызовов инструментов по умолчанию", + "commands.toolInputsVisibility.keywords": "инструмент, вход, аргументы, видимость, скрыть, показать, раскрыть, свернуть, tool, inputs, arguments, visibility, hide, show, expand, collapse", + "commands.tokenUsageDisplay.label": "Отображение token-статистики · {state}", "commands.tokenUsageDisplay.description": "Показать или скрыть статистику token и стоимости для сообщений ассистента", "commands.tokenUsageDisplay.keywords": "token, usage, cost, статистика", diff --git a/packages/ui/src/lib/i18n/messages/zh-Hans/commands.ts b/packages/ui/src/lib/i18n/messages/zh-Hans/commands.ts index 85997488..69eba72f 100644 --- a/packages/ui/src/lib/i18n/messages/zh-Hans/commands.ts +++ b/packages/ui/src/lib/i18n/messages/zh-Hans/commands.ts @@ -130,6 +130,10 @@ export const commandMessages = { "commands.diagnosticsDefault.description": "切换诊断输出是否默认展开", "commands.diagnosticsDefault.keywords": "diagnostics, expand, collapse, 诊断, 展开, 折叠", + "commands.toolInputsVisibility.label": "工具输入可见性 · {state}", + "commands.toolInputsVisibility.description": "设置工具调用输入参数的默认可见性", + "commands.toolInputsVisibility.keywords": "工具, 输入, 参数, 可见性, 隐藏, 显示, 展开, 折叠, tool, inputs, arguments, visibility, hide, show, expand, collapse", + "commands.tokenUsageDisplay.label": "Token 使用显示 · {state}", "commands.tokenUsageDisplay.description": "显示或隐藏助手消息的 token 和费用统计", "commands.tokenUsageDisplay.keywords": "token, usage, cost, stats, 令牌, 用量, 费用, 统计", diff --git a/packages/ui/src/stores/preferences.tsx b/packages/ui/src/stores/preferences.tsx index 00b0a04e..520e6726 100644 --- a/packages/ui/src/stores/preferences.tsx +++ b/packages/ui/src/stores/preferences.tsx @@ -25,6 +25,7 @@ export interface ModelPreference { export type DiffViewMode = "split" | "unified" export type ExpansionPreference = "expanded" | "collapsed" +export type ToolInputsVisibilityPreference = "hidden" | "collapsed" | "expanded" export type ListeningMode = "local" | "all" export interface UiSettings { @@ -37,6 +38,7 @@ export interface UiSettings { diffViewMode: DiffViewMode toolOutputExpansion: ExpansionPreference diagnosticsExpansion: ExpansionPreference + toolInputsVisibility: ToolInputsVisibilityPreference showUsageMetrics: boolean autoCleanupBlankSessions: boolean @@ -108,6 +110,7 @@ const defaultUiSettings: UiSettings = { diffViewMode: "split", toolOutputExpansion: "expanded", diagnosticsExpansion: "expanded", + toolInputsVisibility: "hidden", showUsageMetrics: true, autoCleanupBlankSessions: true, @@ -130,6 +133,10 @@ function normalizeUiSettings(input?: Partial | null): UiSettings { diffViewMode: sanitized.diffViewMode ?? defaultUiSettings.diffViewMode, toolOutputExpansion: sanitized.toolOutputExpansion ?? defaultUiSettings.toolOutputExpansion, diagnosticsExpansion: sanitized.diagnosticsExpansion ?? defaultUiSettings.diagnosticsExpansion, + toolInputsVisibility: + sanitized.toolInputsVisibility === "collapsed" || sanitized.toolInputsVisibility === "expanded" + ? sanitized.toolInputsVisibility + : defaultUiSettings.toolInputsVisibility, showUsageMetrics: sanitized.showUsageMetrics ?? defaultUiSettings.showUsageMetrics, autoCleanupBlankSessions: sanitized.autoCleanupBlankSessions ?? defaultUiSettings.autoCleanupBlankSessions, osNotificationsEnabled: sanitized.osNotificationsEnabled ?? defaultUiSettings.osNotificationsEnabled, @@ -439,6 +446,11 @@ function setDiagnosticsExpansion(mode: ExpansionPreference): void { updateUiSettings({ diagnosticsExpansion: mode }) } +function setToolInputsVisibility(mode: ToolInputsVisibilityPreference): void { + if (preferences().toolInputsVisibility === mode) return + updateUiSettings({ toolInputsVisibility: mode }) +} + function setThinkingBlocksExpansion(mode: ExpansionPreference): void { if (preferences().thinkingBlocksExpansion === mode) return updateUiSettings({ thinkingBlocksExpansion: mode }) @@ -536,6 +548,7 @@ interface ConfigContextValue { setToolOutputExpansion: typeof setToolOutputExpansion setDiagnosticsExpansion: typeof setDiagnosticsExpansion setThinkingBlocksExpansion: typeof setThinkingBlocksExpansion + setToolInputsVisibility: typeof setToolInputsVisibility // instance scoped setAgentModelPreference: typeof setAgentModelPreference @@ -579,6 +592,7 @@ const configContextValue: ConfigContextValue = { setToolOutputExpansion, setDiagnosticsExpansion, setThinkingBlocksExpansion, + setToolInputsVisibility, setAgentModelPreference, getAgentModelPreference, }