feat(ui): toggle tool call input yaml

This commit is contained in:
Shantur Rathore
2026-02-19 18:09:16 +00:00
parent e84adebe61
commit 2a438b2bb3
10 changed files with 130 additions and 4 deletions

3
package-lock.json generated
View File

@@ -12092,7 +12092,8 @@
"shiki": "^3.13.0",
"solid-js": "^1.8.0",
"solid-toast": "^0.5.0",
"tauri-plugin-keepawake-api": "^0.1.0"
"tauri-plugin-keepawake-api": "^0.1.0",
"yaml": "^2.4.2"
},
"devDependencies": {
"@vite-pwa/assets-generator": "^1.0.2",

View File

@@ -30,7 +30,8 @@
"shiki": "^3.13.0",
"solid-js": "^1.8.0",
"solid-toast": "^0.5.0",
"tauri-plugin-keepawake-api": "^0.1.0"
"tauri-plugin-keepawake-api": "^0.1.0",
"yaml": "^2.4.2"
},
"devDependencies": {
"@vite-pwa/assets-generator": "^1.0.2",

View File

@@ -1,5 +1,6 @@
import { createSignal, Show, createEffect, createMemo, onCleanup } from "solid-js"
import { Copy } from "lucide-solid"
import { AlignJustify, Copy } from "lucide-solid"
import { stringify as stringifyYaml } from "yaml"
import { messageStoreBus } from "../stores/message-v2/bus"
import { useTheme } from "../lib/theme"
import { useGlobalCache } from "../lib/hooks/use-global-cache"
@@ -27,7 +28,17 @@ import type {
ToolRendererContext,
ToolScrollHelpers,
} from "./tool-call/types"
import { getRelativePath, getToolIcon, getToolName, isToolStateCompleted, isToolStateError, isToolStateRunning, getDefaultToolAction } from "./tool-call/utils"
import {
ensureMarkdownContent,
getRelativePath,
getToolIcon,
getToolName,
isToolStateCompleted,
isToolStateError,
isToolStateRunning,
getDefaultToolAction,
readToolStatePayload,
} from "./tool-call/utils"
import { resolveTitleForTool } from "./tool-call/tool-title"
import { getLogger } from "../lib/logger"
@@ -161,6 +172,7 @@ export default function ToolCall(props: ToolCallProps) {
})
const [userExpanded, setUserExpanded] = createSignal<boolean | null>(null)
const [inputExpanded, setInputExpanded] = createSignal(false)
const isPermissionActive = createMemo(() => {
const pending = pendingPermission()
@@ -183,6 +195,35 @@ export default function ToolCall(props: ToolCallProps) {
return defaultExpandedForTool()
}
const toolInput = createMemo(() => {
const state = toolState()
return readToolStatePayload(state).input
})
const hasToolInput = createMemo(() => {
const input = toolInput()
return input && Object.keys(input).length > 0
})
const toolInputMarkdown = createMemo(() => {
const input = toolInput()
if (!input || Object.keys(input).length === 0) return null
try {
const yamlText = stringifyYaml(input)
return ensureMarkdownContent(yamlText, "yaml", true)
} catch (error) {
log.error("Failed to convert tool call input to YAML", error)
try {
const jsonText = JSON.stringify(input, null, 2)
return ensureMarkdownContent(jsonText, "json", true)
} catch (nestedError) {
log.error("Failed to stringify tool call input", nestedError)
return null
}
}
})
const permissionDetails = createMemo(() => pendingPermission()?.permission)
const questionDetails = createMemo(() => pendingQuestion()?.request)
@@ -548,6 +589,15 @@ export default function ToolCall(props: ToolCallProps) {
})
}
const handleToggleInput = (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
if (!expanded()) {
toggle()
}
setInputExpanded((prev) => !prev)
}
const renderer = createMemo(() => resolveToolRenderer(toolName()))
const { renderAnsiContent } = createAnsiContentRenderer({
@@ -789,6 +839,23 @@ export default function ToolCall(props: ToolCallProps) {
</span>
</button>
<Show when={hasToolInput()}>
<button
type="button"
class="tool-call-header-input"
onClick={handleToggleInput}
aria-pressed={inputExpanded()}
aria-label={
inputExpanded()
? t("toolCall.header.hideInputAriaLabel")
: t("toolCall.header.showInputAriaLabel")
}
title={inputExpanded() ? t("toolCall.header.hideInputTitle") : t("toolCall.header.showInputTitle")}
>
<AlignJustify class="w-3.5 h-3.5" />
</button>
</Show>
<button
type="button"
class="tool-call-header-copy"
@@ -806,6 +873,14 @@ export default function ToolCall(props: ToolCallProps) {
{expanded() && (
<div class="tool-call-details">
<Show when={inputExpanded() && hasToolInput()}>
{(() => {
const content = toolInputMarkdown()
if (!content) return null
return renderMarkdownContent({ content, cacheKey: "input" })
})()}
</Show>
{renderToolBody()}
{renderError()}

View File

@@ -5,6 +5,11 @@ export const toolCallMessages = {
"toolCall.header.copyTitle": "Copy tool call title",
"toolCall.header.copyAriaLabel": "Copy tool call title",
"toolCall.header.showInputTitle": "Show tool call input",
"toolCall.header.showInputAriaLabel": "Show tool call input",
"toolCall.header.hideInputTitle": "Hide tool call input",
"toolCall.header.hideInputAriaLabel": "Hide tool call input",
"toolCall.diff.label": "Diff",
"toolCall.diff.label.withPath": "Diff · {path}",
"toolCall.diff.viewMode.ariaLabel": "Diff view mode",

View File

@@ -5,6 +5,11 @@ export const toolCallMessages = {
"toolCall.header.copyTitle": "Copy tool call title",
"toolCall.header.copyAriaLabel": "Copy tool call title",
"toolCall.header.showInputTitle": "Show tool call input",
"toolCall.header.showInputAriaLabel": "Show tool call input",
"toolCall.header.hideInputTitle": "Hide tool call input",
"toolCall.header.hideInputAriaLabel": "Hide tool call input",
"toolCall.diff.label": "Diff",
"toolCall.diff.label.withPath": "Diff · {path}",
"toolCall.diff.viewMode.ariaLabel": "Modo de vista de diff",

View File

@@ -5,6 +5,11 @@ export const toolCallMessages = {
"toolCall.header.copyTitle": "Copy tool call title",
"toolCall.header.copyAriaLabel": "Copy tool call title",
"toolCall.header.showInputTitle": "Show tool call input",
"toolCall.header.showInputAriaLabel": "Show tool call input",
"toolCall.header.hideInputTitle": "Hide tool call input",
"toolCall.header.hideInputAriaLabel": "Hide tool call input",
"toolCall.diff.label": "Diff",
"toolCall.diff.label.withPath": "Diff · {path}",
"toolCall.diff.viewMode.ariaLabel": "Mode d'affichage du diff",

View File

@@ -5,6 +5,11 @@ export const toolCallMessages = {
"toolCall.header.copyTitle": "Copy tool call title",
"toolCall.header.copyAriaLabel": "Copy tool call title",
"toolCall.header.showInputTitle": "Show tool call input",
"toolCall.header.showInputAriaLabel": "Show tool call input",
"toolCall.header.hideInputTitle": "Hide tool call input",
"toolCall.header.hideInputAriaLabel": "Hide tool call input",
"toolCall.diff.label": "Diff",
"toolCall.diff.label.withPath": "Diff · {path}",
"toolCall.diff.viewMode.ariaLabel": "diff 表示モード",

View File

@@ -5,6 +5,11 @@ export const toolCallMessages = {
"toolCall.header.copyTitle": "Copy tool call title",
"toolCall.header.copyAriaLabel": "Copy tool call title",
"toolCall.header.showInputTitle": "Show tool call input",
"toolCall.header.showInputAriaLabel": "Show tool call input",
"toolCall.header.hideInputTitle": "Hide tool call input",
"toolCall.header.hideInputAriaLabel": "Hide tool call input",
"toolCall.diff.label": "Diff",
"toolCall.diff.label.withPath": "Diff · {path}",
"toolCall.diff.viewMode.ariaLabel": "Режим просмотра diff",

View File

@@ -5,6 +5,11 @@ export const toolCallMessages = {
"toolCall.header.copyTitle": "Copy tool call title",
"toolCall.header.copyAriaLabel": "Copy tool call title",
"toolCall.header.showInputTitle": "Show tool call input",
"toolCall.header.showInputAriaLabel": "Show tool call input",
"toolCall.header.hideInputTitle": "Hide tool call input",
"toolCall.header.hideInputAriaLabel": "Hide tool call input",
"toolCall.diff.label": "Diff",
"toolCall.diff.label.withPath": "Diff · {path}",
"toolCall.diff.viewMode.ariaLabel": "Diff 视图模式",

View File

@@ -127,11 +127,30 @@
cursor: pointer;
}
.tool-call-header-input {
@apply inline-flex items-center justify-center;
background-color: transparent;
border: none;
color: var(--text-secondary);
padding: 0 0.5rem;
border-radius: 0;
cursor: pointer;
}
.tool-call-header-copy:hover {
background-color: transparent;
color: var(--text-primary);
}
.tool-call-header-input:hover {
background-color: transparent;
color: var(--text-primary);
}
.tool-call-header-input[aria-pressed="true"] {
color: var(--text-primary);
}
.tool-call-header-status {
@apply inline-flex items-center justify-center;
font-size: 0.95rem;