@kolisachint/hoocode-agent 0.5.31 → 0.5.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/dist/core/agent-session-runtime.d.ts +28 -0
  3. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  4. package/dist/core/agent-session-runtime.js +61 -2
  5. package/dist/core/agent-session-runtime.js.map +1 -1
  6. package/dist/core/keybindings.d.ts +102 -30
  7. package/dist/core/keybindings.d.ts.map +1 -1
  8. package/dist/core/keybindings.js +123 -29
  9. package/dist/core/keybindings.js.map +1 -1
  10. package/dist/core/settings-defaults.d.ts +1 -1
  11. package/dist/core/settings-defaults.d.ts.map +1 -1
  12. package/dist/core/settings-defaults.js +1 -1
  13. package/dist/core/settings-defaults.js.map +1 -1
  14. package/dist/core/settings-manager.d.ts +10 -3
  15. package/dist/core/settings-manager.d.ts.map +1 -1
  16. package/dist/core/settings-manager.js +19 -8
  17. package/dist/core/settings-manager.js.map +1 -1
  18. package/dist/core/settings-types.d.ts +1 -1
  19. package/dist/core/settings-types.d.ts.map +1 -1
  20. package/dist/core/settings-types.js.map +1 -1
  21. package/dist/core/slash-commands.d.ts.map +1 -1
  22. package/dist/core/slash-commands.js +5 -1
  23. package/dist/core/slash-commands.js.map +1 -1
  24. package/dist/core/tool-output-view.d.ts +33 -0
  25. package/dist/core/tool-output-view.d.ts.map +1 -0
  26. package/dist/core/tool-output-view.js +31 -0
  27. package/dist/core/tool-output-view.js.map +1 -0
  28. package/dist/core/tools/edit.d.ts.map +1 -1
  29. package/dist/core/tools/edit.js +4 -0
  30. package/dist/core/tools/edit.js.map +1 -1
  31. package/dist/modes/interactive/command-executor.d.ts +20 -0
  32. package/dist/modes/interactive/command-executor.d.ts.map +1 -1
  33. package/dist/modes/interactive/command-executor.js +138 -9
  34. package/dist/modes/interactive/command-executor.js.map +1 -1
  35. package/dist/modes/interactive/components/footer.d.ts +4 -0
  36. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  37. package/dist/modes/interactive/components/footer.js +31 -2
  38. package/dist/modes/interactive/components/footer.js.map +1 -1
  39. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
  40. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  41. package/dist/modes/interactive/components/scoped-models-selector.js +2 -2
  42. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  43. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  44. package/dist/modes/interactive/components/session-selector.js +2 -2
  45. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  46. package/dist/modes/interactive/components/settings-selector.d.ts +3 -2
  47. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  48. package/dist/modes/interactive/components/settings-selector.js +13 -12
  49. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  50. package/dist/modes/interactive/components/tool-chain-summary.d.ts +65 -0
  51. package/dist/modes/interactive/components/tool-chain-summary.d.ts.map +1 -0
  52. package/dist/modes/interactive/components/tool-chain-summary.js +216 -0
  53. package/dist/modes/interactive/components/tool-chain-summary.js.map +1 -0
  54. package/dist/modes/interactive/components/tool-chain.d.ts +61 -0
  55. package/dist/modes/interactive/components/tool-chain.d.ts.map +1 -0
  56. package/dist/modes/interactive/components/tool-chain.js +156 -0
  57. package/dist/modes/interactive/components/tool-chain.js.map +1 -0
  58. package/dist/modes/interactive/components/tool-execution.d.ts +55 -9
  59. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  60. package/dist/modes/interactive/components/tool-execution.js +173 -33
  61. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  62. package/dist/modes/interactive/components/tool-signal.d.ts +50 -0
  63. package/dist/modes/interactive/components/tool-signal.d.ts.map +1 -0
  64. package/dist/modes/interactive/components/tool-signal.js +151 -0
  65. package/dist/modes/interactive/components/tool-signal.js.map +1 -0
  66. package/dist/modes/interactive/interactive-mode.d.ts +50 -1
  67. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  68. package/dist/modes/interactive/interactive-mode.js +239 -29
  69. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  70. package/dist/modes/interactive/resource-display.d.ts +9 -0
  71. package/dist/modes/interactive/resource-display.d.ts.map +1 -1
  72. package/dist/modes/interactive/resource-display.js +13 -0
  73. package/dist/modes/interactive/resource-display.js.map +1 -1
  74. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  75. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  76. package/examples/extensions/sandbox/package.json +1 -1
  77. package/examples/extensions/with-deps/package.json +1 -1
  78. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"tool-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tool-execution.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tBox,\n\ttype Component,\n\tContainer,\n\tgetCapabilities,\n\tImage,\n\tisImageLine,\n\tSpacer,\n\tText,\n\ttype TUI,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport type { ToolDefinition, ToolRenderContext } from \"../../../core/extensions/types.js\";\nimport { createAllToolDefinitions, type ToolName } from \"../../../core/tools/index.js\";\nimport { getTextOutput as getRenderedTextOutput } from \"../../../core/tools/render-utils.js\";\nimport { convertToPng } from \"../../../utils/image-convert.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * How a tool block's result body is displayed:\n * - \"standard\": result shown (truncated preview, expandable) — the default.\n * - \"collapsed\": result hidden; only the call line + status dot render.\n * - \"peek\": result hidden by default with a ▸ affordance; the expand key reveals it.\n */\nexport type ToolOutputDisplayLevel = \"collapsed\" | \"peek\" | \"standard\";\n\nexport interface ToolExecutionOptions {\n\tshowImages?: boolean;\n\timageWidthCells?: number;\n\tdisplayLevel?: ToolOutputDisplayLevel;\n}\n\n/**\n * Renders a child component and prepends a prefix (e.g. a status dot) to its\n * first line, indenting continuation lines so they align under the content.\n *\n * This keeps the dot inline with the first line instead of stacking it on its\n * own line (which happens when a Container holds the dot as a separate child).\n */\nclass PrefixFirstLine implements Component {\n\tprivate prefix: string;\n\tprivate child: Component;\n\tprivate indentWidth: number;\n\t// Keyed on the child's returned array identity so an unchanged child keeps\n\t// this wrapper reference-stable too (parents memoize flattening by ref).\n\tprivate memo?: { src: string[]; width: number; out: string[] };\n\n\tconstructor(prefix: string, child: Component) {\n\t\tthis.prefix = prefix;\n\t\tthis.child = child;\n\t\tthis.indentWidth = visibleWidth(prefix);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.child.invalidate?.();\n\t\tthis.memo = undefined;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Reserve room for the prefix so the child wraps within the remaining width.\n\t\tconst childWidth = Math.max(1, width - this.indentWidth);\n\t\tconst lines = this.child.render(childWidth);\n\t\tif (this.memo && this.memo.src === lines && this.memo.width === width) {\n\t\t\treturn this.memo.out;\n\t\t}\n\t\tconst indent = \" \".repeat(this.indentWidth);\n\t\tconst out =\n\t\t\tlines.length === 0 ? [this.prefix] : lines.map((line, i) => (i === 0 ? this.prefix + line : indent + line));\n\t\tthis.memo = { src: lines, width, out };\n\t\treturn out;\n\t}\n}\n\nexport class ToolExecutionComponent extends Container {\n\tprivate contentBox: Box;\n\tprivate contentText: Text;\n\tprivate selfRenderContainer: Container;\n\tprivate callRendererComponent?: Component;\n\tprivate resultRendererComponent?: Component;\n\tprivate rendererState: any = {};\n\tprivate imageComponents: Image[] = [];\n\tprivate imageSpacers: Spacer[] = [];\n\tprivate toolName: string;\n\tprivate toolCallId: string;\n\tprivate args: any;\n\tprivate expanded = false;\n\t// Persisted display level (collapsed/peek/standard). Controls whether the\n\t// result body renders at all; `revealed` is the per-block override that the\n\t// global expand key flips for collapsed/peek blocks.\n\tprivate displayLevel: ToolOutputDisplayLevel;\n\tprivate revealed = false;\n\tprivate showImages: boolean;\n\tprivate imageWidthCells: number;\n\tprivate isPartial = true;\n\tprivate toolDefinition?: ToolDefinition<any, any>;\n\tprivate builtInToolDefinition?: ToolDefinition<any, any>;\n\tprivate ui: TUI;\n\tprivate cwd: string;\n\tprivate executionStarted = false;\n\tprivate argsComplete = false;\n\tprivate result?: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tisError: boolean;\n\t\tdetails?: any;\n\t};\n\tprivate convertedImages: Map<number, { data: string; mimeType: string }> = new Map();\n\tprivate hideComponent = false;\n\t// Once a finished tool block has scrolled far out of view the interactive mode\n\t// freezes it (see freeze()): its already-rendered lines are captured and the\n\t// heavy source payloads (full tool result, base64 image copies, per-renderer\n\t// state, child component caches) are released. A frozen block is immutable and\n\t// no longer reflows on resize — old off-screen scrollback keeps its wrapping,\n\t// which self-heals on any full rebuild (theme toggle / session reload, both of\n\t// which reconstruct components from the intact session data).\n\tprivate frozen = false;\n\tprivate frozenLines?: string[];\n\t/** Width the frozen snapshot was captured at; a wider terminal is safe as-is,\n\t * a narrower one needs re-truncation to avoid the TUI's over-width crash guard. */\n\tprivate frozenWidth = 0;\n\tprivate frozenTruncated?: string[];\n\tprivate frozenTruncatedWidth = -1;\n\n\tconstructor(\n\t\ttoolName: string,\n\t\ttoolCallId: string,\n\t\targs: any,\n\t\toptions: ToolExecutionOptions = {},\n\t\ttoolDefinition: ToolDefinition<any, any> | undefined,\n\t\tui: TUI,\n\t\tcwd: string,\n\t) {\n\t\tsuper();\n\t\tthis.toolName = toolName;\n\t\tthis.toolCallId = toolCallId;\n\t\tthis.args = args;\n\t\tthis.toolDefinition = toolDefinition;\n\t\tthis.builtInToolDefinition = createAllToolDefinitions(cwd)[toolName as ToolName];\n\t\tthis.showImages = options.showImages ?? true;\n\t\tthis.imageWidthCells = options.imageWidthCells ?? 60;\n\t\tthis.displayLevel = options.displayLevel ?? \"standard\";\n\t\tthis.ui = ui;\n\t\tthis.cwd = cwd;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Always create all shell variants. contentBox is used for default renderer-based composition.\n\t\t// selfRenderContainer is used when the tool renders its own framing.\n\t\t// contentText is reserved for generic fallback rendering when no tool definition exists.\n\t\t// Boxless: no filled background — hierarchy comes from status dots + indent.\n\t\t// paddingY is 0: the single leading Spacer(1) is the only separator between\n\t\t// tool blocks, so consecutive commands don't stack 3 blank lines between them.\n\t\tthis.contentBox = new Box(1, 0);\n\t\tthis.contentText = new Text(\"\", 1, 0);\n\t\tthis.selfRenderContainer = new Container();\n\n\t\tif (this.hasRendererDefinition()) {\n\t\t\tthis.addChild(this.getRenderShell() === \"self\" ? this.selfRenderContainer : this.contentBox);\n\t\t} else {\n\t\t\tthis.addChild(this.contentText);\n\t\t}\n\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate getCallRenderer(): ToolDefinition<any, any>[\"renderCall\"] | undefined {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderCall;\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderCall;\n\t\t}\n\t\treturn this.toolDefinition.renderCall ?? this.builtInToolDefinition.renderCall;\n\t}\n\n\tprivate getResultRenderer(): ToolDefinition<any, any>[\"renderResult\"] | undefined {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderResult;\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderResult;\n\t\t}\n\t\treturn this.toolDefinition.renderResult ?? this.builtInToolDefinition.renderResult;\n\t}\n\n\tprivate hasRendererDefinition(): boolean {\n\t\treturn this.builtInToolDefinition !== undefined || this.toolDefinition !== undefined;\n\t}\n\n\tprivate getRenderShell(): \"default\" | \"self\" {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderShell ?? \"default\";\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderShell ?? \"default\";\n\t\t}\n\t\treturn this.toolDefinition.renderShell ?? this.builtInToolDefinition.renderShell ?? \"default\";\n\t}\n\n\tprivate getRenderContext(lastComponent: Component | undefined): ToolRenderContext {\n\t\treturn {\n\t\t\targs: this.args,\n\t\t\ttoolCallId: this.toolCallId,\n\t\t\tinvalidate: () => {\n\t\t\t\tthis.invalidate();\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tlastComponent,\n\t\t\tstate: this.rendererState,\n\t\t\tcwd: this.cwd,\n\t\t\texecutionStarted: this.executionStarted,\n\t\t\targsComplete: this.argsComplete,\n\t\t\tisPartial: this.isPartial,\n\t\t\texpanded: this.expanded,\n\t\t\tshowImages: this.showImages,\n\t\t\tisError: this.result?.isError ?? false,\n\t\t};\n\t}\n\n\tprivate createCallFallback(): Component {\n\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(this.toolName)), 0, 0);\n\t}\n\n\tprivate createResultFallback(): Component | undefined {\n\t\tconst output = this.getTextOutput();\n\t\tif (!output) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn new Text(theme.fg(\"toolOutput\", output), 0, 0);\n\t}\n\n\tupdateArgs(args: any): void {\n\t\tthis.args = args;\n\t\tthis.updateDisplay();\n\t}\n\n\tmarkExecutionStarted(): void {\n\t\tthis.executionStarted = true;\n\t\tthis.updateDisplay();\n\t\tthis.ui.requestRender();\n\t}\n\n\tsetArgsComplete(): void {\n\t\tthis.argsComplete = true;\n\t\tthis.updateDisplay();\n\t\tthis.ui.requestRender();\n\t}\n\n\tupdateResult(\n\t\tresult: {\n\t\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\t\tdetails?: any;\n\t\t\tisError: boolean;\n\t\t},\n\t\tisPartial = false,\n\t): void {\n\t\tthis.result = result;\n\t\tthis.isPartial = isPartial;\n\t\tthis.updateDisplay();\n\t\tthis.maybeConvertImagesForKitty();\n\t}\n\n\tprivate maybeConvertImagesForKitty(): void {\n\t\tconst caps = getCapabilities();\n\t\tif (caps.images !== \"kitty\") return;\n\t\tif (!this.result) return;\n\n\t\tconst imageBlocks = this.result.content.filter((c) => c.type === \"image\");\n\t\tfor (let i = 0; i < imageBlocks.length; i++) {\n\t\t\tconst img = imageBlocks[i];\n\t\t\tif (!img.data || !img.mimeType) continue;\n\t\t\tif (img.mimeType === \"image/png\") continue;\n\t\t\tif (this.convertedImages.has(i)) continue;\n\n\t\t\tconst index = i;\n\t\t\tconvertToPng(img.data, img.mimeType).then((converted) => {\n\t\t\t\tif (this.frozen) return;\n\t\t\t\tif (converted) {\n\t\t\t\t\tthis.convertedImages.set(index, converted);\n\t\t\t\t\tthis.updateDisplay();\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\t// For collapsed/peek blocks the same global toggle reveals the hidden body;\n\t\t// for standard blocks `expanded` alone switches truncated ↔ full.\n\t\tthis.revealed = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetDisplayLevel(level: ToolOutputDisplayLevel): void {\n\t\tthis.displayLevel = level;\n\t\tthis.updateDisplay();\n\t}\n\n\t/** Whether the result body should render given the level and reveal state. */\n\tprivate shouldShowBody(): boolean {\n\t\treturn this.displayLevel === \"standard\" || this.revealed;\n\t}\n\n\tsetShowImages(show: boolean): void {\n\t\tthis.showImages = show;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetImageWidthCells(width: number): void {\n\t\tthis.imageWidthCells = Math.max(1, Math.floor(width));\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\t// A frozen block has released the state updateDisplay would rebuild from;\n\t\t// its captured snapshot is authoritative, so skip the rebuild entirely.\n\t\tif (this.frozen) return;\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\t/**\n\t * True when this block is a finished, visible tool result that still holds its\n\t * heavy source payloads — i.e. a candidate for freeze() once it scrolls out of\n\t * the live window. In-flight (partial) and already-frozen blocks are excluded.\n\t */\n\tisFreezable(): boolean {\n\t\treturn !this.frozen && !this.isPartial && !this.hideComponent && !!this.result;\n\t}\n\n\t/** Mark this block for freezing; the snapshot is captured on the next render\n\t * (at the real render width) and the heavy state released then. */\n\tfreeze(): void {\n\t\tif (this.isFreezable()) this.frozen = true;\n\t}\n\n\tprivate releaseHeavyState(): void {\n\t\tthis.result = undefined;\n\t\tthis.convertedImages.clear();\n\t\tthis.imageComponents = [];\n\t\tthis.imageSpacers = [];\n\t\tthis.rendererState = {};\n\t\tthis.callRendererComponent = undefined;\n\t\tthis.resultRendererComponent = undefined;\n\t\t// Drop child components so their line caches (which embed base64 image\n\t\t// payloads and full text output) become collectable.\n\t\tthis.clear();\n\t}\n\n\toverride render(width: number): string[] {\n\t\tif (this.hideComponent) {\n\t\t\treturn [];\n\t\t}\n\t\tif (this.frozenLines) {\n\t\t\t// Captured snapshot. Wider (or equal) terminal: emit as-is. Narrower:\n\t\t\t// re-truncate non-image lines to avoid the TUI's over-width crash guard,\n\t\t\t// caching the result per width so it stays O(1) on unchanged frames.\n\t\t\tif (width >= this.frozenWidth) return this.frozenLines;\n\t\t\tif (this.frozenTruncatedWidth === width && this.frozenTruncated) return this.frozenTruncated;\n\t\t\tthis.frozenTruncated = this.frozenLines.map((line) =>\n\t\t\t\tisImageLine(line) || visibleWidth(line) <= width ? line : truncateToWidth(line, width),\n\t\t\t);\n\t\t\tthis.frozenTruncatedWidth = width;\n\t\t\treturn this.frozenTruncated;\n\t\t}\n\t\tconst lines = super.render(width);\n\t\tif (this.frozen) {\n\t\t\tthis.frozenLines = lines;\n\t\t\tthis.frozenWidth = width;\n\t\t\tthis.releaseHeavyState();\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate updateDisplay(): void {\n\t\t// Frozen blocks are immutable snapshots with their source state released.\n\t\tif (this.frozen) return;\n\t\tlet hasContent = false;\n\t\tthis.hideComponent = false;\n\t\tif (this.hasRendererDefinition()) {\n\t\t\tconst renderContainer = this.getRenderShell() === \"self\" ? this.selfRenderContainer : this.contentBox;\n\t\t\t// Boxless: no background fill on the container.\n\t\t\tif (renderContainer instanceof Box) {\n\t\t\t\trenderContainer.setBgFn(undefined);\n\t\t\t}\n\t\t\trenderContainer.clear();\n\n\t\t\t// Status dot prefix: green for complete success, yellow for pending/partial, red for error.\n\t\t\t// The dot is prepended to the first line of the call renderer so it stays inline\n\t\t\t// (adding it as a separate child would stack it on its own line).\n\t\t\tconst dotColor = this.result?.isError ? \"error\" : this.isPartial ? \"warning\" : \"success\";\n\t\t\t// Peek blocks advertise their hidden body with a ▸/▾ caret before the dot.\n\t\t\tconst caret = this.displayLevel === \"peek\" ? theme.fg(\"muted\", this.revealed ? \"▾ \" : \"▸ \") : \"\";\n\t\t\tconst dot = caret + theme.fg(dotColor, \"● \");\n\n\t\t\tconst callRenderer = this.getCallRenderer();\n\t\t\tif (!callRenderer) {\n\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));\n\t\t\t\thasContent = true;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));\n\t\t\t\t\tthis.callRendererComponent = component;\n\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, component));\n\t\t\t\t\thasContent = true;\n\t\t\t\t} catch {\n\t\t\t\t\tthis.callRendererComponent = undefined;\n\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));\n\t\t\t\t\thasContent = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.result && this.shouldShowBody()) {\n\t\t\t\tconst resultRenderer = this.getResultRenderer();\n\t\t\t\tif (!resultRenderer) {\n\t\t\t\t\tconst component = this.createResultFallback();\n\t\t\t\t\tif (component) {\n\t\t\t\t\t\trenderContainer.addChild(component);\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst component = resultRenderer(\n\t\t\t\t\t\t\t{ content: this.result.content as any, details: this.result.details },\n\t\t\t\t\t\t\t{ expanded: this.expanded, isPartial: this.isPartial },\n\t\t\t\t\t\t\ttheme,\n\t\t\t\t\t\t\tthis.getRenderContext(this.resultRendererComponent),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.resultRendererComponent = component;\n\t\t\t\t\t\trenderContainer.addChild(component);\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.resultRendererComponent = undefined;\n\t\t\t\t\t\tconst component = this.createResultFallback();\n\t\t\t\t\t\tif (component) {\n\t\t\t\t\t\t\trenderContainer.addChild(component);\n\t\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Boxless: no background fill.\n\t\t\tthis.contentText.setCustomBgFn(undefined);\n\t\t\tthis.contentText.setText(this.formatToolExecution());\n\t\t\thasContent = true;\n\t\t}\n\n\t\tfor (const img of this.imageComponents) {\n\t\t\tthis.removeChild(img);\n\t\t}\n\t\tthis.imageComponents = [];\n\t\tfor (const spacer of this.imageSpacers) {\n\t\t\tthis.removeChild(spacer);\n\t\t}\n\t\tthis.imageSpacers = [];\n\n\t\tif (this.result) {\n\t\t\tconst imageBlocks = this.result.content.filter((c) => c.type === \"image\");\n\t\t\tconst caps = getCapabilities();\n\t\t\tfor (let i = 0; i < imageBlocks.length; i++) {\n\t\t\t\tconst img = imageBlocks[i];\n\t\t\t\tif (caps.images && this.showImages && img.data && img.mimeType) {\n\t\t\t\t\tconst converted = this.convertedImages.get(i);\n\t\t\t\t\tconst imageData = converted?.data ?? img.data;\n\t\t\t\t\tconst imageMimeType = converted?.mimeType ?? img.mimeType;\n\t\t\t\t\tif (caps.images === \"kitty\" && imageMimeType !== \"image/png\") continue;\n\n\t\t\t\t\tconst spacer = new Spacer(1);\n\t\t\t\t\tthis.addChild(spacer);\n\t\t\t\t\tthis.imageSpacers.push(spacer);\n\t\t\t\t\tconst imageComponent = new Image(\n\t\t\t\t\t\timageData,\n\t\t\t\t\t\timageMimeType,\n\t\t\t\t\t\t{ fallbackColor: (s: string) => theme.fg(\"toolOutput\", s) },\n\t\t\t\t\t\t{ maxWidthCells: this.imageWidthCells },\n\t\t\t\t\t);\n\t\t\t\t\tthis.imageComponents.push(imageComponent);\n\t\t\t\t\tthis.addChild(imageComponent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.hasRendererDefinition() && !hasContent && this.imageComponents.length === 0) {\n\t\t\tthis.hideComponent = true;\n\t\t}\n\t}\n\n\tprivate getTextOutput(): string {\n\t\treturn getRenderedTextOutput(this.result, this.showImages);\n\t}\n\n\tprivate formatToolExecution(): string {\n\t\tconst dotColor = this.result?.isError ? \"error\" : this.isPartial ? \"warning\" : \"success\";\n\t\tconst caret = this.displayLevel === \"peek\" ? theme.fg(\"muted\", this.revealed ? \"▾ \" : \"▸ \") : \"\";\n\t\tlet text = caret + theme.fg(dotColor, \"● \") + theme.fg(\"toolTitle\", theme.bold(this.toolName));\n\t\tconst content = JSON.stringify(this.args, null, 2);\n\t\tif (content) {\n\t\t\ttext += `\\n\\n${content}`;\n\t\t}\n\t\tconst output = this.shouldShowBody() ? this.getTextOutput() : \"\";\n\t\tif (output) {\n\t\t\ttext += `\\n${output}`;\n\t\t}\n\t\treturn text;\n\t}\n}\n"]}
1
+ {"version":3,"file":"tool-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tool-execution.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tBox,\n\ttype Component,\n\tContainer,\n\tgetCapabilities,\n\tImage,\n\tisImageLine,\n\tSpacer,\n\tText,\n\ttype TUI,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport type { ToolDefinition, ToolRenderContext } from \"../../../core/extensions/types.js\";\nimport type { ToolOutputView } from \"../../../core/tool-output-view.js\";\nimport { createAllToolDefinitions, type ToolName } from \"../../../core/tools/index.js\";\nimport { getTextOutput as getRenderedTextOutput } from \"../../../core/tools/render-utils.js\";\nimport { convertToPng } from \"../../../utils/image-convert.js\";\nimport { theme } from \"../theme/theme.js\";\nimport type { ChainEntry } from \"./tool-chain-summary.js\";\nimport { ToolSignalComponent, toolSubject } from \"./tool-signal.js\";\n\n/**\n * Re-exported so tool-block callers keep a single import site for the view\n * dial; the type itself lives in core (the settings manager persists it).\n */\nexport type { ToolOutputView } from \"../../../core/tool-output-view.js\";\n\nexport interface ToolExecutionOptions {\n\tshowImages?: boolean;\n\timageWidthCells?: number;\n\tview?: ToolOutputView;\n}\n\n/**\n * Renders a child component and prepends a prefix (e.g. a status dot) to its\n * first line, indenting continuation lines so they align under the content.\n *\n * This keeps the dot inline with the first line instead of stacking it on its\n * own line (which happens when a Container holds the dot as a separate child).\n */\nclass PrefixFirstLine implements Component {\n\tprivate prefix: string;\n\tprivate child: Component;\n\tprivate indentWidth: number;\n\t// Keyed on the child's returned array identity so an unchanged child keeps\n\t// this wrapper reference-stable too (parents memoize flattening by ref).\n\tprivate memo?: { src: string[]; width: number; out: string[] };\n\n\tconstructor(prefix: string, child: Component) {\n\t\tthis.prefix = prefix;\n\t\tthis.child = child;\n\t\tthis.indentWidth = visibleWidth(prefix);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.child.invalidate?.();\n\t\tthis.memo = undefined;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Reserve room for the prefix so the child wraps within the remaining width.\n\t\tconst childWidth = Math.max(1, width - this.indentWidth);\n\t\tconst lines = this.child.render(childWidth);\n\t\tif (this.memo && this.memo.src === lines && this.memo.width === width) {\n\t\t\treturn this.memo.out;\n\t\t}\n\t\tconst indent = \" \".repeat(this.indentWidth);\n\t\tconst out =\n\t\t\tlines.length === 0 ? [this.prefix] : lines.map((line, i) => (i === 0 ? this.prefix + line : indent + line));\n\t\tthis.memo = { src: lines, width, out };\n\t\treturn out;\n\t}\n}\n\n/**\n * Indents every line of a child, unlike PrefixFirstLine which only offsets the\n * first. Used for a failure's body under a radar row, so the reason reads as\n * hanging off the row rather than starting a new column.\n */\nclass IndentAll implements Component {\n\tprivate child: Component;\n\tprivate indent: string;\n\tprivate memo?: { src: string[]; width: number; out: string[] };\n\n\tconstructor(child: Component, width: number) {\n\t\tthis.child = child;\n\t\tthis.indent = \" \".repeat(width);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.child.invalidate?.();\n\t\tthis.memo = undefined;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines = this.child.render(Math.max(1, width - this.indent.length));\n\t\tif (this.memo && this.memo.src === lines && this.memo.width === width) return this.memo.out;\n\t\tconst out = lines.map((line) => (line.trim() === \"\" ? line : this.indent + line));\n\t\tthis.memo = { src: lines, width, out };\n\t\treturn out;\n\t}\n}\n\nexport class ToolExecutionComponent extends Container {\n\tprivate contentBox: Box;\n\tprivate contentText: Text;\n\tprivate selfRenderContainer: Container;\n\tprivate callRendererComponent?: Component;\n\tprivate resultRendererComponent?: Component;\n\tprivate rendererState: any = {};\n\tprivate imageComponents: Image[] = [];\n\tprivate imageSpacers: Spacer[] = [];\n\tprivate toolName: string;\n\tprivate toolCallId: string;\n\tprivate args: any;\n\tprivate expanded = false;\n\t// Persisted view (radar/glance/full). Controls how the call line is drawn and\n\t// whether the result body renders at all; `revealed` is the per-block override\n\t// the global expand key flips for radar/glance blocks.\n\tprivate view: ToolOutputView;\n\tprivate revealed = false;\n\tprivate signalComponent?: ToolSignalComponent;\n\tprivate leadingSpacer: Spacer;\n\tprivate showImages: boolean;\n\tprivate imageWidthCells: number;\n\tprivate isPartial = true;\n\tprivate toolDefinition?: ToolDefinition<any, any>;\n\tprivate builtInToolDefinition?: ToolDefinition<any, any>;\n\tprivate ui: TUI;\n\tprivate cwd: string;\n\tprivate executionStarted = false;\n\tprivate argsComplete = false;\n\tprivate result?: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tisError: boolean;\n\t\tdetails?: any;\n\t};\n\tprivate convertedImages: Map<number, { data: string; mimeType: string }> = new Map();\n\tprivate hideComponent = false;\n\t// Once a finished tool block has scrolled far out of view the interactive mode\n\t// freezes it (see freeze()): its already-rendered lines are captured and the\n\t// heavy source payloads (full tool result, base64 image copies, per-renderer\n\t// state, child component caches) are released. A frozen block is immutable and\n\t// no longer reflows on resize — old off-screen scrollback keeps its wrapping,\n\t// which self-heals on any full rebuild (theme toggle / session reload, both of\n\t// which reconstruct components from the intact session data).\n\tprivate frozen = false;\n\tprivate frozenLines?: string[];\n\t/** Width the frozen snapshot was captured at; a wider terminal is safe as-is,\n\t * a narrower one needs re-truncation to avoid the TUI's over-width crash guard. */\n\tprivate frozenWidth = 0;\n\tprivate frozenTruncated?: string[];\n\tprivate frozenTruncatedWidth = -1;\n\n\tconstructor(\n\t\ttoolName: string,\n\t\ttoolCallId: string,\n\t\targs: any,\n\t\toptions: ToolExecutionOptions = {},\n\t\ttoolDefinition: ToolDefinition<any, any> | undefined,\n\t\tui: TUI,\n\t\tcwd: string,\n\t) {\n\t\tsuper();\n\t\tthis.toolName = toolName;\n\t\tthis.toolCallId = toolCallId;\n\t\tthis.args = args;\n\t\tthis.toolDefinition = toolDefinition;\n\t\tthis.builtInToolDefinition = createAllToolDefinitions(cwd)[toolName as ToolName];\n\t\tthis.showImages = options.showImages ?? true;\n\t\tthis.imageWidthCells = options.imageWidthCells ?? 60;\n\t\tthis.view = options.view ?? \"glance\";\n\t\tthis.ui = ui;\n\t\tthis.cwd = cwd;\n\n\t\t// The separator between consecutive tool blocks. Radar drops it: rows that\n\t\t// are one line each only read as a map when they stack without gaps.\n\t\tthis.leadingSpacer = new Spacer(1);\n\t\tthis.addChild(this.leadingSpacer);\n\n\t\t// Always create all shell variants. contentBox is used for default renderer-based composition.\n\t\t// selfRenderContainer is used when the tool renders its own framing.\n\t\t// contentText is reserved for generic fallback rendering when no tool definition exists.\n\t\t// Boxless: no filled background — hierarchy comes from status dots + indent.\n\t\t// paddingY is 0: the single leading Spacer(1) is the only separator between\n\t\t// tool blocks, so consecutive commands don't stack 3 blank lines between them.\n\t\tthis.contentBox = new Box(1, 0);\n\t\tthis.contentText = new Text(\"\", 1, 0);\n\t\tthis.selfRenderContainer = new Container();\n\n\t\tif (this.hasRendererDefinition()) {\n\t\t\tthis.addChild(this.activeShell());\n\t\t} else {\n\t\t\tthis.addChild(this.contentText);\n\t\t}\n\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate getCallRenderer(): ToolDefinition<any, any>[\"renderCall\"] | undefined {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderCall;\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderCall;\n\t\t}\n\t\treturn this.toolDefinition.renderCall ?? this.builtInToolDefinition.renderCall;\n\t}\n\n\tprivate getResultRenderer(): ToolDefinition<any, any>[\"renderResult\"] | undefined {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderResult;\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderResult;\n\t\t}\n\t\treturn this.toolDefinition.renderResult ?? this.builtInToolDefinition.renderResult;\n\t}\n\n\tprivate hasRendererDefinition(): boolean {\n\t\treturn this.builtInToolDefinition !== undefined || this.toolDefinition !== undefined;\n\t}\n\n\tprivate getRenderShell(): \"default\" | \"self\" {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderShell ?? \"default\";\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderShell ?? \"default\";\n\t\t}\n\t\treturn this.toolDefinition.renderShell ?? this.builtInToolDefinition.renderShell ?? \"default\";\n\t}\n\n\tprivate getRenderContext(lastComponent: Component | undefined): ToolRenderContext {\n\t\treturn {\n\t\t\targs: this.args,\n\t\t\ttoolCallId: this.toolCallId,\n\t\t\tinvalidate: () => {\n\t\t\t\tthis.invalidate();\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tlastComponent,\n\t\t\tstate: this.rendererState,\n\t\t\tcwd: this.cwd,\n\t\t\texecutionStarted: this.executionStarted,\n\t\t\targsComplete: this.argsComplete,\n\t\t\tisPartial: this.isPartial,\n\t\t\texpanded: this.expanded,\n\t\t\tshowImages: this.showImages,\n\t\t\tisError: this.result?.isError ?? false,\n\t\t};\n\t}\n\n\tprivate createCallFallback(): Component {\n\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(this.toolName)), 0, 0);\n\t}\n\n\tprivate createResultFallback(): Component | undefined {\n\t\tconst output = this.getTextOutput();\n\t\tif (!output) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn new Text(theme.fg(\"toolOutput\", output), 0, 0);\n\t}\n\n\tupdateArgs(args: any): void {\n\t\tthis.args = args;\n\t\tthis.updateDisplay();\n\t}\n\n\tmarkExecutionStarted(): void {\n\t\tthis.executionStarted = true;\n\t\tthis.updateDisplay();\n\t\tthis.ui.requestRender();\n\t}\n\n\tsetArgsComplete(): void {\n\t\tthis.argsComplete = true;\n\t\tthis.updateDisplay();\n\t\tthis.ui.requestRender();\n\t}\n\n\tupdateResult(\n\t\tresult: {\n\t\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\t\tdetails?: any;\n\t\t\tisError: boolean;\n\t\t},\n\t\tisPartial = false,\n\t): void {\n\t\tthis.result = result;\n\t\tthis.isPartial = isPartial;\n\t\tthis.updateDisplay();\n\t\tthis.maybeConvertImagesForKitty();\n\t}\n\n\tprivate maybeConvertImagesForKitty(): void {\n\t\tconst caps = getCapabilities();\n\t\tif (caps.images !== \"kitty\") return;\n\t\tif (!this.result) return;\n\n\t\tconst imageBlocks = this.result.content.filter((c) => c.type === \"image\");\n\t\tfor (let i = 0; i < imageBlocks.length; i++) {\n\t\t\tconst img = imageBlocks[i];\n\t\t\tif (!img.data || !img.mimeType) continue;\n\t\t\tif (img.mimeType === \"image/png\") continue;\n\t\t\tif (this.convertedImages.has(i)) continue;\n\n\t\t\tconst index = i;\n\t\t\tconvertToPng(img.data, img.mimeType).then((converted) => {\n\t\t\t\tif (this.frozen) return;\n\t\t\t\tif (converted) {\n\t\t\t\t\tthis.convertedImages.set(index, converted);\n\t\t\t\t\tthis.updateDisplay();\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\t// For radar/glance blocks the same global toggle reveals the hidden body;\n\t\t// for full blocks `expanded` alone switches truncated ↔ everything.\n\t\tthis.revealed = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetView(view: ToolOutputView): void {\n\t\tthis.view = view;\n\t\tthis.updateDisplay();\n\t}\n\n\t/**\n\t * Whether this block is currently opened up.\n\t *\n\t * Read by the one-at-a-time unfold, which walks back from the newest block\n\t * to find the first one still folded.\n\t */\n\tisRevealed(): boolean {\n\t\treturn this.revealed;\n\t}\n\n\t/** This call's contribution to its chain's summary line. */\n\tchainEntry(): ChainEntry {\n\t\tconst output = this.result ? this.getTextOutput() : \"\";\n\t\treturn {\n\t\t\ttool: this.toolName,\n\t\t\tsubject: toolSubject(this.args, this.cwd),\n\t\t\tisError: this.result?.isError === true,\n\t\t\tisPartial: this.result === undefined || this.isPartial,\n\t\t\toutputLines: output.trim() ? output.split(\"\\n\").length : 0,\n\t\t};\n\t}\n\n\t/** The failure text, for a collapsed chain to print under its line. */\n\terrorText(): string {\n\t\treturn this.result?.isError ? this.getTextOutput() : \"\";\n\t}\n\n\t/**\n\t * Whether the result body should render.\n\t *\n\t * A failure always shows its reason, in every view. Everything else about\n\t * these views is a judgement about how much detail you want; why something\n\t * broke is not detail, and a red dot with no explanation was the one thing\n\t * the folded views got wrong.\n\t */\n\tprivate shouldShowBody(): boolean {\n\t\treturn this.view === \"full\" || this.revealed || this.result?.isError === true;\n\t}\n\n\t/**\n\t * Whether this block draws its radar signal line instead of the tool's own\n\t * call renderer. A revealed block leaves radar for as long as it is open, so\n\t * \"expand\" means the same thing in every view.\n\t */\n\tprivate shouldShowSignalLine(): boolean {\n\t\treturn this.view === \"radar\" && !this.revealed;\n\t}\n\n\t/**\n\t * A failure's body under a radar row hangs off the row; anywhere else it sits\n\t * directly under its own call line and needs no extra indent.\n\t */\n\tprivate indentUnderSignalRow(component: Component): Component {\n\t\treturn this.shouldShowSignalLine() ? new IndentAll(component, 3) : component;\n\t}\n\n\tprivate getSignalComponent(): ToolSignalComponent {\n\t\tconst input = {\n\t\t\ttoolName: this.toolName,\n\t\t\targs: this.args,\n\t\t\tcwd: this.cwd,\n\t\t\tresult: this.result,\n\t\t\tisPartial: this.isPartial,\n\t\t\tshowImages: this.showImages,\n\t\t};\n\t\tif (this.signalComponent) {\n\t\t\tthis.signalComponent.setInput(input);\n\t\t} else {\n\t\t\tthis.signalComponent = new ToolSignalComponent(input);\n\t\t}\n\t\treturn this.signalComponent;\n\t}\n\n\tsetShowImages(show: boolean): void {\n\t\tthis.showImages = show;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetImageWidthCells(width: number): void {\n\t\tthis.imageWidthCells = Math.max(1, Math.floor(width));\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\t// A frozen block has released the state updateDisplay would rebuild from;\n\t\t// its captured snapshot is authoritative, so skip the rebuild entirely.\n\t\tif (this.frozen) return;\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\t/**\n\t * True when this block is a finished, visible tool result that still holds its\n\t * heavy source payloads — i.e. a candidate for freeze() once it scrolls out of\n\t * the live window. In-flight (partial) and already-frozen blocks are excluded.\n\t */\n\tisFreezable(): boolean {\n\t\treturn !this.frozen && !this.isPartial && !this.hideComponent && !!this.result;\n\t}\n\n\t/** Mark this block for freezing; the snapshot is captured on the next render\n\t * (at the real render width) and the heavy state released then. */\n\tfreeze(): void {\n\t\tif (this.isFreezable()) this.frozen = true;\n\t}\n\n\tprivate releaseHeavyState(): void {\n\t\tthis.result = undefined;\n\t\tthis.convertedImages.clear();\n\t\tthis.imageComponents = [];\n\t\tthis.imageSpacers = [];\n\t\tthis.rendererState = {};\n\t\tthis.callRendererComponent = undefined;\n\t\tthis.resultRendererComponent = undefined;\n\t\t// Drop child components so their line caches (which embed base64 image\n\t\t// payloads and full text output) become collectable.\n\t\tthis.clear();\n\t}\n\n\toverride render(width: number): string[] {\n\t\tif (this.hideComponent) {\n\t\t\treturn [];\n\t\t}\n\t\tif (this.frozenLines) {\n\t\t\t// Captured snapshot. Wider (or equal) terminal: emit as-is. Narrower:\n\t\t\t// re-truncate non-image lines to avoid the TUI's over-width crash guard,\n\t\t\t// caching the result per width so it stays O(1) on unchanged frames.\n\t\t\tif (width >= this.frozenWidth) return this.frozenLines;\n\t\t\tif (this.frozenTruncatedWidth === width && this.frozenTruncated) return this.frozenTruncated;\n\t\t\tthis.frozenTruncated = this.frozenLines.map((line) =>\n\t\t\t\tisImageLine(line) || visibleWidth(line) <= width ? line : truncateToWidth(line, width),\n\t\t\t);\n\t\t\tthis.frozenTruncatedWidth = width;\n\t\t\treturn this.frozenTruncated;\n\t\t}\n\t\tconst lines = super.render(width);\n\t\tif (this.frozen) {\n\t\t\tthis.frozenLines = lines;\n\t\t\tthis.frozenWidth = width;\n\t\t\tthis.releaseHeavyState();\n\t\t}\n\t\treturn lines;\n\t}\n\n\t/**\n\t * The container this block renders into.\n\t *\n\t * A self-rendering tool (`edit` draws its own framed diff) normally gets the\n\t * bare container so its frame can use the full width; everything else gets\n\t * the padded box. That only matters while the body is on screen. With the\n\t * body folded away the block is a single line, and the unpadded container\n\t * put it one column left of every other row — invisible while `peek` was an\n\t * opt-in, glaring now that `glance` is the default. So: padded box whenever\n\t * nothing is expanded, which also gives radar the uniform rows it exists for.\n\t */\n\tprivate activeShell(): Box | Container {\n\t\tif (!this.shouldShowBody()) return this.contentBox;\n\t\treturn this.getRenderShell() === \"self\" ? this.selfRenderContainer : this.contentBox;\n\t}\n\n\t/** Swap the attached shell when the view changes which one applies. */\n\tprivate syncShellChild(): void {\n\t\tconst wanted = this.activeShell();\n\t\tconst other = wanted === this.contentBox ? this.selfRenderContainer : this.contentBox;\n\t\tif (this.children.includes(wanted)) return;\n\t\tthis.removeChild(other);\n\t\tother.clear();\n\t\tthis.addChild(wanted);\n\t}\n\n\tprivate updateDisplay(): void {\n\t\t// Frozen blocks are immutable snapshots with their source state released.\n\t\tif (this.frozen) return;\n\t\tlet hasContent = false;\n\t\tthis.hideComponent = false;\n\t\tthis.leadingSpacer.setLines(this.shouldShowSignalLine() ? 0 : 1);\n\t\tif (this.hasRendererDefinition()) {\n\t\t\tthis.syncShellChild();\n\t\t\tconst renderContainer = this.activeShell();\n\t\t\t// Boxless: no background fill on the container.\n\t\t\tif (renderContainer instanceof Box) {\n\t\t\t\trenderContainer.setBgFn(undefined);\n\t\t\t}\n\t\t\trenderContainer.clear();\n\n\t\t\t// Status dot prefix: green for complete success, yellow for pending/partial, red for error.\n\t\t\t// The dot is prepended to the first line of the call renderer so it stays inline\n\t\t\t// (adding it as a separate child would stack it on its own line).\n\t\t\tconst dotColor = this.result?.isError ? \"error\" : this.isPartial ? \"warning\" : \"success\";\n\t\t\t// No disclosure caret. It is a click-target idiom in a TUI with no\n\t\t\t// pointer, it cost two columns on every row for an action that could\n\t\t\t// only ever be reached by keyboard, and reading it as \"this row opens\"\n\t\t\t// set an expectation the view does not meet.\n\t\t\tconst dot = theme.fg(dotColor, \"● \");\n\n\t\t\t// Radar replaces the tool's own call renderer with the uniform signal\n\t\t\t// row, so a screen of mixed tools lines up into one readable map.\n\t\t\tif (this.shouldShowSignalLine()) {\n\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.getSignalComponent()));\n\t\t\t\thasContent = true;\n\t\t\t} else {\n\t\t\t\tconst callRenderer = this.getCallRenderer();\n\t\t\t\tif (!callRenderer) {\n\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));\n\t\t\t\t\thasContent = true;\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));\n\t\t\t\t\t\tthis.callRendererComponent = component;\n\t\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, component));\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.callRendererComponent = undefined;\n\t\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.result && this.shouldShowBody()) {\n\t\t\t\tconst resultRenderer = this.getResultRenderer();\n\t\t\t\tif (!resultRenderer) {\n\t\t\t\t\tconst component = this.createResultFallback();\n\t\t\t\t\tif (component) {\n\t\t\t\t\t\trenderContainer.addChild(this.indentUnderSignalRow(component));\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst component = resultRenderer(\n\t\t\t\t\t\t\t{ content: this.result.content as any, details: this.result.details },\n\t\t\t\t\t\t\t{ expanded: this.expanded, isPartial: this.isPartial },\n\t\t\t\t\t\t\ttheme,\n\t\t\t\t\t\t\tthis.getRenderContext(this.resultRendererComponent),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.resultRendererComponent = component;\n\t\t\t\t\t\trenderContainer.addChild(this.indentUnderSignalRow(component));\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.resultRendererComponent = undefined;\n\t\t\t\t\t\tconst component = this.createResultFallback();\n\t\t\t\t\t\tif (component) {\n\t\t\t\t\t\t\trenderContainer.addChild(component);\n\t\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Boxless: no background fill.\n\t\t\tthis.contentText.setCustomBgFn(undefined);\n\t\t\tthis.contentText.setText(this.formatToolExecution());\n\t\t\thasContent = true;\n\t\t}\n\n\t\tfor (const img of this.imageComponents) {\n\t\t\tthis.removeChild(img);\n\t\t}\n\t\tthis.imageComponents = [];\n\t\tfor (const spacer of this.imageSpacers) {\n\t\t\tthis.removeChild(spacer);\n\t\t}\n\t\tthis.imageSpacers = [];\n\n\t\tif (this.result && !this.shouldShowSignalLine()) {\n\t\t\tconst imageBlocks = this.result.content.filter((c) => c.type === \"image\");\n\t\t\tconst caps = getCapabilities();\n\t\t\tfor (let i = 0; i < imageBlocks.length; i++) {\n\t\t\t\tconst img = imageBlocks[i];\n\t\t\t\tif (caps.images && this.showImages && img.data && img.mimeType) {\n\t\t\t\t\tconst converted = this.convertedImages.get(i);\n\t\t\t\t\tconst imageData = converted?.data ?? img.data;\n\t\t\t\t\tconst imageMimeType = converted?.mimeType ?? img.mimeType;\n\t\t\t\t\tif (caps.images === \"kitty\" && imageMimeType !== \"image/png\") continue;\n\n\t\t\t\t\tconst spacer = new Spacer(1);\n\t\t\t\t\tthis.addChild(spacer);\n\t\t\t\t\tthis.imageSpacers.push(spacer);\n\t\t\t\t\tconst imageComponent = new Image(\n\t\t\t\t\t\timageData,\n\t\t\t\t\t\timageMimeType,\n\t\t\t\t\t\t{ fallbackColor: (s: string) => theme.fg(\"toolOutput\", s) },\n\t\t\t\t\t\t{ maxWidthCells: this.imageWidthCells },\n\t\t\t\t\t);\n\t\t\t\t\tthis.imageComponents.push(imageComponent);\n\t\t\t\t\tthis.addChild(imageComponent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.hasRendererDefinition() && !hasContent && this.imageComponents.length === 0) {\n\t\t\tthis.hideComponent = true;\n\t\t}\n\t}\n\n\tprivate getTextOutput(): string {\n\t\treturn getRenderedTextOutput(this.result, this.showImages);\n\t}\n\n\tprivate formatToolExecution(): string {\n\t\tconst dotColor = this.result?.isError ? \"error\" : this.isPartial ? \"warning\" : \"success\";\n\t\t// A tool with no renderer still gets a radar row, just without the\n\t\t// flush-right signal column (this path has no width to align against).\n\t\tif (this.shouldShowSignalLine()) {\n\t\t\tconst subject = toolSubject(this.args, this.cwd);\n\t\t\treturn (\n\t\t\t\ttheme.fg(dotColor, \"● \") +\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(this.toolName)) +\n\t\t\t\t(subject ? ` ${theme.fg(\"toolOutput\", subject)}` : \"\")\n\t\t\t);\n\t\t}\n\t\tlet text = theme.fg(dotColor, \"● \") + theme.fg(\"toolTitle\", theme.bold(this.toolName));\n\t\tconst content = JSON.stringify(this.args, null, 2);\n\t\tif (content) {\n\t\t\ttext += `\\n\\n${content}`;\n\t\t}\n\t\tconst output = this.shouldShowBody() ? this.getTextOutput() : \"\";\n\t\tif (output) {\n\t\t\ttext += `\\n${output}`;\n\t\t}\n\t\treturn text;\n\t}\n}\n"]}
@@ -3,6 +3,7 @@ import { createAllToolDefinitions } from "../../../core/tools/index.js";
3
3
  import { getTextOutput as getRenderedTextOutput } from "../../../core/tools/render-utils.js";
4
4
  import { convertToPng } from "../../../utils/image-convert.js";
5
5
  import { theme } from "../theme/theme.js";
6
+ import { ToolSignalComponent, toolSubject } from "./tool-signal.js";
6
7
  /**
7
8
  * Renders a child component and prepends a prefix (e.g. a status dot) to its
8
9
  * first line, indenting continuation lines so they align under the content.
@@ -39,6 +40,32 @@ class PrefixFirstLine {
39
40
  return out;
40
41
  }
41
42
  }
43
+ /**
44
+ * Indents every line of a child, unlike PrefixFirstLine which only offsets the
45
+ * first. Used for a failure's body under a radar row, so the reason reads as
46
+ * hanging off the row rather than starting a new column.
47
+ */
48
+ class IndentAll {
49
+ child;
50
+ indent;
51
+ memo;
52
+ constructor(child, width) {
53
+ this.child = child;
54
+ this.indent = " ".repeat(width);
55
+ }
56
+ invalidate() {
57
+ this.child.invalidate?.();
58
+ this.memo = undefined;
59
+ }
60
+ render(width) {
61
+ const lines = this.child.render(Math.max(1, width - this.indent.length));
62
+ if (this.memo && this.memo.src === lines && this.memo.width === width)
63
+ return this.memo.out;
64
+ const out = lines.map((line) => (line.trim() === "" ? line : this.indent + line));
65
+ this.memo = { src: lines, width, out };
66
+ return out;
67
+ }
68
+ }
42
69
  export class ToolExecutionComponent extends Container {
43
70
  contentBox;
44
71
  contentText;
@@ -52,11 +79,13 @@ export class ToolExecutionComponent extends Container {
52
79
  toolCallId;
53
80
  args;
54
81
  expanded = false;
55
- // Persisted display level (collapsed/peek/standard). Controls whether the
56
- // result body renders at all; `revealed` is the per-block override that the
57
- // global expand key flips for collapsed/peek blocks.
58
- displayLevel;
82
+ // Persisted view (radar/glance/full). Controls how the call line is drawn and
83
+ // whether the result body renders at all; `revealed` is the per-block override
84
+ // the global expand key flips for radar/glance blocks.
85
+ view;
59
86
  revealed = false;
87
+ signalComponent;
88
+ leadingSpacer;
60
89
  showImages;
61
90
  imageWidthCells;
62
91
  isPartial = true;
@@ -92,10 +121,13 @@ export class ToolExecutionComponent extends Container {
92
121
  this.builtInToolDefinition = createAllToolDefinitions(cwd)[toolName];
93
122
  this.showImages = options.showImages ?? true;
94
123
  this.imageWidthCells = options.imageWidthCells ?? 60;
95
- this.displayLevel = options.displayLevel ?? "standard";
124
+ this.view = options.view ?? "glance";
96
125
  this.ui = ui;
97
126
  this.cwd = cwd;
98
- this.addChild(new Spacer(1));
127
+ // The separator between consecutive tool blocks. Radar drops it: rows that
128
+ // are one line each only read as a map when they stack without gaps.
129
+ this.leadingSpacer = new Spacer(1);
130
+ this.addChild(this.leadingSpacer);
99
131
  // Always create all shell variants. contentBox is used for default renderer-based composition.
100
132
  // selfRenderContainer is used when the tool renders its own framing.
101
133
  // contentText is reserved for generic fallback rendering when no tool definition exists.
@@ -106,7 +138,7 @@ export class ToolExecutionComponent extends Container {
106
138
  this.contentText = new Text("", 1, 0);
107
139
  this.selfRenderContainer = new Container();
108
140
  if (this.hasRendererDefinition()) {
109
- this.addChild(this.getRenderShell() === "self" ? this.selfRenderContainer : this.contentBox);
141
+ this.addChild(this.activeShell());
110
142
  }
111
143
  else {
112
144
  this.addChild(this.contentText);
@@ -221,18 +253,81 @@ export class ToolExecutionComponent extends Container {
221
253
  }
222
254
  setExpanded(expanded) {
223
255
  this.expanded = expanded;
224
- // For collapsed/peek blocks the same global toggle reveals the hidden body;
225
- // for standard blocks `expanded` alone switches truncated ↔ full.
256
+ // For radar/glance blocks the same global toggle reveals the hidden body;
257
+ // for full blocks `expanded` alone switches truncated ↔ everything.
226
258
  this.revealed = expanded;
227
259
  this.updateDisplay();
228
260
  }
229
- setDisplayLevel(level) {
230
- this.displayLevel = level;
261
+ setView(view) {
262
+ this.view = view;
231
263
  this.updateDisplay();
232
264
  }
233
- /** Whether the result body should render given the level and reveal state. */
265
+ /**
266
+ * Whether this block is currently opened up.
267
+ *
268
+ * Read by the one-at-a-time unfold, which walks back from the newest block
269
+ * to find the first one still folded.
270
+ */
271
+ isRevealed() {
272
+ return this.revealed;
273
+ }
274
+ /** This call's contribution to its chain's summary line. */
275
+ chainEntry() {
276
+ const output = this.result ? this.getTextOutput() : "";
277
+ return {
278
+ tool: this.toolName,
279
+ subject: toolSubject(this.args, this.cwd),
280
+ isError: this.result?.isError === true,
281
+ isPartial: this.result === undefined || this.isPartial,
282
+ outputLines: output.trim() ? output.split("\n").length : 0,
283
+ };
284
+ }
285
+ /** The failure text, for a collapsed chain to print under its line. */
286
+ errorText() {
287
+ return this.result?.isError ? this.getTextOutput() : "";
288
+ }
289
+ /**
290
+ * Whether the result body should render.
291
+ *
292
+ * A failure always shows its reason, in every view. Everything else about
293
+ * these views is a judgement about how much detail you want; why something
294
+ * broke is not detail, and a red dot with no explanation was the one thing
295
+ * the folded views got wrong.
296
+ */
234
297
  shouldShowBody() {
235
- return this.displayLevel === "standard" || this.revealed;
298
+ return this.view === "full" || this.revealed || this.result?.isError === true;
299
+ }
300
+ /**
301
+ * Whether this block draws its radar signal line instead of the tool's own
302
+ * call renderer. A revealed block leaves radar for as long as it is open, so
303
+ * "expand" means the same thing in every view.
304
+ */
305
+ shouldShowSignalLine() {
306
+ return this.view === "radar" && !this.revealed;
307
+ }
308
+ /**
309
+ * A failure's body under a radar row hangs off the row; anywhere else it sits
310
+ * directly under its own call line and needs no extra indent.
311
+ */
312
+ indentUnderSignalRow(component) {
313
+ return this.shouldShowSignalLine() ? new IndentAll(component, 3) : component;
314
+ }
315
+ getSignalComponent() {
316
+ const input = {
317
+ toolName: this.toolName,
318
+ args: this.args,
319
+ cwd: this.cwd,
320
+ result: this.result,
321
+ isPartial: this.isPartial,
322
+ showImages: this.showImages,
323
+ };
324
+ if (this.signalComponent) {
325
+ this.signalComponent.setInput(input);
326
+ }
327
+ else {
328
+ this.signalComponent = new ToolSignalComponent(input);
329
+ }
330
+ return this.signalComponent;
236
331
  }
237
332
  setShowImages(show) {
238
333
  this.showImages = show;
@@ -300,14 +395,42 @@ export class ToolExecutionComponent extends Container {
300
395
  }
301
396
  return lines;
302
397
  }
398
+ /**
399
+ * The container this block renders into.
400
+ *
401
+ * A self-rendering tool (`edit` draws its own framed diff) normally gets the
402
+ * bare container so its frame can use the full width; everything else gets
403
+ * the padded box. That only matters while the body is on screen. With the
404
+ * body folded away the block is a single line, and the unpadded container
405
+ * put it one column left of every other row — invisible while `peek` was an
406
+ * opt-in, glaring now that `glance` is the default. So: padded box whenever
407
+ * nothing is expanded, which also gives radar the uniform rows it exists for.
408
+ */
409
+ activeShell() {
410
+ if (!this.shouldShowBody())
411
+ return this.contentBox;
412
+ return this.getRenderShell() === "self" ? this.selfRenderContainer : this.contentBox;
413
+ }
414
+ /** Swap the attached shell when the view changes which one applies. */
415
+ syncShellChild() {
416
+ const wanted = this.activeShell();
417
+ const other = wanted === this.contentBox ? this.selfRenderContainer : this.contentBox;
418
+ if (this.children.includes(wanted))
419
+ return;
420
+ this.removeChild(other);
421
+ other.clear();
422
+ this.addChild(wanted);
423
+ }
303
424
  updateDisplay() {
304
425
  // Frozen blocks are immutable snapshots with their source state released.
305
426
  if (this.frozen)
306
427
  return;
307
428
  let hasContent = false;
308
429
  this.hideComponent = false;
430
+ this.leadingSpacer.setLines(this.shouldShowSignalLine() ? 0 : 1);
309
431
  if (this.hasRendererDefinition()) {
310
- const renderContainer = this.getRenderShell() === "self" ? this.selfRenderContainer : this.contentBox;
432
+ this.syncShellChild();
433
+ const renderContainer = this.activeShell();
311
434
  // Boxless: no background fill on the container.
312
435
  if (renderContainer instanceof Box) {
313
436
  renderContainer.setBgFn(undefined);
@@ -317,33 +440,43 @@ export class ToolExecutionComponent extends Container {
317
440
  // The dot is prepended to the first line of the call renderer so it stays inline
318
441
  // (adding it as a separate child would stack it on its own line).
319
442
  const dotColor = this.result?.isError ? "error" : this.isPartial ? "warning" : "success";
320
- // Peek blocks advertise their hidden body with a ▸/▾ caret before the dot.
321
- const caret = this.displayLevel === "peek" ? theme.fg("muted", this.revealed ? "▾ " : "▸ ") : "";
322
- const dot = caret + theme.fg(dotColor, " ");
323
- const callRenderer = this.getCallRenderer();
324
- if (!callRenderer) {
325
- renderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));
443
+ // No disclosure caret. It is a click-target idiom in a TUI with no
444
+ // pointer, it cost two columns on every row for an action that could
445
+ // only ever be reached by keyboard, and reading it as "this row opens"
446
+ // set an expectation the view does not meet.
447
+ const dot = theme.fg(dotColor, "● ");
448
+ // Radar replaces the tool's own call renderer with the uniform signal
449
+ // row, so a screen of mixed tools lines up into one readable map.
450
+ if (this.shouldShowSignalLine()) {
451
+ renderContainer.addChild(new PrefixFirstLine(dot, this.getSignalComponent()));
326
452
  hasContent = true;
327
453
  }
328
454
  else {
329
- try {
330
- const component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));
331
- this.callRendererComponent = component;
332
- renderContainer.addChild(new PrefixFirstLine(dot, component));
333
- hasContent = true;
334
- }
335
- catch {
336
- this.callRendererComponent = undefined;
455
+ const callRenderer = this.getCallRenderer();
456
+ if (!callRenderer) {
337
457
  renderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));
338
458
  hasContent = true;
339
459
  }
460
+ else {
461
+ try {
462
+ const component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));
463
+ this.callRendererComponent = component;
464
+ renderContainer.addChild(new PrefixFirstLine(dot, component));
465
+ hasContent = true;
466
+ }
467
+ catch {
468
+ this.callRendererComponent = undefined;
469
+ renderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));
470
+ hasContent = true;
471
+ }
472
+ }
340
473
  }
341
474
  if (this.result && this.shouldShowBody()) {
342
475
  const resultRenderer = this.getResultRenderer();
343
476
  if (!resultRenderer) {
344
477
  const component = this.createResultFallback();
345
478
  if (component) {
346
- renderContainer.addChild(component);
479
+ renderContainer.addChild(this.indentUnderSignalRow(component));
347
480
  hasContent = true;
348
481
  }
349
482
  }
@@ -351,7 +484,7 @@ export class ToolExecutionComponent extends Container {
351
484
  try {
352
485
  const component = resultRenderer({ content: this.result.content, details: this.result.details }, { expanded: this.expanded, isPartial: this.isPartial }, theme, this.getRenderContext(this.resultRendererComponent));
353
486
  this.resultRendererComponent = component;
354
- renderContainer.addChild(component);
487
+ renderContainer.addChild(this.indentUnderSignalRow(component));
355
488
  hasContent = true;
356
489
  }
357
490
  catch {
@@ -379,7 +512,7 @@ export class ToolExecutionComponent extends Container {
379
512
  this.removeChild(spacer);
380
513
  }
381
514
  this.imageSpacers = [];
382
- if (this.result) {
515
+ if (this.result && !this.shouldShowSignalLine()) {
383
516
  const imageBlocks = this.result.content.filter((c) => c.type === "image");
384
517
  const caps = getCapabilities();
385
518
  for (let i = 0; i < imageBlocks.length; i++) {
@@ -408,8 +541,15 @@ export class ToolExecutionComponent extends Container {
408
541
  }
409
542
  formatToolExecution() {
410
543
  const dotColor = this.result?.isError ? "error" : this.isPartial ? "warning" : "success";
411
- const caret = this.displayLevel === "peek" ? theme.fg("muted", this.revealed ? "▾ " : "▸ ") : "";
412
- let text = caret + theme.fg(dotColor, "● ") + theme.fg("toolTitle", theme.bold(this.toolName));
544
+ // A tool with no renderer still gets a radar row, just without the
545
+ // flush-right signal column (this path has no width to align against).
546
+ if (this.shouldShowSignalLine()) {
547
+ const subject = toolSubject(this.args, this.cwd);
548
+ return (theme.fg(dotColor, "● ") +
549
+ theme.fg("toolTitle", theme.bold(this.toolName)) +
550
+ (subject ? ` ${theme.fg("toolOutput", subject)}` : ""));
551
+ }
552
+ let text = theme.fg(dotColor, "● ") + theme.fg("toolTitle", theme.bold(this.toolName));
413
553
  const content = JSON.stringify(this.args, null, 2);
414
554
  if (content) {
415
555
  text += `\n\n${content}`;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-execution.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tool-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,GAAG,EAEH,SAAS,EACT,eAAe,EACf,KAAK,EACL,WAAW,EACX,MAAM,EACN,IAAI,EAEJ,eAAe,EACf,YAAY,GACZ,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,wBAAwB,EAAiB,MAAM,8BAA8B,CAAC;AACvF,OAAO,EAAE,aAAa,IAAI,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAgB1C;;;;;;GAMG;AACH,MAAM,eAAe;IACZ,MAAM,CAAS;IACf,KAAK,CAAY;IACjB,WAAW,CAAS;IAC5B,2EAA2E;IAC3E,yEAAyE;IACjE,IAAI,CAAmD;IAE/D,YAAY,MAAc,EAAE,KAAgB,EAAE;QAC7C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAAA,CACxC;IAED,UAAU,GAAS;QAClB,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;IAAA,CACtB;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,6EAA6E;QAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YACvE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QACtB,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,GAAG,GACR,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;QAC7G,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QACvC,OAAO,GAAG,CAAC;IAAA,CACX;CACD;AAED,MAAM,OAAO,sBAAuB,SAAQ,SAAS;IAC5C,UAAU,CAAM;IAChB,WAAW,CAAO;IAClB,mBAAmB,CAAY;IAC/B,qBAAqB,CAAa;IAClC,uBAAuB,CAAa;IACpC,aAAa,GAAQ,EAAE,CAAC;IACxB,eAAe,GAAY,EAAE,CAAC;IAC9B,YAAY,GAAa,EAAE,CAAC;IAC5B,QAAQ,CAAS;IACjB,UAAU,CAAS;IACnB,IAAI,CAAM;IACV,QAAQ,GAAG,KAAK,CAAC;IACzB,0EAA0E;IAC1E,4EAA4E;IAC5E,qDAAqD;IAC7C,YAAY,CAAyB;IACrC,QAAQ,GAAG,KAAK,CAAC;IACjB,UAAU,CAAU;IACpB,eAAe,CAAS;IACxB,SAAS,GAAG,IAAI,CAAC;IACjB,cAAc,CAA4B;IAC1C,qBAAqB,CAA4B;IACjD,EAAE,CAAM;IACR,GAAG,CAAS;IACZ,gBAAgB,GAAG,KAAK,CAAC;IACzB,YAAY,GAAG,KAAK,CAAC;IACrB,MAAM,CAIZ;IACM,eAAe,GAAoD,IAAI,GAAG,EAAE,CAAC;IAC7E,aAAa,GAAG,KAAK,CAAC;IAC9B,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,+EAA+E;IAC/E,gFAA8E;IAC9E,+EAA+E;IAC/E,8DAA8D;IACtD,MAAM,GAAG,KAAK,CAAC;IACf,WAAW,CAAY;IAC/B;uFACmF;IAC3E,WAAW,GAAG,CAAC,CAAC;IAChB,eAAe,CAAY;IAC3B,oBAAoB,GAAG,CAAC,CAAC,CAAC;IAElC,YACC,QAAgB,EAChB,UAAkB,EAClB,IAAS,EACT,OAAO,GAAyB,EAAE,EAClC,cAAoD,EACpD,EAAO,EACP,GAAW,EACV;QACD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,qBAAqB,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC,QAAoB,CAAC,CAAC;QACjF,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;QAC7C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;QACrD,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,UAAU,CAAC;QACvD,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QAEf,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,+FAA+F;QAC/F,qEAAqE;QACrE,yFAAyF;QACzF,+EAA6E;QAC7E,4EAA4E;QAC5E,+EAA+E;QAC/E,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChC,IAAI,CAAC,WAAW,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACtC,IAAI,CAAC,mBAAmB,GAAG,IAAI,SAAS,EAAE,CAAC;QAE3C,IAAI,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC9F,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEO,eAAe,GAAuD;QAC7E,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;QAC9C,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,IAAI,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;IAAA,CAC/E;IAEO,iBAAiB,GAAyD;QACjF,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC;QAC1C,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC,YAAY,IAAI,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC;IAAA,CACnF;IAEO,qBAAqB,GAAY;QACxC,OAAO,IAAI,CAAC,qBAAqB,KAAK,SAAS,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS,CAAC;IAAA,CACrF;IAEO,cAAc,GAAuB;QAC5C,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,cAAc,EAAE,WAAW,IAAI,SAAS,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,qBAAqB,CAAC,WAAW,IAAI,SAAS,CAAC;QAC5D,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,CAAC,WAAW,IAAI,IAAI,CAAC,qBAAqB,CAAC,WAAW,IAAI,SAAS,CAAC;IAAA,CAC9F;IAEO,gBAAgB,CAAC,aAAoC,EAAqB;QACjF,OAAO;YACN,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,GAAG,EAAE,CAAC;gBACjB,IAAI,CAAC,UAAU,EAAE,CAAC;gBAClB,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;YAAA,CACxB;YACD,aAAa;YACb,KAAK,EAAE,IAAI,CAAC,aAAa;YACzB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,KAAK;SACtC,CAAC;IAAA,CACF;IAEO,kBAAkB,GAAc;QACvC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAAA,CACxE;IAEO,oBAAoB,GAA0B;QACrD,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAAA,CACtD;IAED,UAAU,CAAC,IAAS,EAAQ;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED,oBAAoB,GAAS;QAC5B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;IAAA,CACxB;IAED,eAAe,GAAS;QACvB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;IAAA,CACxB;IAED,YAAY,CACX,MAIC,EACD,SAAS,GAAG,KAAK,EACV;QACP,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,0BAA0B,EAAE,CAAC;IAAA,CAClC;IAEO,0BAA0B,GAAS;QAC1C,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO;QACpC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEzB,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QAC1E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ;gBAAE,SAAS;YACzC,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW;gBAAE,SAAS;YAC3C,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAE1C,MAAM,KAAK,GAAG,CAAC,CAAC;YAChB,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC;gBACxD,IAAI,IAAI,CAAC,MAAM;oBAAE,OAAO;gBACxB,IAAI,SAAS,EAAE,CAAC;oBACf,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;oBAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;oBACrB,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;gBACzB,CAAC;YAAA,CACD,CAAC,CAAC;QACJ,CAAC;IAAA,CACD;IAED,WAAW,CAAC,QAAiB,EAAQ;QACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,4EAA4E;QAC5E,oEAAkE;QAClE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED,eAAe,CAAC,KAA6B,EAAQ;QACpD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED,8EAA8E;IACtE,cAAc,GAAY;QACjC,OAAO,IAAI,CAAC,YAAY,KAAK,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC;IAAA,CACzD;IAED,aAAa,CAAC,IAAa,EAAQ;QAClC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED,kBAAkB,CAAC,KAAa,EAAQ;QACvC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEQ,UAAU,GAAS;QAC3B,0EAA0E;QAC1E,wEAAwE;QACxE,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED;;;;OAIG;IACH,WAAW,GAAY;QACtB,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IAAA,CAC/E;IAED;uEACmE;IACnE,MAAM,GAAS;QACd,IAAI,IAAI,CAAC,WAAW,EAAE;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IAAA,CAC3C;IAEO,iBAAiB,GAAS;QACjC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;QACvC,IAAI,CAAC,uBAAuB,GAAG,SAAS,CAAC;QACzC,uEAAuE;QACvE,qDAAqD;QACrD,IAAI,CAAC,KAAK,EAAE,CAAC;IAAA,CACb;IAEQ,MAAM,CAAC,KAAa,EAAY;QACxC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,EAAE,CAAC;QACX,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,sEAAsE;YACtE,yEAAyE;YACzE,qEAAqE;YACrE,IAAI,KAAK,IAAI,IAAI,CAAC,WAAW;gBAAE,OAAO,IAAI,CAAC,WAAW,CAAC;YACvD,IAAI,IAAI,CAAC,oBAAoB,KAAK,KAAK,IAAI,IAAI,CAAC,eAAe;gBAAE,OAAO,IAAI,CAAC,eAAe,CAAC;YAC7F,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACpD,WAAW,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CACtF,CAAC;YACF,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;YAClC,OAAO,IAAI,CAAC,eAAe,CAAC;QAC7B,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC1B,CAAC;QACD,OAAO,KAAK,CAAC;IAAA,CACb;IAEO,aAAa,GAAS;QAC7B,0EAA0E;QAC1E,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC3B,IAAI,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC;YAClC,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;YACtG,gDAAgD;YAChD,IAAI,eAAe,YAAY,GAAG,EAAE,CAAC;gBACpC,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACpC,CAAC;YACD,eAAe,CAAC,KAAK,EAAE,CAAC;YAExB,4FAA4F;YAC5F,iFAAiF;YACjF,kEAAkE;YAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;YACzF,+EAA2E;YAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAI,CAAC,CAAC,CAAC,MAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACjG,MAAM,GAAG,GAAG,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAI,CAAC,CAAC;YAE7C,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YAC5C,IAAI,CAAC,YAAY,EAAE,CAAC;gBACnB,eAAe,CAAC,QAAQ,CAAC,IAAI,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;gBAC9E,UAAU,GAAG,IAAI,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC;oBACJ,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;oBACpG,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;oBACvC,eAAe,CAAC,QAAQ,CAAC,IAAI,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;oBAC9D,UAAU,GAAG,IAAI,CAAC;gBACnB,CAAC;gBAAC,MAAM,CAAC;oBACR,IAAI,CAAC,qBAAqB,GAAG,SAAS,CAAC;oBACvC,eAAe,CAAC,QAAQ,CAAC,IAAI,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;oBAC9E,UAAU,GAAG,IAAI,CAAC;gBACnB,CAAC;YACF,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC1C,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAChD,IAAI,CAAC,cAAc,EAAE,CAAC;oBACrB,MAAM,SAAS,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;oBAC9C,IAAI,SAAS,EAAE,CAAC;wBACf,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;wBACpC,UAAU,GAAG,IAAI,CAAC;oBACnB,CAAC;gBACF,CAAC;qBAAM,CAAC;oBACP,IAAI,CAAC;wBACJ,MAAM,SAAS,GAAG,cAAc,CAC/B,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAc,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EACrE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,EACtD,KAAK,EACL,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,uBAAuB,CAAC,CACnD,CAAC;wBACF,IAAI,CAAC,uBAAuB,GAAG,SAAS,CAAC;wBACzC,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;wBACpC,UAAU,GAAG,IAAI,CAAC;oBACnB,CAAC;oBAAC,MAAM,CAAC;wBACR,IAAI,CAAC,uBAAuB,GAAG,SAAS,CAAC;wBACzC,MAAM,SAAS,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;wBAC9C,IAAI,SAAS,EAAE,CAAC;4BACf,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;4BACpC,UAAU,GAAG,IAAI,CAAC;wBACnB,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;aAAM,CAAC;YACP,+BAA+B;YAC/B,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YAC1C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;YACrD,UAAU,GAAG,IAAI,CAAC;QACnB,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACxC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACxC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QAEvB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;YAC1E,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;YAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC7C,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;gBAC3B,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;oBAChE,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBAC9C,MAAM,SAAS,GAAG,SAAS,EAAE,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC;oBAC9C,MAAM,aAAa,GAAG,SAAS,EAAE,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC;oBAC1D,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,aAAa,KAAK,WAAW;wBAAE,SAAS;oBAEvE,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;oBAC7B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;oBACtB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAC/B,MAAM,cAAc,GAAG,IAAI,KAAK,CAC/B,SAAS,EACT,aAAa,EACb,EAAE,aAAa,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAC3D,EAAE,aAAa,EAAE,IAAI,CAAC,eAAe,EAAE,CACvC,CAAC;oBACF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;oBAC1C,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;gBAC/B,CAAC;YACF,CAAC;QACF,CAAC;QAED,IAAI,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC3B,CAAC;IAAA,CACD;IAEO,aAAa,GAAW;QAC/B,OAAO,qBAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAAA,CAC3D;IAEO,mBAAmB,GAAW;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QACzF,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAI,CAAC,CAAC,CAAC,MAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAI,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/F,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACnD,IAAI,OAAO,EAAE,CAAC;YACb,IAAI,IAAI,OAAO,OAAO,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACvB,CAAC;QACD,OAAO,IAAI,CAAC;IAAA,CACZ;CACD","sourcesContent":["import {\n\tBox,\n\ttype Component,\n\tContainer,\n\tgetCapabilities,\n\tImage,\n\tisImageLine,\n\tSpacer,\n\tText,\n\ttype TUI,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport type { ToolDefinition, ToolRenderContext } from \"../../../core/extensions/types.js\";\nimport { createAllToolDefinitions, type ToolName } from \"../../../core/tools/index.js\";\nimport { getTextOutput as getRenderedTextOutput } from \"../../../core/tools/render-utils.js\";\nimport { convertToPng } from \"../../../utils/image-convert.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * How a tool block's result body is displayed:\n * - \"standard\": result shown (truncated preview, expandable) — the default.\n * - \"collapsed\": result hidden; only the call line + status dot render.\n * - \"peek\": result hidden by default with a ▸ affordance; the expand key reveals it.\n */\nexport type ToolOutputDisplayLevel = \"collapsed\" | \"peek\" | \"standard\";\n\nexport interface ToolExecutionOptions {\n\tshowImages?: boolean;\n\timageWidthCells?: number;\n\tdisplayLevel?: ToolOutputDisplayLevel;\n}\n\n/**\n * Renders a child component and prepends a prefix (e.g. a status dot) to its\n * first line, indenting continuation lines so they align under the content.\n *\n * This keeps the dot inline with the first line instead of stacking it on its\n * own line (which happens when a Container holds the dot as a separate child).\n */\nclass PrefixFirstLine implements Component {\n\tprivate prefix: string;\n\tprivate child: Component;\n\tprivate indentWidth: number;\n\t// Keyed on the child's returned array identity so an unchanged child keeps\n\t// this wrapper reference-stable too (parents memoize flattening by ref).\n\tprivate memo?: { src: string[]; width: number; out: string[] };\n\n\tconstructor(prefix: string, child: Component) {\n\t\tthis.prefix = prefix;\n\t\tthis.child = child;\n\t\tthis.indentWidth = visibleWidth(prefix);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.child.invalidate?.();\n\t\tthis.memo = undefined;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Reserve room for the prefix so the child wraps within the remaining width.\n\t\tconst childWidth = Math.max(1, width - this.indentWidth);\n\t\tconst lines = this.child.render(childWidth);\n\t\tif (this.memo && this.memo.src === lines && this.memo.width === width) {\n\t\t\treturn this.memo.out;\n\t\t}\n\t\tconst indent = \" \".repeat(this.indentWidth);\n\t\tconst out =\n\t\t\tlines.length === 0 ? [this.prefix] : lines.map((line, i) => (i === 0 ? this.prefix + line : indent + line));\n\t\tthis.memo = { src: lines, width, out };\n\t\treturn out;\n\t}\n}\n\nexport class ToolExecutionComponent extends Container {\n\tprivate contentBox: Box;\n\tprivate contentText: Text;\n\tprivate selfRenderContainer: Container;\n\tprivate callRendererComponent?: Component;\n\tprivate resultRendererComponent?: Component;\n\tprivate rendererState: any = {};\n\tprivate imageComponents: Image[] = [];\n\tprivate imageSpacers: Spacer[] = [];\n\tprivate toolName: string;\n\tprivate toolCallId: string;\n\tprivate args: any;\n\tprivate expanded = false;\n\t// Persisted display level (collapsed/peek/standard). Controls whether the\n\t// result body renders at all; `revealed` is the per-block override that the\n\t// global expand key flips for collapsed/peek blocks.\n\tprivate displayLevel: ToolOutputDisplayLevel;\n\tprivate revealed = false;\n\tprivate showImages: boolean;\n\tprivate imageWidthCells: number;\n\tprivate isPartial = true;\n\tprivate toolDefinition?: ToolDefinition<any, any>;\n\tprivate builtInToolDefinition?: ToolDefinition<any, any>;\n\tprivate ui: TUI;\n\tprivate cwd: string;\n\tprivate executionStarted = false;\n\tprivate argsComplete = false;\n\tprivate result?: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tisError: boolean;\n\t\tdetails?: any;\n\t};\n\tprivate convertedImages: Map<number, { data: string; mimeType: string }> = new Map();\n\tprivate hideComponent = false;\n\t// Once a finished tool block has scrolled far out of view the interactive mode\n\t// freezes it (see freeze()): its already-rendered lines are captured and the\n\t// heavy source payloads (full tool result, base64 image copies, per-renderer\n\t// state, child component caches) are released. A frozen block is immutable and\n\t// no longer reflows on resize — old off-screen scrollback keeps its wrapping,\n\t// which self-heals on any full rebuild (theme toggle / session reload, both of\n\t// which reconstruct components from the intact session data).\n\tprivate frozen = false;\n\tprivate frozenLines?: string[];\n\t/** Width the frozen snapshot was captured at; a wider terminal is safe as-is,\n\t * a narrower one needs re-truncation to avoid the TUI's over-width crash guard. */\n\tprivate frozenWidth = 0;\n\tprivate frozenTruncated?: string[];\n\tprivate frozenTruncatedWidth = -1;\n\n\tconstructor(\n\t\ttoolName: string,\n\t\ttoolCallId: string,\n\t\targs: any,\n\t\toptions: ToolExecutionOptions = {},\n\t\ttoolDefinition: ToolDefinition<any, any> | undefined,\n\t\tui: TUI,\n\t\tcwd: string,\n\t) {\n\t\tsuper();\n\t\tthis.toolName = toolName;\n\t\tthis.toolCallId = toolCallId;\n\t\tthis.args = args;\n\t\tthis.toolDefinition = toolDefinition;\n\t\tthis.builtInToolDefinition = createAllToolDefinitions(cwd)[toolName as ToolName];\n\t\tthis.showImages = options.showImages ?? true;\n\t\tthis.imageWidthCells = options.imageWidthCells ?? 60;\n\t\tthis.displayLevel = options.displayLevel ?? \"standard\";\n\t\tthis.ui = ui;\n\t\tthis.cwd = cwd;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Always create all shell variants. contentBox is used for default renderer-based composition.\n\t\t// selfRenderContainer is used when the tool renders its own framing.\n\t\t// contentText is reserved for generic fallback rendering when no tool definition exists.\n\t\t// Boxless: no filled background — hierarchy comes from status dots + indent.\n\t\t// paddingY is 0: the single leading Spacer(1) is the only separator between\n\t\t// tool blocks, so consecutive commands don't stack 3 blank lines between them.\n\t\tthis.contentBox = new Box(1, 0);\n\t\tthis.contentText = new Text(\"\", 1, 0);\n\t\tthis.selfRenderContainer = new Container();\n\n\t\tif (this.hasRendererDefinition()) {\n\t\t\tthis.addChild(this.getRenderShell() === \"self\" ? this.selfRenderContainer : this.contentBox);\n\t\t} else {\n\t\t\tthis.addChild(this.contentText);\n\t\t}\n\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate getCallRenderer(): ToolDefinition<any, any>[\"renderCall\"] | undefined {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderCall;\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderCall;\n\t\t}\n\t\treturn this.toolDefinition.renderCall ?? this.builtInToolDefinition.renderCall;\n\t}\n\n\tprivate getResultRenderer(): ToolDefinition<any, any>[\"renderResult\"] | undefined {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderResult;\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderResult;\n\t\t}\n\t\treturn this.toolDefinition.renderResult ?? this.builtInToolDefinition.renderResult;\n\t}\n\n\tprivate hasRendererDefinition(): boolean {\n\t\treturn this.builtInToolDefinition !== undefined || this.toolDefinition !== undefined;\n\t}\n\n\tprivate getRenderShell(): \"default\" | \"self\" {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderShell ?? \"default\";\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderShell ?? \"default\";\n\t\t}\n\t\treturn this.toolDefinition.renderShell ?? this.builtInToolDefinition.renderShell ?? \"default\";\n\t}\n\n\tprivate getRenderContext(lastComponent: Component | undefined): ToolRenderContext {\n\t\treturn {\n\t\t\targs: this.args,\n\t\t\ttoolCallId: this.toolCallId,\n\t\t\tinvalidate: () => {\n\t\t\t\tthis.invalidate();\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tlastComponent,\n\t\t\tstate: this.rendererState,\n\t\t\tcwd: this.cwd,\n\t\t\texecutionStarted: this.executionStarted,\n\t\t\targsComplete: this.argsComplete,\n\t\t\tisPartial: this.isPartial,\n\t\t\texpanded: this.expanded,\n\t\t\tshowImages: this.showImages,\n\t\t\tisError: this.result?.isError ?? false,\n\t\t};\n\t}\n\n\tprivate createCallFallback(): Component {\n\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(this.toolName)), 0, 0);\n\t}\n\n\tprivate createResultFallback(): Component | undefined {\n\t\tconst output = this.getTextOutput();\n\t\tif (!output) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn new Text(theme.fg(\"toolOutput\", output), 0, 0);\n\t}\n\n\tupdateArgs(args: any): void {\n\t\tthis.args = args;\n\t\tthis.updateDisplay();\n\t}\n\n\tmarkExecutionStarted(): void {\n\t\tthis.executionStarted = true;\n\t\tthis.updateDisplay();\n\t\tthis.ui.requestRender();\n\t}\n\n\tsetArgsComplete(): void {\n\t\tthis.argsComplete = true;\n\t\tthis.updateDisplay();\n\t\tthis.ui.requestRender();\n\t}\n\n\tupdateResult(\n\t\tresult: {\n\t\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\t\tdetails?: any;\n\t\t\tisError: boolean;\n\t\t},\n\t\tisPartial = false,\n\t): void {\n\t\tthis.result = result;\n\t\tthis.isPartial = isPartial;\n\t\tthis.updateDisplay();\n\t\tthis.maybeConvertImagesForKitty();\n\t}\n\n\tprivate maybeConvertImagesForKitty(): void {\n\t\tconst caps = getCapabilities();\n\t\tif (caps.images !== \"kitty\") return;\n\t\tif (!this.result) return;\n\n\t\tconst imageBlocks = this.result.content.filter((c) => c.type === \"image\");\n\t\tfor (let i = 0; i < imageBlocks.length; i++) {\n\t\t\tconst img = imageBlocks[i];\n\t\t\tif (!img.data || !img.mimeType) continue;\n\t\t\tif (img.mimeType === \"image/png\") continue;\n\t\t\tif (this.convertedImages.has(i)) continue;\n\n\t\t\tconst index = i;\n\t\t\tconvertToPng(img.data, img.mimeType).then((converted) => {\n\t\t\t\tif (this.frozen) return;\n\t\t\t\tif (converted) {\n\t\t\t\t\tthis.convertedImages.set(index, converted);\n\t\t\t\t\tthis.updateDisplay();\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\t// For collapsed/peek blocks the same global toggle reveals the hidden body;\n\t\t// for standard blocks `expanded` alone switches truncated ↔ full.\n\t\tthis.revealed = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetDisplayLevel(level: ToolOutputDisplayLevel): void {\n\t\tthis.displayLevel = level;\n\t\tthis.updateDisplay();\n\t}\n\n\t/** Whether the result body should render given the level and reveal state. */\n\tprivate shouldShowBody(): boolean {\n\t\treturn this.displayLevel === \"standard\" || this.revealed;\n\t}\n\n\tsetShowImages(show: boolean): void {\n\t\tthis.showImages = show;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetImageWidthCells(width: number): void {\n\t\tthis.imageWidthCells = Math.max(1, Math.floor(width));\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\t// A frozen block has released the state updateDisplay would rebuild from;\n\t\t// its captured snapshot is authoritative, so skip the rebuild entirely.\n\t\tif (this.frozen) return;\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\t/**\n\t * True when this block is a finished, visible tool result that still holds its\n\t * heavy source payloads — i.e. a candidate for freeze() once it scrolls out of\n\t * the live window. In-flight (partial) and already-frozen blocks are excluded.\n\t */\n\tisFreezable(): boolean {\n\t\treturn !this.frozen && !this.isPartial && !this.hideComponent && !!this.result;\n\t}\n\n\t/** Mark this block for freezing; the snapshot is captured on the next render\n\t * (at the real render width) and the heavy state released then. */\n\tfreeze(): void {\n\t\tif (this.isFreezable()) this.frozen = true;\n\t}\n\n\tprivate releaseHeavyState(): void {\n\t\tthis.result = undefined;\n\t\tthis.convertedImages.clear();\n\t\tthis.imageComponents = [];\n\t\tthis.imageSpacers = [];\n\t\tthis.rendererState = {};\n\t\tthis.callRendererComponent = undefined;\n\t\tthis.resultRendererComponent = undefined;\n\t\t// Drop child components so their line caches (which embed base64 image\n\t\t// payloads and full text output) become collectable.\n\t\tthis.clear();\n\t}\n\n\toverride render(width: number): string[] {\n\t\tif (this.hideComponent) {\n\t\t\treturn [];\n\t\t}\n\t\tif (this.frozenLines) {\n\t\t\t// Captured snapshot. Wider (or equal) terminal: emit as-is. Narrower:\n\t\t\t// re-truncate non-image lines to avoid the TUI's over-width crash guard,\n\t\t\t// caching the result per width so it stays O(1) on unchanged frames.\n\t\t\tif (width >= this.frozenWidth) return this.frozenLines;\n\t\t\tif (this.frozenTruncatedWidth === width && this.frozenTruncated) return this.frozenTruncated;\n\t\t\tthis.frozenTruncated = this.frozenLines.map((line) =>\n\t\t\t\tisImageLine(line) || visibleWidth(line) <= width ? line : truncateToWidth(line, width),\n\t\t\t);\n\t\t\tthis.frozenTruncatedWidth = width;\n\t\t\treturn this.frozenTruncated;\n\t\t}\n\t\tconst lines = super.render(width);\n\t\tif (this.frozen) {\n\t\t\tthis.frozenLines = lines;\n\t\t\tthis.frozenWidth = width;\n\t\t\tthis.releaseHeavyState();\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate updateDisplay(): void {\n\t\t// Frozen blocks are immutable snapshots with their source state released.\n\t\tif (this.frozen) return;\n\t\tlet hasContent = false;\n\t\tthis.hideComponent = false;\n\t\tif (this.hasRendererDefinition()) {\n\t\t\tconst renderContainer = this.getRenderShell() === \"self\" ? this.selfRenderContainer : this.contentBox;\n\t\t\t// Boxless: no background fill on the container.\n\t\t\tif (renderContainer instanceof Box) {\n\t\t\t\trenderContainer.setBgFn(undefined);\n\t\t\t}\n\t\t\trenderContainer.clear();\n\n\t\t\t// Status dot prefix: green for complete success, yellow for pending/partial, red for error.\n\t\t\t// The dot is prepended to the first line of the call renderer so it stays inline\n\t\t\t// (adding it as a separate child would stack it on its own line).\n\t\t\tconst dotColor = this.result?.isError ? \"error\" : this.isPartial ? \"warning\" : \"success\";\n\t\t\t// Peek blocks advertise their hidden body with a ▸/▾ caret before the dot.\n\t\t\tconst caret = this.displayLevel === \"peek\" ? theme.fg(\"muted\", this.revealed ? \"▾ \" : \"▸ \") : \"\";\n\t\t\tconst dot = caret + theme.fg(dotColor, \"● \");\n\n\t\t\tconst callRenderer = this.getCallRenderer();\n\t\t\tif (!callRenderer) {\n\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));\n\t\t\t\thasContent = true;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));\n\t\t\t\t\tthis.callRendererComponent = component;\n\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, component));\n\t\t\t\t\thasContent = true;\n\t\t\t\t} catch {\n\t\t\t\t\tthis.callRendererComponent = undefined;\n\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));\n\t\t\t\t\thasContent = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.result && this.shouldShowBody()) {\n\t\t\t\tconst resultRenderer = this.getResultRenderer();\n\t\t\t\tif (!resultRenderer) {\n\t\t\t\t\tconst component = this.createResultFallback();\n\t\t\t\t\tif (component) {\n\t\t\t\t\t\trenderContainer.addChild(component);\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst component = resultRenderer(\n\t\t\t\t\t\t\t{ content: this.result.content as any, details: this.result.details },\n\t\t\t\t\t\t\t{ expanded: this.expanded, isPartial: this.isPartial },\n\t\t\t\t\t\t\ttheme,\n\t\t\t\t\t\t\tthis.getRenderContext(this.resultRendererComponent),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.resultRendererComponent = component;\n\t\t\t\t\t\trenderContainer.addChild(component);\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.resultRendererComponent = undefined;\n\t\t\t\t\t\tconst component = this.createResultFallback();\n\t\t\t\t\t\tif (component) {\n\t\t\t\t\t\t\trenderContainer.addChild(component);\n\t\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Boxless: no background fill.\n\t\t\tthis.contentText.setCustomBgFn(undefined);\n\t\t\tthis.contentText.setText(this.formatToolExecution());\n\t\t\thasContent = true;\n\t\t}\n\n\t\tfor (const img of this.imageComponents) {\n\t\t\tthis.removeChild(img);\n\t\t}\n\t\tthis.imageComponents = [];\n\t\tfor (const spacer of this.imageSpacers) {\n\t\t\tthis.removeChild(spacer);\n\t\t}\n\t\tthis.imageSpacers = [];\n\n\t\tif (this.result) {\n\t\t\tconst imageBlocks = this.result.content.filter((c) => c.type === \"image\");\n\t\t\tconst caps = getCapabilities();\n\t\t\tfor (let i = 0; i < imageBlocks.length; i++) {\n\t\t\t\tconst img = imageBlocks[i];\n\t\t\t\tif (caps.images && this.showImages && img.data && img.mimeType) {\n\t\t\t\t\tconst converted = this.convertedImages.get(i);\n\t\t\t\t\tconst imageData = converted?.data ?? img.data;\n\t\t\t\t\tconst imageMimeType = converted?.mimeType ?? img.mimeType;\n\t\t\t\t\tif (caps.images === \"kitty\" && imageMimeType !== \"image/png\") continue;\n\n\t\t\t\t\tconst spacer = new Spacer(1);\n\t\t\t\t\tthis.addChild(spacer);\n\t\t\t\t\tthis.imageSpacers.push(spacer);\n\t\t\t\t\tconst imageComponent = new Image(\n\t\t\t\t\t\timageData,\n\t\t\t\t\t\timageMimeType,\n\t\t\t\t\t\t{ fallbackColor: (s: string) => theme.fg(\"toolOutput\", s) },\n\t\t\t\t\t\t{ maxWidthCells: this.imageWidthCells },\n\t\t\t\t\t);\n\t\t\t\t\tthis.imageComponents.push(imageComponent);\n\t\t\t\t\tthis.addChild(imageComponent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.hasRendererDefinition() && !hasContent && this.imageComponents.length === 0) {\n\t\t\tthis.hideComponent = true;\n\t\t}\n\t}\n\n\tprivate getTextOutput(): string {\n\t\treturn getRenderedTextOutput(this.result, this.showImages);\n\t}\n\n\tprivate formatToolExecution(): string {\n\t\tconst dotColor = this.result?.isError ? \"error\" : this.isPartial ? \"warning\" : \"success\";\n\t\tconst caret = this.displayLevel === \"peek\" ? theme.fg(\"muted\", this.revealed ? \"▾ \" : \"▸ \") : \"\";\n\t\tlet text = caret + theme.fg(dotColor, \"● \") + theme.fg(\"toolTitle\", theme.bold(this.toolName));\n\t\tconst content = JSON.stringify(this.args, null, 2);\n\t\tif (content) {\n\t\t\ttext += `\\n\\n${content}`;\n\t\t}\n\t\tconst output = this.shouldShowBody() ? this.getTextOutput() : \"\";\n\t\tif (output) {\n\t\t\ttext += `\\n${output}`;\n\t\t}\n\t\treturn text;\n\t}\n}\n"]}
1
+ {"version":3,"file":"tool-execution.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tool-execution.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tBox,\n\ttype Component,\n\tContainer,\n\tgetCapabilities,\n\tImage,\n\tisImageLine,\n\tSpacer,\n\tText,\n\ttype TUI,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport type { ToolDefinition, ToolRenderContext } from \"../../../core/extensions/types.js\";\nimport type { ToolOutputView } from \"../../../core/tool-output-view.js\";\nimport { createAllToolDefinitions, type ToolName } from \"../../../core/tools/index.js\";\nimport { getTextOutput as getRenderedTextOutput } from \"../../../core/tools/render-utils.js\";\nimport { convertToPng } from \"../../../utils/image-convert.js\";\nimport { theme } from \"../theme/theme.js\";\nimport type { ChainEntry } from \"./tool-chain-summary.js\";\nimport { ToolSignalComponent, toolSubject } from \"./tool-signal.js\";\n\n/**\n * Re-exported so tool-block callers keep a single import site for the view\n * dial; the type itself lives in core (the settings manager persists it).\n */\nexport type { ToolOutputView } from \"../../../core/tool-output-view.js\";\n\nexport interface ToolExecutionOptions {\n\tshowImages?: boolean;\n\timageWidthCells?: number;\n\tview?: ToolOutputView;\n}\n\n/**\n * Renders a child component and prepends a prefix (e.g. a status dot) to its\n * first line, indenting continuation lines so they align under the content.\n *\n * This keeps the dot inline with the first line instead of stacking it on its\n * own line (which happens when a Container holds the dot as a separate child).\n */\nclass PrefixFirstLine implements Component {\n\tprivate prefix: string;\n\tprivate child: Component;\n\tprivate indentWidth: number;\n\t// Keyed on the child's returned array identity so an unchanged child keeps\n\t// this wrapper reference-stable too (parents memoize flattening by ref).\n\tprivate memo?: { src: string[]; width: number; out: string[] };\n\n\tconstructor(prefix: string, child: Component) {\n\t\tthis.prefix = prefix;\n\t\tthis.child = child;\n\t\tthis.indentWidth = visibleWidth(prefix);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.child.invalidate?.();\n\t\tthis.memo = undefined;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Reserve room for the prefix so the child wraps within the remaining width.\n\t\tconst childWidth = Math.max(1, width - this.indentWidth);\n\t\tconst lines = this.child.render(childWidth);\n\t\tif (this.memo && this.memo.src === lines && this.memo.width === width) {\n\t\t\treturn this.memo.out;\n\t\t}\n\t\tconst indent = \" \".repeat(this.indentWidth);\n\t\tconst out =\n\t\t\tlines.length === 0 ? [this.prefix] : lines.map((line, i) => (i === 0 ? this.prefix + line : indent + line));\n\t\tthis.memo = { src: lines, width, out };\n\t\treturn out;\n\t}\n}\n\n/**\n * Indents every line of a child, unlike PrefixFirstLine which only offsets the\n * first. Used for a failure's body under a radar row, so the reason reads as\n * hanging off the row rather than starting a new column.\n */\nclass IndentAll implements Component {\n\tprivate child: Component;\n\tprivate indent: string;\n\tprivate memo?: { src: string[]; width: number; out: string[] };\n\n\tconstructor(child: Component, width: number) {\n\t\tthis.child = child;\n\t\tthis.indent = \" \".repeat(width);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.child.invalidate?.();\n\t\tthis.memo = undefined;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines = this.child.render(Math.max(1, width - this.indent.length));\n\t\tif (this.memo && this.memo.src === lines && this.memo.width === width) return this.memo.out;\n\t\tconst out = lines.map((line) => (line.trim() === \"\" ? line : this.indent + line));\n\t\tthis.memo = { src: lines, width, out };\n\t\treturn out;\n\t}\n}\n\nexport class ToolExecutionComponent extends Container {\n\tprivate contentBox: Box;\n\tprivate contentText: Text;\n\tprivate selfRenderContainer: Container;\n\tprivate callRendererComponent?: Component;\n\tprivate resultRendererComponent?: Component;\n\tprivate rendererState: any = {};\n\tprivate imageComponents: Image[] = [];\n\tprivate imageSpacers: Spacer[] = [];\n\tprivate toolName: string;\n\tprivate toolCallId: string;\n\tprivate args: any;\n\tprivate expanded = false;\n\t// Persisted view (radar/glance/full). Controls how the call line is drawn and\n\t// whether the result body renders at all; `revealed` is the per-block override\n\t// the global expand key flips for radar/glance blocks.\n\tprivate view: ToolOutputView;\n\tprivate revealed = false;\n\tprivate signalComponent?: ToolSignalComponent;\n\tprivate leadingSpacer: Spacer;\n\tprivate showImages: boolean;\n\tprivate imageWidthCells: number;\n\tprivate isPartial = true;\n\tprivate toolDefinition?: ToolDefinition<any, any>;\n\tprivate builtInToolDefinition?: ToolDefinition<any, any>;\n\tprivate ui: TUI;\n\tprivate cwd: string;\n\tprivate executionStarted = false;\n\tprivate argsComplete = false;\n\tprivate result?: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tisError: boolean;\n\t\tdetails?: any;\n\t};\n\tprivate convertedImages: Map<number, { data: string; mimeType: string }> = new Map();\n\tprivate hideComponent = false;\n\t// Once a finished tool block has scrolled far out of view the interactive mode\n\t// freezes it (see freeze()): its already-rendered lines are captured and the\n\t// heavy source payloads (full tool result, base64 image copies, per-renderer\n\t// state, child component caches) are released. A frozen block is immutable and\n\t// no longer reflows on resize — old off-screen scrollback keeps its wrapping,\n\t// which self-heals on any full rebuild (theme toggle / session reload, both of\n\t// which reconstruct components from the intact session data).\n\tprivate frozen = false;\n\tprivate frozenLines?: string[];\n\t/** Width the frozen snapshot was captured at; a wider terminal is safe as-is,\n\t * a narrower one needs re-truncation to avoid the TUI's over-width crash guard. */\n\tprivate frozenWidth = 0;\n\tprivate frozenTruncated?: string[];\n\tprivate frozenTruncatedWidth = -1;\n\n\tconstructor(\n\t\ttoolName: string,\n\t\ttoolCallId: string,\n\t\targs: any,\n\t\toptions: ToolExecutionOptions = {},\n\t\ttoolDefinition: ToolDefinition<any, any> | undefined,\n\t\tui: TUI,\n\t\tcwd: string,\n\t) {\n\t\tsuper();\n\t\tthis.toolName = toolName;\n\t\tthis.toolCallId = toolCallId;\n\t\tthis.args = args;\n\t\tthis.toolDefinition = toolDefinition;\n\t\tthis.builtInToolDefinition = createAllToolDefinitions(cwd)[toolName as ToolName];\n\t\tthis.showImages = options.showImages ?? true;\n\t\tthis.imageWidthCells = options.imageWidthCells ?? 60;\n\t\tthis.view = options.view ?? \"glance\";\n\t\tthis.ui = ui;\n\t\tthis.cwd = cwd;\n\n\t\t// The separator between consecutive tool blocks. Radar drops it: rows that\n\t\t// are one line each only read as a map when they stack without gaps.\n\t\tthis.leadingSpacer = new Spacer(1);\n\t\tthis.addChild(this.leadingSpacer);\n\n\t\t// Always create all shell variants. contentBox is used for default renderer-based composition.\n\t\t// selfRenderContainer is used when the tool renders its own framing.\n\t\t// contentText is reserved for generic fallback rendering when no tool definition exists.\n\t\t// Boxless: no filled background — hierarchy comes from status dots + indent.\n\t\t// paddingY is 0: the single leading Spacer(1) is the only separator between\n\t\t// tool blocks, so consecutive commands don't stack 3 blank lines between them.\n\t\tthis.contentBox = new Box(1, 0);\n\t\tthis.contentText = new Text(\"\", 1, 0);\n\t\tthis.selfRenderContainer = new Container();\n\n\t\tif (this.hasRendererDefinition()) {\n\t\t\tthis.addChild(this.activeShell());\n\t\t} else {\n\t\t\tthis.addChild(this.contentText);\n\t\t}\n\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate getCallRenderer(): ToolDefinition<any, any>[\"renderCall\"] | undefined {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderCall;\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderCall;\n\t\t}\n\t\treturn this.toolDefinition.renderCall ?? this.builtInToolDefinition.renderCall;\n\t}\n\n\tprivate getResultRenderer(): ToolDefinition<any, any>[\"renderResult\"] | undefined {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderResult;\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderResult;\n\t\t}\n\t\treturn this.toolDefinition.renderResult ?? this.builtInToolDefinition.renderResult;\n\t}\n\n\tprivate hasRendererDefinition(): boolean {\n\t\treturn this.builtInToolDefinition !== undefined || this.toolDefinition !== undefined;\n\t}\n\n\tprivate getRenderShell(): \"default\" | \"self\" {\n\t\tif (!this.builtInToolDefinition) {\n\t\t\treturn this.toolDefinition?.renderShell ?? \"default\";\n\t\t}\n\t\tif (!this.toolDefinition) {\n\t\t\treturn this.builtInToolDefinition.renderShell ?? \"default\";\n\t\t}\n\t\treturn this.toolDefinition.renderShell ?? this.builtInToolDefinition.renderShell ?? \"default\";\n\t}\n\n\tprivate getRenderContext(lastComponent: Component | undefined): ToolRenderContext {\n\t\treturn {\n\t\t\targs: this.args,\n\t\t\ttoolCallId: this.toolCallId,\n\t\t\tinvalidate: () => {\n\t\t\t\tthis.invalidate();\n\t\t\t\tthis.ui.requestRender();\n\t\t\t},\n\t\t\tlastComponent,\n\t\t\tstate: this.rendererState,\n\t\t\tcwd: this.cwd,\n\t\t\texecutionStarted: this.executionStarted,\n\t\t\targsComplete: this.argsComplete,\n\t\t\tisPartial: this.isPartial,\n\t\t\texpanded: this.expanded,\n\t\t\tshowImages: this.showImages,\n\t\t\tisError: this.result?.isError ?? false,\n\t\t};\n\t}\n\n\tprivate createCallFallback(): Component {\n\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(this.toolName)), 0, 0);\n\t}\n\n\tprivate createResultFallback(): Component | undefined {\n\t\tconst output = this.getTextOutput();\n\t\tif (!output) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn new Text(theme.fg(\"toolOutput\", output), 0, 0);\n\t}\n\n\tupdateArgs(args: any): void {\n\t\tthis.args = args;\n\t\tthis.updateDisplay();\n\t}\n\n\tmarkExecutionStarted(): void {\n\t\tthis.executionStarted = true;\n\t\tthis.updateDisplay();\n\t\tthis.ui.requestRender();\n\t}\n\n\tsetArgsComplete(): void {\n\t\tthis.argsComplete = true;\n\t\tthis.updateDisplay();\n\t\tthis.ui.requestRender();\n\t}\n\n\tupdateResult(\n\t\tresult: {\n\t\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\t\tdetails?: any;\n\t\t\tisError: boolean;\n\t\t},\n\t\tisPartial = false,\n\t): void {\n\t\tthis.result = result;\n\t\tthis.isPartial = isPartial;\n\t\tthis.updateDisplay();\n\t\tthis.maybeConvertImagesForKitty();\n\t}\n\n\tprivate maybeConvertImagesForKitty(): void {\n\t\tconst caps = getCapabilities();\n\t\tif (caps.images !== \"kitty\") return;\n\t\tif (!this.result) return;\n\n\t\tconst imageBlocks = this.result.content.filter((c) => c.type === \"image\");\n\t\tfor (let i = 0; i < imageBlocks.length; i++) {\n\t\t\tconst img = imageBlocks[i];\n\t\t\tif (!img.data || !img.mimeType) continue;\n\t\t\tif (img.mimeType === \"image/png\") continue;\n\t\t\tif (this.convertedImages.has(i)) continue;\n\n\t\t\tconst index = i;\n\t\t\tconvertToPng(img.data, img.mimeType).then((converted) => {\n\t\t\t\tif (this.frozen) return;\n\t\t\t\tif (converted) {\n\t\t\t\t\tthis.convertedImages.set(index, converted);\n\t\t\t\t\tthis.updateDisplay();\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\t// For radar/glance blocks the same global toggle reveals the hidden body;\n\t\t// for full blocks `expanded` alone switches truncated ↔ everything.\n\t\tthis.revealed = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetView(view: ToolOutputView): void {\n\t\tthis.view = view;\n\t\tthis.updateDisplay();\n\t}\n\n\t/**\n\t * Whether this block is currently opened up.\n\t *\n\t * Read by the one-at-a-time unfold, which walks back from the newest block\n\t * to find the first one still folded.\n\t */\n\tisRevealed(): boolean {\n\t\treturn this.revealed;\n\t}\n\n\t/** This call's contribution to its chain's summary line. */\n\tchainEntry(): ChainEntry {\n\t\tconst output = this.result ? this.getTextOutput() : \"\";\n\t\treturn {\n\t\t\ttool: this.toolName,\n\t\t\tsubject: toolSubject(this.args, this.cwd),\n\t\t\tisError: this.result?.isError === true,\n\t\t\tisPartial: this.result === undefined || this.isPartial,\n\t\t\toutputLines: output.trim() ? output.split(\"\\n\").length : 0,\n\t\t};\n\t}\n\n\t/** The failure text, for a collapsed chain to print under its line. */\n\terrorText(): string {\n\t\treturn this.result?.isError ? this.getTextOutput() : \"\";\n\t}\n\n\t/**\n\t * Whether the result body should render.\n\t *\n\t * A failure always shows its reason, in every view. Everything else about\n\t * these views is a judgement about how much detail you want; why something\n\t * broke is not detail, and a red dot with no explanation was the one thing\n\t * the folded views got wrong.\n\t */\n\tprivate shouldShowBody(): boolean {\n\t\treturn this.view === \"full\" || this.revealed || this.result?.isError === true;\n\t}\n\n\t/**\n\t * Whether this block draws its radar signal line instead of the tool's own\n\t * call renderer. A revealed block leaves radar for as long as it is open, so\n\t * \"expand\" means the same thing in every view.\n\t */\n\tprivate shouldShowSignalLine(): boolean {\n\t\treturn this.view === \"radar\" && !this.revealed;\n\t}\n\n\t/**\n\t * A failure's body under a radar row hangs off the row; anywhere else it sits\n\t * directly under its own call line and needs no extra indent.\n\t */\n\tprivate indentUnderSignalRow(component: Component): Component {\n\t\treturn this.shouldShowSignalLine() ? new IndentAll(component, 3) : component;\n\t}\n\n\tprivate getSignalComponent(): ToolSignalComponent {\n\t\tconst input = {\n\t\t\ttoolName: this.toolName,\n\t\t\targs: this.args,\n\t\t\tcwd: this.cwd,\n\t\t\tresult: this.result,\n\t\t\tisPartial: this.isPartial,\n\t\t\tshowImages: this.showImages,\n\t\t};\n\t\tif (this.signalComponent) {\n\t\t\tthis.signalComponent.setInput(input);\n\t\t} else {\n\t\t\tthis.signalComponent = new ToolSignalComponent(input);\n\t\t}\n\t\treturn this.signalComponent;\n\t}\n\n\tsetShowImages(show: boolean): void {\n\t\tthis.showImages = show;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetImageWidthCells(width: number): void {\n\t\tthis.imageWidthCells = Math.max(1, Math.floor(width));\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\t// A frozen block has released the state updateDisplay would rebuild from;\n\t\t// its captured snapshot is authoritative, so skip the rebuild entirely.\n\t\tif (this.frozen) return;\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\t/**\n\t * True when this block is a finished, visible tool result that still holds its\n\t * heavy source payloads — i.e. a candidate for freeze() once it scrolls out of\n\t * the live window. In-flight (partial) and already-frozen blocks are excluded.\n\t */\n\tisFreezable(): boolean {\n\t\treturn !this.frozen && !this.isPartial && !this.hideComponent && !!this.result;\n\t}\n\n\t/** Mark this block for freezing; the snapshot is captured on the next render\n\t * (at the real render width) and the heavy state released then. */\n\tfreeze(): void {\n\t\tif (this.isFreezable()) this.frozen = true;\n\t}\n\n\tprivate releaseHeavyState(): void {\n\t\tthis.result = undefined;\n\t\tthis.convertedImages.clear();\n\t\tthis.imageComponents = [];\n\t\tthis.imageSpacers = [];\n\t\tthis.rendererState = {};\n\t\tthis.callRendererComponent = undefined;\n\t\tthis.resultRendererComponent = undefined;\n\t\t// Drop child components so their line caches (which embed base64 image\n\t\t// payloads and full text output) become collectable.\n\t\tthis.clear();\n\t}\n\n\toverride render(width: number): string[] {\n\t\tif (this.hideComponent) {\n\t\t\treturn [];\n\t\t}\n\t\tif (this.frozenLines) {\n\t\t\t// Captured snapshot. Wider (or equal) terminal: emit as-is. Narrower:\n\t\t\t// re-truncate non-image lines to avoid the TUI's over-width crash guard,\n\t\t\t// caching the result per width so it stays O(1) on unchanged frames.\n\t\t\tif (width >= this.frozenWidth) return this.frozenLines;\n\t\t\tif (this.frozenTruncatedWidth === width && this.frozenTruncated) return this.frozenTruncated;\n\t\t\tthis.frozenTruncated = this.frozenLines.map((line) =>\n\t\t\t\tisImageLine(line) || visibleWidth(line) <= width ? line : truncateToWidth(line, width),\n\t\t\t);\n\t\t\tthis.frozenTruncatedWidth = width;\n\t\t\treturn this.frozenTruncated;\n\t\t}\n\t\tconst lines = super.render(width);\n\t\tif (this.frozen) {\n\t\t\tthis.frozenLines = lines;\n\t\t\tthis.frozenWidth = width;\n\t\t\tthis.releaseHeavyState();\n\t\t}\n\t\treturn lines;\n\t}\n\n\t/**\n\t * The container this block renders into.\n\t *\n\t * A self-rendering tool (`edit` draws its own framed diff) normally gets the\n\t * bare container so its frame can use the full width; everything else gets\n\t * the padded box. That only matters while the body is on screen. With the\n\t * body folded away the block is a single line, and the unpadded container\n\t * put it one column left of every other row — invisible while `peek` was an\n\t * opt-in, glaring now that `glance` is the default. So: padded box whenever\n\t * nothing is expanded, which also gives radar the uniform rows it exists for.\n\t */\n\tprivate activeShell(): Box | Container {\n\t\tif (!this.shouldShowBody()) return this.contentBox;\n\t\treturn this.getRenderShell() === \"self\" ? this.selfRenderContainer : this.contentBox;\n\t}\n\n\t/** Swap the attached shell when the view changes which one applies. */\n\tprivate syncShellChild(): void {\n\t\tconst wanted = this.activeShell();\n\t\tconst other = wanted === this.contentBox ? this.selfRenderContainer : this.contentBox;\n\t\tif (this.children.includes(wanted)) return;\n\t\tthis.removeChild(other);\n\t\tother.clear();\n\t\tthis.addChild(wanted);\n\t}\n\n\tprivate updateDisplay(): void {\n\t\t// Frozen blocks are immutable snapshots with their source state released.\n\t\tif (this.frozen) return;\n\t\tlet hasContent = false;\n\t\tthis.hideComponent = false;\n\t\tthis.leadingSpacer.setLines(this.shouldShowSignalLine() ? 0 : 1);\n\t\tif (this.hasRendererDefinition()) {\n\t\t\tthis.syncShellChild();\n\t\t\tconst renderContainer = this.activeShell();\n\t\t\t// Boxless: no background fill on the container.\n\t\t\tif (renderContainer instanceof Box) {\n\t\t\t\trenderContainer.setBgFn(undefined);\n\t\t\t}\n\t\t\trenderContainer.clear();\n\n\t\t\t// Status dot prefix: green for complete success, yellow for pending/partial, red for error.\n\t\t\t// The dot is prepended to the first line of the call renderer so it stays inline\n\t\t\t// (adding it as a separate child would stack it on its own line).\n\t\t\tconst dotColor = this.result?.isError ? \"error\" : this.isPartial ? \"warning\" : \"success\";\n\t\t\t// No disclosure caret. It is a click-target idiom in a TUI with no\n\t\t\t// pointer, it cost two columns on every row for an action that could\n\t\t\t// only ever be reached by keyboard, and reading it as \"this row opens\"\n\t\t\t// set an expectation the view does not meet.\n\t\t\tconst dot = theme.fg(dotColor, \"● \");\n\n\t\t\t// Radar replaces the tool's own call renderer with the uniform signal\n\t\t\t// row, so a screen of mixed tools lines up into one readable map.\n\t\t\tif (this.shouldShowSignalLine()) {\n\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.getSignalComponent()));\n\t\t\t\thasContent = true;\n\t\t\t} else {\n\t\t\t\tconst callRenderer = this.getCallRenderer();\n\t\t\t\tif (!callRenderer) {\n\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));\n\t\t\t\t\thasContent = true;\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));\n\t\t\t\t\t\tthis.callRendererComponent = component;\n\t\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, component));\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.callRendererComponent = undefined;\n\t\t\t\t\t\trenderContainer.addChild(new PrefixFirstLine(dot, this.createCallFallback()));\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.result && this.shouldShowBody()) {\n\t\t\t\tconst resultRenderer = this.getResultRenderer();\n\t\t\t\tif (!resultRenderer) {\n\t\t\t\t\tconst component = this.createResultFallback();\n\t\t\t\t\tif (component) {\n\t\t\t\t\t\trenderContainer.addChild(this.indentUnderSignalRow(component));\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst component = resultRenderer(\n\t\t\t\t\t\t\t{ content: this.result.content as any, details: this.result.details },\n\t\t\t\t\t\t\t{ expanded: this.expanded, isPartial: this.isPartial },\n\t\t\t\t\t\t\ttheme,\n\t\t\t\t\t\t\tthis.getRenderContext(this.resultRendererComponent),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.resultRendererComponent = component;\n\t\t\t\t\t\trenderContainer.addChild(this.indentUnderSignalRow(component));\n\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tthis.resultRendererComponent = undefined;\n\t\t\t\t\t\tconst component = this.createResultFallback();\n\t\t\t\t\t\tif (component) {\n\t\t\t\t\t\t\trenderContainer.addChild(component);\n\t\t\t\t\t\t\thasContent = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Boxless: no background fill.\n\t\t\tthis.contentText.setCustomBgFn(undefined);\n\t\t\tthis.contentText.setText(this.formatToolExecution());\n\t\t\thasContent = true;\n\t\t}\n\n\t\tfor (const img of this.imageComponents) {\n\t\t\tthis.removeChild(img);\n\t\t}\n\t\tthis.imageComponents = [];\n\t\tfor (const spacer of this.imageSpacers) {\n\t\t\tthis.removeChild(spacer);\n\t\t}\n\t\tthis.imageSpacers = [];\n\n\t\tif (this.result && !this.shouldShowSignalLine()) {\n\t\t\tconst imageBlocks = this.result.content.filter((c) => c.type === \"image\");\n\t\t\tconst caps = getCapabilities();\n\t\t\tfor (let i = 0; i < imageBlocks.length; i++) {\n\t\t\t\tconst img = imageBlocks[i];\n\t\t\t\tif (caps.images && this.showImages && img.data && img.mimeType) {\n\t\t\t\t\tconst converted = this.convertedImages.get(i);\n\t\t\t\t\tconst imageData = converted?.data ?? img.data;\n\t\t\t\t\tconst imageMimeType = converted?.mimeType ?? img.mimeType;\n\t\t\t\t\tif (caps.images === \"kitty\" && imageMimeType !== \"image/png\") continue;\n\n\t\t\t\t\tconst spacer = new Spacer(1);\n\t\t\t\t\tthis.addChild(spacer);\n\t\t\t\t\tthis.imageSpacers.push(spacer);\n\t\t\t\t\tconst imageComponent = new Image(\n\t\t\t\t\t\timageData,\n\t\t\t\t\t\timageMimeType,\n\t\t\t\t\t\t{ fallbackColor: (s: string) => theme.fg(\"toolOutput\", s) },\n\t\t\t\t\t\t{ maxWidthCells: this.imageWidthCells },\n\t\t\t\t\t);\n\t\t\t\t\tthis.imageComponents.push(imageComponent);\n\t\t\t\t\tthis.addChild(imageComponent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.hasRendererDefinition() && !hasContent && this.imageComponents.length === 0) {\n\t\t\tthis.hideComponent = true;\n\t\t}\n\t}\n\n\tprivate getTextOutput(): string {\n\t\treturn getRenderedTextOutput(this.result, this.showImages);\n\t}\n\n\tprivate formatToolExecution(): string {\n\t\tconst dotColor = this.result?.isError ? \"error\" : this.isPartial ? \"warning\" : \"success\";\n\t\t// A tool with no renderer still gets a radar row, just without the\n\t\t// flush-right signal column (this path has no width to align against).\n\t\tif (this.shouldShowSignalLine()) {\n\t\t\tconst subject = toolSubject(this.args, this.cwd);\n\t\t\treturn (\n\t\t\t\ttheme.fg(dotColor, \"● \") +\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(this.toolName)) +\n\t\t\t\t(subject ? ` ${theme.fg(\"toolOutput\", subject)}` : \"\")\n\t\t\t);\n\t\t}\n\t\tlet text = theme.fg(dotColor, \"● \") + theme.fg(\"toolTitle\", theme.bold(this.toolName));\n\t\tconst content = JSON.stringify(this.args, null, 2);\n\t\tif (content) {\n\t\t\ttext += `\\n\\n${content}`;\n\t\t}\n\t\tconst output = this.shouldShowBody() ? this.getTextOutput() : \"\";\n\t\tif (output) {\n\t\t\ttext += `\\n${output}`;\n\t\t}\n\t\treturn text;\n\t}\n}\n"]}