dsh-toolfold 0.1.7

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 ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ 所有显著变更将记录于此。格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
+
5
+ ## [0.1.7] - 2026-08-26
6
+
7
+ ### Fixed
8
+ - npm 发布命令增加 `--no-git-checks`,跳过 pnpm 的 git 状态检查,避免在 CI 检出态下发布失败。
9
+
10
+ ### Changed
11
+ - 将发布工作流 Node 版本由 `lts/*` 固定为 `24`,与当前构建环境对齐。
12
+
13
+ ## [0.1.6] - 2026-08-26
14
+
15
+ ### Fixed
16
+ - 修复 WSL `bash` 工具在折叠后标题消失的问题。`BashRow` 使用 `data-sample="bash"` 独立渲染,无 `data-disclosure-row`,旧克隆逻辑回退到空的 `[data-tool]` 导致折叠条只剩计数。现按优先级克隆:`[data-disclosure-row]`(通用 `ToolRow` / `pwsh`)→ `[data-sample="bash"]`(Bash 头部)→ `[data-tool]` 首子 → `lastRow.firstElementChild`,并保持 120 ms 节流。(`lib/client.js`, `lib/dynamic-body.js`)
17
+
18
+ ### Changed
19
+ - 重构 npm 发布工作流 `.github/workflows/npm-publish.yml`:改为 `release.created` 触发,使用 `pnpm@11` + `setup-node` LTS 并通过 `secrets.npm_token` 发布。
20
+
21
+ ## [0.1.5] - 2026-08-21
22
+
23
+ ### Added
24
+ - 兼容 DSH `v0.1.1-rc1` 的设置存储与类型。
25
+
26
+ ### Fixed
27
+ - 修正 `peerDependencies` 中 `@deepseek-ai/dsh-settings` 的预发布版本区间(`>=0.1.0-rc.7 <0.1.1 || >=0.1.1-rc.0 <0.1.2`),避免 pnpm 解析冲突。
28
+
29
+ ## [0.1.4] - 2026-08-20
30
+
31
+ ### Fixed
32
+ - 适配 DSH 0.2.4 `settings.plugin.item` 由 `list` 改为 `keyed`:注册时同时提供 `key: 'toolfold'` 与 `id`,`toolfold settings card failed to register` 消失,设置卡在新版 Web 中重新出现。
33
+ - 同步 `peerDependencies`:`@deepseek-ai/dsh-settings@^0.1.0-rc.7` 与实际 `0.0.1-rc.1` 的差异说明。
34
+
35
+ ## [0.1.3] - 2026-08-16
36
+
37
+ ### Fixed
38
+ - 修复多条目折叠动画尾段卡顿:`ccxRise` 与高度收缩统一使用 `cubic-bezier(0,0,0.58,1)`,清理计时改为 `max(0,N-1)*step+dur+60`(收起)/`+260`(吸底),长卡片不再“先透明后瞬收”。
39
+
40
+ ### Changed
41
+ - 更新依赖 `deepseek-seetings` 版本。
42
+
43
+ ## [0.1.2] - 2026-08-16
44
+
45
+ ### Added
46
+ - `splitThink`(思考分隔调用组)默认开启:已完成的思考把前后两组工具调用隔开、各自独立折叠;关闭时恢复旧的“思考并入同组”行为。
47
+
48
+ ### Fixed
49
+ - 修复 `think` 若在两工具块之间整组被误合并为一条折叠条的问题。
50
+
51
+ ## [0.1.1] - 2026-08-16
52
+
53
+ ### Added
54
+ - 首个可用版本:连续 `tool-call` 折叠为最后一条的单行摘要 + “已折叠 N 个工具调用”,支持 `durMs / keepThink / splitThink / stats` 四项设置(DSH `settings` 服务 → `~/.dsh/settings.yaml`,回退到 Host 路由与 `localStorage`)与瀑布动画。
55
+ - `toolfold` 远端包与一键安装、设置双半通信(Host `GET/POST /api/dsh-toolfold/settings` + Client 桥)。
56
+
57
+ [0.1.7]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.6...v0.1.7
58
+ [0.1.6]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.5...v0.1.6
59
+ [0.1.5]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.4...v0.1.5
60
+ [0.1.4]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.3...v0.1.4
61
+ [0.1.3]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.2...v0.1.3
62
+ [0.1.2]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.1...v0.1.2
63
+ [0.1.1]: https://github.com/Minecraftbe/dsh-toolfold/releases/tag/v0.1.1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Minecraftbe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,136 @@
1
+ # dsh-toolfold · Tool Call Folding
2
+
3
+ > A DSH Web GUI plugin delivering a **Codex-like folding experience**: runs of consecutive
4
+ > **tool calls** fold into a single compact bar showing a one-line summary of the **last call** —
5
+ > click to expand/collapse. It replaces no built-in renderer, and uninstalling restores the UI
6
+ > completely.
7
+
8
+ ![Folding demo](assets/demo-fold.gif)
9
+ <!-- 🖼️ IMAGE SLOT: assets/demo-fold.gif — record 6–10s: a run of tool calls auto-folds into one bar → click to expand (cards waterfall down) → click to collapse (cards rise, heights shrink), 16:9, ≤2MB -->
10
+
11
+ ## Highlights
12
+
13
+ - **Runs of tool calls fold into one bar**: consecutive tool calls collapse into a single bar
14
+ showing the **last call's** one-line summary, labelled "已折叠 N 个工具调用 · 点击展开"
15
+ (N folded calls · click to expand);
16
+ - **Thinking separates call groups (default on)**: settled think blocks SPLIT the calls around
17
+ them into independent bars — calls before and after a completed think never merge into one bar;
18
+ turn it off to restore the original fold, where thinking folds WITH the surrounding tool group;
19
+ - **Thinking hidden by default, keepable**: settled think blocks are hidden outright by default;
20
+ with "保留思考" enabled they never disappear — shown between the folded bars in split mode, or
21
+ **re-inserted in their original order** between the calls on expand in merge mode;
22
+ - **In-progress thinking stays visible**: streaming think blocks render as their own row until
23
+ they complete, then follow the "split thinking" setting;
24
+ - **Spring waterfall animations**: cards cascade down on expand (spring overshoot) and rise with a
25
+ synchronized height shrink on collapse — content below follows continuously, no end snap;
26
+ disabled automatically under `prefers-reduced-motion`;
27
+ - **Bottom-pinned expand**: expanding near the bottom pins the bar's viewport position, so the
28
+ chat's auto-scroll never shoves it off the top of the screen;
29
+ - **Near-zero performance cost**: no page-wide observer, zero work while content streams, and
30
+ everything pauses when the tab is hidden — measured ≈0.03% of one core while idle
31
+ (see [Performance](#performance)).
32
+
33
+ ![Expand / collapse animation](assets/expand-collapse.gif)
34
+ <!-- 🖼️ IMAGE SLOT: assets/expand-collapse.gif — close-up of one 3–4 call run expanding (waterfall + label slide) and collapsing (rise + height shrink), square or 4:3, ≤2MB -->
35
+
36
+ ## Installation
37
+
38
+ ### One-line GitHub install
39
+
40
+ ```sh
41
+ dsh plugin --profile web add github:Minecraftbe/dsh-toolfold
42
+ ```
43
+
44
+ ### Install from source
45
+
46
+ ```sh
47
+ git clone https://github.com/Minecraftbe/dsh-toolfold.git
48
+ dsh plugin --profile web add ./dsh-toolfold
49
+ ```
50
+
51
+ After installing, **restart dsh and refresh the browser** to activate (bundle layers are composed
52
+ at boot). Verify the plugin reached the final config with:
53
+
54
+ ```sh
55
+ dsh --profile web --dump-config
56
+ ```
57
+
58
+ > If an earlier version was mounted manually with a `toolfold` row in `cordis.patch.yml`,
59
+ > remove that row before switching to this channel to avoid double-mounting.
60
+
61
+ ### Uninstall
62
+
63
+ ```sh
64
+ dsh plugin --profile web remove dsh-toolfold
65
+ ```
66
+
67
+ After a restart the UI is completely restored.
68
+
69
+ ## Usage & Settings
70
+
71
+ Settings: **Settings → Plugins → 工具折叠** (the card matches the built-in plugin cards and follows
72
+ the light/dark theme).
73
+
74
+ ![Settings card](assets/settings.png)
75
+ <!-- 🖼️ IMAGE SLOT: assets/settings.png — screenshot of the expanded "工具折叠" card in Settings → Plugins (duration slider + keep-think toggle + stats toggle + storage-location hint), light theme, PNG/WebP ≤1MB -->
76
+
77
+ | Setting | Description |
78
+ | --- | --- |
79
+ | **展开动画时长** (expand duration) | Per-card duration of the expand/collapse waterfall, 0–1000ms, default 240ms; 0 = instant |
80
+ | **保留思考** (keep thinking) | Settled thinking is hidden by default; on: it never disappears — shown between the folded bars in split mode, or re-inserted in its original order between the calls on expand in merge mode |
81
+ | **思考分隔调用组** (split call groups) | On (default): settled thinking splits the calls around it into independent bars; Off: thinking folds with the surrounding tool group (legacy behavior) |
82
+ | **性能统计** (performance stats) | Shows the plugin's own live cost in the card (cumulative counts and ms/s for observer callbacks / engine refreshes / merge passes / safety rescans / summary clones, plus the streaming batches ignored by the zero-cost short-circuit) |
83
+
84
+ Interactions: click the bar to expand/collapse; `Enter` or `Space` works too.
85
+
86
+ ### Where settings live
87
+
88
+ - **Installed (bundle) mode**: the DSH **settings service** persists them to
89
+ `~/.dsh/settings.yaml` (namespace `toolfold`) — the same document the product and other
90
+ plugins use, host-side, so they survive browser/device changes. The card footer shows
91
+ "设置保存在 DSH 主机配置". The file is editable directly:
92
+
93
+ ```yaml
94
+ toolfold:
95
+ durMs: 240 # expand animation duration, 0–2000ms
96
+ keepThink: false # keep settled thinking visible (between bars / on expand)
97
+ splitThink: true # settled thinking splits call groups into independent bars (default true)
98
+ stats: false # performance meter
99
+ ```
100
+
101
+ - **When no DSH settings service is detected** (remote browsers and other setups without the DSH
102
+ settings bridge): degrades to browser `localStorage` (key `dsh-toolfold.settings.v1`) and the
103
+ card says "仅保存在本浏览器"; the legacy key `dsh-codex-collapse.settings.v1` migrates
104
+ automatically on first load.
105
+
106
+ ![Keep-thinking comparison](assets/keep-think.gif)
107
+ <!-- 🖼️ IMAGE SLOT: assets/keep-think.gif — same run expanded twice, "keep thinking" off vs on, showing the think block hidden vs re-inserted in order, ≤2MB -->
108
+
109
+ ## Performance
110
+
111
+ - **Measured on the live GUI (8s idle window)**: engine total ≈1.6–2.8ms (≈0.3ms/s, ≈0.03% of one
112
+ core); a 5s CPU sample in the same window shows **zero samples from the engine** — the page's long
113
+ tasks (141–686ms) come from page load, product rendering, and other plugins;
114
+ - **Zero work while streaming**: text/think token mutations are short-circuited in O(1) and never
115
+ trigger a recompute (≈0.1–0.4µs per node);
116
+ - **Hidden tab = literally zero**: all observers and timers disconnect on hide and re-attach with a
117
+ reconciliation pass on return;
118
+ - Turn on "性能统计" to watch every cost item live inside the settings card.
119
+
120
+ ## Notes & Limitations
121
+
122
+ - Folding works on the rendered DOM and depends on stable product markers; if the product markup
123
+ changes, the worst case is that folding stops applying — the chat itself is never harmed;
124
+ - **In-progress thinking** and **AI text output** separate runs; **settled thinking** also
125
+ separates runs by default ("思考分隔调用组" on) — with it off, settled thinking folds with the
126
+ surrounding tool group instead;
127
+ - Expanded state is remembered per session flow + node key; reopening a session with the same keys
128
+ restores it;
129
+ - Settings are owned by the DSH host config (`~/.dsh/settings.yaml`); browser `localStorage` is
130
+ only the fallback when the host bridge is unreachable (then each browser needs its own setup);
131
+ - Requires `MutationObserver` and `requestAnimationFrame`; without them it degrades to "fold once on
132
+ initial render".
133
+
134
+ ---
135
+
136
+ This project was entirely completed by dsh + deepseek v4 flash(max).
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # dsh-toolfold · 工具调用折叠
2
+
3
+ > DSH Web GUI 插件,提供**类 Codex 的工具折叠体验**:把连续的**工具调用**折叠成一条简洁的
4
+ > 折叠条,只展示最后一个调用的一行摘要,点击即可展开/收起。
5
+ > 不替换任何内置渲染器,卸载后界面完全恢复原样。
6
+
7
+ ![折叠效果演示](assets/demo-fold.gif)
8
+ <!-- 🖼️ 图片位:assets/demo-fold.gif —— 建议录制 6~10s:多个工具调用自动折成一条 → 点击展开(卡片瀑布落下)→ 点击收起(卡片上行、高度收缩),16:9,≤2MB -->
9
+
10
+ ## 特性一览
11
+
12
+ - **连续工具调用折叠为一条**:相邻的工具调用整段收起,条内直接显示**最后一个调用**的一行
13
+ 摘要,并标注「已折叠 N 个工具调用 · 点击展开」;
14
+ - **思考分隔调用组(默认开启)**:已完成的思考会把前后两组工具调用**隔开、各自独立折叠**,
15
+ 不会把思考前后的调用合并成一条;关闭后恢复原折叠方式——思考并入所在工具组一起折叠;
16
+ - **思考默认隐藏,可保留**:已完成的思考默认隐藏(不占版面);开启「保留思考」后思考不再
17
+ 消失——分隔模式下显示在两条折叠条之间,合并模式下展开时按**原顺序插回调用之间**;
18
+ - **进行中的思考独立显示**:流式思考保持原样可见,完成后按「思考分隔」设置处理;
19
+ - **弹性瀑布动画**:展开时卡片自上而下逐张落下(spring 回弹),收起时逐张上行且行高度同步收缩,
20
+ 下方内容连续上移、结尾无跳变;系统开启「减少动态效果」时动画自动关闭;
21
+ - **贴底不顶出**:靠近底部展开时,折叠条视口位置被钉住,不会因聊天自动滚动被顶出屏幕;
22
+ - **接近零性能占用**:无页面级观察器、流式期间引擎零工作、标签页隐藏时全部暂停——
23
+ 实机测量空闲开销约 0.03% 单核(详见[性能](#性能))。
24
+
25
+ ![展开/收起动画](assets/expand-collapse.gif)
26
+ <!-- 🖼️ 图片位:assets/expand-collapse.gif —— 建议:单段 3~4 个调用的展开(瀑布落下 + 标签滑左)与收起(上行 + 高度收缩)特写,正方形或 4:3,≤2MB -->
27
+
28
+ ## 安装
29
+
30
+
31
+
32
+ ### GitHub 一行安装
33
+
34
+ ```sh
35
+ dsh plugin --profile web add github:Minecraftbe/dsh-toolfold
36
+ ```
37
+
38
+ ### 从源码安装
39
+
40
+ ```sh
41
+ git clone https://github.com/Minecraftbe/dsh-toolfold.git
42
+ dsh plugin --profile web add ./dsh-toolfold
43
+ ```
44
+
45
+ 装完**重启 dsh 并刷新浏览器**即可生效(bundle 层在启动时合成)。可用以下命令确认已进入
46
+ 最终配置:
47
+
48
+ ```sh
49
+ dsh --profile web --dump-config
50
+ ```
51
+
52
+ > 早期版本若曾在 `cordis.patch.yml` 手工加过 `toolfold` 行,先删除该行再切换,避免双挂载。
53
+
54
+ ### 卸载
55
+
56
+ ```sh
57
+ dsh plugin --profile web remove dsh-toolfold
58
+ ```
59
+
60
+ 重启后界面完全恢复原样。
61
+
62
+ ## 使用与设置
63
+
64
+ 设置入口:**设置 → 插件 → 工具折叠**(卡片外观与内置插件卡片一致,跟随深浅主题)。
65
+
66
+ ![设置卡片](assets/settings.png)
67
+ <!-- 🖼️ 图片位:assets/settings.png —— 截图建议:设置 → 插件 页内「工具折叠」卡片展开态(时长滑块 + 保留思考开关 + 性能统计开关 + 存储位置提示),浅色主题,PNG/WebP ≤1MB -->
68
+
69
+ | 设置项 | 说明 |
70
+ | --- | --- |
71
+ | **展开动画时长** | 展开/收起瀑布动画的单卡时长,0–1000ms,默认 240ms;0 为瞬时切换 |
72
+ | **保留思考** | 已完成的思考默认隐藏;开启后思考保留——分隔模式下显示在两条折叠条之间,合并模式下展开时按原顺序插回调用之间 |
73
+ | **思考分隔调用组** | 开启(默认):已完成的思考把前后两组工具调用隔开、各自独立折叠;关闭:思考并入所在工具组一起折叠(旧行为) |
74
+ | **性能统计** | 在卡片内实时显示插件自身耗时(观察回调/引擎刷新/合并重算/安全重扫/摘要克隆的累计次数与 ms/s,以及被零开销短路忽略的流式批次),用于验证插件开销 |
75
+
76
+ 交互方式:点击折叠条展开/收起;键盘 `Enter` 或 `Space` 同样有效。
77
+
78
+ ### 设置存到哪里
79
+
80
+ - **已安装(bundle)模式**:设置由 DSH **设置服务**持久化到
81
+ `~/.dsh/settings.yaml`(命名空间 `toolfold`),与产品和其他插件的设置在同一个文档里,
82
+ 随 dsh 主机走,换浏览器/设备不丢失;卡片底部会显示「设置保存在 DSH 主机配置」。
83
+ 也可以直接手工编辑该文件:
84
+
85
+ ```yaml
86
+ toolfold:
87
+ durMs: 240 # 展开动画时长 0–2000ms
88
+ keepThink: false # 是否保留已完成的思考(显示在折叠条之间 / 展开时插回)
89
+ splitThink: true # 是否让已完成的思考隔开前后两组工具调用(默认 true)
90
+ stats: false # 是否开启性能统计
91
+ ```
92
+
93
+ - **未检测到 DSH 设置服务时**(远程浏览器等未接入 DSH 设置服务的场景):降级为浏览器
94
+ `localStorage`(键 `dsh-toolfold.settings.v1`),卡片会显示「仅保存在本浏览器」;
95
+ 旧版键 `dsh-codex-collapse.settings.v1` 首次加载自动迁移。
96
+
97
+ ![保留思考对比](assets/keep-think.gif)
98
+ <!-- 🖼️ 图片位:assets/keep-think.gif —— 建议:同一段调用分别以「保留思考 关 / 开」各演示一次展开,展示思考不显示 / 按原顺序插回,≤2MB -->
99
+
100
+ ## 性能
101
+
102
+ - **实测(真实 GUI 页面,空闲窗口 8s)**:引擎总耗时约 1.6–2.8ms(≈0.3ms/s,约 0.03% 单核);
103
+ 同窗口 5s CPU 采样中引擎函数零样本,页面长任务(141–686ms)来自页面加载与产品/其他插件渲染;
104
+ - **流式零工作**:思考/文本 token 流式变更被 O(1) 短路直接忽略,不触发任何重算(约 0.1–0.4µs/节点);
105
+ - **标签页隐藏 = 字面零占用**:隐藏时断开全部观察器与定时器,回到可见时重挂并补一次对账;
106
+ - 打开「性能统计」即可在设置卡片内实时查看插件自身的每一项耗时。
107
+
108
+ ## 说明与限制
109
+
110
+ - 折叠基于渲染后的 DOM,依赖产品稳定标记;若产品标记变动,最坏情况是折叠停止生效,不会破坏聊天;
111
+ - **进行中的思考**与 **AI 文字输出**会隔开工具调用;**已完成的思考**默认也会隔开(「思考分隔
112
+ 调用组」开启时),关闭该选项后已完成的思考并入所在工具组一起折叠;
113
+ - 展开状态按会话流 + 节点 key 记忆;会话重开且 key 相同时会恢复展开状态;
114
+ - 设置以 DSH 主机配置(`~/.dsh/settings.yaml`)为准;仅当主机桥不可达时才退化为浏览器
115
+ `localStorage`(此时换浏览器/设备需重新设置);
116
+ - 需要浏览器支持 `MutationObserver` 与 `requestAnimationFrame`;不支持时退化为「初始渲染折叠一次」。
117
+
118
+ ---
119
+
120
+ 本项目完全由 dsh + deepseek v4 flash(max) 完成。
@@ -0,0 +1,23 @@
1
+ # dsh-toolfold bundle patch — the `dsh.bundle.patch` layer of the npm package.
2
+ #
3
+ # Installing through the official plugin CLI —
4
+ #
5
+ # dsh plugin --profile web add dsh-toolfold
6
+ # (or, from this checkout: npm run install:dsh)
7
+ #
8
+ # — reconciles `dsh.profile.bundles` against the installed package and, seeing
9
+ # this declaration, appends `dsh-toolfold` to the bundle stack. The profile
10
+ # boot then merges THIS patch (one `insert` of the plugin row) exactly like a
11
+ # manual cordis.patch.yml mount line used to. One command installs AND mounts;
12
+ # no profile file edits needed.
13
+ #
14
+ # The browser half needs no row of its own: the web client loader scans
15
+ # installed bundles for the package's `dsh.client.platform: "web"` manifest
16
+ # and serves `exports["./client"]` to the page.
17
+ #
18
+ # If the profile still carries a manual `toolfold` row from an earlier manual
19
+ # install, remove it before switching to the bundle channel to avoid
20
+ # double-mounting (two host halves, two API routes).
21
+ - insert:
22
+ - id: toolfold
23
+ name: 'dsh-toolfold'
@@ -0,0 +1,69 @@
1
+ /**
2
+ * build-dynamic.js — generate lib/dynamic-body.js (the `code.client` body for
3
+ * cordis_define) from lib/client.js.
4
+ *
5
+ * The dynamic runner wraps the body in `async () => { ... }` and evaluates it
6
+ * in a scope where `React` is a closure parameter, so the body must:
7
+ * - NOT redeclare `var React` (var hoisting would shadow the closure
8
+ * parameter and the settings card would silently never render);
9
+ * - end with `return plugin;` (a bare function body has no exports).
10
+ *
11
+ * Usage: node lib/build-dynamic.js
12
+ */
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const srcPath = path.join(__dirname, 'client.js');
19
+ const outPath = path.join(__dirname, 'dynamic-body.js');
20
+
21
+ let src = fs.readFileSync(srcPath, 'utf8');
22
+
23
+ // 1. Extract the factory function body: everything between
24
+ // "factory: function (require) {" and the final "\n});".
25
+ const factoryMarker = 'factory: function (require) {';
26
+ const start = src.indexOf(factoryMarker);
27
+ if (start === -1) throw new Error('factory marker not found in client.js');
28
+ const bodyStart = start + factoryMarker.length;
29
+
30
+ const endMarker = '\n});';
31
+ const end = src.lastIndexOf(endMarker);
32
+ if (end === -1) throw new Error('module end marker not found in client.js');
33
+
34
+ let body = src.slice(bodyStart, end);
35
+ // Drop the factory's own closing brace (the trailing " }").
36
+ body = body.replace(/\n[ \t]*\}\s*$/, '');
37
+
38
+ // 2. Remove the CommonJS export boilerplate.
39
+ const boilerplate =
40
+ ' var module = { exports: {} };\n' +
41
+ ' var exports = module.exports;\n' +
42
+ ' Object.defineProperty(exports, Symbol.toStringTag, { value: \'Module\' });\n';
43
+ if (body.indexOf(boilerplate) === -1) throw new Error('module boilerplate not found');
44
+ body = body.replace(boilerplate, '');
45
+
46
+ // 3. Replace the React require block with an explanatory comment (see header).
47
+ const reactBlock =
48
+ ' // React is a platform seed word in the web shell; the settings card\n' +
49
+ ' // needs it, the folding engine does not.\n' +
50
+ ' var React = null;\n' +
51
+ ' try {\n' +
52
+ ' React = require(\'react\');\n' +
53
+ ' } catch (err) {\n' +
54
+ ' React = null;\n' +
55
+ ' }\n';
56
+ if (body.indexOf(reactBlock) === -1) throw new Error('React block not found');
57
+ body = body.replace(reactBlock,
58
+ ' // React is the runner\'s closure parameter; the folding engine does not\n' +
59
+ ' // need it directly, and redeclaring it here would shadow the parameter.\n');
60
+
61
+ // 4. Replace the module.exports tail with a bare `return plugin;`.
62
+ const tail =
63
+ ' module.exports = plugin;\n' +
64
+ ' return module.exports;';
65
+ if (body.indexOf(tail) === -1) throw new Error('export tail not found');
66
+ body = body.replace(tail, 'return plugin;\n');
67
+
68
+ fs.writeFileSync(outPath, body, 'utf8');
69
+ console.log('wrote', outPath, body.length, 'bytes');