dsh-quick-toc 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -2,18 +2,28 @@
2
2
 
3
3
  > **English** | [中文](README.md)
4
4
 
5
- A quick conversation TOC plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH): extracts Markdown headings (H1–H6) from AI replies into a navigable outline panel, grouped by conversation turn, with auto-follow highlighting.
5
+ A quick conversation TOC plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH): extracts Markdown headings (H1–H6) from AI replies into a navigable outline panel, grouped by conversation turn, with auto-follow highlighting, keyword search and in-chat match highlighting.
6
6
 
7
7
  ## Features
8
8
 
9
- - **Turn-grouped outline** — each user message + its AI replies form one group, with the group's end time as the header
9
+ - **Turn-grouped outline** — each user message + its AI replies form one group, with the group's end time as the header (plus the turn's first-line preview; click it to jump to the turn start)
10
+ - **Keyword search** — a magnifier in the header opens the search box; Enter cycles matches (n/N counter), Esc closes; toggle between **title / full-text** scope
11
+ - **In-chat highlighting** — matched keywords are highlighted in the conversation; the current match gets a distinct highlight and is scrolled to the upper-middle of the viewport; every occurrence counts toward n/N
10
12
  - **Auto-follow highlight** — as you scroll the conversation, the turns visible in the viewport light up in the outline (multiple at once); the outline auto-loads and scrolls to keep them visible
11
13
  - **Smooth jump** — click a heading to glide to the exact heading position in the conversation
12
14
  - **Dockable & resizable** — dock left or right (drag the top bar), resize from the edges/corner, collapse to a draggable edge handle; panel size/position remembered
13
- - **Scrollbar follows the dock** — scrollbar sits on the left when docked left, right when docked right
14
15
  - **Paged rendering** — shows the latest groups first; scrolling to the top of the outline loads older ones
15
16
  - **Markdown-aware titles** — inline `**bold**`, *italic*, `` `code` ``, `[links](url)`, `~~strike~~` are stripped from heading text
16
- - Auto-hides when the conversation has no headings; works in light/dark themes
17
+ - Auto-hides when the conversation has no headings; works in light/dark themes; the inner-shadow card look adapts to the theme (white bevel in dark)
18
+
19
+ ## Compatibility
20
+
21
+ | Plugin | DSH |
22
+ | --- | --- |
23
+ | **0.3.0** | **≥ 0.1.2-rc.1** (new slot architecture) |
24
+ | 0.2.2 | DSH versions before 0.1.2-rc.1 |
25
+
26
+ 0.3.0 re-targeted the host integration (new slot architecture + `useChat` session data) and **only supports DSH 0.1.2-rc.1 and above**; for older DSH builds install 0.2.2. On install/update the DSH market pre-flights host compatibility from `dsh.compatibility.dshReleases` and `peerDependencies` in package.json and warns on a mismatch.
17
27
 
18
28
  ## Install
19
29
 
@@ -47,9 +57,10 @@ Restart DSH (double-click `restart-dsh.bat` on Windows) and open the Web UI. The
47
57
  ## Development
48
58
 
49
59
  - `lib/client.js` — all UI logic (browser side)
50
- - `lib/index.js` — host-side no-op entry
60
+ - `lib/index.js` — host-side no-op entry (extend with a version gate if needed)
51
61
  - `cordis.patch.yml` — loader patch (official DSH bundle format)
52
- - Client changes hot-load after a browser hard refresh; loader/patch changes need a DSH restart
62
+ - **Since 0.3.0 the plugin targets the new plugin model**: the panel registers into the session-scoped `conversation.input.overlay` slot (session hooks are contributed via `ctx.uiSession.provide`, `useChat` by ui-chat), and the panel body `createPortal`s to `document.body` as a fixed floating dock; data comes from `props.useChat` (`ChatSnapshot.order` + `nodes`; node shape: `kind: user/assistant-step`, `location.turn`, `data.blocks`)
63
+ - Client changes need a DSH restart (boot rev is content-based)
53
64
 
54
65
  ## License
55
66
 
package/README.md CHANGED
@@ -2,18 +2,28 @@
2
2
 
3
3
  > [English](README.en.md) | **中文**
4
4
 
5
- **DeepSeek Harness(DSH)对话大纲插件**:把 AI 回复中的 Markdown 标题(H1–H6)提取成可导航的大纲面板,按对话回合分组,自动跟随阅读位置。
5
+ **DeepSeek Harness(DSH)对话大纲插件**:把 AI 回复中的 Markdown 标题(H1–H6)提取成可导航的大纲面板,按对话回合分组,自动跟随阅读位置,支持关键字搜索与对话内高亮定位。
6
6
 
7
7
  ## 功能
8
8
 
9
- - **按回合分组** —— 每条用户消息 + 其后续 AI 回复为一组,组头显示该组最后一条消息的结束时间
9
+ - **按回合分组** —— 每条用户消息 + 其后续 AI 回复为一组,组头显示该组最后一条消息的结束时间(附带该回合首行预览,点击可跳到回合开头)
10
+ - **关键字搜索** —— 标题栏放大镜打开搜索框,回车逐个定位(n/N 计数),Esc 关闭;支持 **标题 / 全文** 范围切换
11
+ - **对话内高亮** —— 命中关键字在对话中高亮,当前命中单独标亮并滚动到视口上方约 1/3 处;同一条消息内的多次命中都计入 n/N
10
12
  - **自动跟随高亮** —— 滚动对话框时,视口内可见的回合在大纲中自动点亮(多组可同时点亮);大纲自动加载并滚动,保证正在读的组始终可见
11
13
  - **平滑跳转** —— 点击标题平滑滚动到对话中该标题的准确位置
12
14
  - **可停靠、可缩放** —— 左右停靠(拖顶部横条移动)、边缘/角部拖拽调整大小、收起成可拖动的边缘把手;大小与位置自动记忆
13
- - **滚动条跟随停靠方向** —— 停靠左侧时滚动条在左,停靠右侧时在右
14
15
  - **分页渲染** —— 默认显示最近几组,大纲滚动到顶部自动加载更早的组
15
16
  - **Markdown 清理** —— 标题中的 `**加粗**`、*斜体*、`` `代码` ``、`[链接](url)`、`~~删除线~~` 等行内标记自动剥离
16
- - 对话无标题时自动隐藏;适配深色 / 浅色主题
17
+ - 对话无标题时自动隐藏;适配深色 / 浅色主题;内阴影卡片质感随主题切换(深色为白色高光)
18
+
19
+ ## 兼容性
20
+
21
+ | 插件版本 | DSH 版本 |
22
+ | --- | --- |
23
+ | **0.3.0** | **≥ 0.1.2-rc.1**(新 slot 架构) |
24
+ | 0.2.2 | 0.1.2-rc.1 之前的旧版 DSH |
25
+
26
+ 0.3.0 重写了与宿主的对接(新版 slot 架构 + `useChat` 会话数据),**只支持 DSH 0.1.2-rc.1 及以上**;旧版 DSH 请安装 0.2.2。安装/更新时 DSH 市场会根据 package.json 的 `dsh.compatibility.dshReleases` 与 `peerDependencies` 做 host 兼容预检,版本不符会给出提示。
17
27
 
18
28
  ## 安装
19
29
 
@@ -47,9 +57,10 @@ dsh plugin --profile web add <本目录路径>
47
57
  ## 开发
48
58
 
49
59
  - `lib/client.js` —— 全部 UI 逻辑(浏览器端)
50
- - `lib/index.js` —— 宿主端空入口
60
+ - `lib/index.js` —— 宿主端空入口(版本守卫可在此扩展)
51
61
  - `cordis.patch.yml` —— loader patch(符合官方 bundle 规范)
52
- - client.js 后浏览器硬刷新即可生效;改 loader / patch 需重启 DSH
62
+ - **0.3.0 起对接新版插件模型**:面板注册进会话级 `conversation.input.overlay` 槽(会话级 hook `ctx.uiSession.provide` 贡献,`useChat` 由 ui-chat 贡献),面板本体 `createPortal` 到 `document.body` 渲染成固定浮层;数据入口为 `props.useChat`(`ChatSnapshot.order` + `nodes`,节点形状:`kind: user/assistant-step`、`location.turn`、`data.blocks`)
63
+ - 改 client.js 后需重启 DSH(boot rev 按内容算)
53
64
 
54
65
  ## License
55
66
 
package/lib/client.js CHANGED
@@ -6,11 +6,19 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
- require("@deepseek-ai/dsh-client-runtime/client");
9
+ let react_dom = require("react-dom"); // createPortal -> panel stays a top-level overlay (highest pointer priority)
10
+ // DSH 0.1.2-rc.1: the old @deepseek-ai/dsh-client-runtime package is gone.
11
+ // Runtime hooks (useChat etc.) now arrive as session-scope slot props —
12
+ // no runtime package to require here anymore.
10
13
 
11
14
  // ---------- constants ----------
12
15
  var PANEL_WIDTH = 288;
13
- var Z_BASE = 950;
16
+ // Very high base so the portal-rendered panel (top-level stacking context)
17
+ // always sits above DSH content and its transcript width handles.
18
+ // Panel layer: above the app's popovers (z 100) and the transcript width
19
+ // handles (z 8), but BELOW DSH's modal layer (settings/image viewer, z 1000)
20
+ // so opening settings covers the panel instead of the panel floating on top.
21
+ var Z_BASE = 500;
14
22
  var EASE = "cubic-bezier(0.22, 0.9, 0.3, 1)"; // smooth non-linear slide
15
23
 
16
24
  // ---------- markdown helpers ----------
@@ -223,6 +231,42 @@ window.__ModuleLoader__.load({
223
231
  chip: "rgba(79, 140, 255, 0.18)"
224
232
  };
225
233
 
234
+ // ---- theme scheme detection (for the panel's 3D inner shadow) ----
235
+ // Prefer a positive dark signal from the OS preference, then DSH's theme
236
+ // attribute/class, then the --dsw-alias-bg-base luminance as a last resort.
237
+ // Failed-open to light so a light card never gets a dark inner shadow by
238
+ // mistake; the caller flips to the white bevel only on a confident dark read.
239
+ function isDarkScheme() {
240
+ try {
241
+ // DSH's authoritative dark marker: <body data-ds-dark-theme> is set when
242
+ // the dark scheme is active (system OR manual). Check it first.
243
+ var body = document.body;
244
+ if (body) {
245
+ if (body.hasAttribute && body.hasAttribute("data-ds-dark-theme")) return true;
246
+ var cls = body.className || "";
247
+ if (/dark|theme-dark/i.test(cls)) return true;
248
+ var attr = (body.getAttribute("data-theme") || body.getAttribute("data-color-scheme") || "").toLowerCase();
249
+ if (attr === "dark" || /dark/.test(attr)) return true;
250
+ }
251
+ if (typeof matchMedia === "function" && matchMedia("(prefers-color-scheme: dark)").matches) return true;
252
+ var root = body || document.documentElement;
253
+ var cs = getComputedStyle(root).colorScheme;
254
+ if (typeof cs === "string" && /dark/i.test(cs)) return true;
255
+ var v = getComputedStyle(root).getPropertyValue("--dsw-alias-bg-base");
256
+ var m = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/.exec(v);
257
+ if (m) return (Number(m[1]) + Number(m[2]) + Number(m[3])) < 384;
258
+ } catch (e) {}
259
+ return false;
260
+ }
261
+ // Inner shadow: computed per-render (not once) so it follows the live theme.
262
+ // Dark scheme -> soft WHITE bevel (recessed card highlight); light -> soft
263
+ // dark shade. Outer shadow stays off (clip-path would clip it).
264
+ function innerShadow() {
265
+ return isDarkScheme()
266
+ ? "inset 0 0 0 1px rgba(255,255,255,0.16), inset 0 1px 0 rgba(255,255,255,0.12), inset 0 -10px 16px rgba(255,255,255,0.08)"
267
+ : "inset 0 0 0 1px rgba(0,0,0,0.08), inset 0 2px 10px rgba(0,0,0,0.18)";
268
+ }
269
+
226
270
  // ---------- error boundary ----------
227
271
  var ErrorBoundary = (function () {
228
272
  if (!react.Component) return function (p) { return p.children; };
@@ -262,13 +306,17 @@ window.__ModuleLoader__.load({
262
306
 
263
307
  // ---------- component ----------
264
308
  function OutlinePanel(props) {
265
- var useSession = props.useSession;
266
- if (!useSession) {
267
- console.warn("[dsh-quick-toc] useSession prop missing");
309
+ // DSH 0.1.2-rc.1: the conversation moved out of the session snapshot —
310
+ // it is now the session-scope `chat` hook (ChatSnapshot: order + nodes
311
+ // map + legacy projection), contributed by dsh-client-ui-chat. Same node
312
+ // shape as before (kind user/assistant-step, location.turn, data.blocks).
313
+ var useChat = props.useChat;
314
+ if (!useChat) {
315
+ console.warn("[dsh-quick-toc] useChat prop missing (requires DSH >= 0.1.2-rc.1)");
268
316
  return null;
269
317
  }
270
- var order = useSession(function (s) { return s.chat.order; });
271
- var nodes = useSession(function (s) { return s.chat.nodes; });
318
+ var order = useChat(function (s) { return s.order; });
319
+ var nodes = useChat(function (s) { return s.nodes; });
272
320
 
273
321
  // ---- hooks (ALL before any conditional return) ----
274
322
  // collapsed by default; user expands via the edge handle (default dock: left)
@@ -793,24 +841,20 @@ window.__ModuleLoader__.load({
793
841
  // ---- geometry: always position by `left` (px) so dock-switch & slide share one animated transition
794
842
  var dockRight = dock === "right";
795
843
  var panelLeft;
796
- if (open) {
797
- panelLeft = dockRight
798
- ? window.innerWidth - (viewport ? viewport.right + 48 : 60) - panelW
799
- : (viewport ? viewport.left + 8 : 8);
844
+ if (dockRight) {
845
+ // RIGHT dock: keep the panel's VISIBLE right edge pinned at the SAME
846
+ // line when collapsed as when expanded (the expand edge is correct).
847
+ // open: left = openRight, visible right edge = openRight+panelW
848
+ // closed: left = openRight + panelW, clip-right panelW hides it fully,
849
+ // so the visible right edge stays at openRight+panelW -> the
850
+ // collapse shrinks toward that line and vanishes exactly there.
851
+ var openRight = window.innerWidth - (viewport ? viewport.right + 48 : 60) - panelW;
852
+ panelLeft = open ? openRight : (openRight + panelW);
853
+ } else if (open) {
854
+ // LEFT dock open (left dock is the confirmed-correct reference — untouched)
855
+ panelLeft = (viewport ? viewport.left + 8 : 8);
800
856
  } else {
801
- // collapsed position:
802
- // left dock -> slide just past the left sidebar edge (stop there)
803
- // right dock -> boundary is the right sidebar when it is open,
804
- // otherwise the screen's right edge
805
- if (dockRight) {
806
- // with the right sidebar open, slide INTO the sidebar area (covered
807
- // by the cover wall); otherwise fly off the screen's right edge
808
- panelLeft = (viewport && viewport.right > 80)
809
- ? (window.innerWidth - viewport.right)
810
- : window.innerWidth + 24;
811
- } else {
812
- panelLeft = (viewport ? viewport.left - panelW - 8 : -(panelW + 48));
813
- }
857
+ panelLeft = (viewport ? viewport.left - panelW - 8 : -(panelW + 48));
814
858
  }
815
859
  var vMaxH = viewport ? Math.max(200, viewport.height - 28) : "72vh";
816
860
  var baseTopPx = (viewport ? viewport.top + 14 : window.innerHeight * 0.12) + panelY;
@@ -919,6 +963,7 @@ window.__ModuleLoader__.load({
919
963
  alignItems: "center",
920
964
  justifyContent: "center",
921
965
  borderRadius: "50%",
966
+ cornerShape: "round",
922
967
  background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.16))",
923
968
  border: "none",
924
969
  color: C.muted,
@@ -962,9 +1007,12 @@ window.__ModuleLoader__.load({
962
1007
  // like being covered by the sidebar (the sidebar stays untouched):
963
1008
  // left dock -> clipped from the left up to the sidebar line
964
1009
  // right dock -> clipped from the right at the screen/sidebar line
1010
+ // LEFT dock keeps its original clip (collapse left). RIGHT dock clips the
1011
+ // LEFT side so the panel closes toward its right (dock) edge in place.
965
1012
  var panelClip = "inset(0 0 0 0px)";
966
1013
  if (!open) {
967
1014
  if (dockRight) {
1015
+ // mirror of LEFT: clip the RIGHT side (panel flies right, shrinking from its right)
968
1016
  panelClip = "inset(0 " + panelW + "px 0 0)";
969
1017
  } else {
970
1018
  panelClip = "inset(0 0 0 " + (panelW + 8) + "px)";
@@ -985,13 +1033,15 @@ window.__ModuleLoader__.load({
985
1033
  background: C.panelBg,
986
1034
  border: "none",
987
1035
  borderRadius: 12,
988
- boxShadow: "0 2px 10px rgba(0,0,0,0.18)",
989
1036
  zIndex: Z_BASE,
990
1037
  overflow: "hidden",
991
1038
  color: C.text,
992
1039
  opacity: panelOpacity,
993
1040
  clipPath: panelClip,
994
- boxShadow: "none",
1041
+ // inner shadow only (no outer): theme-aware (white bevel in dark mode,
1042
+ // soft shade in light) — outer shadow would be clipped by the collapse
1043
+ // clip-path and the panel edges.
1044
+ boxShadow: innerShadow(),
995
1045
  transition: panelTransition,
996
1046
  pointerEvents: "auto"
997
1047
  },
@@ -1027,6 +1077,7 @@ window.__ModuleLoader__.load({
1027
1077
  width: 56,
1028
1078
  height: 5,
1029
1079
  borderRadius: 999,
1080
+ cornerShape: "round",
1030
1081
  background: "var(--dsw-alias-border-l2, rgba(128,128,128,0.55))",
1031
1082
  opacity: 0.35,
1032
1083
  transition: "opacity 0.2s ease",
@@ -1075,6 +1126,7 @@ window.__ModuleLoader__.load({
1075
1126
  alignItems: "center",
1076
1127
  justifyContent: "center",
1077
1128
  borderRadius: "50%",
1129
+ cornerShape: "round",
1078
1130
  background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.16))",
1079
1131
  border: "none",
1080
1132
  color: C.muted,
@@ -1118,6 +1170,7 @@ window.__ModuleLoader__.load({
1118
1170
  alignItems: "center",
1119
1171
  justifyContent: "center",
1120
1172
  borderRadius: "50%",
1173
+ cornerShape: "round",
1121
1174
  background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.16))",
1122
1175
  border: "none",
1123
1176
  color: C.muted,
@@ -1188,6 +1241,7 @@ window.__ModuleLoader__.load({
1188
1241
  alignItems: "center",
1189
1242
  background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.14))",
1190
1243
  borderRadius: 999,
1244
+ cornerShape: "round",
1191
1245
  padding: "6px 12px"
1192
1246
  },
1193
1247
  children: [
@@ -1274,6 +1328,7 @@ window.__ModuleLoader__.load({
1274
1328
  alignItems: "center",
1275
1329
  justifyContent: "center",
1276
1330
  borderRadius: "50%",
1331
+ cornerShape: "round",
1277
1332
  fontSize: 11,
1278
1333
  color: searchScope === "full" ? "var(--dsw-alias-brand-primary, #4f8cff)" : C.muted,
1279
1334
  background: searchScope === "full" ? "rgba(79,140,255,0.18)" : "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.14))",
@@ -1306,6 +1361,7 @@ window.__ModuleLoader__.load({
1306
1361
  // the scrollbar follows the dock side (rtl flips it to the left)
1307
1362
  react_jsx_runtime.jsx("div", {
1308
1363
  ref: listRef,
1364
+ className: "dqt-list",
1309
1365
  style: {
1310
1366
  overflowY: "auto",
1311
1367
  padding: "6px 8px",
@@ -1395,6 +1451,7 @@ window.__ModuleLoader__.load({
1395
1451
  width: 12,
1396
1452
  height: 80,
1397
1453
  borderRadius: 999,
1454
+ cornerShape: "round",
1398
1455
  background: "var(--dsw-alias-border-l2, rgba(160,160,160,0.55))",
1399
1456
  opacity: handleShown ? 0.5 : 0,
1400
1457
  transition: "opacity 0.35s ease, background 0.25s ease",
@@ -1419,9 +1476,10 @@ window.__ModuleLoader__.load({
1419
1476
  })
1420
1477
  }) : null;
1421
1478
 
1422
- return react_jsx_runtime.jsx(ErrorBoundary, {
1423
- children: [panelEl, edgeEl]
1424
- });
1479
+ return react_dom.createPortal(
1480
+ react_jsx_runtime.jsx(ErrorBoundary, { children: [panelEl, edgeEl] }),
1481
+ document.body
1482
+ );
1425
1483
  }
1426
1484
 
1427
1485
  function renderItem(n, depth, jump, C, uid) {
@@ -1540,15 +1598,6 @@ window.__ModuleLoader__.load({
1540
1598
  return out;
1541
1599
  }
1542
1600
 
1543
- function OutlineOverlay(props) {
1544
- var SessionProvider = props.SessionProvider;
1545
- var renderSlot = props.renderSlot;
1546
- return react_jsx_runtime.jsx(SessionProvider, {
1547
- empty: function () { return null; },
1548
- children: function () { return renderSlot("quick-toc.panel", {}); }
1549
- });
1550
- }
1551
-
1552
1601
  var zh = {
1553
1602
  "panel.title": "对话大纲"
1554
1603
  };
@@ -1556,38 +1605,48 @@ window.__ModuleLoader__.load({
1556
1605
  "panel.title": "Conversation Outline"
1557
1606
  };
1558
1607
 
1559
- var inject = ["slots", "sessions", "locale"];
1608
+ var inject = ["slots", "locale"];
1560
1609
 
1561
1610
  function apply(ctx) {
1562
1611
  ctx.effect(function () {
1563
1612
  return ctx.locale.register("dsh-quick-toc", { zh: zh, en: en });
1564
1613
  }, "dsh-quick-toc: dictionaries");
1565
- ctx.slots.inject("shell.overlay", function () {
1614
+ // DSH 0.1.2-rc.1: session-scoped hooks (useChat/useSession/sessionId) only arrive
1615
+ // inside a declared session slot. Register the panel into the session-scoped
1616
+ // conversation.input.overlay (list/additive) so it receives useChat; the panel
1617
+ // itself renders a fixed, frame-floating dock, so the overlay seat is just the
1618
+ // hook source (it is NOT the panel's visual container).
1619
+ ctx.slots.inject("conversation.input.overlay", function () {
1566
1620
  return ctx.slots.register({
1567
- name: "shell.overlay",
1621
+ name: "conversation.input.overlay",
1568
1622
  id: "quick-toc",
1569
1623
  order: 90,
1570
- children: { "quick-toc.panel": { kind: "single", scope: "session" } }
1571
- }, OutlineOverlay);
1572
- });
1573
- ctx.slots.inject("quick-toc.panel", function () {
1574
- return ctx.slots.register({
1575
- name: "quick-toc.panel",
1576
1624
  locale: "dsh-quick-toc"
1577
1625
  }, OutlinePanel);
1578
1626
  });
1579
1627
  }
1580
1628
 
1629
+ exports.name = "dsh-quick-toc"; // runner attributes slot entries to this registrant
1581
1630
  exports.apply = apply;
1582
1631
  exports.inject = inject;
1583
1632
 
1584
- // inject the small keyframe stylesheet once (spinner rotation)
1633
+ // inject the small keyframe + panel-scrollbar stylesheet once
1585
1634
  (function () {
1586
1635
  if (typeof document === "undefined") return;
1587
1636
  if (document.getElementById("dsh-quick-toc-css")) return;
1588
1637
  var s = document.createElement("style");
1589
1638
  s.id = "dsh-quick-toc-css";
1590
- s.textContent = "@keyframes dqt-spin{to{transform:rotate(360deg)}}@keyframes dqt-fade-in{from{opacity:0}to{opacity:1}}@keyframes dqt-fade-out{from{opacity:1}to{opacity:0}}";
1639
+ s.textContent = "@keyframes dqt-spin{to{transform:rotate(360deg)}}@keyframes dqt-fade-in{from{opacity:0}to{opacity:1}}@keyframes dqt-fade-out{from{opacity:1}to{opacity:0}}" +
1640
+ // Hide the native scrollbar entirely (no arrow buttons, no grey bar) —
1641
+ // custom webkit scrollbar styling was not reliably suppressing the
1642
+ // default arrows, so a hidden native bar + the panel's inner shadow gives
1643
+ // the cleanest look. Scrolling still works (auto-follow + wheel-load-older).
1644
+ ".dqt-list{scrollbar-width:none;-ms-overflow-style:none}" +
1645
+ ".dqt-list::-webkit-scrollbar{display:none;width:0;height:0}" +
1646
+ ".dqt-list::-webkit-scrollbar-button{display:none;height:0;width:0}" +
1647
+ ".dqt-list::-webkit-scrollbar-track{background:transparent}" +
1648
+ ".dqt-list::-webkit-scrollbar-thumb{background:transparent}" +
1649
+ ".dqt-list::-webkit-scrollbar-corner{background:transparent}";
1591
1650
  document.head.appendChild(s);
1592
1651
  })();
1593
1652
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-quick-toc",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Quick conversation TOC for DeepSeek Harness: markdown heading outline grouped by turn, with auto-follow highlight and smooth jump navigation",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -8,8 +8,12 @@
8
8
  "lib",
9
9
  "cordis.patch.yml",
10
10
  "README.md",
11
+ "README.en.md",
11
12
  "LICENSE"
12
13
  ],
14
+ "engines": {
15
+ "dsh": ">=0.1.2-rc.1"
16
+ },
13
17
  "exports": {
14
18
  ".": "./lib/index.js",
15
19
  "./client": "./lib/client.js",
@@ -21,13 +25,24 @@
21
25
  },
22
26
  "client": {
23
27
  "inject": [
24
- "@deepseek-ai/dsh-client-runtime",
28
+ "@deepseek-ai/dsh-client-ui-chat",
25
29
  "@deepseek-ai/dsh-client-ui-conversation",
26
30
  "@deepseek-ai/dsh-client-ui-layout"
27
31
  ],
28
32
  "platform": "web"
33
+ },
34
+ "compatibility": {
35
+ "dshReleases": {
36
+ "0.1.2-rc.1": "compatible",
37
+ "0.1.5-rc.1": "compatible"
38
+ }
29
39
  }
30
40
  },
41
+ "peerDependencies": {
42
+ "react": "^18.2.0",
43
+ "@deepseek-ai/dsh-client-ui-chat": ">=0.1.2-rc.1",
44
+ "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.2-rc.1"
45
+ },
31
46
  "license": "MIT",
32
47
  "keywords": [
33
48
  "dsh",