dsh-mindmap 0.8.1 → 0.8.3
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/CHANGELOG.md +59 -14
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/client.js +9 -3
- package/index.js +17 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,24 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here. Release-specific notes are also published on GitHub Releases.
|
|
4
4
|
|
|
5
|
-
## [
|
|
5
|
+
## [0.8.2] - 2026-09-09
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
- Node right-click menu gains「复制全文」: copies a node's complete own content to the clipboard (code blocks take the full fenced source, table blocks re-emit a valid Markdown table with the separator row restored, everything else takes the raw text), alongside the existing 复制为图片 / 导出为图片. A divider line separates the node title from the actions.
|
|
15
|
-
- Hover overlay for truncated content: prose blocks cut by the line clamp and code blocks share one floating panel (fixed-positioned outside the zoom layer, scrollable full text); it always docks to the right of the node box and never flips left — clicking a node auto-focuses it into view, so hovering again always shows everything.
|
|
16
|
-
- Canvas panning (021): the mindmap canvas can now be dragged around with the middle mouse button (anywhere, even over a node), the left button on blank canvas space (this is the Mac trackpad「click and drag」path), or Space + left button (even over a node, when the drag has to start on a card). Content follows the pointer 1:1, blank space shows a grab hand, and the canvas no longer chains its scroll to the host page at the edges. A 4px threshold separates drag from click, so clicking blank space still clears the selection and clicking a node still focuses it — while a real drag no longer wipes the selection ring on release. Built on pointer events with pointer capture, so the pointer keeps dragging even outside the panel; touch input is left to native scrolling so momentum survives; and the gesture never re-renders the tree (cursor and text-selection lock go straight to the DOM, because a re-render would re-measure every `TreeRow`). Refinements from review (`_issues/001`): sub-threshold jitter no longer writes `scroll` or flashes the grab cursor (a 1–3px hand tremor on a plain click used to shift the canvas, and because the formula is absolute from the press anchor, skipping those early writes still tracks the pointer 1:1 — the full displacement lands in one jump once the threshold is crossed); the grab cursor and the text-selection lock are now applied on the first crossing rather than on press; a pan whose `setPointerCapture` failed no longer hangs when the pointer is released outside the scroller (a later button-less move ends the pan instead of dragging the canvas with nothing held); the click-suppression flag is cleared at the very top of every gesture rather than after the early returns, so a finished drag can no longer leak into a later tap that takes a non-panning path (touch tap, left-press on a node) and swallow it; and a second pointer can no longer drive an existing pan's anchor.
|
|
9
|
+
- Read-size guard for `mindmap_open` / `mindmap_get`: both tools `stat` the file first and refuse to read anything larger than 2 MB (`MAX_READ_BYTES`, the same cap as writes), so opening an accidentally huge document can no longer flood the model context.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Export table rendering: the per-cell wrap width is clamped to at least 12px (`tableCellMinInner`, one full-width character) in both box measurement and SVG drawing, so extremely wide tables (e.g. 60 columns) can never degenerate into one-character-per-line stacking.
|
|
17
14
|
|
|
18
15
|
### Changed
|
|
19
16
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
17
|
+
- Changelog realignment: entries that had shipped with 0.5.0–0.8.1 are moved from `[Unreleased]` into their own version sections, split by the real git tag boundaries; README and README.zh-CN updated to reflect the dual dsh compatibility (`0.1.1-rc.2` + `0.1.2-rc.1`), paragraph promotion (019 block concept), and the published release tag.
|
|
18
|
+
- Dependabot: major-version upgrades of `actions/checkout` and `actions/setup-node` (≥5) are ignored until evaluated separately (the two hanging PRs were closed).
|
|
19
|
+
|
|
20
|
+
## [0.8.1] - 2026-09-08
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `@deepseek-ai/dsh-tools` peer dependency with a four-range version window (`^0.1.0-rc.6 || ^0.1.1-rc.0 || ^0.1.2-alpha.0 || ^0.1.3-alpha.0`), so plugin installs resolve cleanly across the harness tooling line.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Dependency refresh (`@deepseek-ai/cosmokit` 1.8.2 → 1.8.3, `@deepseek-ai/schemastery` 3.18.1 → 3.18.2, lockfile updated accordingly); `.gitignore` also ignores `_*` and `._*` at the repository root.
|
|
29
|
+
|
|
30
|
+
## [0.8.0] - 2026-09-04
|
|
23
31
|
|
|
24
32
|
### Fixed
|
|
25
33
|
|
|
@@ -28,8 +36,11 @@ All notable changes to this project are documented here. Release-specific notes
|
|
|
28
36
|
- **Live panel data**: the session snapshot no longer carries a flat `nodes` array (conversation content moved to the Chat view), so the panel now reads nodes via `useChat → ChatSnapshot.legacy.nodes` (0.1.2-rc.1+) first and falls back to `useSession → SessionSnapshot.nodes` (dsh ≤0.1.1). `ToolResultNode` field names are unchanged across both, so document replay, auto-open, and error surfacing work as before.
|
|
29
37
|
- **Settings panel**: `settings.describe` over the client connection now returns the descriptor array directly (0.1.2-rc.1+) instead of an aggregate `result.value.namespaces` (dsh ≤0.1.1); both envelope shapes are parsed.
|
|
30
38
|
- **Hygiene**: `dsh.client.inject` additionally lists `@deepseek-ai/dsh-cordis-client-runner` (the 0.1.2-rc.1 browser runtime module id; `@deepseek-ai/dsh-client-runtime` is kept for 0.1.1-rc.2 — a missing id is silently skipped by the loader, so both coexist safely).
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
|
|
40
|
+
## [0.7.0] - 2026-08-31
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
33
44
|
- A code-review sweep (`_issues/002`) landed 11 fixes, each with a failing regression test first:
|
|
34
45
|
- **Markdown parsing**: an unclosed `---` frontmatter fence at the top no longer swallows the whole document into an empty tree; the root-title echo merge (`firstTopH1Seen`) is set only by a top-level H1 so an earlier H2 or a heading inside a quote block no longer suppresses it; GFM table column count is pinned to the **separator row** (short rows padded, extra cells dropped) instead of the header; fenced code blocks close only on a matching fence type (`~~~` is no longer ended by a ``` line) and `parseTableRow` honors an escaped `\|` preceded by an escaped backslash (`\\|`).
|
|
35
46
|
- **Document snapshots**: `mergeDocuments` only deletes a dropped local entry when no live snapshot owns the same path, so a recreated file that a snapshot also references keeps its panel openable.
|
|
@@ -37,6 +48,40 @@ All notable changes to this project are documented here. Release-specific notes
|
|
|
37
48
|
- **Host safety & approval**: the tree route and all mindmap path resolutions now reject symlink escapes out of the working directory via a `realpath` walk-up (`resolvesInsideBase`); `requireApproval` gates `mindmap_create` as well as `mindmap_update`; and `listDirectoryLevel` stops iterating once it hits the entry cap (perf) while still flagging truncation.
|
|
38
49
|
- **PNG export**: wide tables measure their box height using the same clamped per-column width the renderer uses, so wrapped cell text no longer overflows the measured box bottom.
|
|
39
50
|
|
|
51
|
+
## [0.6.0] - 2026-08-30
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- Canvas panning (021): the mindmap canvas can now be dragged around with the middle mouse button (anywhere, even over a node), the left button on blank canvas space (this is the Mac trackpad「click and drag」path), or Space + left button (even over a node, when the drag has to start on a card). Content follows the pointer 1:1, blank space shows a grab hand, and the canvas no longer chains its scroll to the host page at the edges. A 4px threshold separates drag from click, so clicking blank space still clears the selection and clicking a node still focuses it — while a real drag no longer wipes the selection ring on release. Built on pointer events with pointer capture, so the pointer keeps dragging even outside the panel; touch input is left to native scrolling so momentum survives; and the gesture never re-renders the tree (cursor and text-selection lock go straight to the DOM, because a re-render would re-measure every `TreeRow`). Refinements from review (`_issues/001`): sub-threshold jitter no longer writes `scroll` or flashes the grab cursor (a 1–3px hand tremor on a plain click used to shift the canvas, and because the formula is absolute from the press anchor, skipping those early writes still tracks the pointer 1:1 — the full displacement lands in one jump once the threshold is crossed); the grab cursor and the text-selection lock are now applied on the first crossing rather than on press; a pan whose `setPointerCapture` failed no longer hangs when the pointer is released outside the scroller (a later button-less move ends the pan instead of dragging the canvas with nothing held); the click-suppression flag is cleared at the very top of every gesture rather than after the early returns, so a finished drag can no longer leak into a later tap that takes a non-panning path (touch tap, left-press on a node) and swallow it; and a second pointer can no longer drive an existing pan's anchor.
|
|
56
|
+
|
|
57
|
+
## [0.5.1] - 2026-08-26
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- Block concept for mindmap nodes (per the 003 living spec): markdown content is now parsed into typed blocks — text, markdown (inline formatting), code (compact summary line + hover overlay for the full source), quote, and table (full grid, cells never split) — while block-level structure syntax (headings/lists/quotes) is split into child nodes and plain paragraphs are promoted to nodes instead of being stuffed into a parent's description. Inline formatting never triggers splitting.
|
|
62
|
+
- Unified clickable links: any URL inside any block is rendered complete (never abbreviated), clickable, and opens in a new browser tab; links inside the hover overlay are clickable too.
|
|
63
|
+
|
|
64
|
+
## [0.5.0] - 2026-08-26
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- Step-by-step update convention for the AI (system prompt + `mindmap_update` tool description): when an edit has several parts, call `mindmap_update` as soon as each part is ready instead of one giant update at the end — each call still carries the FULL document, and the panel's growth animation makes the tree visibly grow while the AI works. The former "panel updates in one step" wording is removed as it pointed the other way.
|
|
69
|
+
- Progressive growth animation for the mindmap panel: after every update, newly added/changed nodes fade in one by one (breadth-first from root to leaves, staggered up to 90ms apart) instead of the whole tree popping in at once. Diffing rides on the existing stable structural node IDs, so unchanged nodes never flicker or replay; the total duration is compressed to stay within ~2s for large documents; connector lines fade in together with their new children. The layout is computed in full up front, so the animation is compositor-only (opacity/transform) with no reflow cost. A new `growthAnimation` setting (settings panel「面板」→「生长动画」, default on) turns it off entirely, and `prefers-reduced-motion` is respected. The `mindmap_update` contract is unchanged (still full markdown).
|
|
70
|
+
- Theme token system: node colors/corners/shadows/states are produced by the pure function `resolveNodeStyle` from theme tokens, so card style and color theme switch cleanly without touching layout code.
|
|
71
|
+
- Node right-click menu gains「复制全文」: copies a node's complete own content to the clipboard (code blocks take the full fenced source, table blocks re-emit a valid Markdown table with the separator row restored, everything else takes the raw text), alongside the existing 复制为图片 / 导出为图片. A divider line separates the node title from the actions.
|
|
72
|
+
- Hover overlay for truncated content: prose blocks cut by the line clamp and code blocks share one floating panel (fixed-positioned outside the zoom layer, scrollable full text); it always docks to the right of the node box and never flips left — clicking a node auto-focuses it into view, so hovering again always shows everything.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- Length governance for node boxes (020 revision, replacing unbounded full-wrap): every node box is capped at 320px wide so long content wraps instead of stretching into horizontal bars. Prose blocks (text/md/list/quote-topic) that still exceed 6 wrapped lines are clamped with an ellipsis — full text stays reachable via the hover overlay. The clamp uses an integer 20px line-height plus a hard `maxHeight` guard so the cut line never leaks half a glyph. Structural blocks keep their shape: tables get their own wider 680px writing surface (overflow scrolls inside the box) with per-cell min/max width guards.
|
|
77
|
+
- GFM table parsing contract: a literal `|` inside a cell must be escaped as `\|` and the parser now honors it; column count is pinned to the separator row — short rows are padded with empty cells and extra cells are dropped, so the grid never turns ragged even when a row contains unescaped pipes.
|
|
78
|
+
- Split the two largest client source fragments for maintainability (pure physical split, no behavior change): `src/client/ui/render.js` (572 lines) becomes `slot.js` (MindmapSlot) + `render.js` (NodeBox/TreeRow) + `canvas.js` (the 016 canvas/zoom region), and `src/client/ui/panel.js` (790 lines) becomes `panel.js` (component head) + `treetab.js` (the 013 directory-tree tab region) + `panelbody.js` (layout effect + overlay JSX). The split was done by line slicing only, and the rebuilt `client.js` is byte-for-byte identical to the previous build. `build-client.mjs` now decides fragment separators via an explicit `PHYSICAL_SPLITS` set instead of hard-coded list indices.
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- The panel now reliably auto-opens when the AI completes `mindmap_open` / `mindmap_create`. A structural fingerprint of the session nodes (`nodesFingerprint`) feeds a second `useSession` selector; its value comparison bypasses the reference-equality short-circuit that starved the auto-open effect whenever the host store mutated the nodes array in place.
|
|
83
|
+
- The "AI 正在打开脑图…" loading state is no longer a dead end. Snapshot documents whose path differs from the tree-click key only by letter case (macOS case-insensitive filesystem) now merge automatically; errored mindmap tool results (`isError` or `ok !== true`) surface as an inline error; a ~30s watchdog switches to a timeout state. Both failure states offer a one-click retry that re-sends the open request.
|
|
84
|
+
|
|
40
85
|
## [0.4.1] - 2026-08-24
|
|
41
86
|
|
|
42
87
|
### Changed
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ A DeepSeek Harness plugin that turns a plain Markdown file into a live mindmap.
|
|
|
22
22
|
- **Directory tree tab** — a persistent tree of the session working directory (served by a plugin-owned read-only route), lazy-loaded per directory; right-click to create a mindmap (at the root or inside a directory); left-click a `.md` to open it instantly and hand it to the AI for editing.
|
|
23
23
|
- **Single-mindmap mode** — two tabs only: 目录 (tree) and 脑图 (the current mindmap); opening another `.md` replaces the previous one.
|
|
24
24
|
- **"What you see is what the AI edits"** — when the visible mindmap differs from the AI's working document, the panel automatically asks the AI to open it, keeping the chat focus in sync.
|
|
25
|
-
- **MarkGrove-style mapping** — heading hierarchy, nested lists (empty items become placeholder nodes), code blocks as leaf nodes, paragraphs
|
|
25
|
+
- **MarkGrove-style mapping** — heading hierarchy, nested lists (empty items become placeholder nodes), code blocks as leaf nodes, paragraphs promoted to their own nodes (019 block concept), stable structural IDs, and orthogonal connector lines between nodes.
|
|
26
26
|
- **Centered canvas with zoom and pan** — the mindmap opens centered in the canvas (scrollable without edge clipping when larger); a floating zoom bar at the canvas top-right (zoom out / percent / zoom in / fit) applies auto fit-to-view on open (small maps stay at 100%), steps through 25%–300% with a stable view center, and keeps re-fitting as the AI edits — until you zoom manually. Click any node to zoom in on it and its whole subtree, with the node pinned at the left-center of the canvas. The canvas also pans by drag: the **middle button** anywhere (even over a node), the **left button on blank canvas** (the Mac trackpad「click and drag」path), or **Space + left button** when the drag must start on a card. Content follows the pointer 1:1, blank space shows a grab hand, and a 4px threshold separates drag from click — so clicking blank space still clears the selection and clicking a node still focuses it, while a real drag never wipes the selection ring.
|
|
27
27
|
- **PNG export** — one click on 导出图片 exports the current mindmap.
|
|
28
28
|
- **Safety** — `mindmap_create` / `mindmap_update` are approval-free by default (files are git-managed) with a `requireApproval` switch as an escape hatch (when on, both write tools ask); the client has **no write path** to the filesystem — every edit goes through the AI tools.
|
|
@@ -32,7 +32,7 @@ A DeepSeek Harness plugin that turns a plain Markdown file into a live mindmap.
|
|
|
32
32
|
| Component | Baseline |
|
|
33
33
|
| --- | --- |
|
|
34
34
|
| Node.js | 20.11 or newer |
|
|
35
|
-
| DeepSeek Harness | tested against `0.1.1-rc.2` |
|
|
35
|
+
| DeepSeek Harness | tested against `0.1.1-rc.2` and `0.1.2-rc.1` |
|
|
36
36
|
|
|
37
37
|
## Installation
|
|
38
38
|
|
|
@@ -42,7 +42,7 @@ Development (link install, live source):
|
|
|
42
42
|
dsh plugin --profile web add link:/path/to/dsh-mindmap
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Released tag
|
|
45
|
+
Released tag:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
dsh plugin --profile <profile> add <pkg>#v<version>
|
package/README.zh-CN.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
- **常驻目录树 tab**——工作目录结构懒加载树(插件自建只读路由);空白处/目录右键新建脑图;左键点 `.md` 秒开 tab 并自动交给 AI 打开,随后直接对话继续编辑。
|
|
23
23
|
- **单脑图模式**——面板只有「目录」和「脑图」两个 tab,打开新脑图替换旧的那颗。
|
|
24
24
|
- **「所见即所编」焦点同步**——可见脑图与 AI 工作文档不一致时,面板自动让 AI 打开它,聊天焦点始终跟随你的眼睛。
|
|
25
|
-
- **MarkGrove 同款映射与连线**——标题层级挂树、列表缩进(空项 =
|
|
25
|
+
- **MarkGrove 同款映射与连线**——标题层级挂树、列表缩进(空项 = 占位节点)、代码块叶节点、段落升格为独立节点(019 块概念)、稳定结构 ID、节点间直角折线。
|
|
26
26
|
- **画布居中、缩放与平移**——脑图打开后居中呈现(超出画布时可滚动、无边缘裁剪);画布右上角浮动缩放条(缩小 / 比例 / 放大 / 适配),打开时自动适配合适比例(小图保持 100%),25%–300% 逐级缩放且视图中心不跳变,AI 编辑后持续自动再适配,直到你手动缩放。点击任意节点即可聚焦:节点滚到画布左侧居中,视图放大到它和整棵子树完整可见(上限 100%)。画布还能拖着走:**中键**随处拖(含节点上)、**空白处左键拖**(Mac 触控板「点按并拖移」)、或 **Space + 左键拖**(必须从节点上起手时);内容跟手 1:1,空白处呈抓手光标,4px 阈值把拖与点分开——点空白仍取消选中、点节点仍聚焦,真拖不会误清选中圈。
|
|
27
27
|
- **PNG 导出**——面板右上角「导出图片」一键导出当前脑图。
|
|
28
28
|
- **安全**——`mindmap_create`/`mindmap_update` 默认免原生审批(文件受 git 管控),留 `requireApproval` 开关作后悔药(开启后两个写工具都走审批);客户端**没有任何写文件通道**,一切编辑都经 AI 工具。
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
| 组件 | 基线 |
|
|
33
33
|
| --- | --- |
|
|
34
34
|
| Node.js | 20.11 及以上 |
|
|
35
|
-
| DeepSeek Harness | 实测于 `0.1.1-rc.2` |
|
|
35
|
+
| DeepSeek Harness | 实测于 `0.1.1-rc.2` 与 `0.1.2-rc.1` |
|
|
36
36
|
|
|
37
37
|
## 安装
|
|
38
38
|
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
dsh plugin --profile web add link:/path/to/dsh-mindmap
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
发布 tag
|
|
45
|
+
发布 tag:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
dsh plugin --profile <profile> add <pkg>#v<version>
|
package/client.js
CHANGED
|
@@ -950,6 +950,10 @@ window.__ModuleLoader__.load({
|
|
|
950
950
|
nodeW: 220, padX: 12, padY: 8, hGap: 48, vGap: 12, pad: 20,
|
|
951
951
|
fontSize: 13, lineHeight: 18,
|
|
952
952
|
tableCellW: 110, tableCellPad: 8, tableMinW: 140, tableMaxW: 480,
|
|
953
|
+
// 单元格折行宽下限 = 导出字号 12px 的全角宽:极宽表(如 60 列)
|
|
954
|
+
// 列盒窄于内距时 cellInner 可能为负,钳到至少容纳一个全角字符;
|
|
955
|
+
// 测量与渲染必须共用同一钳制值(022 契约)。
|
|
956
|
+
tableCellMinInner: 12,
|
|
953
957
|
};
|
|
954
958
|
|
|
955
959
|
function escapeXml(text) {
|
|
@@ -1021,7 +1025,7 @@ window.__ModuleLoader__.load({
|
|
|
1021
1025
|
const rows = (node.data && node.data.rows) || [];
|
|
1022
1026
|
const cols = rows.reduce((mx, row) => Math.max(mx, row.length), 0) || 1;
|
|
1023
1027
|
const w = Math.min(EXPORT.tableMaxW, Math.max(EXPORT.tableMinW, cols * EXPORT.tableCellW));
|
|
1024
|
-
const cellInner = w / cols - EXPORT.tableCellPad * 2;
|
|
1028
|
+
const cellInner = Math.max(w / cols - EXPORT.tableCellPad * 2, EXPORT.tableCellMinInner);
|
|
1025
1029
|
const rowLines = rows.map((row) => row.reduce((mx, cell) => Math.max(mx, wrapExportText(stripInlineForExport(cell), cellInner, EXPORT.fontSize - 1).length), 1));
|
|
1026
1030
|
const h = Math.max(EXPORT.lineHeight, rowLines.reduce((a, b) => a + b, 0) * EXPORT.lineHeight);
|
|
1027
1031
|
return { w, h };
|
|
@@ -1101,14 +1105,15 @@ window.__ModuleLoader__.load({
|
|
|
1101
1105
|
const rows = (node.data && node.data.rows) || [];
|
|
1102
1106
|
const cols = rows.reduce((mx, row) => Math.max(mx, row.length), 0) || 1;
|
|
1103
1107
|
const colW = p.size.w / cols;
|
|
1104
|
-
const
|
|
1108
|
+
const innerW = Math.max(colW - EXPORT.tableCellPad * 2, EXPORT.tableCellMinInner);
|
|
1109
|
+
const rowLines = rows.map((row) => row.reduce((mx, cell) => Math.max(mx, wrapExportText(stripInlineForExport(cell), innerW, EXPORT.fontSize - 1).length), 1));
|
|
1105
1110
|
const rowH = rowLines.map((n) => n * EXPORT.lineHeight);
|
|
1106
1111
|
let ry = boxY;
|
|
1107
1112
|
rows.forEach((row, ri) => {
|
|
1108
1113
|
row.forEach((cell, ci) => {
|
|
1109
1114
|
const cx = p.x + ci * colW;
|
|
1110
1115
|
parts.push(`<rect x="${cx}" y="${ry}" width="${colW}" height="${rowH[ri]}" fill="${ri === 0 ? palette.surfaceCode : "none"}" stroke="${palette.borderSubtle}" stroke-width="1"/>`);
|
|
1111
|
-
const cellLines = wrapExportText(stripInlineForExport(cell),
|
|
1116
|
+
const cellLines = wrapExportText(stripInlineForExport(cell), innerW, EXPORT.fontSize - 1);
|
|
1112
1117
|
cellLines.forEach((ln, li) => {
|
|
1113
1118
|
const ty = ry + (li + 0.5) * EXPORT.lineHeight + (EXPORT.fontSize - 1) * 0.35;
|
|
1114
1119
|
parts.push(`<text x="${cx + EXPORT.tableCellPad}" y="${ty.toFixed(1)}" font-size="${EXPORT.fontSize - 1}" font-weight="${ri === 0 ? 600 : 400}" fill="${palette.text}">${escapeXml(ln)}</text>`);
|
|
@@ -3410,6 +3415,7 @@ window.__ModuleLoader__.load({
|
|
|
3410
3415
|
resultTextOfBlocks,
|
|
3411
3416
|
stemOf,
|
|
3412
3417
|
buildExportSvg,
|
|
3418
|
+
measureExportBox,
|
|
3413
3419
|
createIdFactory,
|
|
3414
3420
|
collectTreeIds,
|
|
3415
3421
|
planGrowthReveal,
|
package/index.js
CHANGED
|
@@ -35,6 +35,7 @@ export const Config = Schema.object({
|
|
|
35
35
|
})
|
|
36
36
|
|
|
37
37
|
const MAX_CONTENT_BYTES = 2 * 1024 * 1024
|
|
38
|
+
const MAX_READ_BYTES = 2 * 1024 * 1024
|
|
38
39
|
const MAX_NAME_CHARS = 80
|
|
39
40
|
const TOOL_TIMEOUT_MS = 15_000
|
|
40
41
|
const MAX_TREE_ENTRIES = 500
|
|
@@ -291,6 +292,18 @@ function byteLength(value) {
|
|
|
291
292
|
return new TextEncoder().encode(value).byteLength
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
/**
|
|
296
|
+
* mindmap_open / mindmap_get 共用的读取路径:先 stat 查文件大小(不为测
|
|
297
|
+
* 大小读全量),超限即拒;不存在时 stat 抛 ENOENT,readFile 的原语义不变。
|
|
298
|
+
*/
|
|
299
|
+
async function readMindmap(path) {
|
|
300
|
+
const { size } = await stat(path)
|
|
301
|
+
if (size > MAX_READ_BYTES) {
|
|
302
|
+
throw new Error(`mindmap size exceeds the ${MAX_READ_BYTES}-byte limit.`)
|
|
303
|
+
}
|
|
304
|
+
return readFile(path, 'utf8')
|
|
305
|
+
}
|
|
306
|
+
|
|
294
307
|
/** 工具结果信封:client 面板与模型共用的唯一载体。 */
|
|
295
308
|
function buildResult(op, path, extra = {}) {
|
|
296
309
|
const base = String(path ?? '').split(/[\\/]/).pop() || 'mindmap'
|
|
@@ -398,7 +411,7 @@ export function apply(ctx, config = {}) {
|
|
|
398
411
|
timeoutMs: TOOL_TIMEOUT_MS,
|
|
399
412
|
async execute(args, exec) {
|
|
400
413
|
const path = await resolveMindmapPath(sessionCwd(exec, ctx.sessions), args?.path)
|
|
401
|
-
const content = await
|
|
414
|
+
const content = await readMindmap(path)
|
|
402
415
|
return buildResult('open', path, { content })
|
|
403
416
|
},
|
|
404
417
|
}))
|
|
@@ -417,7 +430,7 @@ export function apply(ctx, config = {}) {
|
|
|
417
430
|
timeoutMs: TOOL_TIMEOUT_MS,
|
|
418
431
|
async execute(args, exec) {
|
|
419
432
|
const path = await resolveMindmapPath(sessionCwd(exec, ctx.sessions), args?.path)
|
|
420
|
-
const content = await
|
|
433
|
+
const content = await readMindmap(path)
|
|
421
434
|
return buildResult('get', path, { content })
|
|
422
435
|
},
|
|
423
436
|
}))
|
|
@@ -525,6 +538,8 @@ export function apply(ctx, config = {}) {
|
|
|
525
538
|
export const internals = Object.freeze({
|
|
526
539
|
GUIDANCE,
|
|
527
540
|
MAX_CONTENT_BYTES,
|
|
541
|
+
MAX_READ_BYTES,
|
|
542
|
+
readMindmap,
|
|
528
543
|
sanitizeStem,
|
|
529
544
|
resolveMindmapPath,
|
|
530
545
|
sessionCwd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mindmap",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Mindmap plugin for DeepSeek Harness: a plain markdown file in the working directory IS the mindmap; the chat edits it step by step and the right-side panel follows live.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|