@prettier-ai/dsh-client-ui-tool 0.1.2-rc.1 → 0.1.3-alpha.1
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.
- package/README.i18n.yaml +2 -2
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/lib/client.js +264 -13
- package/lib/types/client/contract/slots.d.ts +36 -0
- package/lib/types/client/tool/ToolCallTree.d.ts +1 -1
- package/lib/types/client/tool/components/ToolRow.d.ts +19 -2
- package/lib/types/client/tool/models/image-card-model.d.ts +45 -0
- package/lib/types/client/tool/toolviews/read-family-row.d.ts +25 -0
- package/lib/types/client/tool/toolviews/read-image-row.d.ts +24 -0
- package/lib/types/client/tool/toolviews/read-row.d.ts +1 -1
- package/package.json +14 -13
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/ui-tool/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 2fe410425935aed46ab95844c81568037ac929e9
|
|
6
|
+
README.zh.md: 83440881fe16582a75fd8dd762577dc7b4d5335b
|
package/README.md
CHANGED
|
@@ -39,11 +39,11 @@ ctx.slots.inject('tool.call.toolview', () =>
|
|
|
39
39
|
}, BusinessToolRow))
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
The owner payload is `ToolCallOwnerProps`: `callId`, `toolName`, the frozen `block`, optional `cwd` and `home`, and plain `openFile`/`inspect` callbacks. A Code Dispatch block retains its event's `parentCallId`; a root Session call has no such field, so descendants keep the generic flattened form
|
|
42
|
+
The owner payload is `ToolCallOwnerProps`: `callId`, `toolName`, the frozen `block`, optional `cwd` and `home`, the session-authorized `loadImage` loader (for a view whose result carries durable images), and plain `openFile`/`inspect` callbacks. A Code Dispatch block retains its event's `parentCallId`; a root Session call has no such field, so descendants route through the same keyed dispatch — a registered view such as `read_image` renders its card there, and unregistered descendants keep the generic flattened form. Path summaries relativize to the Session cwd first, then replace a leftover POSIX Host home with `~`; `filePath` and Host open keep the authored filesystem path. The registration receives the normal Session slot runtime share but no React node or Runtime service.
|
|
43
43
|
|
|
44
44
|
### Built-in views
|
|
45
45
|
|
|
46
|
-
This package owns the generic fallback and the built-in shell/pwsh, read, write/edit, running `str_replace_editor` `create`/`str_replace`, grep/glob, web, todo, question, and Code Dispatch presentations. Structured cards derive directly from first-party raw event fields; Host `presentCall` and `presentResult` values never enter the Client. Foreground one-shot shell results use terminal cards. Settled persistent-shell results use the expandable generic input/output card because reset and partial-output diagnostics do not always describe one process exit status; background acknowledgements remain collapsed. A successful question row pairs call questions with result answers by their stable ids and shows readable question/answer lines when expanded. A cancelled or interrupted row shows its verdict and original questions without inventing answers. Unsupported, malformed, or ambiguous inputs fall back to flattened Tool input/result text. `ui-skill` demonstrates a business-owned registration for `skill`.
|
|
46
|
+
This package owns the generic fallback and the built-in shell/pwsh, read, read_image, write/edit, running `str_replace_editor` `create`/`str_replace`, grep/glob, web, todo, question, and Code Dispatch presentations. Structured cards derive directly from first-party raw event fields; Host `presentCall` and `presentResult` values never enter the Client. Foreground one-shot shell results use terminal cards. Settled persistent-shell results use the expandable generic input/output card because reset and partial-output diagnostics do not always describe one process exit status; background acknowledgements remain collapsed. A successful question row pairs call questions with result answers by their stable ids and shows readable question/answer lines when expanded. A cancelled or interrupted row shows its verdict and original questions without inventing answers. Unsupported, malformed, or ambiguous inputs fall back to flattened Tool input/result text. `ui-skill` demonstrates a business-owned registration for `skill`.
|
|
47
47
|
|
|
48
48
|
-----
|
|
49
49
|
|
|
@@ -61,8 +61,8 @@ The package realizes one dispatch rule: atomic Tool views are keyed by wire Tool
|
|
|
61
61
|
|
|
62
62
|
### Details and cards
|
|
63
63
|
|
|
64
|
-
The package fills `conversation.details.tool` with `ToolDetails`. Row and Details renderers share one pure card model for each terminal, read, diff, search, and web card. These models validate raw call arguments, result content, failure state, persisted metadata, Code Dispatch `parentCallId`, and Session path facts. Generic rows retain the original `argsRaw` reference and format their input body only while it is expanded; structured cards skip generic-body formatting. Unsupported or malformed inputs use flattened Tool result text. Card-specific limits and fallback rules remain in the owning [terminal](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md), [diff](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md), [read](../../../.agents/notes/implemented/feature/2026-07-30-web-read-card-frontend.md), [search](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md), [web](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md), and [question](../../../.agents/notes/implemented/feature/2026-07-29-ask-question-web-presentation.md) notes.
|
|
65
64
|
|
|
65
|
+
The package fills `conversation.details.tool` with `ToolDetails`. Row and Details renderers share one pure card model for each terminal, read, diff, search, and web card; the image card is row-only because its gallery renders through the tool-owned `tool.call.images` slot the details panel does not declare. These models validate raw call arguments, result content, failure state, persisted metadata, Code Dispatch `parentCallId`, and Session path facts. Unsupported or malformed inputs use flattened Tool result text. Card-specific limits and fallback rules remain in the owning [terminal](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md), [diff](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md), [read](../../../.agents/notes/implemented/feature/2026-07-30-web-read-card-frontend.md), [image](../../../.agents/notes/implemented/feature/2026-08-20-tool-card-image-results.md), [search](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md), [web](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md), and [question](../../../.agents/notes/implemented/feature/2026-07-29-ask-question-web-presentation.md) notes.
|
|
66
66
|
</details>
|
|
67
67
|
|
|
68
68
|
-----
|
package/README.zh.md
CHANGED
|
@@ -39,11 +39,11 @@ ctx.slots.inject('tool.call.toolview', () =>
|
|
|
39
39
|
}, BusinessToolRow))
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
owner 载荷为 `ToolCallOwnerProps`:`callId`、`toolName`、冻结的 `block`、可选 `cwd` 与 `home
|
|
42
|
+
owner 载荷为 `ToolCallOwnerProps`:`callId`、`toolName`、冻结的 `block`、可选 `cwd` 与 `home`、会话授权的 `loadImage` loader(供结果携带持久图像的视图使用),以及普通的 `openFile`/`inspect` 回调。Code Dispatch block 保留事件的 `parentCallId`;root Session call 没有该字段,因此 descendant 走同一条按 key 分发——注册过视图的调用(如 `read_image`)在嵌套处也渲染其卡片,未注册的 descendant 保持 generic 压平形式。路径摘要先相对 Session cwd 缩短,再把剩余的 POSIX Host home 写成 `~`;`filePath` 与 Host 打开仍使用作者给出的文件系统路径。注册项会收到常规 Session slot runtime share,但不会收到 React node 或 runtime service。
|
|
43
43
|
|
|
44
44
|
### 内置视图
|
|
45
45
|
|
|
46
|
-
本包拥有 generic fallback,以及 shell/pwsh、read、write/edit、running `str_replace_editor` `create`/`str_replace`、grep/glob、web、todo、question 与 Code Dispatch 的内置展示。结构化卡片直接从第一方原始 event 字段派生;Host `presentCall` 与 `presentResult` 值不会进入 Client。前台一次性 shell 结果使用 terminal 卡片。已完成的持久 shell 结果使用可展开的 generic 输入/输出卡片,因为 reset 与部分输出诊断不一定描述单个进程的退出状态;后台启动回执保持折叠。成功的问题行按稳定 id 配对调用中的问题与结果中的回答,展开后显示可读的问答行。已取消或已中断的问题行显示其裁决与原始问题,不虚构回答。不受支持、格式错误或含糊的输入回退为压平的工具输入/结果文本。`ui-skill` 展示了业务包自行拥有的 `skill` 注册项。
|
|
46
|
+
本包拥有 generic fallback,以及 shell/pwsh、read、read_image、write/edit、running `str_replace_editor` `create`/`str_replace`、grep/glob、web、todo、question 与 Code Dispatch 的内置展示。结构化卡片直接从第一方原始 event 字段派生;Host `presentCall` 与 `presentResult` 值不会进入 Client。前台一次性 shell 结果使用 terminal 卡片。已完成的持久 shell 结果使用可展开的 generic 输入/输出卡片,因为 reset 与部分输出诊断不一定描述单个进程的退出状态;后台启动回执保持折叠。成功的问题行按稳定 id 配对调用中的问题与结果中的回答,展开后显示可读的问答行。已取消或已中断的问题行显示其裁决与原始问题,不虚构回答。不受支持、格式错误或含糊的输入回退为压平的工具输入/结果文本。`ui-skill` 展示了业务包自行拥有的 `skill` 注册项。
|
|
47
47
|
|
|
48
48
|
-----
|
|
49
49
|
|
|
@@ -61,8 +61,8 @@ owner 载荷为 `ToolCallOwnerProps`:`callId`、`toolName`、冻结的 `block`
|
|
|
61
61
|
|
|
62
62
|
### 详情与卡片
|
|
63
63
|
|
|
64
|
-
本包通过 `ToolDetails` 填充 `conversation.details.tool`。行 renderer 与 Details renderer 分别为 terminal、read、diff、search 和 web 卡片复用同一个纯 card model。这些 model 校验原始调用参数、结果内容、失败状态、持久 metadata、Code Dispatch `parentCallId` 与 Session 路径事实。Generic row 保留原始 `argsRaw` 引用,只在展开期间格式化 input body;结构化卡片跳过 generic body 格式化。不受支持或格式错误的输入使用压平的工具结果文本。各类卡片的上限与 fallback 规则仍由对应的 [terminal](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.zh.md)、[diff](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.zh.md)、[read](../../../.agents/notes/implemented/feature/2026-07-30-web-read-card-frontend.zh.md)、[search](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.zh.md)、[web](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.zh.md) 与 [question](../../../.agents/notes/implemented/feature/2026-07-29-ask-question-web-presentation.zh.md) 笔记负责。
|
|
65
64
|
|
|
65
|
+
本包通过 `ToolDetails` 填充 `conversation.details.tool`。行 renderer 与 Details renderer 分别为 terminal、read、diff、search 和 web 卡片复用同一个纯 card model;image 卡片仅属于行,因为其图库经由工具自有 `tool.call.images` 槽位渲染,而 details 面板不声明该槽位。这些 model 校验原始调用参数、结果内容、失败状态、持久 metadata、Code Dispatch `parentCallId` 与 Session 路径事实。不受支持或格式错误的输入使用压平的工具结果文本。各类卡片的上限与 fallback 规则仍由对应的 [terminal](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.zh.md)、[diff](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.zh.md)、[read](../../../.agents/notes/implemented/feature/2026-07-30-web-read-card-frontend.zh.md)、[image](../../../.agents/notes/implemented/feature/2026-08-20-tool-card-image-results.zh.md)、[search](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.zh.md)、[web](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.zh.md) 与 [question](../../../.agents/notes/implemented/feature/2026-07-29-ask-question-web-presentation.zh.md) 笔记负责。
|
|
66
66
|
</details>
|
|
67
67
|
|
|
68
68
|
-----
|
package/lib/client.js
CHANGED
|
@@ -25,7 +25,8 @@ window.__ModuleLoader__.load({
|
|
|
25
25
|
function resolveWorkspacePath(cwd, path) {
|
|
26
26
|
if (path.startsWith("/") || isWindowsStylePath(path)) return path;
|
|
27
27
|
if (cwd === void 0 || cwd === "") return path;
|
|
28
|
-
|
|
28
|
+
const separator = isWindowsStylePath(cwd) && cwd.includes("\\") ? "\\" : "/";
|
|
29
|
+
return `${cwd.replace(/[/\\]+$/, "")}${separator}${path.replace(/^[/\\]+/, "")}`;
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
31
32
|
* Abbreviate a POSIX home directory for display.
|
|
@@ -67,6 +68,7 @@ window.__ModuleLoader__.load({
|
|
|
67
68
|
bash: "bash",
|
|
68
69
|
pwsh: "bash",
|
|
69
70
|
read: "read",
|
|
71
|
+
read_image: "read",
|
|
70
72
|
web_fetch: "read",
|
|
71
73
|
web_search: "search",
|
|
72
74
|
grep: "search",
|
|
@@ -87,7 +89,8 @@ window.__ModuleLoader__.load({
|
|
|
87
89
|
cordis_run: "tool.title.runCordis",
|
|
88
90
|
cordis_stop: "tool.title.stopCordis",
|
|
89
91
|
cordis_undefine: "tool.title.removeCordis",
|
|
90
|
-
pwsh: "tool.title.pwsh"
|
|
92
|
+
pwsh: "tool.title.pwsh",
|
|
93
|
+
read_image: "tool.title.readImage"
|
|
91
94
|
};
|
|
92
95
|
/**
|
|
93
96
|
* Classify a tool name into its row variant.
|
|
@@ -1042,7 +1045,7 @@ window.__ModuleLoader__.load({
|
|
|
1042
1045
|
}
|
|
1043
1046
|
//#endregion
|
|
1044
1047
|
//#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css.mjs
|
|
1045
|
-
const css$3 = ".RAJz1a_root{flex-direction:column;display:flex}.RAJz1a_row{position:relative;overflow:hidden}.RAJz1a_root[data-state=running] .RAJz1a_row:after{content:\"\";background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite RAJz1a_dsh-tool-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes RAJz1a_dsh-tool-row-sweep{0%{left:-300px}90%,to{left:100%}}.RAJz1a_leading{flex-shrink:0}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_leading,.RAJz1a_root[data-tool^=cordis_] .RAJz1a_title{color:var(--dsw-alias-state-business-primary)}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_title{font-weight:500}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_sep{background:var(--dsw-alias-state-business-primary)}.RAJz1a_chevron{color:var(--dsw-alias-label-secondary)}.RAJz1a_title{font-weight:400}.RAJz1a_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.RAJz1a_summary{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:auto;overflow:hidden}.RAJz1a_summarySuffix{white-space:nowrap;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:none;margin-left:4px}.RAJz1a_diffStat{font-family:var(--ds-font-family-code);font-size:calc(var(--dsh-content-font-size-secondary,13px) - 2px);color:var(--dsw-alias-label-caption);margin-left:10px;transform:translateY(.5px)}.RAJz1a_fileLink{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:inherit;text-align:left;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-secondary);text-decoration:underline dotted;text-decoration-color:var(--dsw-alias-label-tertiary);text-underline-offset:3px;cursor:pointer;background:0 0;border:none;flex:0 auto;margin:0;padding:0;text-decoration-thickness:1px;overflow:hidden}.RAJz1a_fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}.RAJz1a_errorSummary{color:var(--dsw-alias-state-error-primary)}.RAJz1a_bodyWrap{flex-direction:column;display:flex}.RAJz1a_inspectButton{border:.5px solid var(--dsw-alias-border-l3);corner-shape:round;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;border-radius:999px;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;font-size:11px;line-height:16px;transition:opacity .1s;display:inline-flex}.RAJz1a_root:hover .RAJz1a_inspectButton,.RAJz1a_inspectButton:focus-visible{opacity:1}.RAJz1a_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.RAJz1a_bodyScroll{max-height:260px;overflow-y:auto}.RAJz1a_ioCard{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);border-radius:12px;flex-direction:column;margin:4px 0 4px 4px;display:flex}.RAJz1a_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.RAJz1a_ioSection::-webkit-scrollbar-thumb{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.RAJz1a_ioSection::-webkit-scrollbar-track{margin:6px 0}.RAJz1a_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.RAJz1a_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:.5px}.RAJz1a_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.RAJz1a_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.RAJz1a_codeBody,.RAJz1a_terminalBody,.RAJz1a_diffBody,.RAJz1a_readBody,.RAJz1a_searchBody,.RAJz1a_webBody{margin:4px 0 4px 4px}.RAJz1a_searchRecovery{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary);margin:4px 0 4px 4px}.RAJz1a_codeBody{--dsl-code-block-content-font:var(--dsw-font-markdown-code-block-small)}.RAJz1a_terminalBody{--dsl-terminal-font:var(--dsw-font-markdown-code-block-small);--dsl-terminal-line-height:18px;--dsl-terminal-output-max-height:224px;border:.5px solid var(--dsw-alias-border-l1)}.RAJz1a_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
|
|
1048
|
+
const css$3 = ".RAJz1a_root{flex-direction:column;display:flex}.RAJz1a_row{position:relative;overflow:hidden}.RAJz1a_root[data-state=running] .RAJz1a_row:after{content:\"\";background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite RAJz1a_dsh-tool-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes RAJz1a_dsh-tool-row-sweep{0%{left:-300px}90%,to{left:100%}}.RAJz1a_leading{flex-shrink:0}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_leading,.RAJz1a_root[data-tool^=cordis_] .RAJz1a_title{color:var(--dsw-alias-state-business-primary)}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_title{font-weight:500}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_sep{background:var(--dsw-alias-state-business-primary)}.RAJz1a_chevron{color:var(--dsw-alias-label-secondary)}.RAJz1a_title{font-weight:400}.RAJz1a_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.RAJz1a_summary{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:auto;overflow:hidden}.RAJz1a_summarySuffix{white-space:nowrap;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:none;margin-left:4px}.RAJz1a_diffStat{font-family:var(--ds-font-family-code);font-size:calc(var(--dsh-content-font-size-secondary,13px) - 2px);color:var(--dsw-alias-label-caption);margin-left:10px;transform:translateY(.5px)}.RAJz1a_fileLink{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:inherit;text-align:left;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-secondary);text-decoration:underline dotted;text-decoration-color:var(--dsw-alias-label-tertiary);text-underline-offset:3px;cursor:pointer;background:0 0;border:none;flex:0 auto;margin:0;padding:0;text-decoration-thickness:1px;overflow:hidden}.RAJz1a_fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}.RAJz1a_errorSummary{color:var(--dsw-alias-state-error-primary)}.RAJz1a_bodyWrap{flex-direction:column;display:flex}.RAJz1a_inspectButton{border:.5px solid var(--dsw-alias-border-l3);corner-shape:round;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;border-radius:999px;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;font-size:11px;line-height:16px;transition:opacity .1s;display:inline-flex}.RAJz1a_root:hover .RAJz1a_inspectButton,.RAJz1a_inspectButton:focus-visible{opacity:1}.RAJz1a_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.RAJz1a_bodyScroll{max-height:260px;overflow-y:auto}.RAJz1a_ioCard{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);border-radius:12px;flex-direction:column;margin:4px 0 4px 4px;display:flex}.RAJz1a_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.RAJz1a_ioSection::-webkit-scrollbar-thumb{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.RAJz1a_ioSection::-webkit-scrollbar-track{margin:6px 0}.RAJz1a_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.RAJz1a_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:.5px}.RAJz1a_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.RAJz1a_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.RAJz1a_codeBody,.RAJz1a_terminalBody,.RAJz1a_diffBody,.RAJz1a_readBody,.RAJz1a_imageBody,.RAJz1a_searchBody,.RAJz1a_webBody{margin:4px 0 4px 4px}.RAJz1a_searchRecovery{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary);margin:4px 0 4px 4px}.RAJz1a_imageLabel{overflow-wrap:anywhere;font:var(--dsw-font-sm-13);color:var(--dsw-alias-label-secondary);margin-bottom:4px}.RAJz1a_imageMeta{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary)}.RAJz1a_codeBody{--dsl-code-block-content-font:var(--dsw-font-markdown-code-block-small)}.RAJz1a_terminalBody{--dsl-terminal-font:var(--dsw-font-markdown-code-block-small);--dsl-terminal-line-height:18px;--dsl-terminal-output-max-height:224px;border:.5px solid var(--dsw-alias-border-l1)}.RAJz1a_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
|
|
1046
1049
|
const tagId$3 = "@prettier-ai/dsh-client-ui-tool/ToolRow.module.css";
|
|
1047
1050
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
|
|
1048
1051
|
const tag = document.createElement("style");
|
|
@@ -1061,6 +1064,9 @@ window.__ModuleLoader__.load({
|
|
|
1061
1064
|
"dsh-tool-row-sweep": "RAJz1a_dsh-tool-row-sweep",
|
|
1062
1065
|
"errorSummary": "RAJz1a_errorSummary",
|
|
1063
1066
|
"fileLink": "RAJz1a_fileLink",
|
|
1067
|
+
"imageBody": "RAJz1a_imageBody",
|
|
1068
|
+
"imageLabel": "RAJz1a_imageLabel",
|
|
1069
|
+
"imageMeta": "RAJz1a_imageMeta",
|
|
1064
1070
|
"inspectButton": "RAJz1a_inspectButton",
|
|
1065
1071
|
"ioCard": "RAJz1a_ioCard",
|
|
1066
1072
|
"ioDivider": "RAJz1a_ioDivider",
|
|
@@ -1102,7 +1108,7 @@ window.__ModuleLoader__.load({
|
|
|
1102
1108
|
default: return null;
|
|
1103
1109
|
}
|
|
1104
1110
|
}
|
|
1105
|
-
function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, search, web, state, filePath, onOpenFile, inspect }) {
|
|
1111
|
+
function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, image, renderSlot, loadImage, search, web, state, filePath, onOpenFile, inspect }) {
|
|
1106
1112
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
1107
1113
|
const terminalLabels = (0, react.useMemo)(() => terminalBlockLabels(t), [t]);
|
|
1108
1114
|
const diffLabels = (0, react.useMemo)(() => diffBlockLabels(t), [t]);
|
|
@@ -1112,11 +1118,12 @@ window.__ModuleLoader__.load({
|
|
|
1112
1118
|
const terminalBody = terminal === void 0 || terminal === null ? null : localizeTerminalCardModel(terminal, t);
|
|
1113
1119
|
const diffBody = diff ?? null;
|
|
1114
1120
|
const readBody = read ?? null;
|
|
1121
|
+
const imageBody = image !== void 0 && image !== null && renderSlot !== void 0 && loadImage !== void 0 ? image : null;
|
|
1115
1122
|
const searchBody = search ?? null;
|
|
1116
1123
|
const webBody = web ?? null;
|
|
1117
1124
|
const askQuestionBody = askQuestion ?? null;
|
|
1118
1125
|
const outputText = output ?? null;
|
|
1119
|
-
const card = askQuestionBody ?? terminalBody ?? diffBody ?? readBody ?? searchBody ?? webBody;
|
|
1126
|
+
const card = askQuestionBody ?? terminalBody ?? diffBody ?? readBody ?? imageBody ?? searchBody ?? webBody;
|
|
1120
1127
|
const expandable = bodyRaw != null || outputText !== null || card !== null;
|
|
1121
1128
|
const open = expanded && expandable;
|
|
1122
1129
|
const bodyText = (0, react.useMemo)(() => open && card === null && bodyRaw != null ? formatToolBody(variant, bodyRaw) : null, [
|
|
@@ -1203,6 +1210,23 @@ window.__ModuleLoader__.load({
|
|
|
1203
1210
|
labels: readLabels,
|
|
1204
1211
|
maxLines: 8,
|
|
1205
1212
|
className: ToolRow_module_css_default.readBody
|
|
1213
|
+
}) : imageBody !== null ? (0, react_jsx_runtime.jsxs)("div", {
|
|
1214
|
+
className: ToolRow_module_css_default.imageBody,
|
|
1215
|
+
children: [
|
|
1216
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1217
|
+
className: ToolRow_module_css_default.imageLabel,
|
|
1218
|
+
children: imageBody.label
|
|
1219
|
+
}),
|
|
1220
|
+
renderSlot !== void 0 && loadImage !== void 0 && renderSlot("tool.call.images", {
|
|
1221
|
+
images: imageBody.images,
|
|
1222
|
+
loadImage,
|
|
1223
|
+
align: "start"
|
|
1224
|
+
}),
|
|
1225
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1226
|
+
className: ToolRow_module_css_default.imageMeta,
|
|
1227
|
+
children: imageBody.text
|
|
1228
|
+
})
|
|
1229
|
+
]
|
|
1206
1230
|
}) : searchBody !== null ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.SearchBlock, {
|
|
1207
1231
|
...searchBody.card,
|
|
1208
1232
|
labels: searchLabels,
|
|
@@ -1328,7 +1352,7 @@ window.__ModuleLoader__.load({
|
|
|
1328
1352
|
return "kind" in node ? node.call?.name ?? "" : node.name;
|
|
1329
1353
|
}
|
|
1330
1354
|
/** One atomic call dispatched through the Tool-owned keyed slot. */
|
|
1331
|
-
const ToolCall = (0, react.memo)(function ToolCall({ renderSlot, callId, toolName, block, openFile, selected, cwd, home, inspectCall, t, children }) {
|
|
1355
|
+
const ToolCall = (0, react.memo)(function ToolCall({ renderSlot, callId, toolName, block, openFile, selected, cwd, home, inspectCall, loadImage, t, children }) {
|
|
1332
1356
|
const owner = (0, react.useMemo)(() => ({
|
|
1333
1357
|
callId,
|
|
1334
1358
|
toolName,
|
|
@@ -1336,6 +1360,7 @@ window.__ModuleLoader__.load({
|
|
|
1336
1360
|
openFile,
|
|
1337
1361
|
cwd,
|
|
1338
1362
|
home,
|
|
1363
|
+
loadImage,
|
|
1339
1364
|
inspect: () => {
|
|
1340
1365
|
inspectCall(callId);
|
|
1341
1366
|
}
|
|
@@ -1346,6 +1371,7 @@ window.__ModuleLoader__.load({
|
|
|
1346
1371
|
openFile,
|
|
1347
1372
|
cwd,
|
|
1348
1373
|
home,
|
|
1374
|
+
loadImage,
|
|
1349
1375
|
inspectCall
|
|
1350
1376
|
]);
|
|
1351
1377
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1362,7 +1388,7 @@ window.__ModuleLoader__.load({
|
|
|
1362
1388
|
}), children]
|
|
1363
1389
|
});
|
|
1364
1390
|
});
|
|
1365
|
-
const ToolCallBranch = (0, react.memo)(function ToolCallBranch({ renderSlot, block, selectedCallId, cwd, home, openFile, inspectCall, t }) {
|
|
1391
|
+
const ToolCallBranch = (0, react.memo)(function ToolCallBranch({ renderSlot, block, selectedCallId, cwd, home, openFile, inspectCall, loadImage, t }) {
|
|
1366
1392
|
return (0, react_jsx_runtime.jsx)(ToolCall, {
|
|
1367
1393
|
renderSlot,
|
|
1368
1394
|
callId: block.callId,
|
|
@@ -1373,6 +1399,7 @@ window.__ModuleLoader__.load({
|
|
|
1373
1399
|
cwd,
|
|
1374
1400
|
home,
|
|
1375
1401
|
inspectCall,
|
|
1402
|
+
loadImage,
|
|
1376
1403
|
t,
|
|
1377
1404
|
children: block.subCalls.length > 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
1378
1405
|
className: ToolCallTree_module_css_default.subCalls,
|
|
@@ -1385,6 +1412,7 @@ window.__ModuleLoader__.load({
|
|
|
1385
1412
|
home,
|
|
1386
1413
|
openFile,
|
|
1387
1414
|
inspectCall,
|
|
1415
|
+
loadImage,
|
|
1388
1416
|
t
|
|
1389
1417
|
}, child.callId))
|
|
1390
1418
|
}) : null
|
|
@@ -1396,7 +1424,7 @@ window.__ModuleLoader__.load({
|
|
|
1396
1424
|
* @param props - whole-Tool owner data and the Tool-owned child-slot share.
|
|
1397
1425
|
* @returns the Tool call tree.
|
|
1398
1426
|
*/
|
|
1399
|
-
function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, useHostInfo, t }) {
|
|
1427
|
+
function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, loadImage, useHostInfo, t }) {
|
|
1400
1428
|
const home = useHostInfo((info) => info.home);
|
|
1401
1429
|
const block = node.data.root;
|
|
1402
1430
|
return (0, react_jsx_runtime.jsx)(ToolCallBranch, {
|
|
@@ -1407,6 +1435,7 @@ window.__ModuleLoader__.load({
|
|
|
1407
1435
|
home,
|
|
1408
1436
|
openFile,
|
|
1409
1437
|
inspectCall,
|
|
1438
|
+
loadImage,
|
|
1410
1439
|
t
|
|
1411
1440
|
});
|
|
1412
1441
|
}
|
|
@@ -1872,13 +1901,16 @@ window.__ModuleLoader__.load({
|
|
|
1872
1901
|
}
|
|
1873
1902
|
};
|
|
1874
1903
|
//#endregion
|
|
1875
|
-
//#region lib/types/client/tool/toolviews/read-row.js
|
|
1904
|
+
//#region lib/types/client/tool/toolviews/read-family-row.js
|
|
1876
1905
|
/**
|
|
1877
|
-
*
|
|
1906
|
+
* Compose a read-family row: the shared chrome and model-derived fields, plus the
|
|
1907
|
+
* caller's card material.
|
|
1908
|
+
* @param props - the toolview runtime share and locale seat.
|
|
1909
|
+
* @param card - the card props this row owns.
|
|
1910
|
+
* @returns the assembled ToolRow.
|
|
1878
1911
|
*/
|
|
1879
|
-
function
|
|
1912
|
+
function readFamilyRow({ toolName, block, cwd, home, openFile, inspect, t }, card) {
|
|
1880
1913
|
const model = toolRowModel(toolName, block, cwd, home);
|
|
1881
|
-
const read = readCardModel(block, cwd, home);
|
|
1882
1914
|
return (0, react_jsx_runtime.jsx)(ToolRow, {
|
|
1883
1915
|
t,
|
|
1884
1916
|
variant: model.variant,
|
|
@@ -1886,15 +1918,25 @@ window.__ModuleLoader__.load({
|
|
|
1886
1918
|
icon: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }),
|
|
1887
1919
|
title: t(model.titleKey),
|
|
1888
1920
|
summary: model.summary,
|
|
1921
|
+
bodyRaw: null,
|
|
1889
1922
|
output: model.output,
|
|
1890
1923
|
errorSummary: model.errorSummary,
|
|
1891
|
-
|
|
1924
|
+
...card,
|
|
1892
1925
|
state: model.state,
|
|
1893
1926
|
filePath: model.filePath,
|
|
1894
1927
|
onOpenFile: openFile,
|
|
1895
1928
|
inspect
|
|
1896
1929
|
});
|
|
1897
1930
|
}
|
|
1931
|
+
//#endregion
|
|
1932
|
+
//#region lib/types/client/tool/toolviews/read-row.js
|
|
1933
|
+
/**
|
|
1934
|
+
* Lets users expand a completed read result and open its reported path.
|
|
1935
|
+
*/
|
|
1936
|
+
function ReadRow(props) {
|
|
1937
|
+
const { block, cwd, home } = props;
|
|
1938
|
+
return readFamilyRow(props, { read: readCardModel(block, cwd, home) });
|
|
1939
|
+
}
|
|
1898
1940
|
/** Registers the read tool's conversation row. */
|
|
1899
1941
|
const readToolview = {
|
|
1900
1942
|
name: "read-toolview",
|
|
@@ -1908,6 +1950,214 @@ window.__ModuleLoader__.load({
|
|
|
1908
1950
|
}
|
|
1909
1951
|
};
|
|
1910
1952
|
//#endregion
|
|
1953
|
+
//#region lib/types/client/tool/models/image-card-model.js
|
|
1954
|
+
/**
|
|
1955
|
+
* Whether a wire value is a usable pixel or byte measure.
|
|
1956
|
+
* @param value - unvalidated wire value.
|
|
1957
|
+
* @returns true when it is a positive integer.
|
|
1958
|
+
*/
|
|
1959
|
+
function positiveInteger(value) {
|
|
1960
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
1961
|
+
}
|
|
1962
|
+
/** The envelope `formatImageReadOutput` writes, matched by shape. */
|
|
1963
|
+
const IMAGE_ENVELOPE = /^<path>[^\n]*<\/path>\n<type>image<\/type>\n<content>\n[\s\S]*\n<\/content>$/u;
|
|
1964
|
+
/** The media types a durable image block may claim; anything else declines.
|
|
1965
|
+
* Hand-written mirror of `ImageMediaType` from dsh-attachment — the wire
|
|
1966
|
+
* boundary needs a runtime check and feature plugins must not import values
|
|
1967
|
+
* from each other; a new member added there must be added here too, or the
|
|
1968
|
+
* decline point below silently degrades that image to the generic card. */
|
|
1969
|
+
const IMAGE_MEDIA_TYPES = new Set([
|
|
1970
|
+
"image/png",
|
|
1971
|
+
"image/jpeg",
|
|
1972
|
+
"image/webp",
|
|
1973
|
+
"image/gif"
|
|
1974
|
+
]);
|
|
1975
|
+
/** Runtime membership check; the cast is safe because the set holds exactly the four members. */
|
|
1976
|
+
function isImageMediaType(value) {
|
|
1977
|
+
return IMAGE_MEDIA_TYPES.has(value);
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Narrow the persisted metadata, defensively. Every field arrives unvalidated on
|
|
1981
|
+
* replay (an obsolete or hand-edited log reaches here), so any mismatch declines
|
|
1982
|
+
* to the generic card rather than throwing. An absent `meta` (a nested call
|
|
1983
|
+
* persists none) leaves the path to the call's own `file_path` argument.
|
|
1984
|
+
*
|
|
1985
|
+
* The attachment id is checked for existence only: it is opaque and
|
|
1986
|
+
* provider-owned, and consumers must not parse that representation, so
|
|
1987
|
+
* pattern-matching the local content-address form would reject a legitimate id
|
|
1988
|
+
* minted by an alternative store.
|
|
1989
|
+
* @param meta - persisted presentation metadata of unknown shape.
|
|
1990
|
+
* @returns the narrowed path, or null when it does not match.
|
|
1991
|
+
*/
|
|
1992
|
+
function imageMeta(meta) {
|
|
1993
|
+
if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return null;
|
|
1994
|
+
const { path } = meta;
|
|
1995
|
+
if (typeof path !== "string" || path === "") return null;
|
|
1996
|
+
return { path };
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Narrow every attachment reference carried by the result's image blocks, in
|
|
2000
|
+
* order.
|
|
2001
|
+
*
|
|
2002
|
+
* The content is the single source of truth for the references: it is what the
|
|
2003
|
+
* tool actually returned and what a post-execute hook would replace together
|
|
2004
|
+
* with the rest of the content. Every field arrives unvalidated over the wire,
|
|
2005
|
+
* so any malformed image block declines to the generic card rather than
|
|
2006
|
+
* rendering a partial gallery.
|
|
2007
|
+
*
|
|
2008
|
+
* The attachment id is checked for existence only — it is opaque and
|
|
2009
|
+
* provider-owned, so pattern-matching the local content-address form would reject
|
|
2010
|
+
* a legitimate id minted by an alternative store.
|
|
2011
|
+
* @param content - the settled result's content blocks.
|
|
2012
|
+
* @returns the narrowed references, or null when no valid image block is present.
|
|
2013
|
+
*/
|
|
2014
|
+
function imageReferences(content) {
|
|
2015
|
+
const refs = [];
|
|
2016
|
+
for (const part of content) {
|
|
2017
|
+
if (typeof part !== "object" || part === null) continue;
|
|
2018
|
+
const { type, attachment } = part;
|
|
2019
|
+
if (type !== "image") continue;
|
|
2020
|
+
if (typeof attachment !== "object" || attachment === null || Array.isArray(attachment)) return null;
|
|
2021
|
+
const { attachmentId, mediaType, bytes, width, height, name, originalDimensions } = attachment;
|
|
2022
|
+
if (typeof attachmentId !== "string" || attachmentId === "") return null;
|
|
2023
|
+
if (typeof mediaType !== "string" || !isImageMediaType(mediaType)) return null;
|
|
2024
|
+
if (!positiveInteger(bytes) || !positiveInteger(width) || !positiveInteger(height)) return null;
|
|
2025
|
+
if (name !== void 0 && typeof name !== "string") return null;
|
|
2026
|
+
let inputDimensions;
|
|
2027
|
+
if (originalDimensions !== void 0) {
|
|
2028
|
+
if (typeof originalDimensions !== "object" || originalDimensions === null || Array.isArray(originalDimensions)) return null;
|
|
2029
|
+
const { width: inputWidth, height: inputHeight } = originalDimensions;
|
|
2030
|
+
if (!positiveInteger(inputWidth) || !positiveInteger(inputHeight)) return null;
|
|
2031
|
+
inputDimensions = {
|
|
2032
|
+
width: inputWidth,
|
|
2033
|
+
height: inputHeight
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
refs.push({
|
|
2037
|
+
attachmentId,
|
|
2038
|
+
mediaType,
|
|
2039
|
+
bytes,
|
|
2040
|
+
width,
|
|
2041
|
+
height,
|
|
2042
|
+
...name === void 0 ? {} : { name },
|
|
2043
|
+
...inputDimensions === void 0 ? {} : { originalDimensions: inputDimensions }
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
return refs.length > 0 ? refs : null;
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* Read the text of every text block of a settled image result, joined in order.
|
|
2050
|
+
*
|
|
2051
|
+
* The envelope is one of them; a post-execute hook that appends further text
|
|
2052
|
+
* blocks keeps them visible under the gallery instead of being dropped. The
|
|
2053
|
+
* envelope shape is still the recognition gate: a result without it is not a
|
|
2054
|
+
* well-formed image read and declines.
|
|
2055
|
+
* @param content - the settled result's content blocks.
|
|
2056
|
+
* @returns the joined text, or null when no envelope-shaped block is present.
|
|
2057
|
+
*/
|
|
2058
|
+
function imageTexts(content) {
|
|
2059
|
+
const parts = [];
|
|
2060
|
+
let sawEnvelope = false;
|
|
2061
|
+
for (const part of content) {
|
|
2062
|
+
if (part.type !== "text" || typeof part.text !== "string") continue;
|
|
2063
|
+
if (IMAGE_ENVELOPE.test(part.text)) sawEnvelope = true;
|
|
2064
|
+
parts.push(part.text);
|
|
2065
|
+
}
|
|
2066
|
+
return sawEnvelope && parts.length > 0 ? parts.join("\n") : null;
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* Whether the content carries only blocks the card consumes.
|
|
2070
|
+
*
|
|
2071
|
+
* `ContentBlock` is a merge-extensible union, so a post-execute hook could
|
|
2072
|
+
* append a block of a type this card does not render (reasoning, an extension
|
|
2073
|
+
* type, or a non-object). Rendering the card anyway would silently hide that
|
|
2074
|
+
* block, so anything beyond a well-formed text or image object declines to the
|
|
2075
|
+
* generic card, which shows the flattened content.
|
|
2076
|
+
* @param content - the settled result's content blocks.
|
|
2077
|
+
* @returns true when every block is a text or image object with usable fields.
|
|
2078
|
+
*/
|
|
2079
|
+
function fullyRendered(content) {
|
|
2080
|
+
return content.every((part) => {
|
|
2081
|
+
if (typeof part !== "object" || part === null) return false;
|
|
2082
|
+
const { type, text } = part;
|
|
2083
|
+
return type === "image" || type === "text" && typeof text === "string";
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Derive a settled image card after validating the call head, persisted
|
|
2088
|
+
* metadata (or its argument fallback), and the model-facing image envelope.
|
|
2089
|
+
*
|
|
2090
|
+
* The card is result-side only: a call carries no content until `execute`
|
|
2091
|
+
* returns, so a running `read_image` has none and this returns null for it.
|
|
2092
|
+
* Both root and nested calls settle as ToolResultNode; the nested one (a
|
|
2093
|
+
* read_image dispatched from inside run_code) persists no presentationMeta, so
|
|
2094
|
+
* its label falls back to the call's own `file_path` argument.
|
|
2095
|
+
* @param block - running or settled Tool block.
|
|
2096
|
+
* @param sessionCwd - the session workspace root; a workspace-rooted absolute
|
|
2097
|
+
* path label displays relative to it. Absent leaves the path as authored.
|
|
2098
|
+
* @param home - host account home; a leftover POSIX home path displays as `~`.
|
|
2099
|
+
* @returns the image-card props, or null for the generic path.
|
|
2100
|
+
*/
|
|
2101
|
+
function imageCardModel(block, sessionCwd, home) {
|
|
2102
|
+
if (!("kind" in block) || block.isError) return null;
|
|
2103
|
+
const call = parsedToolCall(block);
|
|
2104
|
+
if (call?.name !== "read_image") return null;
|
|
2105
|
+
const { file_path: filePath } = call.args;
|
|
2106
|
+
if (typeof filePath !== "string" || filePath.trim() === "") return null;
|
|
2107
|
+
const path = imageMeta(block.meta)?.path ?? (block.parentCallId !== void 0 ? filePath : null);
|
|
2108
|
+
if (path === null) return null;
|
|
2109
|
+
if (!fullyRendered(block.content)) return null;
|
|
2110
|
+
const refs = imageReferences(block.content);
|
|
2111
|
+
if (refs === null) return null;
|
|
2112
|
+
const text = imageTexts(block.content);
|
|
2113
|
+
if (text === null) return null;
|
|
2114
|
+
return {
|
|
2115
|
+
label: abbreviateHomePath(relativizeToCwd(path, sessionCwd), home),
|
|
2116
|
+
images: refs.map((ref) => ({ attachment: ref })),
|
|
2117
|
+
text
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
//#endregion
|
|
2121
|
+
//#region lib/types/client/tool/toolviews/read-image-row.js
|
|
2122
|
+
/**
|
|
2123
|
+
* read_image row: the read-family chrome with the durably committed image as the
|
|
2124
|
+
* row's collapsed-by-default card body, rendered through the `tool.call.images`
|
|
2125
|
+
* slot this entry declares.
|
|
2126
|
+
*/
|
|
2127
|
+
function ReadImageRow(props) {
|
|
2128
|
+
const { block, cwd, home, renderSlot, loadImage } = props;
|
|
2129
|
+
return readFamilyRow(props, {
|
|
2130
|
+
image: imageCardModel(block, cwd, home),
|
|
2131
|
+
renderSlot,
|
|
2132
|
+
loadImage
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2135
|
+
/**
|
|
2136
|
+
* The read_image row as a plain registrant plugin following the atomic Tool-view
|
|
2137
|
+
* declaration across independent activation and reload lifetimes. Declaring
|
|
2138
|
+
* `tool.call.images` as a child slot authorizes this entry's `renderSlot` to
|
|
2139
|
+
* dispatch the gallery.
|
|
2140
|
+
*/
|
|
2141
|
+
const readImageToolview = {
|
|
2142
|
+
name: "read-image-toolview",
|
|
2143
|
+
inject: ["slots"],
|
|
2144
|
+
/**
|
|
2145
|
+
* Register the read_image row into the Tool-owned keyed view slot.
|
|
2146
|
+
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
2147
|
+
*/
|
|
2148
|
+
apply(ctx) {
|
|
2149
|
+
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
2150
|
+
name: "tool.call.toolview",
|
|
2151
|
+
key: "read_image",
|
|
2152
|
+
locale: CONVERSATION_NS,
|
|
2153
|
+
children: { "tool.call.images": {
|
|
2154
|
+
kind: "single",
|
|
2155
|
+
scope: "session"
|
|
2156
|
+
} }
|
|
2157
|
+
}, ReadImageRow));
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
//#endregion
|
|
1911
2161
|
//#region lib/types/client/tool/toolviews/search-row.js
|
|
1912
2162
|
const SEARCH_TITLE_KEYS = {
|
|
1913
2163
|
grep: "tool.title.grep",
|
|
@@ -2120,6 +2370,7 @@ window.__ModuleLoader__.load({
|
|
|
2120
2370
|
}, ToolDetails));
|
|
2121
2371
|
ctx.plugin(bashToolviewSample);
|
|
2122
2372
|
ctx.plugin(readToolview);
|
|
2373
|
+
ctx.plugin(readImageToolview);
|
|
2123
2374
|
ctx.plugin(fileMutationToolview);
|
|
2124
2375
|
ctx.plugin(searchToolview);
|
|
2125
2376
|
ctx.plugin(webToolview);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { HostObservable, InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime } from '@prettier-ai/dsh-client-ui-slots';
|
|
3
3
|
import type { RemoteHostFacts } from '@prettier-ai/dsh-api-remotes/client';
|
|
4
4
|
import type { ToolCallBlock } from '@prettier-ai/dsh-client-ui-chat/client';
|
|
5
|
+
import type { MessageImageLoader, MessageImageSource } from '@prettier-ai/dsh-client-ui-conversation/client';
|
|
5
6
|
declare module '@prettier-ai/dsh-client-ui-slots' {
|
|
6
7
|
interface SlotMap {
|
|
7
8
|
/**
|
|
@@ -23,8 +24,35 @@ declare module '@prettier-ai/dsh-client-ui-slots' {
|
|
|
23
24
|
scope: 'session';
|
|
24
25
|
owner: ToolCallOwnerProps;
|
|
25
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Durable images of a settled image-bearing Tool call, rendered through
|
|
29
|
+
* the attachment presentation plugin. The Tool layer never imports an
|
|
30
|
+
* attachment implementation: a toolview declares this slot as a child and
|
|
31
|
+
* renders it with the image card's references plus the session-authorized
|
|
32
|
+
* loader it received in its owner, and the attachment plugin fills the
|
|
33
|
+
* gallery. Composing no attachment presentation plugin renders nothing,
|
|
34
|
+
* which is why the image card keeps its own envelope text beside the
|
|
35
|
+
* gallery. A child slot is declared by exactly one entry: registering a
|
|
36
|
+
* second toolview that declares the same child throws at load, so a
|
|
37
|
+
* future image-bearing tool must reuse this entry or own a distinct
|
|
38
|
+
* slot.
|
|
39
|
+
*/
|
|
40
|
+
'tool.call.images': {
|
|
41
|
+
kind: 'single';
|
|
42
|
+
scope: 'session';
|
|
43
|
+
owner: ToolImagesOwnerProps;
|
|
44
|
+
};
|
|
26
45
|
}
|
|
27
46
|
}
|
|
47
|
+
/** Owner currency of the Tool image gallery slot: references plus the loader. */
|
|
48
|
+
export interface ToolImagesOwnerProps {
|
|
49
|
+
/** Durable references or submission-echo previews in result order. */
|
|
50
|
+
images: readonly MessageImageSource[];
|
|
51
|
+
/** Session-authorized image URL loader for the durable arm. */
|
|
52
|
+
loadImage: MessageImageLoader;
|
|
53
|
+
/** Horizontal placement inside the owning record. */
|
|
54
|
+
align: 'start' | 'end';
|
|
55
|
+
}
|
|
28
56
|
/** Standard owner currency supplied to every atomic Tool view. */
|
|
29
57
|
export interface ToolCallOwnerProps {
|
|
30
58
|
/** Tool call identity, stable across running and settled forms. */
|
|
@@ -39,6 +67,14 @@ export interface ToolCallOwnerProps {
|
|
|
39
67
|
home?: string | undefined;
|
|
40
68
|
/** Open a Tool argument path through the Host. */
|
|
41
69
|
openFile: (path: string) => void;
|
|
70
|
+
/**
|
|
71
|
+
* Session-authorized image loader for the `tool.call.images` slot, supplied
|
|
72
|
+
* by the chat node that owns this call. A composed chat node always
|
|
73
|
+
* supplies it (`ChatNodeOwnerProps.loadImage` is required), so the tool
|
|
74
|
+
* layer never imports an attachment implementation nor handles URL
|
|
75
|
+
* authorization.
|
|
76
|
+
*/
|
|
77
|
+
loadImage: MessageImageLoader;
|
|
42
78
|
/** Inspect this call in the trajectory view when available. */
|
|
43
79
|
inspect?: (() => void) | undefined;
|
|
44
80
|
}
|
|
@@ -5,5 +5,5 @@ import type { ToolTreeProps } from '../contract/slots.ts';
|
|
|
5
5
|
* @param props - whole-Tool owner data and the Tool-owned child-slot share.
|
|
6
6
|
* @returns the Tool call tree.
|
|
7
7
|
*/
|
|
8
|
-
export declare function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, useHostInfo, t, }: ToolTreeProps): import("react").JSX.Element;
|
|
8
|
+
export declare function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, loadImage, useHostInfo, t, }: ToolTreeProps): import("react").JSX.Element;
|
|
9
9
|
//# sourceMappingURL=ToolCallTree.d.ts.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
-
import type { TranslateNS } from '@prettier-ai/dsh-client-ui-slots';
|
|
2
|
+
import type { PropsRenderSlots, TranslateNS } from '@prettier-ai/dsh-client-ui-slots';
|
|
3
|
+
import type { MessageImageLoader } from '@prettier-ai/dsh-client-ui-conversation/client';
|
|
3
4
|
import { type DiffCardModel } from '../models/diff-card-model.ts';
|
|
4
5
|
import { type ReadCardModel } from '../models/read-card-model.ts';
|
|
6
|
+
import type { ImageCardModel } from '../models/image-card-model.ts';
|
|
5
7
|
import { type SearchCardModel } from '../models/search-card-model.ts';
|
|
6
8
|
import { type TerminalCardModel } from '../models/terminal-card-model.ts';
|
|
7
9
|
import type { AskQuestionCardModel } from '../models/ask-question-card-model.ts';
|
|
@@ -35,6 +37,21 @@ export interface ToolRowProps {
|
|
|
35
37
|
terminal?: TerminalCardModel | null | undefined;
|
|
36
38
|
diff?: DiffCardModel | null | undefined;
|
|
37
39
|
read?: ReadCardModel | null | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Image-card material for a call whose result is an image (derived by
|
|
42
|
+
* `imageCardModel`). Rendered through the `tool.call.images` slot, so the
|
|
43
|
+
* tool layer never imports an attachment implementation nor handles URL
|
|
44
|
+
* authorization.
|
|
45
|
+
*/
|
|
46
|
+
image?: ImageCardModel | null | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Dispatch the image gallery through the tool-owned `tool.call.images`
|
|
49
|
+
* slot, supplied by the toolview that owns this row together with the
|
|
50
|
+
* session-authorized loader.
|
|
51
|
+
*/
|
|
52
|
+
renderSlot?: PropsRenderSlots<'tool.call.images'>['renderSlot'] | undefined;
|
|
53
|
+
/** Session-authorized image URL loader for the gallery slot. */
|
|
54
|
+
loadImage?: MessageImageLoader | undefined;
|
|
38
55
|
search?: SearchCardModel | null | undefined;
|
|
39
56
|
web?: WebCardModelProps | null | undefined;
|
|
40
57
|
state: ToolRowState;
|
|
@@ -51,5 +68,5 @@ export interface ToolRowProps {
|
|
|
51
68
|
*/
|
|
52
69
|
inspect?: (() => void) | undefined;
|
|
53
70
|
}
|
|
54
|
-
export declare function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, search, web, state, filePath, onOpenFile, inspect, }: ToolRowProps): import("react").JSX.Element;
|
|
71
|
+
export declare function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, image, renderSlot, loadImage, search, web, state, filePath, onOpenFile, inspect, }: ToolRowProps): import("react").JSX.Element;
|
|
55
72
|
//# sourceMappingURL=ToolRow.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Pure image-card derivation from raw result content and metadata. @module */
|
|
2
|
+
import type { ImageAttachmentRef } from '@prettier-ai/dsh-attachment';
|
|
3
|
+
import { type ToolCallBlock } from './tool-call-model.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The image-card material one settled call contributes: the display label plus
|
|
6
|
+
* the durable references the attachment slot renders as a gallery.
|
|
7
|
+
*
|
|
8
|
+
* The bytes are not here. `attachmentId` is opaque and provider-owned, so a UI
|
|
9
|
+
* resolves it to a session-authorized URL at render time; this model never parses
|
|
10
|
+
* it nor derives a path from it.
|
|
11
|
+
*/
|
|
12
|
+
export interface ImageCardModel {
|
|
13
|
+
/** Card label: the read path, shortened the way every other card's is. */
|
|
14
|
+
label: string;
|
|
15
|
+
/** The durable images this result returned, in result order. */
|
|
16
|
+
images: readonly {
|
|
17
|
+
readonly attachment: ImageAttachmentRef;
|
|
18
|
+
}[];
|
|
19
|
+
/**
|
|
20
|
+
* The model-facing envelope text, for the line under the gallery.
|
|
21
|
+
*
|
|
22
|
+
* Taken from the result's own text block rather than the row's flattened
|
|
23
|
+
* result text: an image read's content is `[text envelope, image block]`, and
|
|
24
|
+
* flattening JSON.stringifies the image block, which would print the raw
|
|
25
|
+
* attachment object under the picture — the symptom this card exists to remove.
|
|
26
|
+
*/
|
|
27
|
+
text: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Derive a settled image card after validating the call head, persisted
|
|
31
|
+
* metadata (or its argument fallback), and the model-facing image envelope.
|
|
32
|
+
*
|
|
33
|
+
* The card is result-side only: a call carries no content until `execute`
|
|
34
|
+
* returns, so a running `read_image` has none and this returns null for it.
|
|
35
|
+
* Both root and nested calls settle as ToolResultNode; the nested one (a
|
|
36
|
+
* read_image dispatched from inside run_code) persists no presentationMeta, so
|
|
37
|
+
* its label falls back to the call's own `file_path` argument.
|
|
38
|
+
* @param block - running or settled Tool block.
|
|
39
|
+
* @param sessionCwd - the session workspace root; a workspace-rooted absolute
|
|
40
|
+
* path label displays relative to it. Absent leaves the path as authored.
|
|
41
|
+
* @param home - host account home; a leftover POSIX home path displays as `~`.
|
|
42
|
+
* @returns the image-card props, or null for the generic path.
|
|
43
|
+
*/
|
|
44
|
+
export declare function imageCardModel(block: ToolCallBlock, sessionCwd?: string, home?: string): ImageCardModel | null;
|
|
45
|
+
//# sourceMappingURL=image-card-model.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { PropsRenderSlots } from '@prettier-ai/dsh-client-ui-slots';
|
|
3
|
+
import type { ToolCallViewProps } from '../../contract/slots.ts';
|
|
4
|
+
import { type ToolRowProps } from '../components/ToolRow.tsx';
|
|
5
|
+
/** Full row props of a read-family toolview: the runtime share plus its locale seat. */
|
|
6
|
+
export type ReadFamilyRowProps = ToolCallViewProps & {
|
|
7
|
+
t: ToolRowProps['t'];
|
|
8
|
+
};
|
|
9
|
+
/** read_image row props: the runtime share, the declared image child slot, and the locale seat. */
|
|
10
|
+
export type ReadImageRowProps = ReadFamilyRowProps & PropsRenderSlots<'tool.call.images'>;
|
|
11
|
+
/**
|
|
12
|
+
* The card material one read-family row contributes: exactly the ToolRow card
|
|
13
|
+
* props that row owns. `read` supplies `read`; `read_image` supplies `image`
|
|
14
|
+
* together with the slot dispatcher and loader that draw it.
|
|
15
|
+
*/
|
|
16
|
+
export type ReadFamilyCard = Pick<ToolRowProps, 'read' | 'image' | 'renderSlot' | 'loadImage'>;
|
|
17
|
+
/**
|
|
18
|
+
* Compose a read-family row: the shared chrome and model-derived fields, plus the
|
|
19
|
+
* caller's card material.
|
|
20
|
+
* @param props - the toolview runtime share and locale seat.
|
|
21
|
+
* @param card - the card props this row owns.
|
|
22
|
+
* @returns the assembled ToolRow.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readFamilyRow({ toolName, block, cwd, home, openFile, inspect, t }: ReadFamilyRowProps, card: ReadFamilyCard): ReactNode;
|
|
25
|
+
//# sourceMappingURL=read-family-row.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Context } from '@prettier-ai/cordis';
|
|
2
|
+
import { type ReadImageRowProps } from './read-family-row.tsx';
|
|
3
|
+
/**
|
|
4
|
+
* read_image row: the read-family chrome with the durably committed image as the
|
|
5
|
+
* row's collapsed-by-default card body, rendered through the `tool.call.images`
|
|
6
|
+
* slot this entry declares.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ReadImageRow(props: ReadImageRowProps): import("react").ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* The read_image row as a plain registrant plugin following the atomic Tool-view
|
|
11
|
+
* declaration across independent activation and reload lifetimes. Declaring
|
|
12
|
+
* `tool.call.images` as a child slot authorizes this entry's `renderSlot` to
|
|
13
|
+
* dispatch the gallery.
|
|
14
|
+
*/
|
|
15
|
+
export declare const readImageToolview: {
|
|
16
|
+
name: string;
|
|
17
|
+
inject: string[];
|
|
18
|
+
/**
|
|
19
|
+
* Register the read_image row into the Tool-owned keyed view slot.
|
|
20
|
+
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
21
|
+
*/
|
|
22
|
+
apply(ctx: Context): void;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=read-image-row.d.ts.map
|
|
@@ -5,7 +5,7 @@ type ReadRowProps = ToolCallViewProps & PropsLocale<'conversation'>;
|
|
|
5
5
|
/**
|
|
6
6
|
* Lets users expand a completed read result and open its reported path.
|
|
7
7
|
*/
|
|
8
|
-
export declare function ReadRow(
|
|
8
|
+
export declare function ReadRow(props: ReadRowProps): import("react").ReactNode;
|
|
9
9
|
/** Registers the read tool's conversation row. */
|
|
10
10
|
export declare const readToolview: {
|
|
11
11
|
name: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prettier-ai/dsh-client-ui-tool",
|
|
3
3
|
"description": "Client Tool call-tree renderer and keyed per-tool presentation slot",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -48,19 +48,20 @@
|
|
|
48
48
|
"@types/react": "~18.3.1",
|
|
49
49
|
"react": "^18.2.0",
|
|
50
50
|
"react-dom": "^18.2.0",
|
|
51
|
-
"@prettier-ai/dsh-api-remotes": "^0.1.
|
|
52
|
-
"@prettier-ai/dsh-client-connection": "^0.1.
|
|
51
|
+
"@prettier-ai/dsh-api-remotes": "^0.1.3-alpha.1",
|
|
52
|
+
"@prettier-ai/dsh-client-connection": "^0.1.3-alpha.1",
|
|
53
53
|
"@prettier-ai/cordis": "^4.0.2",
|
|
54
|
-
"@prettier-ai/dsh-client-locale": "^0.1.
|
|
55
|
-
"@prettier-ai/dsh-client-
|
|
56
|
-
"@prettier-ai/dsh-client-
|
|
57
|
-
"@prettier-ai/dsh-client-ui-
|
|
58
|
-
"@prettier-ai/dsh-
|
|
59
|
-
"@prettier-ai/dsh-
|
|
60
|
-
"@prettier-ai/dsh-client-ui-
|
|
61
|
-
"@prettier-ai/dsh-client-ui-
|
|
62
|
-
"@prettier-ai/dsh-
|
|
63
|
-
"@prettier-ai/dsh-
|
|
54
|
+
"@prettier-ai/dsh-client-locale": "^0.1.3-alpha.1",
|
|
55
|
+
"@prettier-ai/dsh-client-ui-conversation": "^0.1.3-alpha.1",
|
|
56
|
+
"@prettier-ai/dsh-client-test-runtime": "^0.1.3-alpha.1",
|
|
57
|
+
"@prettier-ai/dsh-client-ui-slots": "^0.1.3-alpha.1",
|
|
58
|
+
"@prettier-ai/dsh-client-ui-chat": "^0.1.3-alpha.1",
|
|
59
|
+
"@prettier-ai/dsh-api-workspace-controller": "^0.1.3-alpha.1",
|
|
60
|
+
"@prettier-ai/dsh-client-ui-renderer": "^0.1.3-alpha.1",
|
|
61
|
+
"@prettier-ai/dsh-client-ui-primitives": "^0.1.3-alpha.1",
|
|
62
|
+
"@prettier-ai/dsh-attachment": "^0.1.3-alpha.1",
|
|
63
|
+
"@prettier-ai/dsh-client-ui-session": "^0.1.3-alpha.1",
|
|
64
|
+
"@prettier-ai/dsh-util-workspace-path": "^0.1.3-alpha.1"
|
|
64
65
|
},
|
|
65
66
|
"files": [
|
|
66
67
|
"lib/index.js",
|