@deepseek-ai/dsh-client-ui-tool 0.1.3-alpha.2 → 0.1.5-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 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: e0b49a3b582f5f1e6e9aa19e0b026dedb9ba2a18
6
- README.zh.md: 032f048f2501923c4c9f010b76afe27d98fe7889
5
+ README.md: b8dd2a5e3c6743e496d585a2de238714f00e9f6b
6
+ README.zh.md: 49e82c9fa70953d73171d466f36995ff2870c6b2
package/README.md CHANGED
@@ -57,12 +57,12 @@ The package realizes one dispatch rule: atomic Tool views are keyed by wire Tool
57
57
 
58
58
  ### Rendering contract
59
59
 
60
- `ToolCallTree` receives one root `ToolCallBlock` that already contains recursive `subCalls`, selection state, the session `cwd`, and Host callbacks for opening files and inspecting calls. It recursively walks the standard call blocks and sends the root and children at every depth through the same atomic dispatch path, without subscribing to a separate parent-to-children map. Each root and child wrapper preserves the `data-chat-anchor-key="call:<id>"` and `data-chat-call-id` DOM contract used for paging and selection.
60
+ `ToolCallTree` receives one root `ToolCallBlock` that already contains recursive `subCalls`, the session `cwd`, and the owner's callbacks for opening files and inspecting calls. It recursively walks the standard call blocks and sends the root and children at every depth through the same atomic dispatch path, without subscribing to a separate parent-to-children map. Each root and child wrapper preserves the `data-chat-anchor-key="call:<id>"` and `data-chat-call-id` DOM contract used for paging and selection.
61
61
 
62
- ### Details and cards
62
+ ### Cards
63
63
 
64
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, and Session path facts. Diff, read, search, and web models also reject Code Dispatch children; terminal eligibility does not depend on `parentCallId`. Unsupported or malformed inputs use flattened Tool result text. Card-specific limits and fallback rules for the terminal, diff, read, search, and web cards remain in [the ui-primitives README](../ui-primitives/README.md); the image card's model in this package carries its own fallback rules.
65
+ Every card is read in place in the call tree; there is no second, full-height presentation of a selected call. Row renderers share one pure card model for each terminal, read, diff, search, and web card, and the image card's gallery renders through the tool-owned `tool.call.images` slot. 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. A file-path summary opens the file through the owner's `openFile`, which the chat view routes to the right Sidebar's text preview; `inspect` opens the trajectory view. Card-specific limits and fallback rules for the terminal, diff, read, search, and web cards remain in [the ui-primitives README](../ui-primitives/README.md); the image card's model in this package carries its own fallback rules.
66
66
 
67
67
  The terminal model uses `hasSpillNotice` from the browser-safe `@deepseek-ai/dsh-spill-policy/notice` entry, not an independent UI pattern. The [spill-policy README](../../spill/spill-policy/README.md#shared-notice-ownership) owns notice formatting and recognition. This check conservatively selects generic output; matching text cannot authenticate its source, and replay leaves recorded result bytes untouched.
68
68
  </details>
package/README.zh.md CHANGED
@@ -57,12 +57,12 @@ owner 载荷为 `ToolCallOwnerProps`:`callId`、`toolName`、冻结的 `block`
57
57
 
58
58
  ### 渲染约定
59
59
 
60
- `ToolCallTree` 接收一个已经包含递归 `subCalls` 的 root `ToolCallBlock`、selection 状态、会话 `cwd`,以及用于打开文件和检查调用的 Host 回调。它递归遍历标准调用块,让 root 与任意深度的 child 经过同一条原子分发路径,不订阅独立的 parent-to-children map。每个 root 和 child 包装层都保留 `data-chat-anchor-key="call:<id>"` 与 `data-chat-call-id` DOM 约定,供分页和 selection 使用。
60
+ `ToolCallTree` 接收一个已经包含递归 `subCalls` 的 root `ToolCallBlock`、会话 `cwd`,以及属主用于打开文件和检查调用的回调。它递归遍历标准调用块,让 root 与任意深度的 child 经过同一条原子分发路径,不订阅独立的 parent-to-children map。每个 root 和 child 包装层都保留 `data-chat-anchor-key="call:<id>"` 与 `data-chat-call-id` DOM 约定,供分页和 selection 使用。
61
61
 
62
- ### 详情与卡片
62
+ ### 卡片
63
63
 
64
64
 
65
- 本包通过 `ToolDetails` 填充 `conversation.details.tool`。行 renderer Details renderer 分别为 terminal、read、diff、search 和 web 卡片复用同一个纯 card modelimage 卡片仅属于行,因为其图库经由工具自有 `tool.call.images` 槽位渲染,而 details 面板不声明该槽位。这些 model 校验原始调用参数、结果内容、失败状态、持久 metadata Session 路径事实。diff、read、search web model 还会拒绝 Code Dispatch 子调用;terminal 适用性不依赖 `parentCallId`。不受支持或格式错误的输入使用压平的工具结果文本。terminal、diff、read、search 与 web 卡片的上限与 fallback 规则仍由 [ui-primitives README](../ui-primitives/README.zh.md) 负责;image 卡片的 fallback 规则由本包内的 card model 自行承载。
65
+ 每张卡片都在调用树里就地阅读;不存在选中调用的第二个全高展示面。行 renderer terminal、read、diff、search 和 web 卡片各复用同一个纯 card modelimage 卡片的图库经由工具自有 `tool.call.images` 槽位渲染。这些 model 校验原始调用参数、结果内容、失败状态、持久 metadata、Code Dispatch `parentCallId` Session 路径事实。不受支持或格式错误的输入使用压平的工具结果文本。文件路径摘要经属主的 `openFile` 打开文件,chat 视图把它路由到右侧 Sidebar 的文本预览;`inspect` 打开轨迹视图。terminal、diff、read、search 与 web 卡片的上限与 fallback 规则仍由 [ui-primitives README](../ui-primitives/README.zh.md) 负责;image 卡片的 fallback 规则由本包内的 card model 自行承载。
66
66
 
67
67
  terminal model 使用浏览器安全入口 `@deepseek-ai/dsh-spill-policy/notice` 的 `hasSpillNotice`,而非独立的 UI 匹配规则。[spill-policy README](../../spill/spill-policy/README.zh.md#shared-notice-ownership) 负责通知的格式化与识别。该检查保守地选择通用输出;匹配文本不能认证其来源,回放也不改变已记录的结果字节。
68
68
  </details>
package/lib/client.js CHANGED
@@ -8,22 +8,26 @@ window.__ModuleLoader__.load({
8
8
  let react = require("react");
9
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
10
  //#region ../../util/workspace-path/src/index.ts
11
- /**
12
- * Browser-safe Workspace path and display helpers.
13
- * @module @deepseek-ai/dsh-util-workspace-path
14
- */
15
11
  /** Whether a path uses a Windows drive or UNC prefix. */
16
12
  function isWindowsStylePath(value) {
17
13
  return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
18
14
  }
19
15
  /**
16
+ * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
17
+ * @param path - the path to classify.
18
+ * @returns `true` for an absolute path; `false` for a Workspace-relative one.
19
+ */
20
+ function isAbsoluteWorkspacePath(path) {
21
+ return path.startsWith("/") || isWindowsStylePath(path);
22
+ }
23
+ /**
20
24
  * Resolve a Workspace-relative path into the Host-facing spelling used by path operations.
21
25
  * @param cwd - Session Workspace root, when known.
22
26
  * @param path - Absolute or Workspace-relative path.
23
27
  * @returns an absolute path when a Workspace root is available, otherwise the original path.
24
28
  */
25
29
  function resolveWorkspacePath(cwd, path) {
26
- if (path.startsWith("/") || isWindowsStylePath(path)) return path;
30
+ if (isAbsoluteWorkspacePath(path)) return path;
27
31
  if (cwd === void 0 || cwd === "") return path;
28
32
  const separator = isWindowsStylePath(cwd) && cwd.includes("\\") ? "\\" : "/";
29
33
  return `${cwd.replace(/[/\\]+$/, "")}${separator}${path.replace(/^[/\\]+/, "")}`;
@@ -294,13 +298,17 @@ window.__ModuleLoader__.load({
294
298
  }
295
299
  //#endregion
296
300
  //#region lib/types/client/tool/models/read-card-model.js
301
+ /** Whether a model-supplied argument is a 1-based line position or count: an integer of at least 1. */
302
+ function positiveInteger$1(value) {
303
+ return typeof value === "number" && Number.isInteger(value) && value >= 1;
304
+ }
297
305
  function validReadCall(block) {
298
306
  const call = parsedToolCall(block);
299
307
  if (call?.name !== "read") return false;
300
308
  const { file_path: path, offset, limit } = call.args;
301
309
  if (typeof path !== "string" || path.trim() === "") return false;
302
- if (offset !== void 0 && (typeof offset !== "number" || !Number.isInteger(offset) || offset < 1)) return false;
303
- if (limit !== void 0 && (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1)) return false;
310
+ if (offset !== void 0 && !positiveInteger$1(offset)) return false;
311
+ if (limit !== void 0 && !positiveInteger$1(limit)) return false;
304
312
  return true;
305
313
  }
306
314
  function readMeta(meta) {
@@ -331,6 +339,22 @@ window.__ModuleLoader__.load({
331
339
  };
332
340
  }
333
341
  /**
342
+ * The line one `read` call was about, from its arguments.
343
+ *
344
+ * `offset` is the read tool's own 1-based start line, so opening the path can
345
+ * land where the model looked. Available while the call is still running,
346
+ * unlike the persisted metadata, because the arguments carry it. The arguments
347
+ * are model-produced JSON: only an integer of at least 1 is a line, and a call
348
+ * whose `offset` is anything else names none.
349
+ * @param block - running or settled Tool block.
350
+ * @returns the 1-based line, or undefined when the call named none.
351
+ */
352
+ function readCallLine(block) {
353
+ if (!validReadCall(block)) return void 0;
354
+ const { offset } = parsedToolCall(block)?.args ?? {};
355
+ return positiveInteger$1(offset) ? offset : void 0;
356
+ }
357
+ /**
334
358
  * Derive a settled root read card after validating its persisted metadata and
335
359
  * model-facing read envelope.
336
360
  * @param block - running or settled Tool block.
@@ -1054,13 +1078,13 @@ window.__ModuleLoader__.load({
1054
1078
  }
1055
1079
  //#endregion
1056
1080
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/AskQuestionCard.module.css.mjs
1057
- const css$4 = ".fsXYAq_card{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);border-radius:12px;flex-direction:column;gap:16px;max-height:360px;margin:4px 0 4px 4px;padding:16px 20px;display:flex;overflow-y:auto}.fsXYAq_item{flex-direction:column;gap:2px;min-width:0;display:flex}.fsXYAq_question,.fsXYAq_answer{white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));margin:0}.fsXYAq_question{color:var(--dsw-alias-label-tertiary)}.fsXYAq_answer{color:var(--dsw-alias-label-primary)}.fsXYAq_answerLine{display:block}.fsXYAq_skipped{color:var(--dsw-alias-label-tertiary)}.fsXYAq_verdict{color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));margin:0}.fsXYAq_questionList{flex-direction:column;gap:8px;margin:0;padding-left:20px;display:flex}.fsXYAq_unansweredQuestion{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px))}";
1058
- const tagId$4 = "@deepseek-ai/dsh-client-ui-tool/AskQuestionCard.module.css";
1059
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
1081
+ const css$3 = ".fsXYAq_card{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);border-radius:12px;flex-direction:column;gap:16px;max-height:360px;margin:4px 0 4px 4px;padding:16px 20px;display:flex;overflow-y:auto}.fsXYAq_item{flex-direction:column;gap:2px;min-width:0;display:flex}.fsXYAq_question,.fsXYAq_answer{white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));margin:0}.fsXYAq_question{color:var(--dsw-alias-label-tertiary)}.fsXYAq_answer{color:var(--dsw-alias-label-primary)}.fsXYAq_answerLine{display:block}.fsXYAq_skipped{color:var(--dsw-alias-label-tertiary)}.fsXYAq_verdict{color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));margin:0}.fsXYAq_questionList{flex-direction:column;gap:8px;margin:0;padding-left:20px;display:flex}.fsXYAq_unansweredQuestion{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px))}";
1082
+ const tagId$3 = "@deepseek-ai/dsh-client-ui-tool/AskQuestionCard.module.css";
1083
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1060
1084
  const tag = document.createElement("style");
1061
1085
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1062
- tag.dataset.pluginCss = tagId$4;
1063
- tag.textContent = css$4;
1086
+ tag.dataset.pluginCss = tagId$3;
1087
+ tag.textContent = css$3;
1064
1088
  document.head.appendChild(tag);
1065
1089
  }
1066
1090
  var AskQuestionCard_module_css_default = {
@@ -1117,13 +1141,13 @@ window.__ModuleLoader__.load({
1117
1141
  }
1118
1142
  //#endregion
1119
1143
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css.mjs
1120
- const css$3 = ".o3BgMG_root{flex-direction:column;display:flex}.o3BgMG_row{position:relative;overflow:hidden}.o3BgMG_root[data-state=running] .o3BgMG_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 o3BgMG_dsh-tool-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes o3BgMG_dsh-tool-row-sweep{0%{left:-300px}90%,to{left:100%}}.o3BgMG_leading{flex-shrink:0}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_leading,.o3BgMG_root[data-tool^=cordis_] .o3BgMG_title{color:var(--dsw-alias-state-business-primary)}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_title{font-weight:500}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_sep{background:var(--dsw-alias-state-business-primary)}.o3BgMG_chevron{color:var(--dsw-alias-label-secondary)}.o3BgMG_title{font-weight:400}.o3BgMG_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.o3BgMG_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}.o3BgMG_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}.o3BgMG_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)}.o3BgMG_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}.o3BgMG_fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}.o3BgMG_errorSummary{color:var(--dsw-alias-state-error-primary)}.o3BgMG_bodyWrap{flex-direction:column;display:flex}.o3BgMG_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}.o3BgMG_root:hover .o3BgMG_inspectButton,.o3BgMG_inspectButton:focus-visible{opacity:1}.o3BgMG_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.o3BgMG_bodyScroll{max-height:260px;overflow-y:auto}.o3BgMG_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}.o3BgMG_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.o3BgMG_ioSection::-webkit-scrollbar-thumb{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.o3BgMG_ioSection::-webkit-scrollbar-track{margin:6px 0}.o3BgMG_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.o3BgMG_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:.5px}.o3BgMG_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.o3BgMG_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.o3BgMG_codeBody,.o3BgMG_terminalBody,.o3BgMG_diffBody,.o3BgMG_readBody,.o3BgMG_imageBody,.o3BgMG_searchBody,.o3BgMG_webBody{margin:4px 0 4px 4px}.o3BgMG_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}.o3BgMG_imageLabel{overflow-wrap:anywhere;font:var(--dsw-font-sm-13);color:var(--dsw-alias-label-secondary);margin-bottom:4px}.o3BgMG_imageMeta{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary)}.o3BgMG_codeBody{--dsl-code-block-content-font:var(--dsw-font-markdown-code-block-small)}.o3BgMG_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)}.o3BgMG_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
1121
- const tagId$3 = "@deepseek-ai/dsh-client-ui-tool/ToolRow.module.css";
1122
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1144
+ const css$2 = ".o3BgMG_root{flex-direction:column;display:flex}.o3BgMG_row{position:relative;overflow:hidden}.o3BgMG_root[data-state=running] .o3BgMG_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 o3BgMG_dsh-tool-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes o3BgMG_dsh-tool-row-sweep{0%{left:-300px}90%,to{left:100%}}.o3BgMG_leading{flex-shrink:0}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_leading,.o3BgMG_root[data-tool^=cordis_] .o3BgMG_title{color:var(--dsw-alias-state-business-primary)}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_title{font-weight:500}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_sep{background:var(--dsw-alias-state-business-primary)}.o3BgMG_chevron{color:var(--dsw-alias-label-secondary)}.o3BgMG_title{font-weight:400}.o3BgMG_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.o3BgMG_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}.o3BgMG_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}.o3BgMG_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)}.o3BgMG_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}.o3BgMG_fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}.o3BgMG_errorSummary{color:var(--dsw-alias-state-error-primary)}.o3BgMG_bodyWrap{flex-direction:column;display:flex}.o3BgMG_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}.o3BgMG_root:hover .o3BgMG_inspectButton,.o3BgMG_inspectButton:focus-visible{opacity:1}.o3BgMG_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.o3BgMG_bodyScroll{max-height:260px;overflow-y:auto}.o3BgMG_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}.o3BgMG_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.o3BgMG_ioSection::-webkit-scrollbar-thumb{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.o3BgMG_ioSection::-webkit-scrollbar-track{margin:6px 0}.o3BgMG_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.o3BgMG_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:.5px}.o3BgMG_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.o3BgMG_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.o3BgMG_codeBody,.o3BgMG_terminalBody,.o3BgMG_diffBody,.o3BgMG_readBody,.o3BgMG_imageBody,.o3BgMG_searchBody,.o3BgMG_webBody{margin:4px 0 4px 4px}.o3BgMG_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}.o3BgMG_imageLabel{overflow-wrap:anywhere;font:var(--dsw-font-sm-13);color:var(--dsw-alias-label-secondary);margin-bottom:4px}.o3BgMG_imageMeta{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary)}.o3BgMG_codeBody{--dsl-code-block-content-font:var(--dsw-font-markdown-code-block-small)}.o3BgMG_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)}.o3BgMG_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
1145
+ const tagId$2 = "@deepseek-ai/dsh-client-ui-tool/ToolRow.module.css";
1146
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1123
1147
  const tag = document.createElement("style");
1124
1148
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1125
- tag.dataset.pluginCss = tagId$3;
1126
- tag.textContent = css$3;
1149
+ tag.dataset.pluginCss = tagId$2;
1150
+ tag.textContent = css$2;
1127
1151
  document.head.appendChild(tag);
1128
1152
  }
1129
1153
  var ToolRow_module_css_default = {
@@ -1180,7 +1204,7 @@ window.__ModuleLoader__.load({
1180
1204
  default: return null;
1181
1205
  }
1182
1206
  }
1183
- 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 }) {
1207
+ function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, image, renderSlot, loadImage, search, web, state, filePath, filePathLine, onOpenFile, inspect }) {
1184
1208
  const [expanded, setExpanded] = (0, react.useState)(false);
1185
1209
  const terminalLabels = (0, react.useMemo)(() => terminalBlockLabels(t), [t]);
1186
1210
  const diffLabels = (0, react.useMemo)(() => diffBlockLabels(t), [t]);
@@ -1219,7 +1243,9 @@ window.__ModuleLoader__.load({
1219
1243
  };
1220
1244
  const openFile = (event) => {
1221
1245
  event.stopPropagation();
1222
- if (filePath !== void 0) onOpenFile?.(filePath);
1246
+ if (filePath === void 0 || onOpenFile === void 0) return;
1247
+ if (filePathLine === void 0) onOpenFile(filePath);
1248
+ else onOpenFile(filePath, { line: filePathLine });
1223
1249
  };
1224
1250
  const fileLinkKeyDown = (event) => {
1225
1251
  if (event.key === "Enter" || event.key === " ") event.stopPropagation();
@@ -1403,13 +1429,13 @@ window.__ModuleLoader__.load({
1403
1429
  }
1404
1430
  //#endregion
1405
1431
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/ToolCallTree.module.css.mjs
1406
- const css$2 = ".ztWv_q_callRow{border-radius:6px}.ztWv_q_subCalls{border-left:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:4px;margin:4px 0 2px 22px;padding-left:8px;display:flex}";
1407
- const tagId$2 = "@deepseek-ai/dsh-client-ui-tool/ToolCallTree.module.css";
1408
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1432
+ const css$1 = ".ztWv_q_callRow{border-radius:6px}.ztWv_q_subCalls{border-left:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:4px;margin:4px 0 2px 22px;padding-left:8px;display:flex}";
1433
+ const tagId$1 = "@deepseek-ai/dsh-client-ui-tool/ToolCallTree.module.css";
1434
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1409
1435
  const tag = document.createElement("style");
1410
1436
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1411
- tag.dataset.pluginCss = tagId$2;
1412
- tag.textContent = css$2;
1437
+ tag.dataset.pluginCss = tagId$1;
1438
+ tag.textContent = css$1;
1413
1439
  document.head.appendChild(tag);
1414
1440
  }
1415
1441
  var ToolCallTree_module_css_default = {
@@ -1424,7 +1450,7 @@ window.__ModuleLoader__.load({
1424
1450
  return "kind" in node ? node.call?.name ?? "" : node.name;
1425
1451
  }
1426
1452
  /** One atomic call dispatched through the Tool-owned keyed slot. */
1427
- const ToolCall = (0, react.memo)(function ToolCall({ renderSlot, callId, toolName, block, openFile, selected, cwd, home, inspectCall, loadImage, t, children }) {
1453
+ const ToolCall = (0, react.memo)(function ToolCall({ renderSlot, callId, toolName, block, openFile, cwd, home, inspectCall, loadImage, t, children }) {
1428
1454
  const owner = (0, react.useMemo)(() => ({
1429
1455
  callId,
1430
1456
  toolName,
@@ -1450,7 +1476,6 @@ window.__ModuleLoader__.load({
1450
1476
  className: ToolCallTree_module_css_default.callRow,
1451
1477
  "data-chat-anchor-key": `call:${callId}`,
1452
1478
  "data-chat-call-id": callId,
1453
- "data-selected": selected || void 0,
1454
1479
  children: [renderSlot("tool.call.toolview", owner, {
1455
1480
  entryKey: toolName,
1456
1481
  fallback: (0, react_jsx_runtime.jsx)(GenericToolCard, {
@@ -1460,14 +1485,13 @@ window.__ModuleLoader__.load({
1460
1485
  }), children]
1461
1486
  });
1462
1487
  });
1463
- const ToolCallBranch = (0, react.memo)(function ToolCallBranch({ renderSlot, block, selectedCallId, cwd, home, openFile, inspectCall, loadImage, t }) {
1488
+ const ToolCallBranch = (0, react.memo)(function ToolCallBranch({ renderSlot, block, cwd, home, openFile, inspectCall, loadImage, t }) {
1464
1489
  return (0, react_jsx_runtime.jsx)(ToolCall, {
1465
1490
  renderSlot,
1466
1491
  callId: block.callId,
1467
1492
  toolName: callName(block),
1468
1493
  block,
1469
1494
  openFile,
1470
- selected: block.callId === selectedCallId,
1471
1495
  cwd,
1472
1496
  home,
1473
1497
  inspectCall,
@@ -1479,7 +1503,6 @@ window.__ModuleLoader__.load({
1479
1503
  children: block.subCalls.map((child) => (0, react_jsx_runtime.jsx)(ToolCallBranch, {
1480
1504
  renderSlot,
1481
1505
  block: child,
1482
- selectedCallId,
1483
1506
  cwd,
1484
1507
  home,
1485
1508
  openFile,
@@ -1496,13 +1519,12 @@ window.__ModuleLoader__.load({
1496
1519
  * @param props - whole-Tool owner data and the Tool-owned child-slot share.
1497
1520
  * @returns the Tool call tree.
1498
1521
  */
1499
- function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, loadImage, useHostInfo, t }) {
1522
+ function ToolCallTree({ renderSlot, node, cwd, openFile, inspectCall, loadImage, useHostInfo, t }) {
1500
1523
  const home = useHostInfo((info) => info.home);
1501
1524
  const block = node.data.root;
1502
1525
  return (0, react_jsx_runtime.jsx)(ToolCallBranch, {
1503
1526
  renderSlot,
1504
1527
  block,
1505
- selectedCallId,
1506
1528
  cwd,
1507
1529
  home,
1508
1530
  openFile,
@@ -1512,92 +1534,6 @@ window.__ModuleLoader__.load({
1512
1534
  });
1513
1535
  }
1514
1536
  //#endregion
1515
- //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/ToolDetails.module.css.mjs
1516
- const css$1 = ".xDAfVq_description{color:var(--dsw-alias-label-secondary);font:var(--dsw-font-xs-13);margin:0 0 6px}.xDAfVq_cardBody{margin:0}.xDAfVq_recovery{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-xs-13);margin:6px 0 0}.xDAfVq_code{background:var(--dsw-alias-markdown-code-block);font-family:var(--ds-font-family-code);color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;border-radius:12px;margin:0;padding:16px;font-size:13px;line-height:22px}.xDAfVq_code[data-error]{color:var(--dsw-alias-state-error-primary)}.xDAfVq_read,.xDAfVq_web{margin:0}.xDAfVq_empty{color:var(--dsw-alias-label-tertiary);padding:8px 0;font-size:13px;line-height:20px}";
1517
- const tagId$1 = "@deepseek-ai/dsh-client-ui-tool/ToolDetails.module.css";
1518
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1519
- const tag = document.createElement("style");
1520
- tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1521
- tag.dataset.pluginCss = tagId$1;
1522
- tag.textContent = css$1;
1523
- document.head.appendChild(tag);
1524
- }
1525
- var ToolDetails_module_css_default = {
1526
- "cardBody": "xDAfVq_cardBody",
1527
- "code": "xDAfVq_code",
1528
- "description": "xDAfVq_description",
1529
- "empty": "xDAfVq_empty",
1530
- "read": "xDAfVq_read",
1531
- "recovery": "xDAfVq_recovery",
1532
- "web": "xDAfVq_web"
1533
- };
1534
- //#endregion
1535
- //#region lib/types/client/tool/ToolDetails.js
1536
- /** Card-aware output body for the selected Tool call in details. */
1537
- /**
1538
- * Render the selected Tool call's structured output when its raw fields form a
1539
- * supported card, otherwise preserve the flattened result text.
1540
- * @param props - selected call slice, workspace root, host home, and locale seat.
1541
- * @returns the details output body.
1542
- */
1543
- function ToolDetails({ block, cwd, useHostInfo, t }) {
1544
- const home = useHostInfo((info) => info.home);
1545
- const terminalModel = terminalCardModel(block, cwd);
1546
- if (terminalModel !== null) {
1547
- const terminal = localizeTerminalCardModel(terminalModel, t);
1548
- return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [terminal.description !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
1549
- className: ToolDetails_module_css_default.description,
1550
- children: terminal.description
1551
- }) : null, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
1552
- ...terminal.card,
1553
- labels: terminalBlockLabels(t),
1554
- className: ToolDetails_module_css_default.cardBody
1555
- })] });
1556
- }
1557
- const read = readCardModel(block, cwd, home);
1558
- if (read !== null) return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ReadBlock, {
1559
- ...read,
1560
- labels: readBlockLabels(t),
1561
- className: ToolDetails_module_css_default.read
1562
- });
1563
- const diff = diffCardModel(block);
1564
- if (diff !== null) return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DiffBlock, {
1565
- ...diff.card,
1566
- labels: diffBlockLabels(t),
1567
- className: ToolDetails_module_css_default.cardBody
1568
- });
1569
- const search = searchCardModel(block);
1570
- if (search !== null) return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SearchBlock, {
1571
- ...search.card,
1572
- labels: searchBlockLabels(t),
1573
- className: ToolDetails_module_css_default.cardBody
1574
- }), search.recovery !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
1575
- className: ToolDetails_module_css_default.recovery,
1576
- children: search.recovery
1577
- }) : null] });
1578
- const web = webCardModel(block);
1579
- if (web !== null) {
1580
- const body = "kind" in block ? resultText(block) : "";
1581
- return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.WebBlock, {
1582
- ...web,
1583
- labels: webBlockLabels(t),
1584
- className: ToolDetails_module_css_default.web
1585
- }), body !== "" ? (0, react_jsx_runtime.jsx)("pre", {
1586
- className: ToolDetails_module_css_default.code,
1587
- children: body
1588
- }) : null] });
1589
- }
1590
- if (!("kind" in block)) return (0, react_jsx_runtime.jsx)("div", {
1591
- className: ToolDetails_module_css_default.empty,
1592
- children: t("details.running")
1593
- });
1594
- return (0, react_jsx_runtime.jsx)("pre", {
1595
- className: ToolDetails_module_css_default.code,
1596
- "data-error": block.isError || void 0,
1597
- children: resultText(block)
1598
- });
1599
- }
1600
- //#endregion
1601
1537
  //#region lib/types/client/locale.js
1602
1538
  /** Locale namespace supplied by the conversation owner to Tool renderers. */
1603
1539
  const CONVERSATION_NS = "conversation";
@@ -2003,11 +1939,15 @@ window.__ModuleLoader__.load({
2003
1939
  //#endregion
2004
1940
  //#region lib/types/client/tool/toolviews/read-row.js
2005
1941
  /**
2006
- * Lets users expand a completed read result and open its reported path.
1942
+ * Lets users expand a completed read result and open its reported path at the
1943
+ * line the call started from.
2007
1944
  */
2008
1945
  function ReadRow(props) {
2009
1946
  const { block, cwd, home } = props;
2010
- return readFamilyRow(props, { read: readCardModel(block, cwd, home) });
1947
+ return readFamilyRow(props, {
1948
+ read: readCardModel(block, cwd, home),
1949
+ filePathLine: readCallLine(block)
1950
+ });
2011
1951
  }
2012
1952
  /** Registers the read tool's conversation row. */
2013
1953
  const readToolview = {
@@ -2435,11 +2375,6 @@ window.__ModuleLoader__.load({
2435
2375
  } },
2436
2376
  inject: toolInject
2437
2377
  }, ToolCallTree));
2438
- ctx.slots.inject("conversation.details.tool", () => ctx.slots.register({
2439
- name: "conversation.details.tool",
2440
- locale: CONVERSATION_NS,
2441
- inject: toolInject
2442
- }, ToolDetails));
2443
2378
  ctx.plugin(bashToolviewSample);
2444
2379
  ctx.plugin(readToolview);
2445
2380
  ctx.plugin(readImageToolview);
@@ -1,7 +1,7 @@
1
1
  /** Tool UI slot declarations and their composed component props. */
2
2
  import type { HostObservable, InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
3
  import type { RemoteHostFacts } from '@deepseek-ai/dsh-api-remotes/client';
4
- import type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-chat/client';
4
+ import type { OpenFileOptions, ToolCallBlock } from '@deepseek-ai/dsh-client-ui-chat/client';
5
5
  import type { MessageImageLoader, MessageImageSource } from '@deepseek-ai/dsh-client-ui-conversation/client';
6
6
  declare module '@deepseek-ai/dsh-client-ui-slots' {
7
7
  interface SlotMap {
@@ -65,8 +65,11 @@ export interface ToolCallOwnerProps {
65
65
  cwd?: string | undefined;
66
66
  /** Host account home; POSIX home-rooted summaries display as `~`. */
67
67
  home?: string | undefined;
68
- /** Open a Tool argument path through the Host. */
69
- openFile: (path: string) => void;
68
+ /**
69
+ * Open a Tool argument path. A view that knows which line the call was about
70
+ * passes it, and the opened surface lands there.
71
+ */
72
+ openFile: (path: string, options?: OpenFileOptions) => void;
70
73
  /**
71
74
  * Session-authorized image loader for the `tool.call.images` slot, supplied
72
75
  * by the chat node that owns this call. A composed chat node always
@@ -94,6 +97,4 @@ export type ToolHostInfoInjected = {
94
97
  };
95
98
  /** Full props of the Tool call-tree renderer registered as a `tool-call` Chat Node. */
96
99
  export type ToolTreeProps = PropsRuntime<'conversation.chat.node', 'tool-call'> & PropsRenderSlots<'tool.call.toolview'> & PropsLocale<'conversation'> & InjectFace<ToolHostInfoInjected>;
97
- /** Full props of the selected Tool output renderer in the details panel. */
98
- export type ToolDetailsProps = PropsRuntime<'conversation.details.tool'> & PropsLocale<'conversation'> & InjectFace<ToolHostInfoInjected>;
99
100
  //# sourceMappingURL=slots.d.ts.map
@@ -1,4 +1,4 @@
1
1
  /** Browser Tool plugin: whole-call composition and keyed atomic Tool views. */
2
2
  export { apply, inject } from './apply.ts';
3
- export type { ToolCallOwnerProps, ToolCallViewProps, ToolDetailsProps, ToolHostInfoInjected, ToolTreeProps, } from './contract/slots.ts';
3
+ export type { ToolCallOwnerProps, ToolCallViewProps, ToolHostInfoInjected, ToolTreeProps, } from './contract/slots.ts';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -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, loadImage, useHostInfo, t, }: ToolTreeProps): import("react").JSX.Element;
8
+ export declare function ToolCallTree({ renderSlot, node, cwd, openFile, inspectCall, loadImage, useHostInfo, t, }: ToolTreeProps): import("react").JSX.Element;
9
9
  //# sourceMappingURL=ToolCallTree.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { PropsRenderSlots, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
3
+ import type { OpenFileOptions } from '@deepseek-ai/dsh-client-ui-chat/client';
3
4
  import type { MessageImageLoader } from '@deepseek-ai/dsh-client-ui-conversation/client';
4
5
  import { type DiffCardModel } from '../models/diff-card-model.ts';
5
6
  import { type ReadCardModel } from '../models/read-card-model.ts';
@@ -60,13 +61,15 @@ export interface ToolRowProps {
60
61
  * renders as a hover-underline link that opens the host default app.
61
62
  */
62
63
  filePath?: string | undefined;
63
- /** Open the path with the host OS default application (already cwd-resolved). */
64
- onOpenFile?: ((path: string) => void) | undefined;
64
+ /** 1-based line the call was about; absent = open the file at its beginning. */
65
+ filePathLine?: number | undefined;
66
+ /** Open the path (already cwd-resolved), landing on `filePathLine` when given. */
67
+ onOpenFile?: ((path: string, options?: OpenFileOptions) => void) | undefined;
65
68
  /**
66
69
  * Jump to this call in the trajectory view: a hover-revealed Inspect pill
67
70
  * over the expanded body. Absent = no affordance.
68
71
  */
69
72
  inspect?: (() => void) | undefined;
70
73
  }
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;
74
+ 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, filePathLine, onOpenFile, inspect, }: ToolRowProps): import("react").JSX.Element;
72
75
  //# sourceMappingURL=ToolRow.d.ts.map
@@ -18,6 +18,18 @@ export declare const CHAT_READ_MAX_LINES = 8;
18
18
  * site.
19
19
  */
20
20
  export type ReadCardModel = Pick<ReadBlockProps, 'label' | 'lines' | 'totalLines' | 'lang'>;
21
+ /**
22
+ * The line one `read` call was about, from its arguments.
23
+ *
24
+ * `offset` is the read tool's own 1-based start line, so opening the path can
25
+ * land where the model looked. Available while the call is still running,
26
+ * unlike the persisted metadata, because the arguments carry it. The arguments
27
+ * are model-produced JSON: only an integer of at least 1 is a line, and a call
28
+ * whose `offset` is anything else names none.
29
+ * @param block - running or settled Tool block.
30
+ * @returns the 1-based line, or undefined when the call named none.
31
+ */
32
+ export declare function readCallLine(block: ToolCallBlock): number | undefined;
21
33
  /**
22
34
  * Derive a settled root read card after validating its persisted metadata and
23
35
  * model-facing read envelope.
@@ -10,10 +10,11 @@ export type ReadFamilyRowProps = ToolCallViewProps & {
10
10
  export type ReadImageRowProps = ReadFamilyRowProps & PropsRenderSlots<'tool.call.images'>;
11
11
  /**
12
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.
13
+ * props that row owns. `read` supplies `read` and the line its call named;
14
+ * `read_image` supplies `image` together with the slot dispatcher and loader
15
+ * that draw it.
15
16
  */
16
- export type ReadFamilyCard = Pick<ToolRowProps, 'read' | 'image' | 'renderSlot' | 'loadImage'>;
17
+ export type ReadFamilyCard = Pick<ToolRowProps, 'read' | 'image' | 'renderSlot' | 'loadImage' | 'filePathLine'>;
17
18
  /**
18
19
  * Compose a read-family row: the shared chrome and model-derived fields, plus the
19
20
  * caller's card material.
@@ -3,7 +3,8 @@ import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
3
3
  import type { ToolCallViewProps } from '../../contract/slots.ts';
4
4
  type ReadRowProps = ToolCallViewProps & PropsLocale<'conversation'>;
5
5
  /**
6
- * Lets users expand a completed read result and open its reported path.
6
+ * Lets users expand a completed read result and open its reported path at the
7
+ * line the call started from.
7
8
  */
8
9
  export declare function ReadRow(props: ReadRowProps): import("react").ReactNode;
9
10
  /** Registers the read tool's conversation row. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-tool",
3
3
  "description": "Client Tool call-tree renderer and keyed per-tool presentation slot",
4
- "version": "0.1.3-alpha.2",
4
+ "version": "0.1.5-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -48,28 +48,28 @@
48
48
  "@types/react": "~18.3.1",
49
49
  "react": "^18.2.0",
50
50
  "react-dom": "^18.2.0",
51
- "@deepseek-ai/dsh-spill-policy": "^0.1.3-alpha.2",
52
- "@deepseek-ai/dsh-agent": "^0.1.3-alpha.2",
53
- "@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
54
- "@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
55
- "@deepseek-ai/dsh-system-prompt": "^0.1.3-alpha.2",
56
- "@deepseek-ai/dsh-tools": "^0.1.3-alpha.2",
57
- "@deepseek-ai/dsh-spill": "^0.1.3-alpha.2",
58
- "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.3-alpha.2",
59
- "@deepseek-ai/cordis": "^4.0.2",
60
- "@deepseek-ai/dsh-api-remotes": "^0.1.3-alpha.2",
61
- "@deepseek-ai/dsh-client-connection": "^0.1.3-alpha.2",
62
- "@deepseek-ai/dsh-client-locale": "^0.1.3-alpha.2",
63
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.3-alpha.2",
64
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.3-alpha.2",
65
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.3-alpha.2",
66
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.3-alpha.2",
67
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.3-alpha.2",
68
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.3-alpha.2",
69
- "@deepseek-ai/dsh-util-workspace-path": "^0.1.3-alpha.2",
70
- "@deepseek-ai/dsh-attachment": "^0.1.3-alpha.2",
71
- "@deepseek-ai/dsh-client-ui-session": "^0.1.3-alpha.2",
72
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.3-alpha.2"
51
+ "@deepseek-ai/dsh-spill-policy": "^0.1.5-alpha.1",
52
+ "@deepseek-ai/dsh-agent": "^0.1.5-alpha.1",
53
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.1",
54
+ "@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.1",
55
+ "@deepseek-ai/dsh-tools": "^0.1.5-alpha.1",
56
+ "@deepseek-ai/dsh-spill": "^0.1.5-alpha.1",
57
+ "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.5-alpha.1",
58
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.1",
59
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-alpha.1",
60
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-alpha.1",
61
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.1",
62
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.5-alpha.1",
63
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-alpha.1",
64
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.1",
65
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.1",
66
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-alpha.1",
67
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-alpha.1",
68
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.1",
69
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-alpha.1",
70
+ "@deepseek-ai/dsh-util-workspace-path": "^0.1.5-alpha.1",
71
+ "@deepseek-ai/dsh-attachment": "^0.1.5-alpha.1",
72
+ "@deepseek-ai/cordis": "^4.0.2"
73
73
  },
74
74
  "files": [
75
75
  "lib/index.js",
@@ -1,9 +0,0 @@
1
- import type { ToolDetailsProps } from '../contract/slots.ts';
2
- /**
3
- * Render the selected Tool call's structured output when its raw fields form a
4
- * supported card, otherwise preserve the flattened result text.
5
- * @param props - selected call slice, workspace root, host home, and locale seat.
6
- * @returns the details output body.
7
- */
8
- export declare function ToolDetails({ block, cwd, useHostInfo, t, }: Pick<ToolDetailsProps, 'block' | 'cwd' | 'useHostInfo' | 't'>): import("react").JSX.Element;
9
- //# sourceMappingURL=ToolDetails.d.ts.map