dsh-context 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,10 @@ Type `/context` (or pick it from the `/` menu) and press Enter: a centered dialo
43
43
 
44
44
  ![Context command](https://raw.githubusercontent.com/bowenliang123/dsh-context/main/docs/context-command.png)
45
45
 
46
+ ### ⚙️ Settings — per-user preferences
47
+
48
+ In **Settings → Plugins → Plugin configuration**, the **Context / 上下文** card holds this plugin's per-user preferences — currently the **default trend granularity** (Step/Turn) the Context tab opens with. In-chart toggling stays per-view and never overwrites the stored preference. The card appears only when the Host half is installed and the settings document is writable (a remote browser keeps settings process-local and shows no card).
49
+
46
50
  ## What you'll see
47
51
 
48
52
  ### 📊 Context stats — the session at a glance
@@ -69,7 +73,7 @@ In **Step** granularity, hovering any bar shows that single step's context info
69
73
 
70
74
  ### ⚡ Context events — when and why the window changed
71
75
 
72
- Every compaction, tool-output prune, skill or plugin context injection, and model switch — each with its token delta, turn/step attribution, and timestamp. Filter by category (**Inject / Compact / Prune / Switch**) to see exactly when each kind of event happened and its impact — e.g. when a skill was injected, when instructions were added, or how much a compaction reclaimed:
76
+ Every compaction, tool-output prune, skill or plugin context injection, model switch, and plan-mode toggle — each labeled with its producer source (instruction file paths, plugin id, skill name), its token delta (compactions/prunes show the **net** reclaimed amount, matching the chart's drop), turn/step attribution, and timestamp. Filter by category (**Inject / Compact / Prune / Switch / Mode**) to see exactly when each kind of event happened and its impact — e.g. when a skill was injected, when instructions were added, or how much a compaction reclaimed:
73
77
 
74
78
  ![Context events and messages](https://raw.githubusercontent.com/bowenliang123/dsh-context/main/docs/context-events.png)
75
79
 
package/lib/client.js CHANGED
@@ -44,6 +44,13 @@ window.__ModuleLoader__.load({
44
44
  "trend.title": "历史趋势",
45
45
  "gran.step": "步骤",
46
46
  "gran.turn": "轮次",
47
+ "settings.title": "上下文",
48
+ "settings.desc": "上下文面板的展示偏好",
49
+ "settings.gran": "趋势图默认粒度",
50
+ "settings.mode": "趋势图默认展示",
51
+ "settings.expand": "展开",
52
+ "settings.collapse": "收起",
53
+ "settings.readOnly": "当前环境的设置为只读",
47
54
  "gran.total": "全量",
48
55
  "gran.diff": "增量",
49
56
  "gran.modeHint": "全量:累计构成;增量:相对上一条请求的变化量",
@@ -64,6 +71,7 @@ window.__ModuleLoader__.load({
64
71
  "kind.compaction": "压缩",
65
72
  "kind.prune": "剪枝",
66
73
  "kind.model": "切换",
74
+ "kind.mode": "模式",
67
75
  "nodes.title": "消息构成",
68
76
  "nodes.hint": "当前模型可见的消息,最新在前",
69
77
  "nodes.more": "… 更早的 {n} 条消息已省略",
@@ -81,6 +89,8 @@ window.__ModuleLoader__.load({
81
89
  "ev.prune": "剪枝工具输出",
82
90
  "ev.skill": "Skill 注入({name})",
83
91
  "ev.model": "模型切换:{a} → {b}",
92
+ "ev.mode.plan.on": "进入计划模式",
93
+ "ev.mode.plan.off": "退出计划模式",
84
94
  "form.instructions": "指令注入",
85
95
  "form.catalog": "目录更新",
86
96
  "form.snapshot": "状态快照",
@@ -126,6 +136,8 @@ window.__ModuleLoader__.load({
126
136
  "attach.other": "其他内容",
127
137
  "attach.image": "图片",
128
138
  "attach.open": "查看原图",
139
+ "attach.preview": "图片预览",
140
+ "attach.close": "关闭",
129
141
  "attach.loading": "…",
130
142
  "attach.loadFailed": "加载失败 · 点击重试",
131
143
  "attach.raw": "原图",
@@ -172,6 +184,13 @@ window.__ModuleLoader__.load({
172
184
  "trend.title": "Context History",
173
185
  "gran.step": "Step",
174
186
  "gran.turn": "Turn",
187
+ "settings.title": "Context",
188
+ "settings.desc": "Display preferences for the Context panel",
189
+ "settings.gran": "Default trend granularity",
190
+ "settings.mode": "Default trend display",
191
+ "settings.expand": "Expand",
192
+ "settings.collapse": "Collapse",
193
+ "settings.readOnly": "Settings are read-only in this environment",
175
194
  "gran.total": "Total",
176
195
  "gran.diff": "Diff",
177
196
  "gran.modeHint": "Total: cumulative makeup; Diff: change vs the previous request",
@@ -192,6 +211,7 @@ window.__ModuleLoader__.load({
192
211
  "kind.compaction": "Compact",
193
212
  "kind.prune": "Prune",
194
213
  "kind.model": "Switch",
214
+ "kind.mode": "Mode",
195
215
  "nodes.title": "Messages",
196
216
  "nodes.hint": "currently model-visible, newest first",
197
217
  "nodes.more": "… {n} earlier messages omitted",
@@ -209,6 +229,8 @@ window.__ModuleLoader__.load({
209
229
  "ev.prune": "Tool output pruned",
210
230
  "ev.skill": "Skill injected ({name})",
211
231
  "ev.model": "Model switched: {a} → {b}",
232
+ "ev.mode.plan.on": "Plan mode on",
233
+ "ev.mode.plan.off": "Plan mode off",
212
234
  "form.instructions": "Instructions",
213
235
  "form.catalog": "Catalog Update",
214
236
  "form.snapshot": "State Snapshot",
@@ -254,6 +276,8 @@ window.__ModuleLoader__.load({
254
276
  "attach.other": "Other content",
255
277
  "attach.image": "Image",
256
278
  "attach.open": "Open full image",
279
+ "attach.preview": "Image preview",
280
+ "attach.close": "Close",
257
281
  "attach.loading": "…",
258
282
  "attach.loadFailed": "Load failed · click to retry",
259
283
  "attach.raw": "Raw",
@@ -652,6 +676,7 @@ window.__ModuleLoader__.load({
652
676
  //#region src/client/react.ts
653
677
  const React = require("react");
654
678
  const h = React.createElement;
679
+ const ReactDOM = require("react-dom");
655
680
  //#endregion
656
681
  //#region src/client/components/nodes.tsx
657
682
  /** One-line preview of a surface node (shared by the node list and the Context browser's element rows). */
@@ -876,12 +901,58 @@ window.__ModuleLoader__.load({
876
901
  };
877
902
  }
878
903
  /**
879
- * One attachment card: a 64px cover tile (click opens the full image in a
880
- * new tab; load failures retry on click) beside a metadata column — display
881
- * name, then one labeled row per known fact: Raw (the pre-normalization
882
- * raster dsh 0.1.1 records when normalization reduced the image), Sent (the
883
- * normalized raster the model receives, with its stored byte size), and the
884
- * estimated provider-billed tokens. Unknown facts leave no row behind.
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.
910
+ */
911
+ function AttachmentLightbox(props) {
912
+ const { src, alt, labels, onClose } = props;
913
+ const closeRef = React.useRef(null);
914
+ const restoreRef = React.useRef(null);
915
+ React.useEffect(() => {
916
+ restoreRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
917
+ closeRef.current?.focus();
918
+ const onKeyDown = (event) => {
919
+ if (event.key === "Escape") onClose();
920
+ };
921
+ window.addEventListener("keydown", onKeyDown);
922
+ return () => {
923
+ window.removeEventListener("keydown", onKeyDown);
924
+ restoreRef.current?.focus();
925
+ };
926
+ }, [onClose]);
927
+ return ReactDOM.createPortal(/* @__PURE__ */ React.createElement("div", {
928
+ className: "lc-att-lightbox",
929
+ role: "dialog",
930
+ "aria-modal": "true",
931
+ "aria-label": labels.dialog
932
+ }, /* @__PURE__ */ React.createElement("div", {
933
+ className: "lc-att-lightbox-mask",
934
+ "aria-hidden": "true",
935
+ onMouseDown: onClose
936
+ }), /* @__PURE__ */ React.createElement("img", {
937
+ className: "lc-att-lightbox-img",
938
+ src,
939
+ alt
940
+ }), /* @__PURE__ */ React.createElement("button", {
941
+ ref: closeRef,
942
+ type: "button",
943
+ className: "lc-att-lightbox-close",
944
+ "aria-label": labels.close,
945
+ onClick: onClose
946
+ }, /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 16 }))), document.body);
947
+ }
948
+ /**
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.
885
956
  */
886
957
  function makeImageCard(kit) {
887
958
  const { t, fmt } = kit;
@@ -890,6 +961,10 @@ window.__ModuleLoader__.load({
890
961
  const [src, setSrc] = React.useState(null);
891
962
  const [error, setError] = React.useState(false);
892
963
  const [attempt, setAttempt] = React.useState(0);
964
+ const [preview, setPreview] = React.useState(false);
965
+ const closePreview = React.useCallback(() => {
966
+ setPreview(false);
967
+ }, []);
893
968
  React.useEffect(() => {
894
969
  if (load === void 0) return;
895
970
  let live = true;
@@ -927,30 +1002,37 @@ window.__ModuleLoader__.load({
927
1002
  value: `≈${fmt(tokens)}`,
928
1003
  tip: t("attach.tokensTip")
929
1004
  });
930
- const open = () => {
931
- if (src === null) return;
932
- try {
933
- window.open(src, "_blank", "noopener");
934
- } catch {}
1005
+ const activate = () => {
1006
+ if (error) {
1007
+ setAttempt((a) => a + 1);
1008
+ return;
1009
+ }
1010
+ if (src !== null) setPreview(true);
935
1011
  };
936
- return /* @__PURE__ */ React.createElement("div", { className: "lc-att-item" }, /* @__PURE__ */ React.createElement("button", {
1012
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", {
937
1013
  type: "button",
938
- className: "lc-att-thumb",
1014
+ className: "lc-att-item",
939
1015
  title: error ? t("attach.loadFailed") : t("attach.open"),
940
- onClick: error ? () => {
941
- setAttempt((a) => a + 1);
942
- } : open
943
- }, src !== null ? /* @__PURE__ */ React.createElement("img", {
1016
+ onClick: activate
1017
+ }, /* @__PURE__ */ React.createElement("span", { className: "lc-att-thumb" }, src !== null ? /* @__PURE__ */ React.createElement("img", {
944
1018
  src,
945
1019
  alt: name
946
- }) : /* @__PURE__ */ React.createElement("span", { className: error ? "lc-att-err" : "lc-att-ph" }, error ? "⚠" : load === void 0 ? "🖼" : t("attach.loading"))), /* @__PURE__ */ React.createElement("div", { className: "lc-att-meta" }, /* @__PURE__ */ React.createElement("span", {
1020
+ }) : /* @__PURE__ */ React.createElement("span", { className: error ? "lc-att-err" : "lc-att-ph" }, error ? "⚠" : load === void 0 ? "🖼" : t("attach.loading"))), /* @__PURE__ */ React.createElement("span", { className: "lc-att-meta" }, /* @__PURE__ */ React.createElement("span", {
947
1021
  className: "lc-att-name",
948
1022
  title: name
949
1023
  }, name), rows.map((r) => /* @__PURE__ */ React.createElement("span", {
950
1024
  key: r.label,
951
1025
  className: "lc-att-row",
952
1026
  title: r.tip
953
- }, /* @__PURE__ */ React.createElement("b", { className: "lc-att-row-label" }, r.label), r.value))));
1027
+ }, /* @__PURE__ */ React.createElement("b", { className: "lc-att-row-label" }, r.label), r.value)))), preview && src !== null && /* @__PURE__ */ React.createElement(AttachmentLightbox, {
1028
+ src,
1029
+ alt: name,
1030
+ labels: {
1031
+ dialog: t("attach.preview"),
1032
+ close: t("attach.close")
1033
+ },
1034
+ onClose: closePreview
1035
+ }));
954
1036
  };
955
1037
  }
956
1038
  //#endregion
@@ -1862,12 +1944,154 @@ window.__ModuleLoader__.load({
1862
1944
  };
1863
1945
  }
1864
1946
  //#endregion
1947
+ //#region src/client/components/settingsCard.tsx
1948
+ /** One preference row: label + selector pill opening the framework Menu. */
1949
+ function PrefRow(props) {
1950
+ const [open, setOpen] = React.useState(false);
1951
+ const active = props.options.find((o) => o.id === props.value)?.label ?? props.value;
1952
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-settings-row" }, /* @__PURE__ */ React.createElement("span", { className: "lc-settings-label" }, props.label), /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1953
+ open,
1954
+ onClose: () => {
1955
+ setOpen(false);
1956
+ },
1957
+ items: props.options,
1958
+ selectedId: props.value,
1959
+ onSelect: (id) => {
1960
+ setOpen(false);
1961
+ props.onPick(id);
1962
+ },
1963
+ align: "end",
1964
+ portal: true,
1965
+ anchor: /* @__PURE__ */ React.createElement("button", {
1966
+ type: "button",
1967
+ className: "lc-settings-select",
1968
+ disabled: props.disabled,
1969
+ "aria-haspopup": "menu",
1970
+ "aria-expanded": open,
1971
+ onClick: () => {
1972
+ setOpen((v) => !v);
1973
+ }
1974
+ }, active, /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, null))
1975
+ }));
1976
+ }
1977
+ function makeSettingsCard(kit) {
1978
+ const { t } = kit;
1979
+ return function SettingsCard(props) {
1980
+ const [open, setOpen] = React.useState(false);
1981
+ const state = typeof props.useContextSettings === "function" ? props.useContextSettings((s) => s) : void 0;
1982
+ if (state === void 0 || state.status === "unavailable") return null;
1983
+ const disabled = state.status !== "ready" || !state.writable;
1984
+ return /* @__PURE__ */ React.createElement("li", { className: "lc-settings-card" + (open ? " lc-settings-open" : "") }, /* @__PURE__ */ React.createElement("button", {
1985
+ type: "button",
1986
+ className: "lc-settings-head",
1987
+ "aria-expanded": open,
1988
+ "aria-label": `${t(open ? "settings.collapse" : "settings.expand")}: ${t("settings.title")}`,
1989
+ onClick: () => {
1990
+ setOpen(!open);
1991
+ }
1992
+ }, /* @__PURE__ */ React.createElement("span", { className: "lc-settings-headtext" }, /* @__PURE__ */ React.createElement("span", { className: "lc-settings-name" }, t("settings.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-settings-desc" }, t("settings.desc"))), /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "lc-settings-chevron" })), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-settings-body" }, !state.writable && state.status === "ready" ? /* @__PURE__ */ React.createElement("p", {
1993
+ className: "lc-settings-note",
1994
+ role: "status"
1995
+ }, t("settings.readOnly")) : null, /* @__PURE__ */ React.createElement(PrefRow, {
1996
+ label: t("settings.gran"),
1997
+ value: state.granularity,
1998
+ disabled,
1999
+ options: [{
2000
+ id: "step",
2001
+ label: t("gran.step")
2002
+ }, {
2003
+ id: "turn",
2004
+ label: t("gran.turn")
2005
+ }],
2006
+ onPick: (id) => {
2007
+ props.set?.("defaultGranularity", id);
2008
+ }
2009
+ }), /* @__PURE__ */ React.createElement(PrefRow, {
2010
+ label: t("settings.mode"),
2011
+ value: state.mode,
2012
+ disabled,
2013
+ options: [{
2014
+ id: "total",
2015
+ label: t("gran.total")
2016
+ }, {
2017
+ id: "diff",
2018
+ label: t("gran.diff")
2019
+ }],
2020
+ onPick: (id) => {
2021
+ props.set?.("defaultTrendMode", id);
2022
+ }
2023
+ })) : null);
2024
+ };
2025
+ }
2026
+ //#endregion
2027
+ //#region src/client/settings.ts
2028
+ /** Read the known preferences out of a raw section; unknown/absent fields drop out. */
2029
+ function prefsOf(value) {
2030
+ if (value === null || typeof value !== "object") return {};
2031
+ const v = value;
2032
+ return {
2033
+ ...v.defaultGranularity === "step" || v.defaultGranularity === "turn" ? { granularity: v.defaultGranularity } : {},
2034
+ ...v.defaultTrendMode === "total" || v.defaultTrendMode === "diff" ? { mode: v.defaultTrendMode } : {}
2035
+ };
2036
+ }
2037
+ function createContextSettings() {
2038
+ let state = {
2039
+ status: "loading",
2040
+ granularity: "step",
2041
+ mode: "total",
2042
+ writable: false
2043
+ };
2044
+ let scope;
2045
+ const listeners = /* @__PURE__ */ new Set();
2046
+ const publish = (next) => {
2047
+ if (next.status === state.status && next.granularity === state.granularity && next.mode === state.mode && next.writable === state.writable) return;
2048
+ state = next;
2049
+ for (const listener of listeners) listener();
2050
+ };
2051
+ return {
2052
+ store: {
2053
+ subscribe(listener) {
2054
+ listeners.add(listener);
2055
+ return () => {
2056
+ listeners.delete(listener);
2057
+ };
2058
+ },
2059
+ getSnapshot: () => state
2060
+ },
2061
+ defaultGranularity: () => state.granularity,
2062
+ defaultTrendMode: () => state.mode,
2063
+ attach(bound) {
2064
+ scope = bound;
2065
+ const sync = () => {
2066
+ const snap = bound.getSnapshot();
2067
+ const prefs = prefsOf(snap.value);
2068
+ publish({
2069
+ status: snap.status === "ready" || snap.status === "unavailable" ? snap.status : "loading",
2070
+ granularity: prefs.granularity ?? state.granularity,
2071
+ mode: prefs.mode ?? state.mode,
2072
+ writable: snap.writable
2073
+ });
2074
+ };
2075
+ sync();
2076
+ return bound.subscribe(sync);
2077
+ },
2078
+ set(field, value) {
2079
+ publish({
2080
+ ...state,
2081
+ ...prefsOf({ [field]: value })
2082
+ });
2083
+ scope?.set(field, value);
2084
+ }
2085
+ };
2086
+ }
2087
+ //#endregion
1865
2088
  //#region src/client/components/events.tsx
1866
2089
  const EVENT_ICONS = {
1867
2090
  compaction: "✂",
1868
2091
  prune: "✂",
1869
2092
  inject: "+",
1870
- model: "⇄"
2093
+ model: "⇄",
2094
+ mode: "⇄"
1871
2095
  };
1872
2096
  /** Build the event text helpers bound to the translate function. */
1873
2097
  function makeEventText(t) {
@@ -1878,9 +2102,12 @@ window.__ModuleLoader__.load({
1878
2102
  a: ev.from || "?",
1879
2103
  b: ev.to || "?"
1880
2104
  });
2105
+ if (ev.kind === "mode") return t("ev.mode." + (ev.name || "?"));
1881
2106
  if (ev.sub === "skill") return t("ev.skill", { name: ev.name || "?" });
1882
2107
  const base = t("form." + (ev.form || "context"));
1883
- return ev.name ? base + " · " + ev.name : base;
2108
+ let label = ev.name ? base + " · " + ev.name : base;
2109
+ if (ev.detail) label += " · " + ev.detail;
2110
+ return label;
1884
2111
  }
1885
2112
  /**
1886
2113
  * Where this event sits in the request timeline, as a label or null.
@@ -2004,7 +2231,7 @@ window.__ModuleLoader__.load({
2004
2231
  return function PluginInfo() {
2005
2232
  const [latest, setLatest] = React.useState(null);
2006
2233
  React.useEffect(() => {
2007
- if ("0.27.0".includes("-dev")) return;
2234
+ if ("0.29.0".includes("-dev")) return;
2008
2235
  let on = true;
2009
2236
  fetchLatestVersion().then((v) => {
2010
2237
  if (on && v) setLatest(v);
@@ -2013,8 +2240,8 @@ window.__ModuleLoader__.load({
2013
2240
  on = false;
2014
2241
  };
2015
2242
  }, []);
2016
- const update = latest !== null && isNewerVersion(latest, "0.27.0") ? latest : null;
2017
- const nameValue = ["dsh-context (v0.27.0)"];
2243
+ const update = latest !== null && isNewerVersion(latest, "0.29.0") ? latest : null;
2244
+ const nameValue = ["dsh-context (v0.29.0)"];
2018
2245
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
2019
2246
  key: "update",
2020
2247
  className: "lc-pi-update"
@@ -2466,9 +2693,10 @@ window.__ModuleLoader__.load({
2466
2693
  "inject",
2467
2694
  "compaction",
2468
2695
  "prune",
2469
- "model"
2696
+ "model",
2697
+ "mode"
2470
2698
  ];
2471
- function makeContextView(ctx, kit) {
2699
+ function makeContextView(ctx, kit, settings) {
2472
2700
  const { t } = kit;
2473
2701
  const StackedBar = makeStackedBar(kit);
2474
2702
  const CurrentComposition = makeCurrentComposition(kit, StackedBar, makeLegend(kit));
@@ -2491,8 +2719,8 @@ window.__ModuleLoader__.load({
2491
2719
  const [hoveredSeq, setHoveredSeq] = React.useState(null);
2492
2720
  const [hoverTurn, setHoverTurn] = React.useState(null);
2493
2721
  const [tick, setTick] = React.useState(0);
2494
- const [granularity, setGranularity] = React.useState("step");
2495
- const [trendMode, setTrendMode] = React.useState("total");
2722
+ const [granularity, setGranularity] = React.useState(() => settings.defaultGranularity());
2723
+ const [trendMode, setTrendMode] = React.useState(() => settings.defaultTrendMode());
2496
2724
  const [focusTurn, setFocusTurn] = React.useState(null);
2497
2725
  const [hoverCat, setHoverCat] = React.useState(null);
2498
2726
  const [pickedKinds, setPickedKinds] = React.useState([...EVENT_KINDS]);
@@ -2678,7 +2906,7 @@ window.__ModuleLoader__.load({
2678
2906
  }
2679
2907
  //#endregion
2680
2908
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
2681
- 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-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-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-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{align-items:center;gap:8px;min-width:0;display:flex}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);cursor:pointer;width:64px;height:64px;font:inherit;border-radius:6px;flex:none;justify-content:center;align-items:center;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}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
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}";
2682
2910
  const tagId = "dsh-context/styles.css";
2683
2911
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2684
2912
  const tag = document.createElement("style");
@@ -2721,7 +2949,8 @@ window.__ModuleLoader__.load({
2721
2949
  }, "dsh-context: dictionaries");
2722
2950
  const t = ctx.locale.bind(NS);
2723
2951
  const kit = makeViewKit(t);
2724
- const ContextView = makeContextView(ctx, kit);
2952
+ const settings = createContextSettings();
2953
+ const ContextView = makeContextView(ctx, kit, settings);
2725
2954
  ctx.effect(() => {
2726
2955
  const noop = () => {};
2727
2956
  const sessions = ctx.get("sessions");
@@ -2755,6 +2984,26 @@ window.__ModuleLoader__.load({
2755
2984
  inject: (sessionId) => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
2756
2985
  }, (props) => h(ContextModal, props));
2757
2986
  });
2987
+ ctx.inject(["settingsScope"], (raw) => {
2988
+ const c = raw;
2989
+ const binder = c.settingsScope;
2990
+ if (binder === void 0) return;
2991
+ c.effect(() => settings.attach(binder.bind({ namespace: NS })), "dsh-context: settings scope");
2992
+ const SettingsCard = makeSettingsCard(kit);
2993
+ c.slots.inject("settings.plugin.item", () => {
2994
+ return c.slots.register({
2995
+ name: "settings.plugin.item",
2996
+ key: NS,
2997
+ locale: NS,
2998
+ inject: () => ({
2999
+ hooks: { contextSettings: settings.store },
3000
+ set: (field, value) => {
3001
+ settings.set(field, value);
3002
+ }
3003
+ })
3004
+ }, (props) => h(SettingsCard, props));
3005
+ });
3006
+ });
2758
3007
  }
2759
3008
  module.exports = {
2760
3009
  name: "dsh-context",
package/lib/index.d.ts CHANGED
@@ -253,12 +253,14 @@ interface RequestRecord {
253
253
  interface ContextEventRecord {
254
254
  seq: number;
255
255
  time: number;
256
- kind: 'compaction' | 'prune' | 'inject' | 'model';
256
+ kind: 'compaction' | 'prune' | 'inject' | 'model' | 'mode';
257
257
  form?: string;
258
258
  tokens?: number;
259
259
  count?: number;
260
260
  sub?: string;
261
261
  name?: string;
262
+ /** One-line producer account (notice-form summary), shown after the name. */
263
+ detail?: string;
262
264
  from?: string;
263
265
  to?: string;
264
266
  /** Turn/step of the request logged right BEFORE the event (host-stamped). */
@@ -369,6 +371,14 @@ interface TimelineState {
369
371
  * to keep the state plain JSON for the projection cache.
370
372
  */
371
373
  pendingShadowedSeqs?: number[];
374
+ /**
375
+ * The seq of the compaction/prune event that armed `pendingShadowedSeqs` —
376
+ * the shadowed path rewrites that event's `tokens` from the gross shadow
377
+ * price to the NET freed amount (removed nodes minus the synchronous
378
+ * replacement), so the row matches the drop the trend chart shows. Same
379
+ * arm/remove lifecycle as `pendingShadowedSeqs`.
380
+ */
381
+ pendingShadowEventSeq?: number;
372
382
  }
373
383
  //#endregion
374
384
  //#region node_modules/.pnpm/@deepseek-ai+dsh-session-projection@0.1.0-rc.8_@deepseek-ai+cordis@4.0.1_@deepseek-ai+d_c0ffab70d18276fadfe75ed916313ea3/node_modules/@deepseek-ai/dsh-session-projection/lib/types/types.d.ts
package/lib/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { z } from "zod";
2
+ import { settingsNamespace } from "@deepseek-ai/dsh-settings";
3
+ import z$1 from "@deepseek-ai/schemastery";
2
4
  import { deriveEventMessage } from "@deepseek-ai/dsh-session";
3
5
  //#region src/host/config.ts
4
6
  /**
@@ -254,8 +256,27 @@ function toolCallNames(blocks) {
254
256
  for (const b of blocks) if (b.type === "tool-call" && typeof b.name === "string") names.push(b.name);
255
257
  return names;
256
258
  }
259
+ /**
260
+ * Producer label for an injection event, mirroring the dsh transcript's
261
+ * context provenance (client-runtime context-provenance.ts): workspace
262
+ * instructions name the files they were reconciled from, a plugin source its
263
+ * plugin id, and any other producer its own durable kind. Returns '' when
264
+ * the source carries no readable identity at all.
265
+ */
266
+ function injectionSourceName(source) {
267
+ if (source.kind === "agent-instructions" && Array.isArray(source.changes)) {
268
+ const paths = [];
269
+ for (const change of source.changes) {
270
+ const path = change?.path;
271
+ if (typeof path === "string" && path !== "" && !paths.includes(path)) paths.push(path);
272
+ }
273
+ if (paths.length > 0) return paths.join(", ");
274
+ }
275
+ if (typeof source.plugin === "string" && source.plugin !== "") return source.plugin;
276
+ return typeof source.kind === "string" && source.kind !== "" ? source.kind : "";
277
+ }
257
278
  function isInjection(source) {
258
- return source !== null && source !== void 0 && (source.kind === "plugin" || source.kind === "skill-invocation" || typeof source.form === "string");
279
+ return source !== null && source !== void 0 && (typeof source.kind === "string" && source.kind !== "" && source.kind !== "user" || typeof source.form === "string");
259
280
  }
260
281
  //#endregion
261
282
  //#region src/host/headers.ts
@@ -357,6 +378,21 @@ function createContextHeadersDefinition() {
357
378
  };
358
379
  }
359
380
  //#endregion
381
+ //#region src/host/settings.ts
382
+ /** The namespace is the join key between the Host registration and the browser card. */
383
+ const SETTINGS_NAMESPACE = "dsh-context";
384
+ /** Section schema: also the wire envelope the browser scope validates against. */
385
+ const SettingsSchema = z$1.object({
386
+ defaultGranularity: z$1.union(["step", "turn"]).default("step"),
387
+ defaultTrendMode: z$1.union(["total", "diff"]).default("total")
388
+ });
389
+ /** Serve the namespace while a settings provider is composed; inert otherwise. */
390
+ function installSettings(ctx) {
391
+ ctx.inject(["settings"], (sctx) => {
392
+ sctx.settings.register(settingsNamespace(SETTINGS_NAMESPACE), SettingsSchema);
393
+ });
394
+ }
395
+ //#endregion
360
396
  //#region src/host/fold.ts
361
397
  /** Keep only the trailing `maxTurns` turn-runs of a request timeline. */
362
398
  function trimToLastTurns(requests, maxTurns) {
@@ -507,7 +543,9 @@ function applySurface(st, ev, type, data, message) {
507
543
  if (utext !== "") node.text = utext;
508
544
  }
509
545
  const shadowedSeqs = st.pendingShadowedSeqs;
546
+ const shadowEventSeq = st.pendingShadowEventSeq;
510
547
  delete st.pendingShadowedSeqs;
548
+ delete st.pendingShadowEventSeq;
511
549
  const op = ev.surfaceOp;
512
550
  if (op !== null && typeof op === "object" && op.op === "replace") {
513
551
  if (Array.isArray(shadowedSeqs) && shadowedSeqs.length > 0) {
@@ -522,6 +560,14 @@ function applySurface(st, ev, type, data, message) {
522
560
  st.surface = kept;
523
561
  st.sums[cat] += node.tokens;
524
562
  st.surface.push(node);
563
+ if (shadowEventSeq !== void 0) {
564
+ const removedSum = removed.reduce((sum, n) => sum + n.tokens, 0);
565
+ const i = st.events.findIndex((e) => e.seq === shadowEventSeq);
566
+ if (i >= 0) st.events[i] = {
567
+ ...st.events[i],
568
+ tokens: Math.max(0, removedSum - node.tokens)
569
+ };
570
+ }
525
571
  return node;
526
572
  }
527
573
  let si = -1;
@@ -633,7 +679,7 @@ function applyTimeline(state, event, bounds) {
633
679
  s.systemTokens = estimateSystem(header.system);
634
680
  if (header.config && typeof header.config.model === "string") s.model = header.config.model;
635
681
  if (header.config && typeof header.config.provider === "string") s.provider = header.config.provider;
636
- if (data?.reason === "change" && s.model && s.lastModel && s.model !== s.lastModel) s.events.push({
682
+ if ((data?.reason === "change" || data?.reason === "resume") && s.model && s.lastModel && s.model !== s.lastModel) s.events.push({
637
683
  seq: event.seq,
638
684
  time: event.time,
639
685
  kind: "model",
@@ -672,7 +718,11 @@ function applyTimeline(state, event, bounds) {
672
718
  if (source.kind === "skill-invocation") {
673
719
  rec.sub = "skill";
674
720
  rec.name = typeof source.name === "string" ? source.name : "?";
675
- } else if (typeof source.plugin === "string" && source.plugin !== "") rec.name = source.plugin;
721
+ } else {
722
+ const label = injectionSourceName(source);
723
+ if (label !== "") rec.name = label;
724
+ if (source.form === "notice" && typeof source.summary === "string" && source.summary !== "") rec.detail = source.summary;
725
+ }
676
726
  s.events.push(rec);
677
727
  }
678
728
  break;
@@ -725,10 +775,21 @@ function applyTimeline(state, event, bounds) {
725
775
  applySurface(s, event, event.type, data, asstMsg);
726
776
  break;
727
777
  }
778
+ case "plan/mode":
779
+ if (data && typeof data.active === "boolean") ensure().events.push({
780
+ seq: event.seq,
781
+ time: event.time,
782
+ kind: "mode",
783
+ name: data.active ? "plan.on" : "plan.off"
784
+ });
785
+ break;
728
786
  case "compaction/summary":
729
787
  case "compaction/prune": {
730
788
  const s = ensure();
731
- if (data && Array.isArray(data.shadowedSeqs)) s.pendingShadowedSeqs = data.shadowedSeqs.filter((x) => typeof x === "number");
789
+ if (data && Array.isArray(data.shadowedSeqs)) {
790
+ s.pendingShadowedSeqs = data.shadowedSeqs.filter((x) => typeof x === "number");
791
+ s.pendingShadowEventSeq = event.seq;
792
+ }
732
793
  s.events.push({
733
794
  seq: event.seq,
734
795
  time: event.time,
@@ -885,13 +946,15 @@ const contextEventSchema = z.object({
885
946
  "compaction",
886
947
  "prune",
887
948
  "inject",
888
- "model"
949
+ "model",
950
+ "mode"
889
951
  ]),
890
952
  form: z.string().optional(),
891
953
  tokens: z.number().optional(),
892
954
  count: z.number().optional(),
893
955
  sub: z.string().optional(),
894
956
  name: z.string().optional(),
957
+ detail: z.string().optional(),
895
958
  from: z.string().optional(),
896
959
  to: z.string().optional(),
897
960
  fromTurn: z.number().optional(),
@@ -975,7 +1038,8 @@ const timelineStateSchema = z.object({
975
1038
  }).strict().optional(),
976
1039
  archiveFloor: z.number().optional(),
977
1040
  callNames: z.record(z.string(), z.string()),
978
- pendingShadowedSeqs: z.array(z.number()).optional()
1041
+ pendingShadowedSeqs: z.array(z.number()).optional(),
1042
+ pendingShadowEventSeq: z.number().optional()
979
1043
  });
980
1044
  /**
981
1045
  * The context-timeline projection unit, created per plugin instance with its
@@ -1019,6 +1083,7 @@ const inject = ["sessionProjections"];
1019
1083
  function apply(ctx, config) {
1020
1084
  ctx.sessionProjections.register(createContextTimelineDefinition(config));
1021
1085
  ctx.sessionProjections.register(createContextHeadersDefinition());
1086
+ installSettings(ctx);
1022
1087
  }
1023
1088
  //#endregion
1024
1089
  export { Config, apply, inject, name };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {
@@ -51,7 +51,8 @@
51
51
  "@deepseek-ai/dsh-client-connection",
52
52
  "@deepseek-ai/dsh-client-locale",
53
53
  "@deepseek-ai/dsh-client-runtime",
54
- "@deepseek-ai/dsh-client-ui-conversation"
54
+ "@deepseek-ai/dsh-client-ui-conversation",
55
+ "@deepseek-ai/dsh-client-ui-settings"
55
56
  ],
56
57
  "platform": "web"
57
58
  }
@@ -70,6 +71,8 @@
70
71
  "@deepseek-ai/cordis": "^4.0.1",
71
72
  "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.7",
72
73
  "@deepseek-ai/dsh-session": "^0.1.0-rc.7",
74
+ "@deepseek-ai/dsh-settings": "^0.1.0-rc.7",
75
+ "@deepseek-ai/schemastery": "^3.18.1",
73
76
  "react": "^18.3.1",
74
77
  "zod": "^4.4.3"
75
78
  },
@@ -86,6 +89,8 @@
86
89
  "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.7",
87
90
  "@deepseek-ai/dsh-session": "^0.1.0-rc.7",
88
91
  "@deepseek-ai/dsh-session-projection": "^0.1.0-rc.7",
92
+ "@deepseek-ai/dsh-settings": "^0.1.0-rc.7",
93
+ "@deepseek-ai/schemastery": "^3.18.1",
89
94
  "@stylistic/eslint-plugin": "^5.10.0",
90
95
  "@types/react": "^18.3.31",
91
96
  "eslint-plugin-sonarjs": "^4.2.0",