style(ui): render skill output like read

Route the skill tool through the read renderer so skill output uses the same preview, language detection, copy, and wrapping chrome as read output while preserving a skill-specific title label.

Keep skill collapsed by default with the same error-expands behavior as read, and include path/name inputs in read-style search text so skill content remains discoverable.

Validated with npm run typecheck --workspace @codenomad/ui.
This commit is contained in:
Shantur Rathore 2026-06-17 22:42:10 +01:00
parent 64671da4cf
commit 2cc6630d40
4 changed files with 19 additions and 7 deletions

View file

@ -756,7 +756,7 @@ export default function ToolCall(props: ToolCallProps) {
}
const prefExpanded = toolOutputDefaultExpanded()
const toolName = toolCallMemo()?.tool || ""
if (toolName === "read") {
if (toolName === "read" || toolName === "skill") {
const state = toolState()
if (state?.status === "error") {
return true

View file

@ -3,15 +3,23 @@ import { ensureMarkdownContent, getRelativePath, getToolName, inferLanguageFromP
import { tGlobal } from "../../../lib/i18n"
import { getReadToolSearchText } from "../search-text"
function getReadPath(input: Record<string, any>): string {
const filePath = typeof input.filePath === "string" ? input.filePath : ""
if (filePath) return filePath
const path = typeof input.path === "string" ? input.path : ""
if (path) return path
return typeof input.name === "string" ? input.name : ""
}
export const readRenderer: ToolRenderer = {
tools: ["read"],
tools: ["read", "skill"],
getSearchText: getReadToolSearchText,
getAction: () => tGlobal("toolCall.renderer.action.readingFile"),
getTitle({ toolState }) {
getTitle({ toolName, toolState }) {
const state = toolState()
if (!state) return undefined
const { input } = readToolStatePayload(state)
const filePath = typeof input.filePath === "string" ? input.filePath : ""
const filePath = getReadPath(input)
const offset = typeof input.offset === "number" ? input.offset : undefined
const limit = typeof input.limit === "number" ? input.limit : undefined
const relativePath = filePath ? getRelativePath(filePath) : ""
@ -25,7 +33,8 @@ export const readRenderer: ToolRenderer = {
detailParts.push(tGlobal("toolCall.renderer.read.detail.limit", { limit }))
}
const baseTitle = relativePath ? `${getToolName("read")} ${relativePath}` : getToolName("read")
const toolLabel = getToolName(toolName())
const baseTitle = relativePath ? `${toolLabel} ${relativePath}` : toolLabel
if (!detailParts.length) {
return baseTitle
}
@ -38,7 +47,7 @@ export const readRenderer: ToolRenderer = {
const { metadata, input } = readToolStatePayload(state)
const preview = typeof metadata.preview === "string" ? metadata.preview : null
if (!preview) return undefined
const language = inferLanguageFromPath(typeof input.filePath === "string" ? input.filePath : undefined) ?? "text"
const language = inferLanguageFromPath(getReadPath(input)) ?? "text"
return { language, copyText: preview, wrapToggle: true, suppressInnerHeader: true }
},
renderBody({ toolState, renderMarkdown }) {
@ -46,7 +55,7 @@ export const readRenderer: ToolRenderer = {
if (!state || state.status === "pending") return null
const { metadata, input } = readToolStatePayload(state)
const preview = typeof metadata.preview === "string" ? metadata.preview : null
const language = inferLanguageFromPath(typeof input.filePath === "string" ? input.filePath : undefined)
const language = inferLanguageFromPath(getReadPath(input))
const content = ensureMarkdownContent(preview, language, true)
if (!content) return null
return renderMarkdown({ content, disableHighlight: state.status === "running" })

View file

@ -75,6 +75,8 @@ export function getReadToolSearchText(context: ToolSearchTextContext): string[]
const { input, metadata } = readToolStatePayload(context.toolState)
appendBaseToolText(values, context)
appendString(values, input.filePath)
appendString(values, input.path)
appendString(values, input.name)
appendString(values, metadata.preview)
appendToolErrorText(values, context)
return values

View file

@ -17,6 +17,7 @@ import { searchRenderer } from "./renderers/search"
const TITLE_RENDERERS: Record<string, ToolRenderer> = {
bash: bashRenderer,
read: readRenderer,
skill: readRenderer,
write: writeRenderer,
edit: editRenderer,
apply_patch: applyPatchRenderer,