dsh-context 0.29.0 → 0.30.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/lib/client.js CHANGED
@@ -62,6 +62,7 @@ window.__ModuleLoader__.load({
62
62
  "detail.estTotal": "估算合计 ≈ {n}",
63
63
  "detail.actual": "实际 prompt {n}",
64
64
  "detail.output": "输出 {n}",
65
+ "detail.cache": "缓存 {n}%",
65
66
  "events.title": "上下文事件",
66
67
  "events.empty": "暂无上下文事件(压缩、注入、模型切换会出现在这里)",
67
68
  "events.at": "第 {t} 轮 · 第 {s} 步",
@@ -132,6 +133,12 @@ window.__ModuleLoader__.load({
132
133
  "block.content": "内容",
133
134
  "block.result": "结果",
134
135
  "block.summary": "摘要",
136
+ "block.line": "{n} 行",
137
+ "block.lines": "{n} 行",
138
+ "call.ok": "正常",
139
+ "call.fail": "失败",
140
+ "call.exit": "exit {n}",
141
+ "node.failed": "工具执行失败",
135
142
  "attach.images": "图片附件",
136
143
  "attach.other": "其他内容",
137
144
  "attach.image": "图片",
@@ -202,6 +209,7 @@ window.__ModuleLoader__.load({
202
209
  "detail.estTotal": "Estimated ≈ {n}",
203
210
  "detail.actual": "Actual Prompt {n}",
204
211
  "detail.output": "Output {n}",
212
+ "detail.cache": "Cache {n}%",
205
213
  "events.title": "Context Events",
206
214
  "events.empty": "No context events yet (compaction, injections, model switches appear here)",
207
215
  "events.at": "Turn {t} · Step {s}",
@@ -272,6 +280,12 @@ window.__ModuleLoader__.load({
272
280
  "block.content": "Content",
273
281
  "block.result": "Result",
274
282
  "block.summary": "Summary",
283
+ "block.line": "1 line",
284
+ "block.lines": "{n} lines",
285
+ "call.ok": "OK",
286
+ "call.fail": "Failed",
287
+ "call.exit": "exit {n}",
288
+ "node.failed": "Tool execution failed",
275
289
  "attach.images": "Images",
276
290
  "attach.other": "Other content",
277
291
  "attach.image": "Image",
@@ -314,7 +328,6 @@ window.__ModuleLoader__.load({
314
328
  function setPendingConsume(sessionId, guard) {
315
329
  pendingConsume.set(sessionId, guard);
316
330
  }
317
- /** Take (and clear) the guard recorded by the open path, if any. */
318
331
  function takePendingConsume(sessionId) {
319
332
  const guard = pendingConsume.get(sessionId);
320
333
  if (guard !== void 0) pendingConsume.delete(sessionId);
@@ -514,7 +527,6 @@ window.__ModuleLoader__.load({
514
527
  function numOf(value) {
515
528
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
516
529
  }
517
- /** The usable (non-null object) entries of a delivered list; [] when the field is not a list. */
518
530
  function objectsOf(value) {
519
531
  if (!Array.isArray(value)) return [];
520
532
  return value.filter((v) => v !== null && typeof v === "object");
@@ -644,10 +656,6 @@ window.__ModuleLoader__.load({
644
656
  for (let i = headers.headers.length - 1; i >= 0; i--) if (headers.headers[i].seq < seq) return headers.headers[i];
645
657
  return null;
646
658
  }
647
- /**
648
- * Reconstruct the assembled surface for one step (`seq` = the request
649
- * record's seq) or the live surface (`seq` = null).
650
- */
651
659
  function assemble(data, headers, seq) {
652
660
  const live = seq === null;
653
661
  let nodes;
@@ -679,7 +687,6 @@ window.__ModuleLoader__.load({
679
687
  const ReactDOM = require("react-dom");
680
688
  //#endregion
681
689
  //#region src/client/components/nodes.tsx
682
- /** One-line preview of a surface node (shared by the node list and the Context browser's element rows). */
683
690
  function makeNodeText(kit) {
684
691
  const { t } = kit;
685
692
  return function nodeText(n) {
@@ -714,10 +721,7 @@ window.__ModuleLoader__.load({
714
721
  }
715
722
  //#endregion
716
723
  //#region src/client/format.ts
717
- /**
718
- * Number/time formatting for the UI. `fmt` uses the same k/M suffix style
719
- * everywhere (bars, details, stats); `fmtTime` renders a local HH:MM:SS.
720
- */
724
+ /** `fmt`: the k/M suffix style shared by bars/details/stats; `fmtTime`: local HH:MM:SS. */
721
725
  function fmt(n) {
722
726
  if (n === void 0 || n === null || isNaN(n)) return "—";
723
727
  const sign = n < 0 ? "-" : "";
@@ -733,6 +737,18 @@ window.__ModuleLoader__.load({
733
737
  if (n >= 1e3) return (n / 1e3).toFixed(1) + " kB";
734
738
  return String(Math.round(n)) + " B";
735
739
  }
740
+ /**
741
+ * Cache-hit share of billed prompt-side input (`reads` over `billed`),
742
+ * TRUNCATED to two decimals (cut, not round) — same formula as the harness
743
+ * chat stats line's '缓存命中' figure and the stats board's cell. Null when
744
+ * nothing was billed. The 1e-9 epsilon absorbs only float noise (integer
745
+ * token counts never sit that close to a boundary).
746
+ */
747
+ function cacheHitPercent$1(reads, billed) {
748
+ if (!(billed > 0)) return null;
749
+ const hundredths = Math.trunc(reads / billed * 1e4 + 1e-9);
750
+ return `${Math.floor(hundredths / 100)}.${String(hundredths % 100).padStart(2, "0")}`;
751
+ }
736
752
  function fmtTime(t) {
737
753
  const d = new Date(t);
738
754
  if (isNaN(d.getTime())) return "—";
@@ -767,7 +783,6 @@ window.__ModuleLoader__.load({
767
783
  const MIN_PIXELS = 147456;
768
784
  const floorDiv = (a, b) => Math.floor(a / b);
769
785
  const ceilDiv = (a, b) => Math.floor((a + b - 1) / b);
770
- /** Token count of one patch grid (rows×cols) under the v4 layout rule. */
771
786
  function gridTokens(rows, cols) {
772
787
  let n = rows * (cols + 1) + 2;
773
788
  if (rows % 2 === 1) n += cols + 1;
@@ -849,10 +864,8 @@ window.__ModuleLoader__.load({
849
864
  return safeResize(h, w, paddedHeight, paddedWidth);
850
865
  }
851
866
  /**
852
- * Estimate the tokens one image consumes in a DeepSeek vision request, from
853
- * its pixel dimensions. Returns null for non-positive/non-finite dimensions
854
- * or when the official iteration fails to converge — callers fall back to
855
- * the generic structural price.
867
+ * Estimate the tokens one image consumes in a DeepSeek vision request from its pixel dimensions. Returns null for non-positive/non-finite
868
+ * dimensions or when the official iteration fails to converge — callers fall back to the generic structural price.
856
869
  */
857
870
  function estimateImageTokens(width, height) {
858
871
  if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return null;
@@ -871,11 +884,9 @@ window.__ModuleLoader__.load({
871
884
  //#endregion
872
885
  //#region src/client/components/images.tsx
873
886
  /**
874
- * Narrow an unknown content block to a durable image reference. Accepts both
875
- * raw message blocks (`{ type: 'image', attachment }`) and the conversation
876
- * snapshot's assistant blocks (`{ kind: 'image', attachment }`); everything
877
- * else returns null. Lenient on the ref's optional facts (name/bytes/dims) —
878
- * `resolveImage` reads only `attachmentId`.
887
+ * Narrow an unknown content block to a durable image ref: accepts both raw message blocks (`{ type: 'image', attachment }`) and the
888
+ * snapshot's assistant blocks (`{ kind: 'image', attachment }`); everything else null. Lenient on the optional facts — resolveImage reads
889
+ * only attachmentId.
879
890
  */
880
891
  function imageRefOf(block) {
881
892
  if (block === null || typeof block !== "object") return null;
@@ -901,12 +912,9 @@ window.__ModuleLoader__.load({
901
912
  };
902
913
  }
903
914
  /**
904
- * Document-level original-image preview — the chat history's ImageLightbox
905
- * recipe (dsh ui-attachment, which the browser module table does not seed)
906
- * ported onto the plugin's lc-* classes: body portal so a transformed or
907
- * filtered ancestor cannot trap the fixed backdrop, blurred mask layer,
908
- * contain-fit image, circular close control, Escape/mask close, and focus
909
- * restored to the opener on unmount.
915
+ * Document-level original-image preview — the chat history's ImageLightbox recipe (dsh ui-attachment, which the browser module table does
916
+ * not seed) ported onto the plugin's lc-* classes: body portal (a transformed/filtered ancestor cannot trap the fixed backdrop), blurred
917
+ * mask, contain-fit image, circular close, Escape/mask close, focus restored to the opener.
910
918
  */
911
919
  function AttachmentLightbox(props) {
912
920
  const { src, alt, labels, onClose } = props;
@@ -946,13 +954,9 @@ window.__ModuleLoader__.load({
946
954
  }, /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 16 }))), document.body);
947
955
  }
948
956
  /**
949
- * One attachment card: the WHOLE card is the click target — a 64px cover
950
- * tile beside a metadata column (display name, then one labeled row per
951
- * known fact: Raw, the pre-normalization raster dsh 0.1.1 records when
952
- * normalization reduced the image; Sent, the normalized raster the model
953
- * receives, with its stored byte size; and the estimated provider-billed
954
- * tokens). Clicking opens the chat-style lightbox preview (load failures
955
- * retry on click instead). Unknown facts leave no row behind.
957
+ * One attachment card, the WHOLE card the click target: 64px cover tile + metadata column Raw (the pre-normalization raster dsh records
958
+ * when normalization reduced the image), Sent (the normalized raster the model receives, with byte size), estimated provider-billed tokens.
959
+ * Click opens the chat-style lightbox; load failures retry on click; unknown facts leave no row.
956
960
  */
957
961
  function makeImageCard(kit) {
958
962
  const { t, fmt } = kit;
@@ -1066,7 +1070,6 @@ window.__ModuleLoader__.load({
1066
1070
  }
1067
1071
  //#endregion
1068
1072
  //#region src/client/components/browser.tsx
1069
- /** Flatten the `anyOf` / `oneOf` alternation into one displayable string. */
1070
1073
  function unionTypesOf(p) {
1071
1074
  const branches = [];
1072
1075
  if (Array.isArray(p.anyOf)) branches.push(...p.anyOf);
@@ -1076,12 +1079,6 @@ window.__ModuleLoader__.load({
1076
1079
  for (const b of branches) if (b !== null && typeof b === "object") parts.push(typeOf(b));
1077
1080
  return parts.length > 0 ? parts.join(" | ") : null;
1078
1081
  }
1079
- /**
1080
- * Derive a short, human-readable type label from a JSON Schema fragment.
1081
- * Arrays show their element type (`array<number>`); unions fold into
1082
- * `a | b`; enums collapse into `(enum)`. Returns `unknown` when the schema
1083
- * carries no usable signal — the row falls back to its description then.
1084
- */
1085
1082
  function typeOf(p) {
1086
1083
  const u = unionTypesOf(p);
1087
1084
  if (u !== null) return u;
@@ -1103,9 +1100,8 @@ window.__ModuleLoader__.load({
1103
1100
  return "unknown";
1104
1101
  }
1105
1102
  /**
1106
- * Locate the parameter-bearing object inside a raw tool schema. Producers
1107
- * may nest it under `parameters`, `input_schema`, `inputSchema`, or hand
1108
- * the schema as the bare JSON Schema (when `type === 'object'`).
1103
+ * Tool schemas nest parameters under `parameters`, `input_schema`, or `inputSchema` (producer-dependent), or bare when `type === 'object'`
1104
+ * `{type:'object', properties}` at the root is itself the parameter object.
1109
1105
  */
1110
1106
  function paramsOf(schema) {
1111
1107
  if (schema === null || typeof schema !== "object") return null;
@@ -1116,33 +1112,19 @@ window.__ModuleLoader__.load({
1116
1112
  if (s.type === "object" && s.properties !== void 0 && typeof s.properties === "object") return s;
1117
1113
  return null;
1118
1114
  }
1119
- /**
1120
- * One row of the parsed parameter table — name (mono-styled, with a
1121
- * required chip), short type label, and the description on its own line
1122
- * (so a long blurb never breaks the name/type rhythm).
1123
- */
1124
1115
  function ParamRow(props) {
1125
1116
  const typeLabel = typeOf(props.schema);
1126
1117
  const desc = props.schema.description;
1127
1118
  return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-param-row" }, /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-name" }, props.name), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-type" }, typeLabel), /* @__PURE__ */ React.createElement("span", { className: props.required ? "lc-ts-param-req" : "lc-ts-param-req-off" }, props.required ? "✓" : "·"), typeof desc === "string" && desc !== "" ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-desc" }, desc) : null);
1128
1119
  }
1129
1120
  /**
1130
- * Section — the ONE detail chrome of the Context browser: a labeled card
1131
- * (`lc-ts-card`) whose head carries the section title on the left and the
1132
- * extras on the right (a count badge and/or the Raw/MD switch), with the
1133
- * body below. Every expanded element renders as a stack of these — prose,
1134
- * tool calls, parameter rows, image grids, and raw JSON all share the same
1135
- * frame, so the reader scans one repeating anatomy instead of a different
1136
- * layout per content kind.
1121
+ * Section — the ONE detail chrome of the browser: every expanded element is a stack of these (labeled head + body), so the reader scans one
1122
+ * repeating anatomy per content kind.
1137
1123
  */
1138
1124
  function Section(props) {
1139
- return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", { className: props.labelClass }, props.label), props.actions ?? null, props.count !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, props.count) : null), props.children);
1125
+ const right = props.actions !== void 0 || props.meta !== void 0;
1126
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", { className: props.labelClass }, props.label), right ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-right" }, props.meta ?? null, props.actions ?? null) : null, props.count !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, props.count) : null), props.children);
1140
1127
  }
1141
- /**
1142
- * TextSection — a prose Section (system prompt, message/injection/summary
1143
- * body, thinking, answer, tool description): the head's right edge carries
1144
- * the Raw/MD switch and the body follows the per-card mode.
1145
- */
1146
1128
  function TextSection(props) {
1147
1129
  const { rich } = props;
1148
1130
  const [mode, setMode] = rich.useRichMode();
@@ -1151,21 +1133,19 @@ window.__ModuleLoader__.load({
1151
1133
  actions: /* @__PURE__ */ React.createElement(rich.RichSwitch, {
1152
1134
  mode,
1153
1135
  onPick: setMode
1154
- })
1136
+ }),
1137
+ meta: props.meta
1155
1138
  }, /* @__PURE__ */ React.createElement(rich.RichText, {
1156
1139
  text: props.text,
1157
1140
  mode
1158
1141
  }));
1159
1142
  }
1160
- /** RawSection — a dimmed text Section without a view switch (structured payloads: tool results, raw JSON). */
1161
1143
  function RawSection(props) {
1162
1144
  return /* @__PURE__ */ React.createElement(Section, { label: props.label }, /* @__PURE__ */ React.createElement("pre", { className: "lc-ts-desc-body lc-br-dim" }, props.text));
1163
1145
  }
1164
1146
  /**
1165
- * The full body of one expanded tool row: description, a parsed parameter
1166
- * table (when the schema carries one), and the raw JSON behind a toggle.
1167
- * Owns its own open/closed state for the JSON so two expanded tools stay
1168
- * independent.
1147
+ * Full tool-row body: description, parsed parameter table (when the schema carries one), raw JSON behind a per-row toggle — the JSON open
1148
+ * state is per-row so two expanded tools stay independent.
1169
1149
  */
1170
1150
  function ToolSchema(props) {
1171
1151
  const { rich } = props;
@@ -1210,14 +1190,9 @@ window.__ModuleLoader__.load({
1210
1190
  }, (jsonOpen ? "▾ " : "▸ ") + (jsonOpen ? props.labels.hide : props.labels.show)), jsonOpen ? /* @__PURE__ */ React.createElement("pre", { className: "lc-ts-desc-body lc-br-dim" }, schemaJson) : null) : null);
1211
1191
  }
1212
1192
  /**
1213
- * Render one message's content blocks as a uniform Section stack. Both
1214
- * block vocabularies normalize here raw durable blocks (`type`: text /
1215
- * reasoning / tool-call / tool-result / image, arguments) and the
1216
- * conversation snapshot's assistant blocks (`kind`: text / reasoning /
1217
- * tool-call / image, argsRaw). Consecutive images group into one grid
1218
- * section; `richable` bodies (messages, injections) carry the Raw/MD
1219
- * switch while tool-result payloads stay raw (structured data, not prose)
1220
- * and dim; nested tool-result blocks flatten into the same flow.
1193
+ * Both block vocabularies normalize here — raw durable blocks (`type`: text/reasoning/tool-call/tool-result/image) and snapshot assistant
1194
+ * blocks (`kind`: text/reasoning/tool-call/image, argsRaw). Consecutive images group into one grid; tool-result payloads carry the Raw/MD
1195
+ * switch + line count; nested tool-result blocks flatten into the same flow.
1221
1196
  */
1222
1197
  function BlocksBody(props) {
1223
1198
  const { rich, img, labels } = props;
@@ -1248,15 +1223,12 @@ window.__ModuleLoader__.load({
1248
1223
  const blockKind = blk !== null ? typeof blk.type === "string" ? blk.type : typeof blk.kind === "string" ? blk.kind : "" : "";
1249
1224
  if ((blockKind === "text" || blockKind === "reasoning") && typeof blk?.text === "string") {
1250
1225
  const label = blockKind === "reasoning" ? labels.thinking : props.textLabel;
1251
- out.push(props.richable ? /* @__PURE__ */ React.createElement(TextSection, {
1226
+ out.push(/* @__PURE__ */ React.createElement(TextSection, {
1252
1227
  key: out.length,
1253
1228
  label,
1254
1229
  text: blk.text,
1255
- rich
1256
- }) : /* @__PURE__ */ React.createElement(RawSection, {
1257
- key: out.length,
1258
- label,
1259
- text: blk.text
1230
+ rich,
1231
+ meta: props.textLabel === labels.result ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-meta" }, labels.lines(blk.text.split("\n").length)) : void 0
1260
1232
  }));
1261
1233
  continue;
1262
1234
  }
@@ -1289,11 +1261,6 @@ window.__ModuleLoader__.load({
1289
1261
  flushImages();
1290
1262
  return /* @__PURE__ */ React.createElement(React.Fragment, null, out);
1291
1263
  }
1292
- /**
1293
- * Parse a call's raw argument payload into an object; null when the payload
1294
- * is missing, malformed, or not a JSON object (callers fall back to the raw
1295
- * text).
1296
- */
1297
1264
  function parseArgs(raw) {
1298
1265
  if (typeof raw !== "string" || raw === "") return null;
1299
1266
  try {
@@ -1304,11 +1271,8 @@ window.__ModuleLoader__.load({
1304
1271
  }
1305
1272
  }
1306
1273
  /**
1307
- * The one-line summary a call's arguments carry about the call: a
1308
- * bash-style `description` says what it does; path-taking tools (edit /
1309
- * read / write) name their target via `file_path`/`path`/`filePath`. Null
1310
- * when the arguments hold neither — the row then falls back to the generic
1311
- * label.
1274
+ * A call's preview: bash-style `description`, or the target path (`file_path`/`path`/`filePath`) for edit/read/write tools; null when the
1275
+ * arguments hold neither.
1312
1276
  */
1313
1277
  function summaryIn(args) {
1314
1278
  if (args === null) return null;
@@ -1323,18 +1287,24 @@ window.__ModuleLoader__.load({
1323
1287
  }
1324
1288
  return null;
1325
1289
  }
1326
- /**
1327
- * The summary of a tool result's own call — the collapsed row's preview.
1328
- * Null when the call summarizes to nothing.
1329
- */
1330
1290
  function callSummaryOf(conv) {
1331
1291
  return summaryIn(parseArgs(conv?.call?.argsRaw));
1332
1292
  }
1333
1293
  /**
1334
- * The first call summary inside an assistant message's tool-call blocks —
1335
- * the collapsed-row preview for a text-less assistant turn. Null when no
1336
- * block summarizes.
1294
+ * The dsh `[exit code: N]` marker across the payload's direct text blocks — surfaced only on the FAILED run-state pill, the one place the
1295
+ * number matters.
1337
1296
  */
1297
+ function exitCodeOf(conv) {
1298
+ if (conv === void 0 || !Array.isArray(conv.content)) return null;
1299
+ for (const b of conv.content) {
1300
+ if (b === null || typeof b !== "object") continue;
1301
+ const blk = b;
1302
+ if (typeof blk.text !== "string") continue;
1303
+ const m = blk.text.match(/\[exit code:\s*(\d+)\]/);
1304
+ if (m !== null) return Number(m[1]);
1305
+ }
1306
+ return null;
1307
+ }
1338
1308
  function blockSummaryOf(conv) {
1339
1309
  if (conv === void 0 || !Array.isArray(conv.blocks)) return null;
1340
1310
  for (const b of conv.blocks) {
@@ -1345,37 +1315,23 @@ window.__ModuleLoader__.load({
1345
1315
  }
1346
1316
  return null;
1347
1317
  }
1348
- /**
1349
- * One tool call as a Section, mirroring the tool-definition parameter
1350
- * card: the head names the call target (mono, with the parsed argument
1351
- * count), the body lists the arguments as name/value rows. Arguments that
1352
- * are not a parseable JSON object fall back to the raw payload inside the
1353
- * same card. Shared by assistant tool-call blocks (`→`) and the call half
1354
- * of a tool result (`←`).
1355
- */
1356
1318
  function ToolCallCard(props) {
1357
1319
  const args = React.useMemo(() => parseArgs(props.argsRaw), [props.argsRaw]);
1358
1320
  return /* @__PURE__ */ React.createElement(Section, {
1359
1321
  label: (props.arrow ?? "→") + " " + props.name,
1360
1322
  labelClass: "lc-ts-call-name",
1361
- count: args !== null ? Object.keys(args).length : void 0
1323
+ meta: props.status
1362
1324
  }, args !== null ? Object.keys(args).map((k) => /* @__PURE__ */ React.createElement(CallArgRow, {
1363
1325
  key: k,
1364
1326
  name: k,
1365
1327
  value: args[k]
1366
1328
  })) : typeof props.argsRaw === "string" && props.argsRaw !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-ts-desc-body lc-br-dim" }, props.argsRaw) : null);
1367
1329
  }
1368
- /** One parsed call argument: the name on the left, the value on the right. */
1369
1330
  function CallArgRow(props) {
1370
1331
  const v = props.value;
1371
1332
  const text = typeof v === "string" ? v : v === void 0 ? "" : JSON.stringify(v);
1372
1333
  return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-arg-row" }, /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-name" }, props.name), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-arg-val" }, text));
1373
1334
  }
1374
- /**
1375
- * The actual content of one surface element, joined from the conversation
1376
- * snapshot — rendered as a uniform Section stack (see BlocksBody); the
1377
- * element row's open body wraps the stack in `lc-br-content`.
1378
- */
1379
1335
  function NodeContent(props) {
1380
1336
  const { node, conv, rich, img, labels } = props;
1381
1337
  if (conv === void 0) {
@@ -1394,18 +1350,22 @@ window.__ModuleLoader__.load({
1394
1350
  img,
1395
1351
  labels
1396
1352
  });
1397
- if (conv.kind === "tool-result") return /* @__PURE__ */ React.createElement(React.Fragment, null, conv.call != null ? /* @__PURE__ */ React.createElement(ToolCallCard, {
1398
- arrow: "←",
1399
- name: conv.call.name,
1400
- argsRaw: conv.call.argsRaw
1401
- }) : null, Array.isArray(conv.content) ? /* @__PURE__ */ React.createElement(BlocksBody, {
1402
- blocks: conv.content,
1403
- richable: false,
1404
- textLabel: labels.result,
1405
- rich,
1406
- img,
1407
- labels
1408
- }) : null);
1353
+ if (conv.kind === "tool-result") {
1354
+ const err = node.err === true || conv.isError === true;
1355
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, conv.call != null ? /* @__PURE__ */ React.createElement(ToolCallCard, {
1356
+ arrow: "←",
1357
+ name: conv.call.name,
1358
+ argsRaw: conv.call.argsRaw,
1359
+ status: labels.callState(err, err ? exitCodeOf(conv) : null)
1360
+ }) : null, Array.isArray(conv.content) ? /* @__PURE__ */ React.createElement(BlocksBody, {
1361
+ blocks: conv.content,
1362
+ richable: false,
1363
+ textLabel: labels.result,
1364
+ rich,
1365
+ img,
1366
+ labels
1367
+ }) : null);
1368
+ }
1409
1369
  if (conv.kind === "compaction") return typeof conv.summary === "string" && conv.summary !== "" ? /* @__PURE__ */ React.createElement(TextSection, {
1410
1370
  label: labels.summary,
1411
1371
  text: conv.summary,
@@ -1421,19 +1381,16 @@ window.__ModuleLoader__.load({
1421
1381
  });
1422
1382
  return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint);
1423
1383
  }
1424
- /** Group an assembled surface's nodes by category (for per-category counts). */
1425
1384
  function byCatOf(asm) {
1426
1385
  const m = {};
1427
1386
  for (const n of asm.nodes) (m[n.cat] ??= []).push(n);
1428
1387
  return m;
1429
1388
  }
1430
- /** Count one category's elements in an assembled surface (header cats included). */
1431
1389
  function countOf(asm, c) {
1432
1390
  if (c === "system") return asm.header !== null && asm.header.system !== void 0 ? 1 : 0;
1433
1391
  if (c === "tools") return asm.header !== null ? asm.header.tools.length : 0;
1434
1392
  return byCatOf(asm)[c]?.length ?? 0;
1435
1393
  }
1436
- /** The last request of `turn` in a seq-ordered timeline, or null. */
1437
1394
  function lastOfTurn(requests, turn) {
1438
1395
  for (let i = requests.length - 1; i >= 0; i--) if ((requests[i].turn ?? 0) === turn) return requests[i];
1439
1396
  return null;
@@ -1534,9 +1491,11 @@ window.__ModuleLoader__.load({
1534
1491
  const toggleElem = (key) => {
1535
1492
  setOpenElem(openElem === key ? null : key);
1536
1493
  };
1537
- /** One expandable element row (preview line; the open body is the
1538
- * uniform indented section stack `lc-br-content`). */
1539
- const elemRow = (key, tag, preview, tokens, time, body) => {
1494
+ /**
1495
+ * Expandable element row; `err` rows carry the red run-state dot right after the chevron (the chat's failed-tool marker) so a failed
1496
+ * result scans while collapsed.
1497
+ */
1498
+ const elemRow = (key, tag, preview, tokens, time, body, err = false) => {
1540
1499
  const open = openElem === key;
1541
1500
  return /* @__PURE__ */ React.createElement("div", {
1542
1501
  key,
@@ -1547,7 +1506,10 @@ window.__ModuleLoader__.load({
1547
1506
  onClick: () => {
1548
1507
  toggleElem(key);
1549
1508
  }
1550
- }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, tag) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-preview" }, preview), time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-time" }, fmtTime(time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "≈" + fmt(tokens))), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, body) : null);
1509
+ }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), err ? /* @__PURE__ */ React.createElement("span", {
1510
+ className: "lc-br-err-dot",
1511
+ title: t("node.failed")
1512
+ }) : null, tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, tag) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-preview" }, preview), time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-time" }, fmtTime(time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "≈" + fmt(tokens))), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, body) : null);
1551
1513
  };
1552
1514
  const catBody = (c) => {
1553
1515
  if (c === "system") {
@@ -1579,17 +1541,18 @@ window.__ModuleLoader__.load({
1579
1541
  });
1580
1542
  }
1581
1543
  return (byCat[c] ?? []).slice().reverse().map((n) => {
1544
+ const conv = bySeq.get(n.seq);
1545
+ const rowErr = n.cat === "tool" && (n.err === true || conv !== void 0 && conv.isError === true);
1582
1546
  let tag = null;
1583
1547
  let preview = nodeText(n);
1584
1548
  if (n.cat === "tool") {
1585
- tag = n.skill ? t("node.skillTag", { name: n.skill }) : (n.tool ?? "?") + (n.err ? " ⚠" : "");
1586
- preview = callSummaryOf(bySeq.get(n.seq)) ?? t("node.toolResult") + (n.err ? " ⚠" : "");
1549
+ tag = n.skill ? t("node.skillTag", { name: n.skill }) : n.tool ?? "?";
1550
+ preview = callSummaryOf(conv) ?? t("node.toolResult");
1587
1551
  } else if (n.cat === "assistant" && Array.isArray(n.calls) && n.calls.length > 0) {
1588
1552
  tag = n.calls.join(" › ");
1589
- preview = (n.text !== void 0 && n.text !== "" ? n.text : null) ?? blockSummaryOf(bySeq.get(n.seq)) ?? t("node.empty");
1590
- } else if (n.cat === "assistant" && (n.text === void 0 || n.text === "")) preview = blockSummaryOf(bySeq.get(n.seq)) ?? preview;
1553
+ preview = (n.text !== void 0 && n.text !== "" ? n.text : null) ?? blockSummaryOf(conv) ?? t("node.empty");
1554
+ } else if (n.cat === "assistant" && (n.text === void 0 || n.text === "")) preview = blockSummaryOf(conv) ?? preview;
1591
1555
  else if (n.cat === "user") {
1592
- const conv = bySeq.get(n.seq);
1593
1556
  const imgCount = conv !== void 0 && Array.isArray(conv.content) ? conv.content.filter((b) => imageRefOf(b) !== null).length : 0;
1594
1557
  if (imgCount > 0 && openElem !== `n${n.seq}`) tag = t("attach.image") + (imgCount > 1 ? " ×" + String(imgCount) : "");
1595
1558
  } else if (n.cat === "inject" && !n.skill) {
@@ -1598,7 +1561,7 @@ window.__ModuleLoader__.load({
1598
1561
  }
1599
1562
  return elemRow(`n${n.seq}`, tag, preview, n.tokens, n.time, /* @__PURE__ */ React.createElement(NodeContent, {
1600
1563
  node: n,
1601
- conv: bySeq.get(n.seq),
1564
+ conv,
1602
1565
  rich,
1603
1566
  img: {
1604
1567
  Card: ImageCard,
@@ -1611,10 +1574,12 @@ window.__ModuleLoader__.load({
1611
1574
  result: t("block.result"),
1612
1575
  summary: t("block.summary"),
1613
1576
  images: t("attach.images"),
1614
- other: t("attach.other")
1577
+ other: t("attach.other"),
1578
+ lines: (n) => t(n === 1 ? "block.line" : "block.lines", { n }),
1579
+ callState: (err, exit) => /* @__PURE__ */ React.createElement("span", { className: "lc-ts-call-state " + (err ? "lc-ts-call-err" : "lc-ts-call-ok") }, /* @__PURE__ */ React.createElement("i", null), err ? t("call.fail") + (exit !== null ? " · " + t("call.exit", { n: exit }) : "") : t("call.ok"))
1615
1580
  },
1616
- hint: bySeq.get(n.seq) === void 0 && awaiting ? t("browser.loading") : t("browser.noContent")
1617
- }));
1581
+ hint: conv === void 0 && awaiting ? t("browser.loading") : t("browser.noContent")
1582
+ }), rowErr);
1618
1583
  });
1619
1584
  };
1620
1585
  return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("browser.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-br-hint" }, t("browser.deltaHint")), /* @__PURE__ */ React.createElement("select", {
@@ -1669,12 +1634,9 @@ window.__ModuleLoader__.load({
1669
1634
  //#endregion
1670
1635
  //#region src/client/components/stackedBar.tsx
1671
1636
  /**
1672
- * DSH's automatic compaction trigger as a fraction of the routed context
1673
- * window the default `thresholdRatio` of `@deepseek-ai/dsh-compaction-basic`
1674
- * (it compacts at step boundaries once `floor(contextWindow × ratio)` is
1675
- * reached). DSH does not publish the configured ratio to plugins or clients,
1676
- * so the UI mirrors the default here; deployments that tune `thresholdRatio`
1677
- * / `modelPolicies` can adjust it if they want the reserve band to match.
1637
+ * Mirror of dsh-compaction-basic's default `thresholdRatio` (0.8): it compacts at step boundaries once `floor(contextWindow × ratio)` is
1638
+ * reached; DSH does not publish the configured ratio to plugins/clients, so the reserve band mirrors the default deployments tuning
1639
+ * `thresholdRatio`/`modelPolicies` should adjust it to match.
1678
1640
  */
1679
1641
  const AUTO_COMPACT_RATIO = .8;
1680
1642
  function makeStackedBar(kit) {
@@ -1945,7 +1907,6 @@ window.__ModuleLoader__.load({
1945
1907
  }
1946
1908
  //#endregion
1947
1909
  //#region src/client/components/settingsCard.tsx
1948
- /** One preference row: label + selector pill opening the framework Menu. */
1949
1910
  function PrefRow(props) {
1950
1911
  const [open, setOpen] = React.useState(false);
1951
1912
  const active = props.options.find((o) => o.id === props.value)?.label ?? props.value;
@@ -2025,7 +1986,6 @@ window.__ModuleLoader__.load({
2025
1986
  }
2026
1987
  //#endregion
2027
1988
  //#region src/client/settings.ts
2028
- /** Read the known preferences out of a raw section; unknown/absent fields drop out. */
2029
1989
  function prefsOf(value) {
2030
1990
  if (value === null || typeof value !== "object") return {};
2031
1991
  const v = value;
@@ -2093,7 +2053,6 @@ window.__ModuleLoader__.load({
2093
2053
  model: "⇄",
2094
2054
  mode: "⇄"
2095
2055
  };
2096
- /** Build the event text helpers bound to the translate function. */
2097
2056
  function makeEventText(t) {
2098
2057
  function eventLabel(ev) {
2099
2058
  if (ev.kind === "compaction") return t("ev.compaction", { n: ev.count || 0 });
@@ -2110,11 +2069,8 @@ window.__ModuleLoader__.load({
2110
2069
  return label;
2111
2070
  }
2112
2071
  /**
2113
- * Where this event sits in the request timeline, as a label or null.
2114
- * Boundary events (compaction/prune) show the GAP they sit in: same-turn
2115
- * "Step 2→3", cross-turn "Turn 50 · Step 8 → Turn 51 · Step 1". Injections
2116
- * and model switches belong to one request and keep the single point.
2117
- * Events with no following request (in flight) stay unlabeled.
2072
+ * Where this event sits in the timeline: boundary events (compaction/prune) label the GAP they sit in — same-turn 'Turn 2 · Step 3→4',
2073
+ * cross-turn 'Turn 50 · Step 8 Turn 51 · Step 1'; other kinds keep their single point; no turn/step (in flight) → null.
2118
2074
  */
2119
2075
  function eventAt(ev) {
2120
2076
  if (ev.kind === "compaction" || ev.kind === "prune") {
@@ -2183,20 +2139,14 @@ window.__ModuleLoader__.load({
2183
2139
  };
2184
2140
  }
2185
2141
  const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
2186
- /** Short `owner/repo` form of the GitHub URL, for display only. */
2187
2142
  const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
2188
2143
  //#endregion
2189
2144
  //#region src/client/latestVersion.ts
2190
2145
  /**
2191
- * Latest published version of this plugin on the npm registry the one
2192
- * live check the Plugin info card performs. Fetched lazily on the first
2193
- * card mount and cached with a 1-hour TTL, so the registry is hit at most
2194
- * once per hour per page. Every failure mode (offline, CSP-blocked,
2195
- * non-200, malformed body) narrows to null = no upgrade hint, so the card
2196
- * silently keeps its static version.
2146
+ * One live check for the Plugin-info card: lazy with 1h TTL; every failure
2147
+ * mode narrows to null so the card silently keeps its static version.
2197
2148
  */
2198
2149
  const REGISTRY_URL = "https://registry.npmjs.org/dsh-context/latest";
2199
- /** How long a registry result stays fresh before the next check refetches. */
2200
2150
  const TTL_MS = 36e5;
2201
2151
  let cached = null;
2202
2152
  function fetchLatestVersion() {
@@ -2231,7 +2181,7 @@ window.__ModuleLoader__.load({
2231
2181
  return function PluginInfo() {
2232
2182
  const [latest, setLatest] = React.useState(null);
2233
2183
  React.useEffect(() => {
2234
- if ("0.29.0".includes("-dev")) return;
2184
+ if ("0.30.1".includes("-dev")) return;
2235
2185
  let on = true;
2236
2186
  fetchLatestVersion().then((v) => {
2237
2187
  if (on && v) setLatest(v);
@@ -2240,8 +2190,8 @@ window.__ModuleLoader__.load({
2240
2190
  on = false;
2241
2191
  };
2242
2192
  }, []);
2243
- const update = latest !== null && isNewerVersion(latest, "0.29.0") ? latest : null;
2244
- const nameValue = ["dsh-context (v0.29.0)"];
2193
+ const update = latest !== null && isNewerVersion(latest, "0.30.1") ? latest : null;
2194
+ const nameValue = ["dsh-context (v0.30.1)"];
2245
2195
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
2246
2196
  key: "update",
2247
2197
  className: "lc-pi-update"
@@ -2269,7 +2219,7 @@ window.__ModuleLoader__.load({
2269
2219
  return /* @__PURE__ */ React.createElement("div", { className: "lc-detail" }, /* @__PURE__ */ React.createElement("div", { className: "lc-detail-head" }, /* @__PURE__ */ React.createElement("b", null, head), marker !== null && markerAt !== null ? /* @__PURE__ */ React.createElement("span", {
2270
2220
  className: "lc-detail-marker",
2271
2221
  title: eventLabel(marker)
2272
- }, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, /* @__PURE__ */ React.createElement("span", null, fmtTime(req.time)), /* @__PURE__ */ React.createElement("span", null, t("detail.estTotal", { n: fmt(req.total) })), req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt(req.prompt) })) : null, req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", null, t("detail.output", { n: fmt(req.output) })) : null), /* @__PURE__ */ React.createElement(StackedBar, {
2222
+ }, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, /* @__PURE__ */ React.createElement("span", null, fmtTime(req.time)), req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt(req.prompt) })) : null, req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", null, t("detail.output", { n: fmt(req.output) })) : null, req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-cache" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null), /* @__PURE__ */ React.createElement(StackedBar, {
2273
2223
  parts: partsOf(req),
2274
2224
  height: 10
2275
2225
  }), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c) => {
@@ -2366,7 +2316,6 @@ window.__ModuleLoader__.load({
2366
2316
  }
2367
2317
  return any ? total : null;
2368
2318
  }
2369
- /** Short money format: two decimals at and above one unit, two significant digits below. */
2370
2319
  function formatCost(amount, currency) {
2371
2320
  return (currency === "cny" ? "¥" : "$") + (amount >= 1 ? amount.toFixed(2) : amount.toPrecision(2));
2372
2321
  }
@@ -2391,24 +2340,15 @@ window.__ModuleLoader__.load({
2391
2340
  //#endregion
2392
2341
  //#region src/client/components/statsBoard.tsx
2393
2342
  /**
2394
- * Cache-hit share of billed prompt-side input — same buckets as the harness's
2395
- * chat stats line below the input box (`cacheReadTokens` over the three
2396
- * disjoint billed buckets: uncached + reads + writes). Unlike that line's
2397
- * whole-percent rounding, this one TRUNCATES to two decimal places (cut, not
2398
- * round). Null when no input was billed. A tiny epsilon keeps a double stored
2399
- * a hair below a cent boundary (e.g. 80.00 as 79.9999999999…) from losing its
2400
- * last digit — with integer token counts no genuine value ever sits that close
2401
- * to a boundary, so the epsilon can only absorb float noise. Bucket reads go
2402
- * through numOf so a malformed payload degrades to a dash instead of NaN text.
2343
+ * Cache-hit share of billed prompt-side input — same three disjoint buckets
2344
+ * as the harness chat line (`cacheReadTokens` over uncached + reads + writes)
2345
+ * and the step line's 缓存 figure; null when nothing was billed.
2403
2346
  */
2404
2347
  function cacheHitPercent(usage) {
2405
2348
  const uncached = numOf(usage.uncachedInputTokens);
2406
2349
  const reads = numOf(usage.cacheReadTokens);
2407
2350
  const writes = numOf(usage.cacheWriteTokens);
2408
- const billed = uncached + reads + writes;
2409
- if (billed === 0) return null;
2410
- const hundredths = Math.trunc(reads / billed * 1e4 + 1e-9);
2411
- return `${Math.floor(hundredths / 100)}.${String(hundredths % 100).padStart(2, "0")}`;
2351
+ return cacheHitPercent$1(reads, uncached + reads + writes);
2412
2352
  }
2413
2353
  function makeStatsBoard(kit) {
2414
2354
  const { t, fmt } = kit;
@@ -2446,12 +2386,8 @@ window.__ModuleLoader__.load({
2446
2386
  //#endregion
2447
2387
  //#region src/client/components/trendChart.tsx
2448
2388
  /**
2449
- * Collapse a per-step request timeline into one bar per turn: each turn is
2450
- * represented by its LAST step's record (the context state when the turn
2451
- * finished), tagged with the number of steps the turn spans so the bar can
2452
- * keep the turn's column width and the detail can label it. Requests of one
2453
- * turn are consecutive in the log, so a run of equal turns is replaced by
2454
- * its final record.
2389
+ * Collapse per-step requests into one bar per turn each turn is represented by its LAST step's record, tagged `stepCount` for the bar's
2390
+ * column width; the log keeps one turn's requests consecutive, so a run of equal turns collapses to its final record.
2455
2391
  */
2456
2392
  function aggregateByTurn(requests) {
2457
2393
  const out = [];
@@ -2475,10 +2411,8 @@ window.__ModuleLoader__.load({
2475
2411
  return out;
2476
2412
  }
2477
2413
  /**
2478
- * Attach each boundary event (compaction/prune) to the first request
2479
- * logged after it one entry per request index, for the marker above
2480
- * the bar and the range chip in the detail header. Shared by TrendChart
2481
- * and the detail panel so both show the SAME event for a request.
2414
+ * Attach each boundary event (compaction/prune) to the first request logged after it — one entry per index, for the ✂ marker and the detail
2415
+ * chip; shared with the detail panel so both show the SAME event.
2482
2416
  */
2483
2417
  function attachMarkers(requests, events) {
2484
2418
  const markers = new Array(requests.length);
@@ -2500,14 +2434,8 @@ window.__ModuleLoader__.load({
2500
2434
  const anchorOf = (req) => typeof req.prompt === "number" && req.prompt > 0 && req.total > 0 ? req.prompt / req.total : 1;
2501
2435
  const barTotalOf = (req) => typeof req.prompt === "number" && req.prompt > 0 ? req.prompt : req.total;
2502
2436
  /**
2503
- * Transform one request into its per-request incremental diff for the
2504
- * diff mode. Each category becomes |current previous| (the magnitude of
2505
- * change, stacked like the cumulative bars), `total` becomes the summed
2506
- * magnitude, and `net` keeps the signed change for the tooltip. The first
2507
- * request (no previous) diff from empty is zero, so the diff mode's scale
2508
- * is driven purely by inter-request changes rather than the initial
2509
- * cumulative baseline. Provider prompt/output are dropped: they are
2510
- * per-request values, not diffs.
2437
+ * Diff mode: each category becomes |current previous|, `total` the summed magnitude, `net` the signed change for the tooltip; the first
2438
+ * request diffs from zero so the scale is change-driven, and per-request provider prompt/output are dropped (they are not diffs).
2511
2439
  */
2512
2440
  const diffOf = (req, prev) => {
2513
2441
  const out = { ...req };
@@ -2577,6 +2505,17 @@ window.__ModuleLoader__.load({
2577
2505
  grp.count++;
2578
2506
  grp.span += req.stepCount ?? 1;
2579
2507
  }
2508
+ const turnOffsets = [];
2509
+ const turnWidths = [];
2510
+ {
2511
+ let x = 0;
2512
+ for (const grp of groups) {
2513
+ const w = grp.agg ? BAR_W : grp.span * 16 - BAR_GAP;
2514
+ turnOffsets.push(x);
2515
+ turnWidths.push(w);
2516
+ x += w + BAR_GAP;
2517
+ }
2518
+ }
2580
2519
  const scrollRef = React.useRef(null);
2581
2520
  const scrolledOnce = React.useRef(false);
2582
2521
  const lastGranRef = React.useRef(props.granularity);
@@ -2599,6 +2538,35 @@ window.__ModuleLoader__.load({
2599
2538
  right
2600
2539
  });
2601
2540
  };
2541
+ /**
2542
+ * Keep each turn label centered within its block's VISIBLE slice so it never scrolls out while any part of the block is on screen
2543
+ * (narrower-than-label blocks stay put); reads (offsetWidth) batch before writes (transform) to avoid layout thrash — out-of-view
2544
+ * blocks need no measurement.
2545
+ */
2546
+ const updateTurnLabels = (el) => {
2547
+ const labels = el.querySelectorAll(".lc-turn-label");
2548
+ const n = Math.min(labels.length, turnOffsets.length);
2549
+ const sl = el.scrollLeft;
2550
+ const vr = sl + el.clientWidth;
2551
+ const writes = [];
2552
+ for (let i = 0; i < n; i++) {
2553
+ const off = turnOffsets[i];
2554
+ const w = turnWidths[i];
2555
+ const visL = Math.max(off, sl);
2556
+ const visR = Math.min(off + w, vr);
2557
+ let dx = 0;
2558
+ if (visR > visL) {
2559
+ const lw = labels[i].offsetWidth;
2560
+ if (lw < w) {
2561
+ const center = (visL + visR) / 2 - off;
2562
+ dx = Math.min(Math.max(center, lw / 2), w - lw / 2) - w / 2;
2563
+ }
2564
+ }
2565
+ const next = dx !== 0 ? `translateX(${dx}px)` : "";
2566
+ if (labels[i].style.transform !== next) writes.push([labels[i], next]);
2567
+ }
2568
+ for (const [label, next] of writes) label.style.transform = next;
2569
+ };
2602
2570
  React.useLayoutEffect(() => {
2603
2571
  const el = scrollRef.current;
2604
2572
  if (el === null) return;
@@ -2619,6 +2587,7 @@ window.__ModuleLoader__.load({
2619
2587
  el.scrollLeft = el.scrollWidth;
2620
2588
  } else if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 24) el.scrollLeft = el.scrollWidth;
2621
2589
  updateEdges(el);
2590
+ updateTurnLabels(el);
2622
2591
  });
2623
2592
  const tipOf = (req) => {
2624
2593
  const head = req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", {
@@ -2641,6 +2610,7 @@ window.__ModuleLoader__.load({
2641
2610
  ref: scrollRef,
2642
2611
  onScroll: (e) => {
2643
2612
  updateEdges(e.currentTarget);
2613
+ updateTurnLabels(e.currentTarget);
2644
2614
  }
2645
2615
  }, edges.left ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-l" }) : null, /* @__PURE__ */ React.createElement("div", {
2646
2616
  className: "lc-chart",
@@ -2667,12 +2637,11 @@ window.__ModuleLoader__.load({
2667
2637
  }
2668
2638
  }, groups.map((grp, gi) => {
2669
2639
  const on = props.activeTurn === grp.turn;
2670
- const blockW = grp.agg ? BAR_W : grp.span * 16 - BAR_GAP;
2671
2640
  return /* @__PURE__ */ React.createElement("span", {
2672
2641
  key: `turn-${gi}`,
2673
2642
  className: "lc-turn" + (on ? " lc-turn-on" : ""),
2674
2643
  style: {
2675
- width: `${blockW}px`,
2644
+ width: `${turnWidths[gi]}px`,
2676
2645
  background: TURN_FILLS[gi % TURN_FILLS.length]
2677
2646
  },
2678
2647
  title: `T${grp.turn}`,
@@ -2682,7 +2651,7 @@ window.__ModuleLoader__.load({
2682
2651
  onClick: () => {
2683
2652
  props.onPickTurn(grp.turn);
2684
2653
  }
2685
- }, `T${grp.turn}`);
2654
+ }, /* @__PURE__ */ React.createElement("span", { className: "lc-turn-label" }, `T${grp.turn}`));
2686
2655
  }))), edges.right ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-r" }) : null);
2687
2656
  };
2688
2657
  }
@@ -2906,7 +2875,7 @@ window.__ModuleLoader__.load({
2906
2875
  }
2907
2876
  //#endregion
2908
2877
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
2909
- const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{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}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i,.lc-node i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-tools{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;display:flex}.lc-tools-label,.lc-tools-more{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;padding:0;font-family:inherit;font-size:12px}.lc-tools-label:hover,.lc-tools-more:hover{text-decoration:underline}.lc-tool-chip{background:var(--dsw-alias-bg-layer-2);font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:0;border-radius:4px;padding:1px 7px;font-size:12px}.lc-tool-chip:hover{text-decoration:underline}.lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary)}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-bg-layer-2);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-head .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-detail-num{text-align:right;width:44px}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-nodes{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-node{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-node-preview{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);flex:1;overflow:hidden}.lc-node-time{color:var(--dsw-alias-label-secondary);text-align:right;min-width:54px;font-size:12px}.lc-node-tokens{color:var(--dsw-alias-label-secondary)}.lc-nodes-more{color:var(--dsw-alias-label-secondary);padding:3px 0}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-tag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;scrollbar-width:thin;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p,.lc-ts-desc-md div pre{margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
2878
+ const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{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}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i,.lc-node i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-tools{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;display:flex}.lc-tools-label,.lc-tools-more{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;padding:0;font-family:inherit;font-size:12px}.lc-tools-label:hover,.lc-tools-more:hover{text-decoration:underline}.lc-tool-chip{background:var(--dsw-alias-bg-layer-2);font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:0;border-radius:4px;padding:1px 7px;font-size:12px}.lc-tool-chip:hover{text-decoration:underline}.lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary)}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-bg-layer-2);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-head .lc-actual,.lc-detail-head .lc-cache{color:var(--dsw-alias-state-success-primary)}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-detail-num{text-align:right;width:44px}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-nodes{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-node{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-node-preview{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);flex:1;overflow:hidden}.lc-node-time{color:var(--dsw-alias-label-secondary);text-align:right;min-width:54px;font-size:12px}.lc-node-tokens{color:var(--dsw-alias-label-secondary)}.lc-nodes-more{color:var(--dsw-alias-label-secondary);padding:3px 0}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
2910
2879
  const tagId = "dsh-context/styles.css";
2911
2880
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2912
2881
  const tag = document.createElement("style");