@deepseek-ai/dsh-client-ui-chat 0.1.3-alpha.2 → 0.1.5-alpha.2

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/lib/client.js CHANGED
@@ -9,6 +9,28 @@ window.__ModuleLoader__.load({
9
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
10
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
11
11
  let react_dom = require("react-dom");
12
+ //#region ../../util/workspace-path/src/file-address.ts
13
+ /** The scheme and type every file address opens with. */
14
+ const FILE_ADDRESS_PREFIX = "dsh-resource://file/";
15
+ /** Component-encode one id or path segment, keeping `:` literal for drive letters. */
16
+ function encodeSegment(segment) {
17
+ return encodeURIComponent(segment).replace(/%3A/gi, ":");
18
+ }
19
+ /** Encode a `/`-separated path segment by segment. */
20
+ function encodePath(path) {
21
+ return path.split("/").map(encodeSegment).join("/");
22
+ }
23
+ /**
24
+ * Build the address of a file read through one Session.
25
+ * @param sessionId - the Session whose Host workspace resolves the path.
26
+ * @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
27
+ * @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
28
+ */
29
+ function sessionFileAddress(sessionId, path) {
30
+ const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
31
+ return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
32
+ }
33
+ //#endregion
12
34
  //#region ../../util/workspace-path/src/index.ts
13
35
  /**
14
36
  * Browser-safe Workspace path and display helpers.
@@ -19,16 +41,30 @@ window.__ModuleLoader__.load({
19
41
  return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
20
42
  }
21
43
  /**
22
- * Resolve a Workspace-relative path into the Host-facing spelling used by path operations.
23
- * @param cwd - Session Workspace root, when known.
24
- * @param path - Absolute or Workspace-relative path.
25
- * @returns an absolute path when a Workspace root is available, otherwise the original path.
44
+ * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
45
+ * @param path - the path to classify.
46
+ * @returns `true` for an absolute path; `false` for a Workspace-relative one.
47
+ */
48
+ function isAbsoluteWorkspacePath(path) {
49
+ return path.startsWith("/") || isWindowsStylePath(path);
50
+ }
51
+ /**
52
+ * The address for a path as a caller holds it: a relative path, or an absolute
53
+ * path inside the Session's workspace, becomes a `session`-scoped address; an
54
+ * absolute path outside it, or one whose workspace root is unknown, keeps its
55
+ * absolute path in that Session's address.
56
+ * @param sessionId - the Session the path is read in.
57
+ * @param cwd - that Session's workspace root, when known.
58
+ * @param path - absolute or workspace-relative path, in either separator spelling.
59
+ * @returns the `dsh-resource://file/…` address.
26
60
  */
27
- function resolveWorkspacePath(cwd, path) {
28
- if (path.startsWith("/") || isWindowsStylePath(path)) return path;
29
- if (cwd === void 0 || cwd === "") return path;
30
- const separator = isWindowsStylePath(cwd) && cwd.includes("\\") ? "\\" : "/";
31
- return `${cwd.replace(/[/\\]+$/, "")}${separator}${path.replace(/^[/\\]+/, "")}`;
61
+ function fileAddressFor(sessionId, cwd, path) {
62
+ const normalized = path.replace(/\\/g, "/");
63
+ if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized);
64
+ const root = cwd === void 0 ? "" : cwd.replace(/\\/g, "/").replace(/\/+$/, "");
65
+ if (root !== "" && normalized === root) return sessionFileAddress(sessionId, "");
66
+ if (root !== "" && normalized.startsWith(`${root}/`)) return sessionFileAddress(sessionId, normalized.slice(root.length + 1));
67
+ return sessionFileAddress(sessionId, normalized);
32
68
  }
33
69
  //#endregion
34
70
  //#region lib/types/client/contract/snapshot.js
@@ -116,7 +152,7 @@ window.__ModuleLoader__.load({
116
152
  }
117
153
  //#endregion
118
154
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/MessageItem.module.css.mjs
119
- const css$15 = ".Sixlwa_userRow{flex-direction:column;align-items:flex-end;gap:6px;display:flex}.Sixlwa_userStack{min-width:0;max-width:min(calc(var(--dsh-chat-content-width,748px) * .702), 82%);flex-direction:column;align-items:flex-end;gap:8px;display:flex}.Sixlwa_bubble{background:var(--dsw-specific-bubble);max-width:100%;font-size:var(--dsh-content-font-size,14px);line-height:calc(22px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;border-radius:22px;padding:10px 16px}.Sixlwa_referenceSummary{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px))}.Sixlwa_contextRow,.Sixlwa_compactionRow{padding:2px 0}.Sixlwa_compactionButton{width:100%;height:calc(24px + var(--dsh-content-font-delta,0px));min-width:0;color:inherit;font:inherit;text-align:left;background:0 0;border:none;border-radius:6px;align-items:center;padding:0;display:flex}.Sixlwa_compactionButton:not(:disabled){cursor:pointer}.Sixlwa_compactionButton:not(:disabled):hover{background:var(--dsw-alias-interactive-bg-hover)}.Sixlwa_compactionLeading{width:calc(16px + var(--dsh-content-font-delta,0px));height:calc(16px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-secondary);flex:none;place-items:center;margin-right:6px;display:inline-grid}.Sixlwa_compactionLeading svg{width:calc(14px + var(--dsh-content-font-delta,0px));height:calc(14px + var(--dsh-content-font-delta,0px))}.Sixlwa_compactionContextIcon,.Sixlwa_compactionDisclosureIcon{grid-area:1/1;justify-content:center;align-items:center;display:inline-flex}.Sixlwa_compactionDisclosureIcon,.Sixlwa_compactionButton:not(:disabled):hover .Sixlwa_compactionContextIcon,.Sixlwa_compactionButton:not(:disabled):focus-visible .Sixlwa_compactionContextIcon{opacity:0}.Sixlwa_compactionButton:not(:disabled):hover .Sixlwa_compactionDisclosureIcon,.Sixlwa_compactionButton:not(:disabled):focus-visible .Sixlwa_compactionDisclosureIcon{opacity:1}.Sixlwa_compactionTitle{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-primary-dimmed);flex:none}.Sixlwa_compactionSep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.Sixlwa_compactionSummary{min-width:0;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));text-overflow:ellipsis;white-space:nowrap;flex:auto;overflow:hidden}.Sixlwa_compactionBody{padding:4px 0 4px calc(22px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px))}.Sixlwa_retryRow{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px))}.Sixlwa_retrySummary{width:fit-content;color:inherit;cursor:pointer;user-select:none;border-radius:3px;align-items:center;gap:7px;padding:2px 0;list-style:none;display:inline-flex}.Sixlwa_retrySummary::-webkit-details-marker{display:none}.Sixlwa_retrySummary:after{content:\"\";opacity:.8;border-bottom:1.5px solid;border-right:1.5px solid;width:6px;height:6px;transition:transform .12s;transform:rotate(-45deg)}.Sixlwa_retrySummary:hover{color:var(--dsw-alias-label-secondary)}.Sixlwa_retrySummary:focus-visible{outline:1.5px solid var(--dsw-alias-button-info-fill);outline-offset:2px}.Sixlwa_retryText{color:inherit}.Sixlwa_retryRow[data-active] .Sixlwa_retryText{background:linear-gradient(90deg, var(--dsw-alias-label-tertiary) 0%, var(--dsw-alias-label-tertiary) 40%, var(--dsw-alias-label-secondary) 50%, var(--dsw-alias-label-tertiary) 60%, var(--dsw-alias-label-tertiary) 100%);color:#0000;background-position:100%;background-size:200% 100%;background-clip:text;animation:1.6s ease-in-out infinite Sixlwa_retry-shimmer}.Sixlwa_retryRow[open] .Sixlwa_retrySummary:after{transform:rotate(45deg)}.Sixlwa_retryDetails{overflow-wrap:anywhere;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px));gap:2px;margin-top:3px;padding-left:14px;display:grid}.Sixlwa_retryDetailLabel{color:var(--dsw-alias-label-secondary)}.Sixlwa_turnErrorRow{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));grid-template-columns:10px minmax(0,1fr) auto;align-items:start;gap:8px;padding:2px 0;display:grid}.Sixlwa_turnErrorDot{margin-top:5px}.Sixlwa_turnErrorCopy{overflow-wrap:anywhere;min-width:0}.Sixlwa_turnErrorTitle{color:var(--dsw-alias-state-error-primary);margin-right:6px;font-weight:600}.Sixlwa_turnErrorMessage{color:var(--dsw-alias-label-secondary)}.Sixlwa_turnErrorCode{color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-markdown-code-block-small)}.Sixlwa_maxTokensTitle{color:var(--dsw-alias-state-warn-primary);margin-right:6px;font-weight:600}@keyframes Sixlwa_retry-shimmer{0%{background-position:100%}to{background-position:0}}@media (prefers-reduced-motion:reduce){.Sixlwa_retryRow[data-active] .Sixlwa_retryText{color:inherit;background:0 0;animation:none}}.Sixlwa_attachmentRow{flex-wrap:wrap;justify-content:flex-end;gap:8px;max-width:100%;display:flex}.Sixlwa_fileCard{border:.5px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-specific-input-major,transparent);box-sizing:border-box;border-radius:16px;flex:0 0 240px;align-items:center;gap:10px;width:240px;min-height:64px;padding:8px 12px;display:inline-flex}.Sixlwa_fileIcon{flex:none;width:24px;height:28px}.Sixlwa_fileContent{flex-direction:column;flex:1;min-width:0;display:flex}.Sixlwa_fileName{white-space:nowrap;text-overflow:ellipsis;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px;overflow:hidden}.Sixlwa_fileMeta{white-space:nowrap;text-overflow:ellipsis;color:var(--dsw-alias-label-tertiary,#00000073);font-size:12px;line-height:15px;overflow:hidden}";
155
+ const css$15 = ".Sixlwa_userRow{flex-direction:column;align-items:flex-end;gap:6px;display:flex}.Sixlwa_userStack{min-width:0;max-width:min(calc(var(--dsh-chat-content-width,748px) * .702), 82%);flex-direction:column;align-items:flex-end;gap:8px;display:flex}.Sixlwa_bubble{background:var(--dsw-specific-bubble);max-width:100%;font-size:var(--dsh-content-font-size,14px);line-height:calc(22px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;border-radius:22px;padding:10px 16px}.Sixlwa_referenceSummary{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px))}.Sixlwa_contextRow,.Sixlwa_compactionRow{padding:2px 0}.Sixlwa_compactionButton{width:100%;height:calc(24px + var(--dsh-content-font-delta,0px));min-width:0;color:inherit;font:inherit;text-align:left;background:0 0;border:none;border-radius:6px;align-items:center;padding:0;display:flex}.Sixlwa_compactionButton:not(:disabled){cursor:pointer}.Sixlwa_compactionButton:not(:disabled):hover{background:var(--dsw-alias-interactive-bg-hover)}.Sixlwa_compactionLeading{width:calc(16px + var(--dsh-content-font-delta,0px));height:calc(16px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-secondary);flex:none;place-items:center;margin-right:6px;display:inline-grid}.Sixlwa_compactionLeading svg{width:calc(14px + var(--dsh-content-font-delta,0px));height:calc(14px + var(--dsh-content-font-delta,0px))}.Sixlwa_compactionContextIcon,.Sixlwa_compactionDisclosureIcon{grid-area:1/1;justify-content:center;align-items:center;display:inline-flex}.Sixlwa_compactionDisclosureIcon,.Sixlwa_compactionButton:not(:disabled):hover .Sixlwa_compactionContextIcon,.Sixlwa_compactionButton:not(:disabled):focus-visible .Sixlwa_compactionContextIcon{opacity:0}.Sixlwa_compactionButton:not(:disabled):hover .Sixlwa_compactionDisclosureIcon,.Sixlwa_compactionButton:not(:disabled):focus-visible .Sixlwa_compactionDisclosureIcon{opacity:1}.Sixlwa_compactionTitle{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-primary-dimmed);flex:none}.Sixlwa_compactionSep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.Sixlwa_compactionSummary{min-width:0;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));text-overflow:ellipsis;white-space:nowrap;flex:auto;overflow:hidden}.Sixlwa_compactionBody{padding:4px 0 4px calc(22px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px))}.Sixlwa_retryRow{color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px))}.Sixlwa_retrySummary{width:fit-content;color:inherit;cursor:pointer;user-select:none;border-radius:3px;align-items:center;gap:7px;padding:2px 0;list-style:none;display:inline-flex}.Sixlwa_retrySummary::-webkit-details-marker{display:none}.Sixlwa_retrySummary:after{content:\"\";opacity:.8;border-bottom:1.5px solid;border-right:1.5px solid;width:6px;height:6px;transition:transform .12s;transform:rotate(-45deg)}.Sixlwa_retrySummary:hover{color:var(--dsw-alias-label-secondary)}.Sixlwa_retrySummary:focus-visible{outline:1.5px solid var(--dsw-alias-button-info-fill);outline-offset:2px}.Sixlwa_retryText{color:inherit}.Sixlwa_retryRow[data-active] .Sixlwa_retryText{background:linear-gradient(90deg, var(--dsw-alias-label-tertiary) 0%, var(--dsw-alias-label-tertiary) 40%, var(--dsw-alias-label-secondary) 50%, var(--dsw-alias-label-tertiary) 60%, var(--dsw-alias-label-tertiary) 100%);color:#0000;background-position:100%;background-size:200% 100%;background-clip:text;animation:1.6s ease-in-out infinite Sixlwa_retry-shimmer}.Sixlwa_retryRow[open] .Sixlwa_retrySummary:after{transform:rotate(45deg)}.Sixlwa_retryDetails{overflow-wrap:anywhere;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(18px + var(--dsh-content-font-delta-secondary,0px));gap:2px;margin-top:3px;padding-left:14px;display:grid}.Sixlwa_retryDetailLabel{color:var(--dsw-alias-label-secondary)}.Sixlwa_turnErrorRow{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));grid-template-columns:10px minmax(0,1fr) auto;align-items:start;gap:8px;padding:2px 0;display:grid}.Sixlwa_turnErrorDot{margin-top:5px}.Sixlwa_turnErrorCopy{overflow-wrap:anywhere;min-width:0}.Sixlwa_turnErrorTitle{color:var(--dsw-alias-state-error-primary);margin-right:6px;font-weight:600}.Sixlwa_turnErrorMessage{color:var(--dsw-alias-label-secondary)}.Sixlwa_turnErrorCode{color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-markdown-code-block-small)}.Sixlwa_maxTokensTitle{color:var(--dsw-alias-state-warn-primary);margin-right:6px;font-weight:600}@keyframes Sixlwa_retry-shimmer{0%{background-position:100%}to{background-position:0}}@media (prefers-reduced-motion:reduce){.Sixlwa_retryRow[data-active] .Sixlwa_retryText{color:inherit;background:0 0;animation:none}}.Sixlwa_attachmentRow{flex-wrap:wrap;justify-content:flex-end;gap:8px;max-width:100%;display:flex}.Sixlwa_fileCard{border:.5px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-specific-input-major,transparent);box-sizing:border-box;border-radius:16px;flex:0 0 240px;align-items:center;gap:10px;width:240px;min-height:64px;padding:8px 12px;display:inline-flex}.Sixlwa_fileIcon{flex:none;width:28px;height:28px}.Sixlwa_fileContent{flex-direction:column;flex:1;min-width:0;display:flex}.Sixlwa_fileName{white-space:nowrap;text-overflow:ellipsis;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px;overflow:hidden}.Sixlwa_fileMeta{white-space:nowrap;text-overflow:ellipsis;color:var(--dsw-alias-label-tertiary,#00000073);font-size:12px;line-height:15px;overflow:hidden}";
120
156
  const tagId$15 = "@deepseek-ai/dsh-client-ui-chat/MessageItem.module.css";
121
157
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$15) + "]") === null) {
122
158
  const tag = document.createElement("style");
@@ -1061,11 +1097,6 @@ window.__ModuleLoader__.load({
1061
1097
  }
1062
1098
  //#endregion
1063
1099
  //#region lib/types/client/chat/MessageItem.js
1064
- function extensionOf(name) {
1065
- const dot = name.lastIndexOf(".");
1066
- if (dot <= 0 || dot === name.length - 1) return "";
1067
- return name.slice(dot + 1).toUpperCase().slice(0, 8);
1068
- }
1069
1100
  function contentParts(content) {
1070
1101
  const texts = [];
1071
1102
  const attachments = [];
@@ -1222,14 +1253,17 @@ window.__ModuleLoader__.load({
1222
1253
  }) }, `image:${index}`) : (0, react_jsx_runtime.jsxs)("span", {
1223
1254
  className: MessageItem_module_css_default.fileCard,
1224
1255
  title: attachment.file.name,
1225
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DocumentFileIcon, { className: MessageItem_module_css_default.fileIcon }), (0, react_jsx_runtime.jsxs)("span", {
1256
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FileTypeIcon, {
1257
+ path: attachment.file.name,
1258
+ className: MessageItem_module_css_default.fileIcon
1259
+ }), (0, react_jsx_runtime.jsxs)("span", {
1226
1260
  className: MessageItem_module_css_default.fileContent,
1227
1261
  children: [(0, react_jsx_runtime.jsx)("span", {
1228
1262
  className: MessageItem_module_css_default.fileName,
1229
1263
  children: attachment.file.name
1230
1264
  }), (0, react_jsx_runtime.jsx)("span", {
1231
1265
  className: MessageItem_module_css_default.fileMeta,
1232
- children: [extensionOf(attachment.file.name), (0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(attachment.file.bytes)].filter(Boolean).join(" ")
1266
+ children: [(0, _deepseek_ai_dsh_client_ui_primitives.fileExtension)(attachment.file.name).toUpperCase().slice(0, 8), (0, _deepseek_ai_dsh_client_ui_primitives.fileSizeText)(attachment.file.bytes)].filter(Boolean).join(" ")
1233
1267
  })]
1234
1268
  })]
1235
1269
  }, `file:${index}`))
@@ -1407,7 +1441,7 @@ window.__ModuleLoader__.load({
1407
1441
  }
1408
1442
  //#endregion
1409
1443
  //#region lib/types/client/stores.js
1410
- /** Per-Session Chat selection store shared by the transcript and details panel. */
1444
+ /** Per-Session Chat view store. */
1411
1445
  /**
1412
1446
  * Resolve the manually expanded answer for one Turn.
1413
1447
  * @param state - Chat store snapshot.
@@ -1418,33 +1452,25 @@ window.__ModuleLoader__.load({
1418
1452
  return state.turnProcesses.find((entry) => entry.turn === turn);
1419
1453
  }
1420
1454
  /**
1421
- * Create the Chat selection store handle.
1455
+ * Create the Chat view store handle.
1422
1456
  * @returns a handle instantiated once per rendered Session scope.
1423
1457
  */
1424
1458
  function createChatStore() {
1425
1459
  return (0, _deepseek_ai_dsh_client_store.defineStore)({
1426
- init: () => ({
1427
- selection: null,
1428
- turnProcesses: []
1429
- }),
1430
- actions: {
1431
- select: (draft, target) => {
1432
- draft.selection = target;
1433
- },
1434
- setTurnProcessOpen: (draft, turn, answerStep, open) => {
1435
- const index = draft.turnProcesses.findIndex((entry) => entry.turn === turn);
1436
- if (!open) {
1437
- if (index >= 0) draft.turnProcesses.splice(index, 1);
1438
- return;
1439
- }
1440
- const next = {
1441
- turn,
1442
- answerStep
1443
- };
1444
- if (index < 0) draft.turnProcesses.push(next);
1445
- else draft.turnProcesses[index] = next;
1460
+ init: () => ({ turnProcesses: [] }),
1461
+ actions: { setTurnProcessOpen: (draft, turn, answerStep, open) => {
1462
+ const index = draft.turnProcesses.findIndex((entry) => entry.turn === turn);
1463
+ if (!open) {
1464
+ if (index >= 0) draft.turnProcesses.splice(index, 1);
1465
+ return;
1446
1466
  }
1447
- }
1467
+ const next = {
1468
+ turn,
1469
+ answerStep
1470
+ };
1471
+ if (index < 0) draft.turnProcesses.push(next);
1472
+ else draft.turnProcesses[index] = next;
1473
+ } }
1448
1474
  });
1449
1475
  }
1450
1476
  //#endregion
@@ -1515,7 +1541,7 @@ window.__ModuleLoader__.load({
1515
1541
  return location?.kind === "turn" || location?.kind === "step" ? location.turn.turn : void 0;
1516
1542
  }
1517
1543
  /** Subscribe, apply Turn-process visibility, and dispatch one stable Context key. */
1518
- const ChatNodeSeat = (0, react.memo)(function ChatNodeSeat({ nodeKey, useChatNode, useChatNodeProcess, historyIncomplete, compactTranscript, selectedCallId, cwd, openFile, inspectCall, forkAt, loadImage, renderMessageImages, fileMentions, useStore, actions, renderSlot, t }) {
1544
+ const ChatNodeSeat = (0, react.memo)(function ChatNodeSeat({ nodeKey, useChatNode, useChatNodeProcess, historyIncomplete, compactTranscript, cwd, openFile, inspectCall, forkAt, loadImage, renderMessageImages, fileMentions, useStore, actions, renderSlot, t }) {
1519
1545
  const node = useChatNode(nodeKey);
1520
1546
  const routedNode = node;
1521
1547
  const turn = turnOf(routedNode);
@@ -1549,7 +1575,6 @@ window.__ModuleLoader__.load({
1549
1575
  if (processMember) setOpen(true);
1550
1576
  }, [processMember, setOpen]));
1551
1577
  const owner = (0, react.useMemo)(() => node === void 0 ? null : {
1552
- selectedCallId,
1553
1578
  cwd,
1554
1579
  openFile,
1555
1580
  inspectCall,
@@ -1560,7 +1585,6 @@ window.__ModuleLoader__.load({
1560
1585
  turnProcess
1561
1586
  }, [
1562
1587
  node,
1563
- selectedCallId,
1564
1588
  cwd,
1565
1589
  openFile,
1566
1590
  inspectCall,
@@ -1985,10 +2009,6 @@ window.__ModuleLoader__.load({
1985
2009
  const message = error instanceof Error ? error.message : String(error);
1986
2010
  return message === "" ? fallback : message;
1987
2011
  }
1988
- /** ProducedFiles opens the session workspace as `.`. */
1989
- function isFolderOpenPath(path) {
1990
- return path === ".";
1991
- }
1992
2012
  /**
1993
2013
  * Prompt-RPC identities already rendered by durable material: user/steering
1994
2014
  * node sources plus queue occurrences. A submission echo whose identity
@@ -2063,7 +2083,6 @@ window.__ModuleLoader__.load({
2063
2083
  const openError = useSession((s) => s.openError);
2064
2084
  const hasMore = useSession((s) => s.hasMore);
2065
2085
  const loadingOlder = useSession((s) => s.loadingOlder);
2066
- const selectedCallId = useStore((s) => s.selection?.callId);
2067
2086
  const compactTranscript = useTranscriptView((mode) => mode === "compact");
2068
2087
  const inspectCall = (0, react.useCallback)((callId) => {
2069
2088
  openView("trajectory", callId);
@@ -2071,10 +2090,10 @@ window.__ModuleLoader__.load({
2071
2090
  const [fileOpenError, setFileOpenError] = (0, react.useState)(null);
2072
2091
  const [fileOpenBusy, setFileOpenBusy] = (0, react.useState)(false);
2073
2092
  const fileOpenRequest = (0, react.useRef)(0);
2074
- const requestOpenFile = (0, react.useCallback)((path) => {
2093
+ const requestOpenFile = (0, react.useCallback)((path, options) => {
2075
2094
  const id = ++fileOpenRequest.current;
2076
2095
  setFileOpenBusy(true);
2077
- openFile(path).then(() => {
2096
+ (options === void 0 ? openFile(path) : openFile(path, options)).then(() => {
2078
2097
  if (id !== fileOpenRequest.current) return;
2079
2098
  setFileOpenError(null);
2080
2099
  setFileOpenBusy(false);
@@ -2082,7 +2101,7 @@ window.__ModuleLoader__.load({
2082
2101
  if (id !== fileOpenRequest.current) return;
2083
2102
  setFileOpenError({
2084
2103
  path,
2085
- message: openFailureMessage(error, t(isFolderOpenPath(path) ? "fileOpen.folderUnknown" : "fileOpen.unknown"))
2104
+ message: openFailureMessage(error, t("fileOpen.unknown"))
2086
2105
  });
2087
2106
  setFileOpenBusy(false);
2088
2107
  });
@@ -2521,7 +2540,6 @@ window.__ModuleLoader__.load({
2521
2540
  compactTranscript,
2522
2541
  useStore,
2523
2542
  actions,
2524
- selectedCallId,
2525
2543
  cwd,
2526
2544
  openFile: requestOpenFile,
2527
2545
  inspectCall,
@@ -2564,7 +2582,6 @@ window.__ModuleLoader__.load({
2564
2582
  })
2565
2583
  ]
2566
2584
  }), fileOpenError !== null && (0, react_jsx_runtime.jsx)(FileOpenErrorDialog, {
2567
- path: fileOpenError.path,
2568
2585
  message: fileOpenError.message,
2569
2586
  busy: fileOpenBusy,
2570
2587
  onClose: closeFileOpenError,
@@ -2576,12 +2593,12 @@ window.__ModuleLoader__.load({
2576
2593
  });
2577
2594
  }
2578
2595
  /** In-page Host open-path refusal: the wire reason plus a retry of the same path. */
2579
- function FileOpenErrorDialog({ path, message, busy, onClose, onRetry, t }) {
2596
+ function FileOpenErrorDialog({ message, busy, onClose, onRetry, t }) {
2580
2597
  return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2581
2598
  open: true,
2582
2599
  onClose,
2583
2600
  closeLabel: t("close"),
2584
- title: t(isFolderOpenPath(path) ? "fileOpen.folderTitle" : "fileOpen.title"),
2601
+ title: t("fileOpen.title"),
2585
2602
  description: message,
2586
2603
  footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2587
2604
  variant: "outline",
@@ -2609,20 +2626,14 @@ window.__ModuleLoader__.load({
2609
2626
  "duration.compactSeconds": "{seconds}秒",
2610
2627
  "duration.compactMinutes": "{minutes}分{seconds}秒",
2611
2628
  "duration.milliseconds": "{milliseconds}毫秒",
2612
- "stats.counts": "{turns} 轮 · {steps} 步",
2613
- "stats.llm": "LLM {duration}",
2614
- "stats.toolCall": "工具调用 {duration}",
2615
- "stats.ttftAverage": "首 token 平均 {duration}",
2616
- "stats.tokensPerSecond": "{throughput} tok/s",
2629
+ "stats.counts": "{turns} 轮 {steps} 步",
2617
2630
  "stats.cacheHit": "缓存命中 {percent}%",
2618
- "stats.tokens": "输入 {input} tok · 输出 {output} tok",
2619
- "details.title": "详情",
2620
- "details.close": "关闭详情",
2621
- "details.empty": "点击消息流中的工具行查看详情",
2622
- "details.notInWindow": "该调用不在当前窗口内",
2623
- "details.input": "输入",
2624
- "details.output": "输出",
2625
- "details.running": "运行中…",
2631
+ "stats.dialog.title": "会话统计",
2632
+ "stats.dialog.usageTitle": "Token 用量",
2633
+ "stats.dialog.llmTime": "模型用时",
2634
+ "stats.dialog.toolTime": "工具调用用时",
2635
+ "stats.dialog.ttft": "首 token 平均(TTFT)",
2636
+ "stats.dialog.speed": "输出速度(TPS)",
2626
2637
  "chat.loadingHistory": "载入历史…",
2627
2638
  "chat.loadError": "历史加载失败:{message}({code})",
2628
2639
  "chat.loadOlder": "加载更早",
@@ -2634,14 +2645,13 @@ window.__ModuleLoader__.load({
2634
2645
  "chat.turnNavigation.turn": "第 {turn} 轮",
2635
2646
  "settings.transcript.title": "对话显示",
2636
2647
  "settings.transcript.description": "控制已完成轮次的过程内容",
2637
- "settings.transcript.normal": "Normal",
2638
- "settings.transcript.compact": "Compact",
2648
+ "settings.transcript.normal": "标准",
2649
+ "settings.transcript.compact": "紧凑",
2639
2650
  "fileOpen.title": "无法打开文件",
2640
2651
  "fileOpen.unknown": "无法打开此文件",
2641
- "fileOpen.folderTitle": "无法打开文件夹",
2642
- "fileOpen.folderUnknown": "无法打开此文件夹",
2643
2652
  "message.extraBlock": "附加内容块",
2644
2653
  "message.systemPrompt": "系统提示词",
2654
+ "message.systemPromptUpdate": "系统提示词更新",
2645
2655
  "message.contextInjection": "上下文注入",
2646
2656
  "message.contextRecall": "跨会话召回",
2647
2657
  "message.referenceSummary": "引用会话 · {labels}",
@@ -2722,20 +2732,14 @@ window.__ModuleLoader__.load({
2722
2732
  "duration.compactSeconds": "{seconds}s",
2723
2733
  "duration.compactMinutes": "{minutes}m{seconds}s",
2724
2734
  "duration.milliseconds": "{milliseconds}ms",
2725
- "stats.counts": "{turns} turns · {steps} steps",
2726
- "stats.llm": "LLM {duration}",
2727
- "stats.toolCall": "Tool call {duration}",
2728
- "stats.ttftAverage": "TTFT avg {duration}",
2729
- "stats.tokensPerSecond": "{throughput} tok/s",
2735
+ "stats.counts": "{turns} turns {steps} steps",
2730
2736
  "stats.cacheHit": "Cache hit {percent}%",
2731
- "stats.tokens": "Input {input} tok · Output {output} tok",
2732
- "details.title": "Details",
2733
- "details.close": "Close details",
2734
- "details.empty": "Click a tool row in the message flow to view its details",
2735
- "details.notInWindow": "This call is outside the current window",
2736
- "details.input": "Input",
2737
- "details.output": "Output",
2738
- "details.running": "Running…",
2737
+ "stats.dialog.title": "Session statistics",
2738
+ "stats.dialog.usageTitle": "Token usage",
2739
+ "stats.dialog.llmTime": "LLM time",
2740
+ "stats.dialog.toolTime": "Tool time",
2741
+ "stats.dialog.ttft": "Avg time to first token (TTFT)",
2742
+ "stats.dialog.speed": "Tokens per second (TPS)",
2739
2743
  "chat.loadingHistory": "Loading history…",
2740
2744
  "chat.loadError": "Failed to load history: {message} ({code})",
2741
2745
  "chat.loadOlder": "Load earlier",
@@ -2751,10 +2755,9 @@ window.__ModuleLoader__.load({
2751
2755
  "settings.transcript.compact": "Compact",
2752
2756
  "fileOpen.title": "Couldn’t open file",
2753
2757
  "fileOpen.unknown": "Couldn’t open this file",
2754
- "fileOpen.folderTitle": "Couldn’t open folder",
2755
- "fileOpen.folderUnknown": "Couldn’t open this folder",
2756
2758
  "message.extraBlock": "Extra content block",
2757
2759
  "message.systemPrompt": "System prompt",
2760
+ "message.systemPromptUpdate": "System prompt update",
2758
2761
  "message.contextInjection": "Context injection",
2759
2762
  "message.contextRecall": "Session recall",
2760
2763
  "message.referenceSummary": "Referenced session · {labels}",
@@ -2876,7 +2879,9 @@ window.__ModuleLoader__.load({
2876
2879
  return newline === -1 ? visible : visible.slice(newline + 1);
2877
2880
  }
2878
2881
  /**
2879
- * Render one assistant reasoning block as the Think disclosure row.
2882
+ * Render one assistant reasoning block as the Think disclosure row. The
2883
+ * collapsed summary omits double-asterisk markers; expanded content preserves
2884
+ * the complete text.
2880
2885
  * @param props.text - complete or streaming reasoning text.
2881
2886
  * @param props.running - whether this block is the streaming tail.
2882
2887
  * @param props.t - conversation locale seat for the running status.
@@ -2884,7 +2889,7 @@ window.__ModuleLoader__.load({
2884
2889
  */
2885
2890
  function ReasoningRow({ text, running, t }) {
2886
2891
  const [expanded, setExpanded] = (0, react.useState)(false);
2887
- const summary = running ? latestLine(text) : firstLine(text);
2892
+ const summary = (running ? latestLine(text) : firstLine(text)).replaceAll("**", "");
2888
2893
  return (0, react_jsx_runtime.jsxs)("div", {
2889
2894
  className: ReasoningRow_module_css_default.root,
2890
2895
  "data-variant": "think",
@@ -2943,9 +2948,28 @@ window.__ModuleLoader__.load({
2943
2948
  };
2944
2949
  //#endregion
2945
2950
  //#region lib/types/client/chat/AssistantMarkdown.js
2951
+ /**
2952
+ * Map one authored media destination to the same-origin workspace-file URL.
2953
+ * @param protocol - `window.location.protocol` at render time.
2954
+ * @param origin - `window.location.origin` at render time.
2955
+ * @param value - The authored markdown destination, exactly as written.
2956
+ * @returns The API URL for an absolute POSIX path on an HTTP(S) page, or
2957
+ * undefined when the destination cannot be a Host-served local file
2958
+ * (non-HTTP transport such as Electron `file://`, protocol-relative or
2959
+ * relative destinations).
2960
+ */
2961
+ function localPathMediaUrl(protocol, origin, value) {
2962
+ if (protocol !== "http:" && protocol !== "https:") return void 0;
2963
+ if (value.length === 0 || !value.startsWith("/") || value.startsWith("//")) return void 0;
2964
+ return `${origin}/api/file?path=${encodeURIComponent(value)}`;
2965
+ }
2946
2966
  /** Reasoning block as the Think variant summary row (figma 39:28304). */
2947
2967
  const AssistantMarkdown = (0, react.memo)(function AssistantMarkdown({ blocks, streaming, interrupted, renderMessageImages, reasoningHidden = false, revealProcess, mentions, t }) {
2948
2968
  const labels = (0, react.useMemo)(() => markdownLabels(t), [t]);
2969
+ const pathImages = (0, react.useMemo)(() => {
2970
+ const { protocol, origin } = window.location;
2971
+ return { resolve: (value) => localPathMediaUrl(protocol, origin, value) };
2972
+ }, []);
2949
2973
  const last = blocks.length - 1;
2950
2974
  if (!(streaming || interrupted === true || blocks.some((block) => block.kind !== "tool-call"))) return null;
2951
2975
  const rendered = [];
@@ -2958,7 +2982,8 @@ window.__ModuleLoader__.load({
2958
2982
  text: block.text,
2959
2983
  streaming,
2960
2984
  labels,
2961
- fileMentions: mentions
2985
+ fileMentions: mentions,
2986
+ pathImages
2962
2987
  }, i));
2963
2988
  break;
2964
2989
  case "reasoning":
@@ -3190,17 +3215,18 @@ window.__ModuleLoader__.load({
3190
3215
  /**
3191
3216
  * Render one complete system prompt as a collapsed disclosure whose expanded
3192
3217
  * body is the same opaque context chrome: 141px code-block scrollport and
3193
- * model-facing text with its real line breaks.
3194
- * @param props - Complete prompt text and the locale seat.
3218
+ * model-facing text with its real line breaks. An in-history update uses the
3219
+ * same row under its own title.
3220
+ * @param props - Complete prompt text, whether it is an update, and the locale seat.
3195
3221
  * @returns The system-prompt disclosure row.
3196
3222
  */
3197
- function SystemPromptRow({ text, t }) {
3223
+ function SystemPromptRow({ text, update = false, t }) {
3198
3224
  const [open, setOpen] = (0, react.useState)(false);
3199
3225
  return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
3200
3226
  className: ContextInjectionRow_module_css_default.root,
3201
3227
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }),
3202
3228
  chevronClassName: ContextInjectionRow_module_css_default.chevron,
3203
- title: t("message.systemPrompt"),
3229
+ title: t(update ? "message.systemPromptUpdate" : "message.systemPrompt"),
3204
3230
  open,
3205
3231
  expandable: true,
3206
3232
  expandOnRowClick: true,
@@ -3225,6 +3251,7 @@ window.__ModuleLoader__.load({
3225
3251
  const SystemPromptNodeView = (0, react.memo)(function SystemPromptNodeView({ node, t }) {
3226
3252
  return (0, react_jsx_runtime.jsx)(SystemPromptRow, {
3227
3253
  text: node.data.text,
3254
+ update: node.data.update === true,
3228
3255
  t
3229
3256
  });
3230
3257
  });
@@ -3357,37 +3384,7 @@ window.__ModuleLoader__.load({
3357
3384
  return `99.${"9".repeat(distinguishingPlaces - 1)}${10 - roundedLoss}`;
3358
3385
  }
3359
3386
  //#endregion
3360
- //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/TurnUsagePanel.module.css.mjs
3361
- const css$4 = ".Q51KRG_root{min-width:0;display:inline-flex}.Q51KRG_root+.Q51KRG_root{margin-left:-6px}.Q51KRG_trigger{min-width:0;height:calc(28px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);font-variant-numeric:tabular-nums;line-height:calc(24px + var(--dsh-content-font-delta,0px));white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:28px;align-items:center;gap:4px;padding:6px 8px;display:inline-flex}.Q51KRG_label{text-overflow:ellipsis;min-width:0;overflow:hidden}.Q51KRG_trigger svg{width:calc(15px + var(--dsh-content-font-delta,0px));height:calc(15px + var(--dsh-content-font-delta,0px));flex:none}.Q51KRG_trigger:hover,.Q51KRG_trigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}@media (width<=480px){.Q51KRG_trigger{width:calc(28px + var(--dsh-content-font-delta,0px));justify-content:center;padding:6px}.Q51KRG_trigger .Q51KRG_label{display:none}.Q51KRG_root+.Q51KRG_root{margin-left:0}}.Q51KRG_panel{z-index:1100;box-sizing:border-box;background:var(--dsw-specific-menu);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);width:max-content;min-width:min(300px,100vw - 24px);max-width:min(440px,100vw - 24px);box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-secondary);cursor:default;border:0;border-radius:12px;padding:16px;font-size:12px;line-height:18px;position:fixed}.Q51KRG_title{color:var(--dsw-alias-label-primary);justify-content:space-between;gap:16px;margin-bottom:8px;font-weight:500;display:flex}.Q51KRG_titleRule{border-top:.5px solid var(--dsw-alias-border-l2);margin-bottom:10px}.Q51KRG_titleValue{font-variant-numeric:tabular-nums}.Q51KRG_titleLabel{align-items:center;gap:6px;min-width:0;display:inline-flex}.Q51KRG_titleLabel svg{flex:none;width:14px;height:14px}.Q51KRG_details{color:var(--dsw-alias-label-tertiary);grid-template-columns:minmax(76px,auto) minmax(0,1fr);gap:6px 16px;margin:0;display:grid}.Q51KRG_details dt,.Q51KRG_details dd{min-width:0;margin:0}.Q51KRG_details dd{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-align:right}.Q51KRG_details .Q51KRG_route{overflow-wrap:anywhere}.Q51KRG_reasoning{color:var(--dsw-alias-label-tertiary);white-space:nowrap}";
3362
- const tagId$4 = "@deepseek-ai/dsh-client-ui-chat/TurnUsagePanel.module.css";
3363
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
3364
- const tag = document.createElement("style");
3365
- tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-chat";
3366
- tag.dataset.pluginCss = tagId$4;
3367
- tag.textContent = css$4;
3368
- document.head.appendChild(tag);
3369
- }
3370
- var TurnUsagePanel_module_css_default = {
3371
- "details": "Q51KRG_details",
3372
- "label": "Q51KRG_label",
3373
- "panel": "Q51KRG_panel",
3374
- "reasoning": "Q51KRG_reasoning",
3375
- "root": "Q51KRG_root",
3376
- "route": "Q51KRG_route",
3377
- "title": "Q51KRG_title",
3378
- "titleLabel": "Q51KRG_titleLabel",
3379
- "titleRule": "Q51KRG_titleRule",
3380
- "titleValue": "Q51KRG_titleValue",
3381
- "trigger": "Q51KRG_trigger"
3382
- };
3383
- //#endregion
3384
- //#region lib/types/client/chat/TurnUsagePanel.js
3385
- function formatCompactCount(value, t) {
3386
- return t("message.turnUsage.count", { count: formatTokens(value, t) });
3387
- }
3388
- function formatExactCount(value, t) {
3389
- return t("message.turnUsage.count", { count: formatExactTokens(value, t) });
3390
- }
3387
+ //#region lib/types/client/chat/stat-dialog.js
3391
3388
  /** Viewport margin the placement clamp keeps (the Menu portal margin). */
3392
3389
  const PANEL_MARGIN = 12;
3393
3390
  /** Distance between the trigger's top edge and the panel's bottom. */
@@ -3401,9 +3398,16 @@ window.__ModuleLoader__.load({
3401
3398
  left: 0,
3402
3399
  top: 0
3403
3400
  };
3404
- /** One trigger-anchored dialog seat: open state, viewport-clamped placement, outside-close. */
3405
- function useStatDialog() {
3406
- const [open, setOpen] = (0, react.useState)(false);
3401
+ /**
3402
+ * One trigger-anchored dialog seat: open state, viewport-clamped placement, outside-close.
3403
+ * @param controlled - external open state; when given the seat reads and writes
3404
+ * it instead of owning its own, letting sibling dialogs share one exclusive slot.
3405
+ * @returns the seat; spread `pos ?? MEASURE_STYLE` onto the portaled panel.
3406
+ */
3407
+ function useStatDialog(controlled) {
3408
+ const [ownOpen, setOwnOpen] = (0, react.useState)(false);
3409
+ const open = controlled?.open ?? ownOpen;
3410
+ const setOpen = controlled?.setOpen ?? setOwnOpen;
3407
3411
  const rootRef = (0, react.useRef)(null);
3408
3412
  const panelRef = (0, react.useRef)(null);
3409
3413
  const pos = (0, _deepseek_ai_dsh_client_ui_primitives.useAnchoredPosition)({
@@ -3424,7 +3428,7 @@ window.__ModuleLoader__.load({
3424
3428
  return () => {
3425
3429
  document.removeEventListener("keydown", onKeyDown);
3426
3430
  };
3427
- }, [open]);
3431
+ }, [open, setOpen]);
3428
3432
  return {
3429
3433
  open,
3430
3434
  setOpen,
@@ -3433,6 +3437,51 @@ window.__ModuleLoader__.load({
3433
3437
  pos
3434
3438
  };
3435
3439
  }
3440
+ //#endregion
3441
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/TurnUsagePanel.module.css.mjs
3442
+ const css$4 = ".Q51KRG_root{min-width:0;display:inline-flex}.Q51KRG_root+.Q51KRG_root{margin-left:-6px}.Q51KRG_trigger{min-width:0;height:calc(28px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);font-variant-numeric:tabular-nums;line-height:calc(24px + var(--dsh-content-font-delta,0px));white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:28px;align-items:center;gap:4px;padding:6px 8px;display:inline-flex}.Q51KRG_label{text-overflow:ellipsis;min-width:0;overflow:hidden}.Q51KRG_trigger svg{width:calc(15px + var(--dsh-content-font-delta,0px));height:calc(15px + var(--dsh-content-font-delta,0px));flex:none}.Q51KRG_trigger:hover,.Q51KRG_trigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}@media (width<=480px){.Q51KRG_trigger{width:calc(28px + var(--dsh-content-font-delta,0px));justify-content:center;padding:6px}.Q51KRG_trigger .Q51KRG_label{display:none}.Q51KRG_root+.Q51KRG_root{margin-left:0}}";
3443
+ const tagId$4 = "@deepseek-ai/dsh-client-ui-chat/TurnUsagePanel.module.css";
3444
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
3445
+ const tag = document.createElement("style");
3446
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-chat";
3447
+ tag.dataset.pluginCss = tagId$4;
3448
+ tag.textContent = css$4;
3449
+ document.head.appendChild(tag);
3450
+ }
3451
+ var TurnUsagePanel_module_css_default = {
3452
+ "label": "Q51KRG_label",
3453
+ "root": "Q51KRG_root",
3454
+ "trigger": "Q51KRG_trigger"
3455
+ };
3456
+ //#endregion
3457
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/stat-dialog.module.css.mjs
3458
+ const css$3 = ".bRhRbq_panel{z-index:1100;box-sizing:border-box;background:var(--dsw-specific-menu);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);width:max-content;min-width:min(300px,100vw - 24px);max-width:min(440px,100vw - 24px);box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-secondary);cursor:default;border:0;border-radius:12px;padding:16px;font-size:12px;line-height:18px;position:fixed}.bRhRbq_title{color:var(--dsw-alias-label-primary);justify-content:space-between;gap:16px;margin-bottom:8px;font-weight:500;display:flex}.bRhRbq_titleRule{border-top:.5px solid var(--dsw-alias-border-l2);margin-bottom:10px}.bRhRbq_titleValue{font-variant-numeric:tabular-nums}.bRhRbq_titleLabel{align-items:center;gap:6px;min-width:0;display:inline-flex}.bRhRbq_titleLabel svg{flex:none;width:14px;height:14px}.bRhRbq_details{color:var(--dsw-alias-label-tertiary);grid-template-columns:minmax(76px,auto) minmax(0,1fr);gap:6px 16px;margin:0;display:grid}.bRhRbq_details dt,.bRhRbq_details dd{min-width:0;margin:0}.bRhRbq_details dd{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-align:right}.bRhRbq_details .bRhRbq_route{overflow-wrap:anywhere}.bRhRbq_reasoning{color:var(--dsw-alias-label-tertiary);white-space:nowrap}";
3459
+ const tagId$3 = "@deepseek-ai/dsh-client-ui-chat/stat-dialog.module.css";
3460
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
3461
+ const tag = document.createElement("style");
3462
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-chat";
3463
+ tag.dataset.pluginCss = tagId$3;
3464
+ tag.textContent = css$3;
3465
+ document.head.appendChild(tag);
3466
+ }
3467
+ var stat_dialog_module_css_default = {
3468
+ "details": "bRhRbq_details",
3469
+ "panel": "bRhRbq_panel",
3470
+ "reasoning": "bRhRbq_reasoning",
3471
+ "route": "bRhRbq_route",
3472
+ "title": "bRhRbq_title",
3473
+ "titleLabel": "bRhRbq_titleLabel",
3474
+ "titleRule": "bRhRbq_titleRule",
3475
+ "titleValue": "bRhRbq_titleValue"
3476
+ };
3477
+ //#endregion
3478
+ //#region lib/types/client/chat/TurnUsagePanel.js
3479
+ function formatCompactCount(value, t) {
3480
+ return t("message.turnUsage.count", { count: formatTokens(value, t) });
3481
+ }
3482
+ function formatExactCount(value, t) {
3483
+ return t("message.turnUsage.count", { count: formatExactTokens(value, t) });
3484
+ }
3436
3485
  /**
3437
3486
  * Turn-usage IconActions pill with a click-open Turn-usage details dialog.
3438
3487
  * @param props - Turn usage buckets and locale seat.
@@ -3460,31 +3509,31 @@ window.__ModuleLoader__.load({
3460
3509
  })]
3461
3510
  }), open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
3462
3511
  ref: panelRef,
3463
- className: TurnUsagePanel_module_css_default.panel,
3512
+ className: stat_dialog_module_css_default.panel,
3464
3513
  role: "dialog",
3465
3514
  "aria-label": t("message.turnUsage.title"),
3466
3515
  style: pos ?? MEASURE_STYLE,
3467
3516
  children: [
3468
3517
  (0, react_jsx_runtime.jsxs)("div", {
3469
- className: TurnUsagePanel_module_css_default.title,
3518
+ className: stat_dialog_module_css_default.title,
3470
3519
  children: [(0, react_jsx_runtime.jsxs)("span", {
3471
- className: TurnUsagePanel_module_css_default.titleLabel,
3520
+ className: stat_dialog_module_css_default.titleLabel,
3472
3521
  children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDatabaseOutline16, {}), t("message.turnUsage.title")]
3473
3522
  }), (0, react_jsx_runtime.jsx)("span", {
3474
- className: TurnUsagePanel_module_css_default.titleValue,
3523
+ className: stat_dialog_module_css_default.titleValue,
3475
3524
  children: formatExactCount(usage.totalTokens, t)
3476
3525
  })]
3477
3526
  }),
3478
3527
  (0, react_jsx_runtime.jsx)("div", {
3479
- className: TurnUsagePanel_module_css_default.titleRule,
3528
+ className: stat_dialog_module_css_default.titleRule,
3480
3529
  "aria-hidden": true
3481
3530
  }),
3482
3531
  (0, react_jsx_runtime.jsxs)("dl", {
3483
- className: TurnUsagePanel_module_css_default.details,
3532
+ className: stat_dialog_module_css_default.details,
3484
3533
  "data-turn-usage-details": true,
3485
3534
  children: [
3486
3535
  routes !== "" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.model") }), (0, react_jsx_runtime.jsx)("dd", {
3487
- className: TurnUsagePanel_module_css_default.route,
3536
+ className: stat_dialog_module_css_default.route,
3488
3537
  children: routes
3489
3538
  })] }),
3490
3539
  cacheHit !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheHit") }), (0, react_jsx_runtime.jsx)("dd", { children: `${cacheHit}%` })] }),
@@ -3494,7 +3543,7 @@ window.__ModuleLoader__.load({
3494
3543
  usage.cacheWriteTokens !== void 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheWrite") }), (0, react_jsx_runtime.jsx)("dd", { children: formatExactCount(usage.cacheWriteTokens, t) })] }),
3495
3544
  (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.output") }),
3496
3545
  (0, react_jsx_runtime.jsxs)("dd", { children: [formatExactCount(usage.outputTokens, t), usage.reasoningTokens !== void 0 && (0, react_jsx_runtime.jsx)("span", {
3497
- className: TurnUsagePanel_module_css_default.reasoning,
3546
+ className: stat_dialog_module_css_default.reasoning,
3498
3547
  children: t("message.turnUsage.reasoning", { tokens: formatExactCount(usage.reasoningTokens, t) })
3499
3548
  })] })
3500
3549
  ]
@@ -3527,24 +3576,24 @@ window.__ModuleLoader__.load({
3527
3576
  })]
3528
3577
  }), open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
3529
3578
  ref: panelRef,
3530
- className: TurnUsagePanel_module_css_default.panel,
3579
+ className: stat_dialog_module_css_default.panel,
3531
3580
  role: "dialog",
3532
3581
  "aria-label": t("message.turnTime.title"),
3533
3582
  style: pos ?? MEASURE_STYLE,
3534
3583
  children: [
3535
3584
  (0, react_jsx_runtime.jsx)("div", {
3536
- className: TurnUsagePanel_module_css_default.title,
3585
+ className: stat_dialog_module_css_default.title,
3537
3586
  children: (0, react_jsx_runtime.jsxs)("span", {
3538
- className: TurnUsagePanel_module_css_default.titleLabel,
3587
+ className: stat_dialog_module_css_default.titleLabel,
3539
3588
  children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconClockOutline16, {}), t("message.turnTime.title")]
3540
3589
  })
3541
3590
  }),
3542
3591
  (0, react_jsx_runtime.jsx)("div", {
3543
- className: TurnUsagePanel_module_css_default.titleRule,
3592
+ className: stat_dialog_module_css_default.titleRule,
3544
3593
  "aria-hidden": true
3545
3594
  }),
3546
3595
  (0, react_jsx_runtime.jsxs)("dl", {
3547
- className: TurnUsagePanel_module_css_default.details,
3596
+ className: stat_dialog_module_css_default.details,
3548
3597
  "data-turn-time-details": true,
3549
3598
  children: [
3550
3599
  (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnTime.duration") }),
@@ -3569,13 +3618,13 @@ window.__ModuleLoader__.load({
3569
3618
  }
3570
3619
  //#endregion
3571
3620
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/TurnTailNodeView.module.css.mjs
3572
- const css$3 = ".TS9iAW_root{flex-direction:column;gap:16px;display:flex}.TS9iAW_actions{margin-left:-6px}";
3573
- const tagId$3 = "@deepseek-ai/dsh-client-ui-chat/TurnTailNodeView.module.css";
3574
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
3621
+ const css$2 = ".TS9iAW_root{flex-direction:column;gap:16px;display:flex}.TS9iAW_actions{margin-left:-6px}";
3622
+ const tagId$2 = "@deepseek-ai/dsh-client-ui-chat/TurnTailNodeView.module.css";
3623
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
3575
3624
  const tag = document.createElement("style");
3576
3625
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-chat";
3577
- tag.dataset.pluginCss = tagId$3;
3578
- tag.textContent = css$3;
3626
+ tag.dataset.pluginCss = tagId$2;
3627
+ tag.textContent = css$2;
3579
3628
  document.head.appendChild(tag);
3580
3629
  }
3581
3630
  var TurnTailNodeView_module_css_default = {
@@ -3789,22 +3838,25 @@ window.__ModuleLoader__.load({
3789
3838
  return metrics;
3790
3839
  }
3791
3840
  //#endregion
3792
- //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/StatsLine.module.css.mjs
3793
- const css$2 = ".-NDN2W_root{text-align:center;max-width:var(--dsh-chat-content-width);box-sizing:border-box;width:100%;padding:4px calc(var(--dsh-composer-side-clearance) + 16px) 0px;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;margin:0 auto;display:block;overflow:hidden}.-NDN2W_sep{color:var(--dsw-alias-separator-primary);margin:0 10px}";
3794
- const tagId$2 = "@deepseek-ai/dsh-client-ui-chat/StatsLine.module.css";
3795
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
3841
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/chat/StatsPills.module.css.mjs
3842
+ const css$1 = ".bOPqQW_root{max-width:var(--dsh-chat-content-width);box-sizing:border-box;width:100%;padding:4px calc(var(--dsh-composer-side-clearance) + 16px) 0px;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));justify-content:center;gap:12px;margin:0 auto;display:flex}.bOPqQW_anchor{min-width:0;display:inline-flex}.bOPqQW_pill{box-sizing:border-box;max-width:100%;color:var(--dsw-alias-label-tertiary);font:inherit;font-variant-numeric:tabular-nums;line-height:inherit;white-space:nowrap;background:0 0;border:none;border-radius:24px;align-items:center;gap:6px;padding:1px 8px;display:inline-flex}.bOPqQW_pill svg{flex:none;width:14px;height:14px}button.bOPqQW_pill{cursor:pointer}button.bOPqQW_pill:hover,button.bOPqQW_pill[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.bOPqQW_label{text-overflow:ellipsis;min-width:0;overflow:hidden}.bOPqQW_sep{color:var(--dsw-alias-separator-primary);margin:0 6px}";
3843
+ const tagId$1 = "@deepseek-ai/dsh-client-ui-chat/StatsPills.module.css";
3844
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
3796
3845
  const tag = document.createElement("style");
3797
3846
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-chat";
3798
- tag.dataset.pluginCss = tagId$2;
3799
- tag.textContent = css$2;
3847
+ tag.dataset.pluginCss = tagId$1;
3848
+ tag.textContent = css$1;
3800
3849
  document.head.appendChild(tag);
3801
3850
  }
3802
- var StatsLine_module_css_default = {
3803
- "root": "-NDN2W_root",
3804
- "sep": "-NDN2W_sep"
3851
+ var StatsPills_module_css_default = {
3852
+ "anchor": "bOPqQW_anchor",
3853
+ "label": "bOPqQW_label",
3854
+ "pill": "bOPqQW_pill",
3855
+ "root": "bOPqQW_root",
3856
+ "sep": "bOPqQW_sep"
3805
3857
  };
3806
3858
  //#endregion
3807
- //#region lib/types/client/chat/StatsLine.js
3859
+ //#region lib/types/client/chat/StatsPills.js
3808
3860
  /**
3809
3861
  * Fold assistant and tool-result nodes into window-scoped display totals —
3810
3862
  * the FALLBACK for assemblies without the `sessionStats` projection.
@@ -3889,75 +3941,172 @@ window.__ModuleLoader__.load({
3889
3941
  function billedInputTokens(usage) {
3890
3942
  return usage.uncachedInputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
3891
3943
  }
3892
- /** Render and measure one non-empty statistics line. */
3893
- const StatsLineContent = (0, react.memo)(function StatsLineContent({ groups, line }) {
3894
- const rootRef = (0, react.useRef)(null);
3895
- const [truncated, setTruncated] = (0, react.useState)(false);
3896
- const measure = (0, react.useCallback)(() => {
3897
- const el = rootRef.current;
3898
- if (el === null) return;
3899
- const next = el.scrollWidth > el.clientWidth;
3900
- setTruncated((current) => current === next ? current : next);
3901
- }, []);
3902
- (0, react.useLayoutEffect)(() => {
3903
- const el = rootRef.current;
3904
- if (el === null || typeof ResizeObserver === "undefined") return;
3905
- const observer = new ResizeObserver(measure);
3906
- observer.observe(el);
3907
- return () => {
3908
- observer.disconnect();
3909
- };
3910
- }, [measure]);
3911
- (0, react.useLayoutEffect)(measure, [line, measure]);
3912
- return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
3913
- label: line,
3914
- side: "top",
3915
- delayMs: 500,
3916
- disabled: !truncated,
3917
- children: (0, react_jsx_runtime.jsx)("div", {
3918
- ref: rootRef,
3919
- className: StatsLine_module_css_default.root,
3920
- children: groups.map((group, i) => (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [i > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
3921
- className: StatsLine_module_css_default.sep,
3922
- "aria-hidden": true,
3923
- children: "|"
3924
- }), " "] }), (0, react_jsx_runtime.jsx)("span", { children: group })] }, group))
3944
+ function exactCount(value, t) {
3945
+ return t("message.turnUsage.count", { count: formatExactTokens(value, t) });
3946
+ }
3947
+ function TimePill({ stats, t, dialog }) {
3948
+ const { open, setOpen, rootRef, panelRef, pos } = useStatDialog(dialog);
3949
+ const counts = t("stats.counts", {
3950
+ turns: stats.turns,
3951
+ steps: stats.steps
3952
+ });
3953
+ const tps = stats.decodeMs > 0 ? t("message.tokensPerSecond", { tps: formatTokensPerSecond(stats.decodeTokens / (stats.decodeMs / 1e3)) }) : null;
3954
+ const label = (0, react_jsx_runtime.jsxs)("span", {
3955
+ className: StatsPills_module_css_default.label,
3956
+ children: [counts, tps !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
3957
+ className: StatsPills_module_css_default.sep,
3958
+ "aria-hidden": true,
3959
+ children: "·"
3960
+ }), tps] })]
3961
+ });
3962
+ if (stats.llmMs <= 0 && stats.toolMs <= 0 && stats.ttftSteps <= 0 && stats.decodeMs <= 0) return (0, react_jsx_runtime.jsx)("span", {
3963
+ className: StatsPills_module_css_default.anchor,
3964
+ children: (0, react_jsx_runtime.jsxs)("span", {
3965
+ className: StatsPills_module_css_default.pill,
3966
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGaugeOutline16, {}), label]
3925
3967
  })
3926
3968
  });
3927
- });
3928
- const StatsLine = (0, react.memo)(function StatsLine({ useChat, useProjection, t }) {
3969
+ return (0, react_jsx_runtime.jsxs)("span", {
3970
+ ref: rootRef,
3971
+ className: StatsPills_module_css_default.anchor,
3972
+ children: [(0, react_jsx_runtime.jsxs)("button", {
3973
+ type: "button",
3974
+ className: StatsPills_module_css_default.pill,
3975
+ "aria-haspopup": "dialog",
3976
+ "aria-expanded": open,
3977
+ "aria-label": tps === null ? counts : `${counts} · ${tps}`,
3978
+ onClick: () => {
3979
+ setOpen(!open);
3980
+ },
3981
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGaugeOutline16, {}), label]
3982
+ }), open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
3983
+ ref: panelRef,
3984
+ className: stat_dialog_module_css_default.panel,
3985
+ role: "dialog",
3986
+ "aria-label": t("stats.dialog.title"),
3987
+ style: pos ?? MEASURE_STYLE,
3988
+ children: [
3989
+ (0, react_jsx_runtime.jsx)("div", {
3990
+ className: stat_dialog_module_css_default.title,
3991
+ children: (0, react_jsx_runtime.jsxs)("span", {
3992
+ className: stat_dialog_module_css_default.titleLabel,
3993
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGaugeOutline16, {}), t("stats.dialog.title")]
3994
+ })
3995
+ }),
3996
+ (0, react_jsx_runtime.jsx)("div", {
3997
+ className: stat_dialog_module_css_default.titleRule,
3998
+ "aria-hidden": true
3999
+ }),
4000
+ (0, react_jsx_runtime.jsxs)("dl", {
4001
+ className: stat_dialog_module_css_default.details,
4002
+ "data-session-stats-details": true,
4003
+ children: [
4004
+ stats.llmMs > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("stats.dialog.llmTime") }), (0, react_jsx_runtime.jsx)("dd", { children: formatDuration(stats.llmMs, t) })] }),
4005
+ stats.toolMs > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("stats.dialog.toolTime") }), (0, react_jsx_runtime.jsx)("dd", { children: formatDuration(stats.toolMs, t) })] }),
4006
+ stats.ttftSteps > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("stats.dialog.ttft") }), (0, react_jsx_runtime.jsx)("dd", { children: formatDuration(stats.ttftMs / stats.ttftSteps, t) })] }),
4007
+ stats.decodeMs > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("stats.dialog.speed") }), (0, react_jsx_runtime.jsx)("dd", { children: t("message.tokensPerSecond", { tps: formatTokensPerSecond(stats.decodeTokens / (stats.decodeMs / 1e3)) }) })] })
4008
+ ]
4009
+ })
4010
+ ]
4011
+ }), document.body)]
4012
+ });
4013
+ }
4014
+ function UsagePill({ usage, t, dialog }) {
4015
+ const { open, setOpen, rootRef, panelRef, pos } = useStatDialog(dialog);
4016
+ const total = billedInputTokens(usage) + usage.outputTokens;
4017
+ const totalText = t("message.turnUsage.count", { count: formatTokens(total, t) });
4018
+ const cacheHit = cacheHitPercent(usage);
4019
+ const cacheHitText = cacheHit !== null ? t("stats.cacheHit", { percent: cacheHit }) : null;
4020
+ return (0, react_jsx_runtime.jsxs)("span", {
4021
+ ref: rootRef,
4022
+ className: StatsPills_module_css_default.anchor,
4023
+ children: [(0, react_jsx_runtime.jsxs)("button", {
4024
+ type: "button",
4025
+ className: StatsPills_module_css_default.pill,
4026
+ "aria-haspopup": "dialog",
4027
+ "aria-expanded": open,
4028
+ "aria-label": cacheHitText === null ? totalText : `${totalText} · ${cacheHitText}`,
4029
+ onClick: () => {
4030
+ setOpen(!open);
4031
+ },
4032
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDatabaseOutline16, {}), (0, react_jsx_runtime.jsxs)("span", {
4033
+ className: StatsPills_module_css_default.label,
4034
+ children: [totalText, cacheHitText !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
4035
+ className: StatsPills_module_css_default.sep,
4036
+ "aria-hidden": true,
4037
+ children: "·"
4038
+ }), cacheHitText] })]
4039
+ })]
4040
+ }), open && (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
4041
+ ref: panelRef,
4042
+ className: stat_dialog_module_css_default.panel,
4043
+ role: "dialog",
4044
+ "aria-label": t("stats.dialog.usageTitle"),
4045
+ style: pos ?? MEASURE_STYLE,
4046
+ children: [
4047
+ (0, react_jsx_runtime.jsxs)("div", {
4048
+ className: stat_dialog_module_css_default.title,
4049
+ children: [(0, react_jsx_runtime.jsxs)("span", {
4050
+ className: stat_dialog_module_css_default.titleLabel,
4051
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDatabaseOutline16, {}), t("stats.dialog.usageTitle")]
4052
+ }), (0, react_jsx_runtime.jsx)("span", {
4053
+ className: stat_dialog_module_css_default.titleValue,
4054
+ children: exactCount(total, t)
4055
+ })]
4056
+ }),
4057
+ (0, react_jsx_runtime.jsx)("div", {
4058
+ className: stat_dialog_module_css_default.titleRule,
4059
+ "aria-hidden": true
4060
+ }),
4061
+ (0, react_jsx_runtime.jsxs)("dl", {
4062
+ className: stat_dialog_module_css_default.details,
4063
+ "data-session-stats-usage": true,
4064
+ children: [
4065
+ cacheHit !== null && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheHit") }), (0, react_jsx_runtime.jsx)("dd", { children: `${cacheHit}%` })] }),
4066
+ (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.input") }),
4067
+ (0, react_jsx_runtime.jsx)("dd", { children: exactCount(usage.uncachedInputTokens, t) }),
4068
+ (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheRead") }),
4069
+ (0, react_jsx_runtime.jsx)("dd", { children: exactCount(usage.cacheReadTokens, t) }),
4070
+ (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.cacheWrite") }),
4071
+ (0, react_jsx_runtime.jsx)("dd", { children: exactCount(usage.cacheWriteTokens, t) }),
4072
+ (0, react_jsx_runtime.jsx)("dt", { children: t("message.turnUsage.output") }),
4073
+ (0, react_jsx_runtime.jsx)("dd", { children: exactCount(usage.outputTokens, t) })
4074
+ ]
4075
+ })
4076
+ ]
4077
+ }), document.body)]
4078
+ });
4079
+ }
4080
+ const StatsPills = (0, react.memo)(function StatsPills({ useChat, useProjection, t }) {
3929
4081
  const settledNodes = useChat((s) => s.legacy.nodes);
3930
4082
  const usage = useProjection("tokenUsage");
4083
+ const [openPill, setOpenPill] = (0, react.useState)(null);
3931
4084
  const projected = useProjection("sessionStats");
3932
4085
  const stats = (0, react.useMemo)(() => projected ?? deriveStats(settledNodes), [projected, settledNodes]);
3933
- const groups = [];
3934
- if (stats.steps > 0) {
3935
- groups.push(t("stats.counts", {
3936
- turns: stats.turns,
3937
- steps: stats.steps
3938
- }));
3939
- const durations = [];
3940
- if (stats.llmMs > 0) durations.push(t("stats.llm", { duration: formatDuration(stats.llmMs, t) }));
3941
- if (stats.toolMs > 0) durations.push(t("stats.toolCall", { duration: formatDuration(stats.toolMs, t) }));
3942
- if (durations.length > 0) groups.push(durations.join(" · "));
3943
- const speeds = [];
3944
- if (stats.ttftSteps > 0) speeds.push(t("stats.ttftAverage", { duration: formatDuration(stats.ttftMs / stats.ttftSteps, t) }));
3945
- if (stats.decodeMs > 0) speeds.push(t("stats.tokensPerSecond", { throughput: formatTokensPerSecond(stats.decodeTokens / (stats.decodeMs / 1e3)) }));
3946
- if (speeds.length > 0) groups.push(speeds.join(" · "));
3947
- }
3948
- if (usage !== void 0 && (billedInputTokens(usage) > 0 || usage.outputTokens > 0)) {
3949
- const cacheHit = cacheHitPercent(usage);
3950
- if (cacheHit !== null) groups.push(t("stats.cacheHit", { percent: cacheHit }));
3951
- groups.push(t("stats.tokens", {
3952
- input: formatTokens(billedInputTokens(usage), t),
3953
- output: formatTokens(usage.outputTokens, t)
3954
- }));
3955
- }
3956
- const line = groups.join(" | ");
3957
- if (groups.length === 0) return null;
3958
- return (0, react_jsx_runtime.jsx)(StatsLineContent, {
3959
- groups,
3960
- line
4086
+ const hasTokens = usage !== void 0 && (billedInputTokens(usage) > 0 || usage.outputTokens > 0);
4087
+ if (stats.steps === 0 && !hasTokens) return null;
4088
+ return (0, react_jsx_runtime.jsxs)("div", {
4089
+ className: StatsPills_module_css_default.root,
4090
+ "data-composer-stats": true,
4091
+ children: [stats.steps > 0 && (0, react_jsx_runtime.jsx)(TimePill, {
4092
+ stats,
4093
+ t,
4094
+ dialog: {
4095
+ open: openPill === "time",
4096
+ setOpen: (open) => {
4097
+ setOpenPill(open ? "time" : null);
4098
+ }
4099
+ }
4100
+ }), hasTokens && (0, react_jsx_runtime.jsx)(UsagePill, {
4101
+ usage,
4102
+ t,
4103
+ dialog: {
4104
+ open: openPill === "usage",
4105
+ setOpen: (open) => {
4106
+ setOpenPill(open ? "usage" : null);
4107
+ }
4108
+ }
4109
+ })]
3961
4110
  });
3962
4111
  });
3963
4112
  //#endregion
@@ -5314,7 +5463,7 @@ window.__ModuleLoader__.load({
5314
5463
  function sameContribution(left, right) {
5315
5464
  return left !== void 0 && left.anchorSeq === right.anchorSeq && left.partial?.blocks === right.partial?.blocks && left.partial?.turn === right.partial?.turn && left.partial?.step === right.partial?.step && left.running === right.running && sameReferences$1(left.nodes, right.nodes);
5316
5465
  }
5317
- /** Incremental compatibility projection for StatsLine and legacy top-level snapshot fields. */
5466
+ /** Incremental compatibility projection for StatsPills and legacy top-level snapshot fields. */
5318
5467
  var LegacySliceBuilder = class {
5319
5468
  contributions = /* @__PURE__ */ new Map();
5320
5469
  finalizedContributions = /* @__PURE__ */ new Map();
@@ -5521,6 +5670,7 @@ window.__ModuleLoader__.load({
5521
5670
  //#region ../../core/session/src/surface.ts
5522
5671
  /** Runtime counterpart of the message-producing event union. */
5523
5672
  const SURFACE_EVENT_TYPES = new Set([
5673
+ "system/message",
5524
5674
  "user/message",
5525
5675
  "assistant/message",
5526
5676
  "tool/result"
@@ -5945,7 +6095,7 @@ window.__ModuleLoader__.load({
5945
6095
  }
5946
6096
  //#endregion
5947
6097
  //#region lib/types/client/conversation-nodes/request-prompt.js
5948
- /** Place a request's system field at the start of its visible message series. */
6098
+ /** Place a request's system prompt at the start of its visible message series. */
5949
6099
  function requestPromptAnchor(match, previous, isInitial) {
5950
6100
  if (match.location.kind !== "step") return match.event.seq;
5951
6101
  if (previous === void 0 && !isInitial) return match.event.seq;
@@ -5958,7 +6108,41 @@ window.__ModuleLoader__.load({
5958
6108
  return current?.kind === "system-prompt" ? current.anchorSeq : requestPromptAnchor(match, previous, isInitial);
5959
6109
  }
5960
6110
  /**
5961
- * Request-header prompt Definition for the Chat target.
6111
+ * System-prompt surface node Definition for the Chat target. It owns every
6112
+ * `system/message` event on the Chat target so the unknown-surface fallback
6113
+ * never renders the prompt as a transcript row. Each nonempty append owns a
6114
+ * prompt card, even without a loaded request header. Initial cards precede
6115
+ * their step's input; in-history updates stay at their own positions. The
6116
+ * request-prompt Definition owns replacement and later-series cards. Positional
6117
+ * replacements advance the effective prompt without changing historical cards.
6118
+ * @param inspect - Pure surface interpretation supplied by uiConversation.
6119
+ * @returns The Chat system-prompt Definition.
6120
+ */
6121
+ function systemMessageDefinition(inspect) {
6122
+ return {
6123
+ kind: "system-message",
6124
+ target: "chat",
6125
+ match: (event) => event.type === "system/message" || "surfaceOp" in event && event.surfaceOp !== "append" ? {
6126
+ id: String(event.seq),
6127
+ role: "start"
6128
+ } : null,
6129
+ start: (_context, match, reader) => {
6130
+ return inspect(reader.previous("system-message")?.state, match.event);
6131
+ },
6132
+ update: (context) => context.state,
6133
+ buildViewNode: (context) => {
6134
+ const state = context.state?.introduced;
6135
+ if (state === void 0 || state.text === "" || context.start?.event.type !== "system/message" || context.start.event.surfaceOp !== "append") return null;
6136
+ return chatNode(context, "system-prompt", state.update ? state.seq : requestPromptAnchor(context.start, void 0, true), {
6137
+ text: state.text,
6138
+ ...state.update ? { update: true } : {}
6139
+ });
6140
+ }
6141
+ };
6142
+ }
6143
+ /**
6144
+ * Request-header prompt Definition for the Chat target. Resume and explicit
6145
+ * series starts retain a prompt card even when the system text is unchanged.
5962
6146
  * @param inspect - the shared prompt interpretation, supplied by the
5963
6147
  * uiConversation service (a client bundle cannot value-import it).
5964
6148
  * @returns the Chat request-prompt Definition.
@@ -5974,15 +6158,19 @@ window.__ModuleLoader__.load({
5974
6158
  start: (context, match, reader) => {
5975
6159
  if (match.event.type !== "request/header") throw new Error("request-prompt start requires request/header");
5976
6160
  const previous = reader.previous("request-prompt")?.state;
6161
+ const systemContext = reader.previous("system-message");
6162
+ const system = systemContext?.state.effective;
5977
6163
  const location = match.location.kind === "step" ? {
5978
6164
  turn: match.location.turn.turn,
5979
6165
  step: match.location.step.step
5980
6166
  } : {};
5981
- const inspection = inspect(previous?.prompt, match.event);
6167
+ const inspection = inspect(previous?.prompt, match.event, system);
5982
6168
  const change = inspection.change?.kind;
6169
+ const systemEvent = systemContext?.matches[0]?.event;
6170
+ const shownByUpdate = system !== void 0 && systemEvent?.type === "system/message" && systemEvent.surfaceOp === "append" && (system.update || previous === void 0) && system.turn === location.turn && system.step === location.step;
5983
6171
  return {
5984
6172
  anchorSeq: stableRequestPromptAnchor(context, match, previous, match.event.data.reason === "initial"),
5985
- showsPrompt: previous === void 0 || match.event.data.reason === "series" || match.event.data.startsSeries === true || change === "system" || change === "system-and-tools",
6173
+ showsPrompt: !shownByUpdate && (previous === void 0 || match.event.data.reason !== "change" || match.event.data.startsSeries === true || change === "system" || change === "system-and-tools"),
5986
6174
  ...location,
5987
6175
  ...inspection
5988
6176
  };
@@ -5999,11 +6187,12 @@ window.__ModuleLoader__.load({
5999
6187
  };
6000
6188
  }
6001
6189
  /**
6002
- * Register model-request system prompts in the Chat flow.
6190
+ * Register the system-prompt surface node and the model-request prompt card in the Chat flow.
6003
6191
  * @param ctx - Owning UI Conversation context.
6004
6192
  */
6005
6193
  function registerRequestPromptConversationNode(ctx) {
6006
- ctx.uiConversation.events.register(requestPromptDefinition((previous, event) => ctx.uiConversation.inspectRequestPrompt(previous, event)));
6194
+ ctx.uiConversation.events.register(systemMessageDefinition((previous, event) => ctx.uiConversation.inspectSystemPrompt(previous, event)));
6195
+ ctx.uiConversation.events.register(requestPromptDefinition((previous, event, system) => ctx.uiConversation.inspectRequestPrompt(previous, event, system)));
6007
6196
  }
6008
6197
  //#endregion
6009
6198
  //#region lib/types/client/conversation-nodes/retry.js
@@ -6198,13 +6387,13 @@ window.__ModuleLoader__.load({
6198
6387
  }
6199
6388
  function updateDispatch(state, match) {
6200
6389
  const event = match.event;
6201
- if (event.type !== "tool/code-dispatch-start" && event.type !== "tool/code-dispatch") return state;
6390
+ if (event.type !== "tool/ptc-dispatch-start" && event.type !== "tool/ptc-dispatch") return state;
6202
6391
  const data = event.data;
6203
6392
  const parentCallId = String(data.parentCallId);
6204
6393
  const subCallId = String(data.subCallId);
6205
6394
  const siblings = state.children.get(parentCallId) ?? [];
6206
6395
  const index = siblings.findIndex((candidate) => candidate.callId === subCallId);
6207
- if (event.type === "tool/code-dispatch-start") {
6396
+ if (event.type === "tool/ptc-dispatch-start") {
6208
6397
  if (index >= 0 || !acceptsEdge(state, parentCallId, subCallId)) return state;
6209
6398
  const children = new Map(state.children);
6210
6399
  children.set(parentCallId, [...siblings, childCall(match, data)]);
@@ -6290,7 +6479,7 @@ window.__ModuleLoader__.load({
6290
6479
  for (const candidate of context.matches) state = updateDispatch(state, candidate);
6291
6480
  return state;
6292
6481
  }
6293
- /** Root Tool lifecycle and nested Code Dispatch Definition. */
6482
+ /** Root Tool lifecycle and nested PTC dispatch Definition. */
6294
6483
  const toolDefinition = {
6295
6484
  kind: "tool-call",
6296
6485
  target: "chat",
@@ -6303,7 +6492,7 @@ window.__ModuleLoader__.load({
6303
6492
  id: String(event.data.message.source.callId),
6304
6493
  role: "update"
6305
6494
  };
6306
- if (event.type === "tool/code-dispatch-start" || event.type === "tool/code-dispatch") {
6495
+ if (event.type === "tool/ptc-dispatch-start" || event.type === "tool/ptc-dispatch") {
6307
6496
  const rootCallId = event.data.rootCallId;
6308
6497
  return typeof rootCallId === "string" && rootCallId !== "" ? {
6309
6498
  id: rootCallId,
@@ -7102,160 +7291,6 @@ window.__ModuleLoader__.load({
7102
7291
  registerChatConversationView(ctx);
7103
7292
  }
7104
7293
  //#endregion
7105
- //#region lib/types/client/details/tool-node-reader.js
7106
- function toolNode(node) {
7107
- return node?.kind === "tool-call" ? node : void 0;
7108
- }
7109
- /**
7110
- * Find any root or nested Tool lifecycle through the internal Node store.
7111
- * @param snapshot - current Conversation snapshot.
7112
- * @param callId - root or nested call identity.
7113
- * @returns current Tool lifecycle when materialized in the loaded window.
7114
- */
7115
- function findToolCall(snapshot, callId) {
7116
- const visit = (block) => {
7117
- if (block.callId === callId) return block;
7118
- for (const child of block.subCalls) {
7119
- const found = visit(child);
7120
- if (found !== void 0) return found;
7121
- }
7122
- };
7123
- for (const node of snapshot.nodes.values()) {
7124
- const root = toolNode(node)?.data.root;
7125
- if (root === void 0) continue;
7126
- const found = visit(root);
7127
- if (found !== void 0) return found;
7128
- }
7129
- }
7130
- //#endregion
7131
- //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/details/DetailsPanel.module.css.mjs
7132
- const css$1 = "._2ctAZa_root{border-left:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex-direction:column;min-width:0;height:100%;display:flex}._2ctAZa_header{border-bottom:.5px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:8px;padding:14px 12px 12px;display:flex}._2ctAZa_title{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:20px;overflow:hidden}._2ctAZa_close{corner-shape:round;width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:999px;flex:none;place-items:center;display:grid}._2ctAZa_close:hover{background:var(--dsw-alias-interactive-bg-hover)}._2ctAZa_body{flex:1;min-height:0;padding:12px 16px;overflow-y:auto}._2ctAZa_empty{color:var(--dsw-alias-label-tertiary);padding:8px 0;font-size:13px;line-height:20px}._2ctAZa_section{margin-bottom:16px}._2ctAZa_sectionLabel{color:var(--dsw-alias-label-secondary);margin-bottom:6px;font-size:12px;font-weight:500;line-height:18px}._2ctAZa_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}._2ctAZa_code[data-error]{color:var(--dsw-alias-state-error-primary)}";
7133
- const tagId$1 = "@deepseek-ai/dsh-client-ui-chat/DetailsPanel.module.css";
7134
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
7135
- const tag = document.createElement("style");
7136
- tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-chat";
7137
- tag.dataset.pluginCss = tagId$1;
7138
- tag.textContent = css$1;
7139
- document.head.appendChild(tag);
7140
- }
7141
- var DetailsPanel_module_css_default = {
7142
- "body": "_2ctAZa_body",
7143
- "close": "_2ctAZa_close",
7144
- "code": "_2ctAZa_code",
7145
- "empty": "_2ctAZa_empty",
7146
- "header": "_2ctAZa_header",
7147
- "root": "_2ctAZa_root",
7148
- "section": "_2ctAZa_section",
7149
- "sectionLabel": "_2ctAZa_sectionLabel",
7150
- "title": "_2ctAZa_title"
7151
- };
7152
- //#endregion
7153
- //#region lib/types/client/details/DetailsPanel.js
7154
- function settledMaterial(node, callId) {
7155
- return {
7156
- name: node.call?.name ?? callId,
7157
- argsRaw: node.call?.argsRaw ?? null,
7158
- block: node
7159
- };
7160
- }
7161
- function runningMaterial(call) {
7162
- return {
7163
- name: call.name,
7164
- argsRaw: call.argsRaw,
7165
- block: call
7166
- };
7167
- }
7168
- function materialFor(s, callId) {
7169
- const found = findToolCall(s, callId);
7170
- if (found === void 0) return null;
7171
- return "kind" in found ? settledMaterial(found, callId) : runningMaterial(found);
7172
- }
7173
- function pretty(raw) {
7174
- try {
7175
- return JSON.stringify(JSON.parse(raw), null, 2);
7176
- } catch {
7177
- return raw;
7178
- }
7179
- }
7180
- /** Flatten a settled result for the no-ui-tool fallback. */
7181
- function rawResultText(block) {
7182
- if (!("kind" in block)) return "";
7183
- const parts = block.content.map((item) => item.type === "text" ? item.text : JSON.stringify(item, null, 2));
7184
- if (parts.length === 0 && block.error !== void 0) parts.push(`${block.error.name}: ${block.error.code}`);
7185
- return parts.join("\n");
7186
- }
7187
- function DetailsPanel({ useChat, useSessions, sessionId, useStore, renderSlot, closeDetails, t }) {
7188
- const selection = useStore((s) => s.selection);
7189
- const sessionCwd = useSessions((list) => list.byId[sessionId]?.cwd);
7190
- const callId = selection?.callId;
7191
- const material = useChat((s) => callId === void 0 ? null : materialFor(s, callId), (a, b) => (0, _deepseek_ai_dsh_client_store.shallowEqual)(a, b));
7192
- return (0, react_jsx_runtime.jsxs)("div", {
7193
- className: DetailsPanel_module_css_default.root,
7194
- children: [(0, react_jsx_runtime.jsxs)("div", {
7195
- className: DetailsPanel_module_css_default.header,
7196
- children: [(0, react_jsx_runtime.jsx)("div", {
7197
- className: DetailsPanel_module_css_default.title,
7198
- children: selection === null ? t("details.title") : material?.name ?? selection.toolName ?? t("details.title")
7199
- }), (0, react_jsx_runtime.jsx)("button", {
7200
- type: "button",
7201
- className: DetailsPanel_module_css_default.close,
7202
- "aria-label": t("details.close"),
7203
- onClick: () => {
7204
- closeDetails();
7205
- },
7206
- children: (0, react_jsx_runtime.jsx)("svg", {
7207
- viewBox: "0 0 16 16",
7208
- width: "14",
7209
- height: "14",
7210
- "aria-hidden": true,
7211
- children: (0, react_jsx_runtime.jsx)("path", {
7212
- d: "M4 4l8 8M12 4l-8 8",
7213
- stroke: "currentColor",
7214
- strokeWidth: "1.5",
7215
- strokeLinecap: "round"
7216
- })
7217
- })
7218
- })]
7219
- }), (0, react_jsx_runtime.jsx)("div", {
7220
- className: DetailsPanel_module_css_default.body,
7221
- children: selection === null || callId === void 0 ? (0, react_jsx_runtime.jsx)("div", {
7222
- className: DetailsPanel_module_css_default.empty,
7223
- children: t("details.empty")
7224
- }) : material === null ? (0, react_jsx_runtime.jsx)("div", {
7225
- className: DetailsPanel_module_css_default.empty,
7226
- children: t("details.notInWindow")
7227
- }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [material.argsRaw !== null && (0, react_jsx_runtime.jsxs)("section", {
7228
- className: DetailsPanel_module_css_default.section,
7229
- children: [(0, react_jsx_runtime.jsx)("div", {
7230
- className: DetailsPanel_module_css_default.sectionLabel,
7231
- children: t("details.input")
7232
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.CodeBlock, {
7233
- code: pretty(material.argsRaw),
7234
- lang: "json",
7235
- copyLabel: t("copy"),
7236
- copiedLabel: t("copied")
7237
- })]
7238
- }), (0, react_jsx_runtime.jsxs)("section", {
7239
- className: DetailsPanel_module_css_default.section,
7240
- children: [(0, react_jsx_runtime.jsx)("div", {
7241
- className: DetailsPanel_module_css_default.sectionLabel,
7242
- children: t("details.output")
7243
- }), (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderSlot("conversation.details.tool", {
7244
- block: material.block,
7245
- cwd: sessionCwd
7246
- }, { fallback: "kind" in material.block ? (0, react_jsx_runtime.jsx)("pre", {
7247
- className: DetailsPanel_module_css_default.code,
7248
- "data-error": material.block.isError || void 0,
7249
- children: rawResultText(material.block)
7250
- }) : (0, react_jsx_runtime.jsx)("div", {
7251
- className: DetailsPanel_module_css_default.empty,
7252
- children: t("details.running")
7253
- }) }) }, callId)]
7254
- })] })
7255
- })]
7256
- });
7257
- }
7258
- //#endregion
7259
7294
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-chat/src/client/settings/TranscriptViewRow.module.css.mjs
7260
7295
  const css = ".lats3W_row{border-bottom:.5px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.lats3W_rowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.lats3W_title{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.lats3W_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.lats3W_selector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lats3W_selector:hover{background:var(--dsw-alias-interactive-bg-hover)}.lats3W_chevron{flex:none}";
7261
7296
  const tagId = "@deepseek-ai/dsh-client-ui-chat/TranscriptViewRow.module.css";
@@ -8201,11 +8236,11 @@ window.__ModuleLoader__.load({
8201
8236
  "sessions",
8202
8237
  "uiSession",
8203
8238
  "uiConversation",
8204
- "layout",
8205
8239
  "locale",
8206
8240
  "settingsScope",
8207
8241
  "remote",
8208
- "remote.session"
8242
+ "remote.session",
8243
+ "sidebarRight"
8209
8244
  ];
8210
8245
  /**
8211
8246
  * Mount all Chat-owned contributions.
@@ -8270,7 +8305,7 @@ window.__ModuleLoader__.load({
8270
8305
  }
8271
8306
  },
8272
8307
  store: chatStore,
8273
- inject: (sessionId, actions) => {
8308
+ inject: (sessionId) => {
8274
8309
  const binding = ctx.sessions.binding(sessionId);
8275
8310
  if (binding === void 0) throw new Error(`ui-chat: unknown session "${sessionId}"`);
8276
8311
  const session = binding.session;
@@ -8281,15 +8316,13 @@ window.__ModuleLoader__.load({
8281
8316
  chatNode: (key) => chat.getSnapshot().nodes.source(key),
8282
8317
  chatNodeProcess: (key) => chat.getSnapshot().nodes.processSource(key)
8283
8318
  },
8284
- openDetails: (target) => {
8285
- actions.select(target);
8286
- ctx.layout.openDetails();
8287
- },
8288
- fileMentions: (owner) => ctx.get("chatFileMentions")?.forClosing(owner),
8289
- openFile: async (path) => {
8319
+ fileMentions: (owner) => ctx.get("chatFileMentions")?.forClosing(owner, sessionId),
8320
+ openFile: async (path, options) => {
8290
8321
  const cwd = ctx.sessions.list.getSnapshot().byId[sessionId]?.cwd;
8291
- const result = await ctx.remote.session.openWorkspacePath({ path: resolveWorkspacePath(cwd, path) });
8292
- if (!result.ok) throw new Error(`path open failed: ${result.error.message}`);
8322
+ const url = fileAddressFor(sessionId, cwd, path);
8323
+ if (options?.line === void 0) ctx.sidebarRight.openResource(url);
8324
+ else ctx.sidebarRight.openResource(url, { params: { line: options.line } });
8325
+ await Promise.resolve();
8293
8326
  },
8294
8327
  loadOlder: () => {
8295
8328
  session.loadOlder();
@@ -8321,20 +8354,8 @@ window.__ModuleLoader__.load({
8321
8354
  id: "stats",
8322
8355
  order: 0,
8323
8356
  locale: NS
8324
- }, StatsLine));
8357
+ }, StatsPills));
8325
8358
  ctx.slots.inject("conversation.approval.detail", () => ctx.slots.register({ name: "conversation.approval.detail" }, ApprovalCommand));
8326
- ctx.slots.inject("details", () => ctx.slots.register({
8327
- name: "details",
8328
- locale: NS,
8329
- children: { "conversation.details.tool": {
8330
- kind: "single",
8331
- scope: "session"
8332
- } },
8333
- store: chatStore,
8334
- inject: () => ({ closeDetails: () => {
8335
- ctx.layout.closeDetails();
8336
- } })
8337
- }, DetailsPanel));
8338
8359
  }
8339
8360
  //#endregion
8340
8361
  exports.EMPTY_CHAT_SNAPSHOT = EMPTY_CHAT_SNAPSHOT;