dsh-sidebar-organizer 0.0.0-stage → 0.4.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/CHANGELOG.md ADDED
@@ -0,0 +1,77 @@
1
+ # Changelog
2
+
3
+ 版本格式遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
4
+ 本文件的中文条目为默认语言;英文条目见英文段落(同一版本内)。
5
+
6
+ ## 0.4.0 — 2026-11-02
7
+
8
+ - 源码拆分为 `src/` 下的模块(constants / styles / storage / icons / slot /
9
+ runtime / menu / plugin),新增 `scripts/build.mjs` 在构建期拼接为
10
+ `lib/client.js`(DSH 客户端以经典脚本加载,运行时无法 `import`)。
11
+ - 版本号改为构建期从 `package.json` 注入,避免多处手写漂移。
12
+ - 新增 `README.en.md`、`NOTICE.md`、`NOTICE.en.md`、`CITATION.cff`:
13
+ README/NOTICE 中英文分文件、顶部链接切换;补齐 AI 使用、代码来源、
14
+ 商标与引用声明。
15
+ - `package.json` 增加 `repository`/`homepage`/`bugs`/`keywords`,`private`
16
+ 设为 `false`,补充构建与检查脚本,准备发布 npm。
17
+ - 行为无变化,仍为 0.3.6 的功能集。
18
+
19
+ ### English
20
+
21
+ - Split sources into modules under `src/` and added `scripts/build.mjs`, which
22
+ concatenates them into `lib/client.js` at build time (the DSH client loads
23
+ plugins as classic scripts, so there is no runtime `import`).
24
+ - The version is injected from `package.json` during the build.
25
+ - Added `README.en.md`, `NOTICE.md`, `NOTICE.en.md`, and `CITATION.cff` with
26
+ language switching, AI-usage/code-origin disclosure, trademark notes, and
27
+ citation metadata.
28
+ - `package.json`: added repository/homepage/bugs/keywords, set `private: false`,
29
+ added build/check scripts, preparing for npm publication.
30
+ - No behavioural changes; feature set is the same as 0.3.6.
31
+
32
+ ## 0.3.6 — 2026-11-02
33
+
34
+ - 点击「更多」菜单中「已收纳」的条目只打开对应面板,不再顺带把它放回侧栏;
35
+ 恢复显示只通过「拖回左侧」触发。
36
+
37
+ ## 0.3.5 — 2026-11-02
38
+
39
+ - 指针捕获从 `pointerdown` 推迟到长按成立之后,修复「已收纳」条目无法单击打开。
40
+
41
+ ## 0.3.4 — 2026-11-02
42
+
43
+ - 图标 SVG 内嵌 id 统一加前缀,修复「上下文洞察」等带 `mask` 的面板收纳后图标空白。
44
+ - 「已收纳」分组内支持长按拖动排序。
45
+
46
+ ## 0.3.3 — 2026-11-02
47
+
48
+ - 修复 0.3.2 回归:React 18 不接受 DOM 节点作为 children,改为 SVG 标记 +
49
+ `dangerouslySetInnerHTML`,并加 `MenuBoundary` 降级兜底。
50
+
51
+ ## 0.3.2 — 2026-11-02
52
+
53
+ - 「已收纳」行改用 `MenuItemButton` 的 `icon` 槽位,与原生菜单行排版一致。
54
+
55
+ ## 0.3.1 — 2026-11-02
56
+
57
+ - 拖动期性能:按下时冻结几何快照、`requestAnimationFrame` 合并写入、
58
+ 拖动中暂停布局 pass。
59
+ - 「已收纳」行沿用面板本地化名称与图标;移除菜单提示行。
60
+
61
+ ## 0.3.0 — 2026-11-02
62
+
63
+ - 「更多」收纳:拖到「更多」= 收纳,菜单内长按拖出 = 恢复。
64
+ - `Alt+↑/↓` 键盘排序;拖动期间抑制 `:hover` 与 tooltip。
65
+
66
+ ## 0.2.1 — 2026-11-02
67
+
68
+ - 改用属性/结构选择器定位面板行,修复 CSS Modules 哈希类名导致的静默失效。
69
+
70
+ ## 0.2.0 — 2026-11-02
71
+
72
+ - 重写为只读 DOM 版本:`style.order` 排序、`localStorage` 持久化、看门狗、
73
+ 逃生开关;移除会卡死页面的插槽重注册(0.1.0 事故)。
74
+
75
+ ## 0.1.0 — 2026-11-02
76
+
77
+ - 初版:通过影子注册重排 `sidebar.panellist`,因反馈环导致页面卡死,已废弃。
package/CITATION.cff ADDED
@@ -0,0 +1,22 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ title: "dsh-sidebar-organizer: Drag-to-reorder and tuck-away for the DSH sidebar panel strip"
4
+ version: 0.4.0
5
+ date-released: "2026-11-02"
6
+ license: MIT
7
+ type: software
8
+ authors:
9
+ - alias: TLEWA
10
+ name: TLEWA
11
+ repository-code: "https://github.com/TLEWA/dsh-sidebar-organizer"
12
+ url: "https://github.com/TLEWA/dsh-sidebar-organizer"
13
+ abstract: >-
14
+ A DSH (DeepSeek Harness) sidebar plugin that adds long-press drag reordering and
15
+ tuck-away-to-More for the global panel strip, implemented with read-only slot
16
+ access and inline DOM ordering to avoid re-registration feedback loops.
17
+ keywords:
18
+ - DSH plugin
19
+ - DeepSeek Harness
20
+ - sidebar
21
+ - drag and drop
22
+ - UI customization
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-modmenu contributors
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/NOTICE.en.md ADDED
@@ -0,0 +1,88 @@
1
+ # NOTICE — dsh-sidebar-organizer
2
+
3
+ [简体中文](NOTICE.md) · **English**
4
+
5
+ This file records code origin, third-party components, AI involvement, and how to
6
+ cite the project. Last updated: 2026-11-02 (v0.4.0).
7
+
8
+ ## 1. What this project is
9
+
10
+ `dsh-sidebar-organizer` is a community third-party plugin. It is **not** an
11
+ official DeepSeek product and is not endorsed or certified by DeepSeek. It uses
12
+ the plugin interfaces the DSH client exposes at runtime (`window.__ModuleLoader__.load`,
13
+ `ctx.slots`, `ctx.locale`, `ctx.effect`) and does not modify any file in the host
14
+ installation.
15
+
16
+ ## 2. Third-party code and assets
17
+
18
+ - **No host source code is included.** This project does not copy, adapt, or
19
+ redistribute any DSH (DeepSeek Harness) source code or assets. DSH is
20
+ proprietary; this project is written against its public plugin mechanism
21
+ (bundle package format, client module loader, slot API).
22
+ - **Runtime dependencies come from the host.** `react` is provided by the DSH
23
+ client module loader; `MenuItemButton` from
24
+ `@deepseek-ai/dsh-client-ui-primitives` is an optional enhancement used only
25
+ when present. Neither package is redistributed here.
26
+ - **Icons are read from the host at runtime.** The icon at the left of each menu
27
+ row is cloned from the SVG the sidebar panel itself renders (element ids such
28
+ as `mask` / `gradient` references are rewritten with a prefix). No icon assets
29
+ are bundled.
30
+ - **No third-party runtime code.** Nothing in `src/`, `lib/`, or
31
+ `test/harness.html` inlines a third-party library; the harness uses native DOM
32
+ APIs and a hand-written fake module loader.
33
+ - **Development tools** (not shipped): Node.js (MIT), npm (Artistic License 2.0).
34
+ - If you believe a part of this implementation closely resembles your project
35
+ without attribution, please open an issue; we will verify and either add
36
+ credit or change the implementation.
37
+
38
+ ## 3. AI usage and code origin
39
+
40
+ - **How it was built.** The project was built in DeepSeek Harness by an AI coding
41
+ agent (DeepSeek model) working with the maintainer: the maintainer set
42
+ requirements, reviewed the interaction, reported defects, and made acceptance
43
+ and release decisions; the AI implemented, debugged, and drafted docs.
44
+ - **Scope of AI involvement.** All client code under `src/` and `lib/`, the host
45
+ placeholder, the offline harness, and the first drafts of README / NOTICE /
46
+ CHANGELOG are AI-generated or substantially AI-edited, then verified by the
47
+ maintainer release by release on a real DSH desktop install (dragging, tucking,
48
+ menu behaviour, performance, freeze regression).
49
+ - **Human responsibility.** The maintainer is ultimately responsible for the
50
+ repository content, build artifacts, and published versions.
51
+ - **Privacy.** The plugin makes no network requests and collects no telemetry.
52
+ All state (order, tucked list, kill switch) lives in browser `localStorage`
53
+ under the keys documented in the README.
54
+ - **Stability caveat.** The plugin depends on the host's rendered DOM structure
55
+ and slot projection (undocumented details). A DSH upgrade may break it; when it
56
+ breaks, the sidebar keeps the host's default order, and the escape hatches in
57
+ the README disable it.
58
+
59
+ ## 4. Citing this project
60
+
61
+ Use [CITATION.cff](CITATION.cff):
62
+
63
+ ```bibtex
64
+ @software{dsh_sidebar_organizer,
65
+ author = {TLEWA},
66
+ title = {dsh-sidebar-organizer: Drag-to-reorder and tuck-away for the DSH sidebar panel strip},
67
+ version = {0.4.0},
68
+ year = {2026},
69
+ license = {MIT},
70
+ url = {https://github.com/TLEWA/dsh-sidebar-organizer}
71
+ }
72
+ ```
73
+
74
+ When citing code, include the version or commit SHA, e.g.
75
+ `dsh-sidebar-organizer v0.4.0 (https://github.com/TLEWA/dsh-sidebar-organizer)`.
76
+
77
+ ## 5. Trademarks
78
+
79
+ "DeepSeek", "DeepSeek Harness", and "DSH" are marks of their respective owners.
80
+ The name `dsh-sidebar-organizer` is descriptive (naming the host and purpose) and
81
+ implies no affiliation or endorsement. Third-party product names in the README
82
+ are used only to describe compatibility.
83
+
84
+ ## 6. License
85
+
86
+ Released under the [MIT](LICENSE) license. When redistributing sources or build
87
+ artifacts, keep `LICENSE` and this `NOTICE` file. The MIT license provides no
88
+ warranty.
package/NOTICE.md ADDED
@@ -0,0 +1,73 @@
1
+ # NOTICE — dsh-sidebar-organizer
2
+
3
+ **简体中文** · [English](NOTICE.en.md)
4
+
5
+ 本文件说明本项目的代码来源、第三方组件、AI 参与方式与引用方式。
6
+ 最后更新:2026-11-02(v0.4.0)。
7
+
8
+ ## 1. 项目性质
9
+
10
+ `dsh-sidebar-organizer` 是社区第三方插件,**不是** DeepSeek 官方产品,也未获得
11
+ DeepSeek 的背书或认证。它通过 DSH 客户端在运行时暴露的插件接口
12
+ (`window.__ModuleLoader__.load`、`ctx.slots`、`ctx.locale`、`ctx.effect`)
13
+ 与宿主交互,不修改宿主安装目录中的任何文件。
14
+
15
+ ## 2. 第三方代码与资源
16
+
17
+ - **不包含宿主源代码。** 本项目未复制、未改编、未再分发 DSH(DeepSeek Harness)
18
+ 的任何源代码或资源。DSH 为专有软件,其版权归 DeepSeek 所有;本项目仅依据其
19
+ 公开的插件机制(bundle 包格式、客户端模块加载器、插槽 API)编写。
20
+ - **运行时依赖由宿主提供。** `react` 由 DSH 客户端模块加载器提供;
21
+ `@deepseek-ai/dsh-client-ui-primitives` 的 `MenuItemButton` 为可选增强,
22
+ 仅在宿主存在时调用,本项目不分发这两个包。
23
+ - **图标由宿主实时提供。** 菜单行左侧图标是运行时从侧栏面板自身渲染的 SVG
24
+ 克隆而来(含 `mask` / `gradient` 的 id 前缀改写),本项目不附带任何图标资源。
25
+ - **无第三方运行时代码。** `src/`、`lib/`、`test/harness.html` 中未内联任何
26
+ 第三方库;测试夹具使用原生 DOM API 与手写的假模块加载器。
27
+ - **开发工具**(不随包分发):Node.js(MIT)、npm(Artistic License 2.0)。
28
+ - 若你认为本项目某段实现与你的项目高度相似且未被署名,请通过 issue 联系,
29
+ 我们会核实并补充署名或调整实现。
30
+
31
+ ## 3. AI 使用与代码来源声明
32
+
33
+ - **开发方式。** 本项目在 DeepSeek Harness 中、由 AI 编程代理(DeepSeek 模型)
34
+ 与维护者结对完成:维护者负责提出需求、评审交互、指出缺陷、验收与发布决策;
35
+ AI 负责实现、调试与文档初稿。
36
+ - **AI 参与范围。** `src/` 与 `lib/` 的全部客户端代码、宿主侧占位代码、离线
37
+ 测试夹具、README / NOTICE / CHANGELOG 的初稿,均由 AI 生成或大幅修改,
38
+ 并经维护者在真实 DSH 桌面端逐版验证(拖动、收纳、菜单、性能与卡死回归)。
39
+ - **人类责任。** 维护者对仓库内容、构建产物与发布版本负最终责任。
40
+ - **隐私。** 插件不发起网络请求、不采集遥测;全部状态(顺序、收纳列表、
41
+ 停用开关)只写入浏览器 `localStorage`,键名见 README。
42
+ - **稳定性提示。** 本插件依赖宿主渲染出的 DOM 结构与插槽投影(未文档化细节)。
43
+ DSH 升级后可能失效;失效时侧栏会保持宿主默认顺序,并可用 README 中的
44
+ 逃生开关停用。
45
+
46
+ ## 4. 引用方式
47
+
48
+ 若在教学、文章或研究中引用本项目,请使用 [CITATION.cff](CITATION.cff):
49
+
50
+ ```bibtex
51
+ @software{dsh_sidebar_organizer,
52
+ author = {TLEWA},
53
+ title = {dsh-sidebar-organizer: Drag-to-reorder and tuck-away for the DSH sidebar panel strip},
54
+ version = {0.4.0},
55
+ year = {2026},
56
+ license = {MIT},
57
+ url = {https://github.com/TLEWA/dsh-sidebar-organizer}
58
+ }
59
+ ```
60
+
61
+ 代码引用请同时标注版本号或 commit SHA,例如
62
+ `dsh-sidebar-organizer v0.4.0 (https://github.com/TLEWA/dsh-sidebar-organizer)`。
63
+
64
+ ## 5. 商标
65
+
66
+ 「DeepSeek」「DeepSeek Harness」「DSH」等名称与标识归其权利人所有。本项目名称
67
+ `dsh-sidebar-organizer` 仅作描述性使用(说明其适用的宿主与用途),不代表任何
68
+ 关联或背书。README 中出现的第三方产品名仅用于说明兼容性。
69
+
70
+ ## 6. 许可
71
+
72
+ 本项目以 [MIT](LICENSE) 许可发布。再分发源码或构建产物时,请保留 `LICENSE`
73
+ 与本 `NOTICE` 文件。MIT 许可不提供任何担保。
package/README.en.md ADDED
@@ -0,0 +1,149 @@
1
+ # dsh-sidebar-organizer
2
+
3
+ [简体中文](README.md) · **English**
4
+
5
+ Adds **custom ordering** and **tucking away** to the "global panels" strip at the
6
+ top-left of the DSH (DeepSeek Harness) sidebar — the column of icons for plugins,
7
+ marketplace, skills manager, scheduled tasks, and so on.
8
+
9
+ > Version **0.4.0**: sources split into `src/` modules with a build script;
10
+ > AI-usage and code-origin disclosure added; multilingual docs.
11
+
12
+ ## What it does
13
+
14
+ - **Long-press to reorder**: hold a panel icon for ~0.26 s; it lifts (a ghost
15
+ follows the pointer, plus a drop indicator) and you drag it up or down.
16
+ The order applies immediately and is persisted.
17
+ - **Tuck into "More"**: drop a row on the More button and that panel disappears
18
+ from the strip, moving into the "Tucked away" group of the More menu.
19
+ - **Pull back out of "More"**: open the More menu, long-press a tucked panel and
20
+ drag it onto the strip to restore it.
21
+ - **Reorder inside the menu**: long-press drag within the tucked group to change
22
+ the order of tucked panels.
23
+ - **Keyboard**: focus a row and press `Alt+↑` / `Alt+↓`.
24
+ - **Reset**: once the layout is customized, "Reset layout" appears at the bottom
25
+ of the More menu.
26
+ - The layout survives page reloads and DSH restarts.
27
+ - While dragging, rows under the pointer do not react with `:hover` or tooltips
28
+ (rows get `pointer-events: none`; hit-testing is pure geometry).
29
+ - The "Plugins" entry can be reordered and tucked too; the More button always
30
+ stays last.
31
+
32
+ ## Install
33
+
34
+ ### From npm (recommended)
35
+
36
+ ```bash
37
+ dsh plugin install dsh-sidebar-organizer
38
+ ```
39
+
40
+ Or search for `dsh-sidebar-organizer` in the DSH plugin manager.
41
+
42
+ ### From source
43
+
44
+ This repository is itself a DSH plugin bundle. Clone it, then point the plugin
45
+ manager at the checkout:
46
+
47
+ ```
48
+ plugin_manager action: install_bundle target: <absolute path to the checkout>
49
+ ```
50
+
51
+ Refresh the Web UI (`Ctrl+R`) afterwards. When the profile links the checkout,
52
+ edit `src/` and run `npm run build`, then refresh — no reinstall needed.
53
+
54
+ > Versions up to 0.3.6 were called `dsh-modmenu`. The old layout key
55
+ > `dsh.modmenu.layout.v1` migrates automatically to
56
+ > `dsh.sidebar-organizer.layout.v1`, and `window.__dshModmenu` remains an alias
57
+ > of `window.__dshSidebarOrganizer`.
58
+
59
+ ## Safety design
60
+
61
+ 0.1.0 re-registered the whole panel column from inside the slot's own change
62
+ notification, creating a `register → notify → register` feedback loop that froze
63
+ the page. Since 0.2.0 the mechanism is different:
64
+
65
+ 1. **The panel slot is read-only.** The plugin never registers or shadows a
66
+ `sidebar.panellist` entry; it only reads `entriesOfSlot` for ids and labels.
67
+ 2. **Order/visibility is written as inline DOM styles.** The strip is
68
+ `display:flex; flex-direction:column`, so `style.order` on each row changes
69
+ the visual order; tucking uses `style.display:none` plus `data-dso-hidden`.
70
+ 3. **Exactly one static slot write**: a row registered into
71
+ `sidebar.panellist.actions` (the More menu) named `panel-organizer`. It is
72
+ registered once and never re-registers or subscribes.
73
+ 4. **Idempotent and rate-limited**: a pass only writes when a value actually
74
+ changes, and passes are coalesced via `requestAnimationFrame`.
75
+ 5. **Watchdog**: more than 90 passes in 3 s disables observers and listeners.
76
+ 6. **`childList` only**: the plugin's own style/attribute writes cannot retrigger
77
+ the observer.
78
+ 7. **Escape hatches** (browser console):
79
+
80
+ ```js
81
+ window.__dshSidebarOrganizer.reset(); // forget the saved layout
82
+ window.__dshSidebarOrganizer.disable(); // persist a kill switch and stop
83
+ window.__dshSidebarOrganizer.hide("id"); // tuck away manually
84
+ window.__dshSidebarOrganizer.reveal("id"); // restore manually
85
+ window.__dshSidebarOrganizer.status(); // inspect current state
86
+ ```
87
+
88
+ To undo `disable()`, run
89
+ `localStorage.removeItem("dsh.sidebar-organizer.disabled")` and refresh.
90
+ 8. If this plugin stops working entirely, the sidebar simply keeps DSH's default
91
+ order.
92
+
93
+ ## How it works
94
+
95
+ - The panel list is the list slot `sidebar.panellist`; registrations look like
96
+ `{ options: { id, order, label }, component }`, and the sidebar projects
97
+ `entriesOfSlot` into `{id, order, label}` sorted by `order`.
98
+ - Rendered rows are mapped back to registration ids by exact match between the
99
+ resolved label and the row's `aria-label`, falling back to the projection
100
+ index, then to `label:<text>`.
101
+ - Persisted layout:
102
+ `localStorage["dsh.sidebar-organizer.layout.v1"] = { version: 2, order: [id…], hidden: [id…] }`.
103
+ - Drag layer: document-capture `pointerdown` → 260 ms long-press timer (cancelled
104
+ by >8 px movement) → ghost clone plus indicator; drop target by vertical
105
+ midpoints; dropping inside the More button rect (6 px tolerance) tucks the
106
+ row; clicks within 500 ms of a drop are swallowed.
107
+
108
+ ## Repository layout
109
+
110
+ ```
111
+ src/ sources (concatenated in dependency order at build time)
112
+ scripts/build.mjs assembles src/ into lib/client.js
113
+ lib/client.js generated client half (what DSH loads)
114
+ lib/index.js host half (placeholder)
115
+ cordis.patch.yml bundle patch applied on install
116
+ test/harness.html offline harness: fake sidebar DOM + slot projection
117
+ ```
118
+
119
+ DSH loads client plugins as classic scripts via
120
+ `window.__ModuleLoader__.load({ id, factory })`, not as ES modules, so there is
121
+ no runtime `import` — hence module sources plus build-time concatenation.
122
+
123
+ ## Development
124
+
125
+ ```bash
126
+ npm run build # src/ → lib/client.js
127
+ npm run check # syntax check
128
+ ```
129
+
130
+ Open `test/harness.html` in a browser for the self-checking regression run
131
+ (ordering, persistence, reset, tuck/restore, in-menu drag, DOM churn during drag).
132
+
133
+ ## Compatibility
134
+
135
+ - DSH `>= 0.2.1-rc.3` (developed against `0.2.1-rc.5`).
136
+ - The client only requires `react`; `MenuItemButton` from
137
+ `@deepseek-ai/dsh-client-ui-primitives` is used when present, with a built-in
138
+ fallback otherwise.
139
+
140
+ ## Notices
141
+
142
+ - Third-party code and trademarks: [NOTICE.en.md](NOTICE.en.md)
143
+ - AI assistance and code origin: the "AI usage and code origin" section of
144
+ [NOTICE.en.md](NOTICE.en.md)
145
+ - Citation metadata: [CITATION.cff](CITATION.cff)
146
+
147
+ ## License
148
+
149
+ [MIT](LICENSE)
package/README.md CHANGED
@@ -1,3 +1,146 @@
1
- # Temporary Holding Version
1
+ # dsh-sidebar-organizer
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **简体中文** · [English](README.en.md)
4
+
5
+ 给 DSH(DeepSeek Harness)左侧栏左上角那块「全局面板」栏——插件、市场、技能管理、定时任务……那一列图标——加上**自定义排序**与**收纳**能力。
6
+
7
+ > 当前版本 **0.4.0**:源码拆分为 `src/` 模块并加入构建脚本;补齐 AI 使用与代码来源声明;多语言文档。
8
+
9
+ ## 现在能做什么
10
+
11
+ - **长按拖动排序**:按住某个面板图标约 0.26 秒,图标浮起(跟随指针的 ghost + 一条落点指示线),上下拖到目标位置松手即可。顺序即时生效并保存。
12
+ - **收进「更多」**:把行拖到「更多」按钮上松手,该面板就从面板栏消失,收进「更多」菜单的「已收纳」分组。
13
+ - **从「更多」拖回来**:打开「更多」菜单,长按被收纳的面板并拖到左侧面板栏的目标位置松手,即恢复显示。
14
+ - **菜单内排序**:在「已收纳」分组里长按拖动,也能调整被收纳面板之间的顺序。
15
+ - **键盘排序**:聚焦某行后 `Alt+↑` / `Alt+↓` 上下移动。
16
+ - **恢复默认排序**:自定义过布局后,「更多」菜单底部会出现「恢复默认排序」。
17
+ - 刷新页面、重启 DSH 后布局保持。
18
+ - 拖动过程中,指针下方的行不会触发 `:hover` 或 tooltip(拖动期间行临时 `pointer-events: none`,命中判定全部走几何矩形)。
19
+ - 「插件」入口可以参与排序,也可以被收纳;「更多」按钮永远是最后一个。
20
+
21
+ ## 安装
22
+
23
+ ### 从 npm 安装(推荐)
24
+
25
+ ```bash
26
+ dsh plugin install dsh-sidebar-organizer
27
+ ```
28
+
29
+ 或在 DSH 的插件管理器里搜索 `dsh-sidebar-organizer`。
30
+
31
+ ### 从源码安装
32
+
33
+ 本仓库本身就是一个 DSH 插件包(bundle)。克隆后,用插件管理器指向仓库目录安装:
34
+
35
+ ```
36
+ plugin_manager action: install_bundle target: <仓库的绝对路径>
37
+ ```
38
+
39
+ 安装后刷新 Web 界面(`Ctrl+R`)。以 `link:` 方式装进 profile 时,改 `src/` 并执行 `npm run build` 后刷新页面即可生效,不必重装。
40
+
41
+ > 0.3.6 及更早版本名为 `dsh-modmenu`。改名后旧的布局存储键
42
+ > `dsh.modmenu.layout.v1` 会自动迁移到 `dsh.sidebar-organizer.layout.v1`,
43
+ > 控制台句柄 `window.__dshModmenu` 也保留为 `window.__dshSidebarOrganizer` 的别名。
44
+
45
+ ## 安全设计
46
+
47
+ 0.1.0 曾在每次插槽变更通知里重新注册整列面板,形成
48
+ `注册 → 通知 → 再注册` 的反馈环,把页面拖死。0.2.0 起彻底换掉机制:
49
+
50
+ 1. **面板列表只读**:不注册、不 shadow 任何 `sidebar.panellist` 条目,只读
51
+ `ctx.slots.entriesOfSlot("sidebar.panellist")` 获取面板 `id` 与标签。
52
+ 2. **排序 / 隐藏只写 DOM 内联样式**:面板列是 `display:flex; flex-direction:column`,
53
+ 给每行写 `style.order` 即可改视觉顺序;收纳用 `style.display:none` + `data-dso-hidden`。
54
+ 3. **唯一一次插槽写入**:往 `sidebar.panellist.actions`(「更多」菜单)注册一个静态行
55
+ `panel-organizer`,用于列出被收纳面板并支持拖回。只注册一次,不订阅、不重注册。
56
+ 4. **幂等 + 限流**:每次布局 pass 都是「值不同才写」,并用 `requestAnimationFrame` 合并到每帧最多一次。
57
+ 5. **看门狗**:3 秒内超过 90 次 pass 即判定异常并停止观察与监听。
58
+ 6. **只观察 `childList`**:自身写入的 `style` / `data-*` 不会反过来触发观察器。
59
+ 7. **逃生开关**(浏览器控制台):
60
+
61
+ ```js
62
+ window.__dshSidebarOrganizer.reset(); // 忘掉保存的布局
63
+ window.__dshSidebarOrganizer.disable(); // 置 localStorage 开关并停用,刷新后也不启动
64
+ window.__dshSidebarOrganizer.hide("id"); // 手动收纳
65
+ window.__dshSidebarOrganizer.reveal("id"); // 手动恢复
66
+ window.__dshSidebarOrganizer.status(); // 查看当前状态
67
+ ```
68
+
69
+ `disable()` 之后要恢复,执行
70
+ `localStorage.removeItem("dsh.sidebar-organizer.disabled")` 再刷新。
71
+ 8. 就算本插件完全不工作,侧栏也只是保持 DSH 默认顺序,不会有其他副作用。
72
+
73
+ ## 实现说明
74
+
75
+ - 面板所在的 list 插槽为 `sidebar.panellist`,注册项形如 `{ options: { id, order, label }, component }`;
76
+ 侧栏把 `entriesOfSlot` 投影成 `{id, order, label}` 并按 `order` 升序渲染为 `nav > button`。
77
+ - 本插件把渲染出来的行映射回注册 `id`:先按解析后的 `label` 与行 `aria-label` 精确匹配,
78
+ 匹配不到再按投影下标兜底,两者都不行就用 `label:<文字>` 做键。
79
+ - 保存的布局:`localStorage["dsh.sidebar-organizer.layout.v1"] = { version: 2, order: [id…], hidden: [id…] }`
80
+ (`order` 同时包含可见与已收纳的 id)。
81
+ - 拖拽层:document 捕获阶段 `pointerdown` → 260ms 长按计时(指针移动超过 8px 取消)→
82
+ 激活后克隆行做 ghost、插入落点指示线;落点按其余行的竖直中点判定;拖到「更多」按钮矩形
83
+ (外扩 6px)内即收纳;松手后 500ms 内吞掉紧随的 click,避免误触打开面板。
84
+ - 「更多」菜单里的行包在 `div[data-dso-more-key]` 中,同一套拖拽层据此识别来源并恢复。
85
+
86
+ ## 目录结构
87
+
88
+ ```
89
+ src/ 源码(模块化,按依赖顺序拼接)
90
+ constants.js 常量、文案、共享句柄
91
+ styles.js 注入的样式表
92
+ storage.js localStorage 布局持久化
93
+ icons.js 图标 SVG 序列化与 id 前缀处理
94
+ slot.js 只读访问 sidebar.panellist 投影
95
+ runtime.js DOM 运行时:定位面板列、应用顺序、拖拽手势
96
+ menu.js 「更多」菜单里注册的那一行
97
+ plugin.js 插件入口 apply()
98
+ scripts/build.mjs 把 src/ 拼成 lib/client.js
99
+ lib/client.js 构建产物(DSH 实际加载的客户端半)
100
+ lib/index.js 宿主半(占位)
101
+ cordis.patch.yml bundle 补丁(安装时插入 profile)
102
+ test/harness.html 离线夹具:伪造侧栏 DOM 与插槽数据,浏览器打开即回归
103
+ ```
104
+
105
+ DSH 的客户端插件是以经典脚本方式通过 `window.__ModuleLoader__.load({ id, factory })`
106
+ 加载的,不是 ES 模块,因此运行时无法 `import`;源码模块化 + 构建期拼接是这个约束下的折中。
107
+
108
+ ## 开发
109
+
110
+ ```bash
111
+ npm run build # src/ → lib/client.js
112
+ npm run check # 语法检查
113
+ ```
114
+
115
+ 回归测试不碰真实 UI:用浏览器打开 `test/harness.html`,页面会自检并打印顺序、
116
+ 持久化、`reset()`、收纳 / 恢复、菜单内拖动、拖动途中 DOM 替换与滚动的结果。
117
+
118
+ ## 兼容性
119
+
120
+ - DSH `>= 0.2.1-rc.3`(在 `0.2.1-rc.5` 上开发)。
121
+ - 客户端只 `require("react")`;`@deepseek-ai/dsh-client-ui-primitives` 的 `MenuItemButton`
122
+ 存在时优先使用,缺失时降级为内置按钮样式。
123
+
124
+ ## 路线图
125
+
126
+ - [x] 0.2.0 — 只读 + DOM `order` 的最小可行版本
127
+ - [x] 0.2.1 — 属性选择器修复(CSS Modules 哈希类名导致选择器失配)
128
+ - [x] 0.3.0 — 「更多」收纳、键盘排序、拖动期抑制 `:hover`
129
+ - [x] 0.3.1 — 拖动期性能:几何快照、rAF 合并、暂停布局 pass
130
+ - [x] 0.3.2 — 「已收纳」行使用 `MenuItemButton` 的图标槽位
131
+ - [x] 0.3.3 — 修复 DOM 节点作为 React children 的回归
132
+ - [x] 0.3.4 — 图标内嵌 id 加前缀;「已收纳」内排序
133
+ - [x] 0.3.5 — 指针捕获延后到拖动成立,修复收纳条目无法点击
134
+ - [x] 0.3.6 — 点击收纳条目只打开面板,不再自动放回侧栏
135
+ - [x] 0.4.0 — 源码模块化与构建脚本、多语言文档、AI 使用与代码来源声明
136
+ - [ ] 0.5.0 — 无障碍补强(菜单内上移 / 下移按钮、屏幕阅读器朗读顺序变化)
137
+
138
+ ## 声明与引用
139
+
140
+ - 第三方代码与商标说明:[NOTICE.md](NOTICE.md)
141
+ - AI 参与方式、代码来源与引用方式:见 [NOTICE.md](NOTICE.md) 的「AI 使用与代码来源声明」一节
142
+ - 引用本项目的 BibTeX:[CITATION.cff](CITATION.cff)
143
+
144
+ ## 许可
145
+
146
+ [MIT](LICENSE)
@@ -0,0 +1,8 @@
1
+ # dsh-sidebar-organizer bundle patch
2
+ #
3
+ # Installed by the DSH plugin manager (or `dsh plugin add <path>`); this
4
+ # patch inserts the plugin row into the active profile.
5
+ - insert:
6
+ - id: sidebar-organizer
7
+ name: 'dsh-sidebar-organizer'
8
+ config: {}