dsh-sidebar-row-menu 1.9.3 → 1.11.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/AGENTS.md ADDED
@@ -0,0 +1,77 @@
1
+ # 仓库约定(AGENTS.md)
2
+
3
+ 本仓库是 [DeepSeek Harness](https://github.com/deepseek-ai) 的插件
4
+ `dsh-sidebar-row-menu`:宿主半边在 `lib/`,客户端半边是 **`src/client/**` 的源码 +
5
+ esbuild 打包出的单文件 `lib/client.js`**。
6
+
7
+ ## 硬性约束
8
+
9
+ 1. **不要手改 `lib/client.js`。** 它是构建产物,任何手工修改都会在下次构建时丢失。
10
+ 客户端改动只写 `src/client/**`,然后运行 `npm run build:client`。
11
+ 2. **客户端只有一个产物文件。** DSH 的客户端模块系统直接执行
12
+ `window.__ModuleLoader__.load({ factory })`,没有打包步骤的旧版插件靠单文件分发;
13
+ 所以源码可以拆多文件,但必须打包回一个 `lib/client.js`,且 `package.json` 的
14
+ `exports["./client"]` 指向它。
15
+ 3. **平台模块只能从 `src/client/platform.js` 引入。** react / react-dom / ui-primitives
16
+ 由 DSH 的静态模块表提供,构建脚本把它们标成 external;分散 import 会让
17
+ 「插件依赖了平台哪些导出」不可见,也容易漏配 external。
18
+ 4. **不新增运行时依赖。** 运行时只有宿主平台模块;`esbuild` 是唯一的 devDependency。
19
+ 5. **对外导出的名字保持稳定**(`apply` / `inject` / `name` / `closeMenu` / `closeToast`),
20
+ 测试与宿主都按这些名字取值。
21
+
22
+ ## 目录与分层
23
+
24
+ ```
25
+ src/client/
26
+ index.js 入口:apply(ctx)、slot 注册、样式安装、生命周期清理
27
+ platform.js 平台模块的唯一入口(react / react-dom / ui-primitives)
28
+ constants.js 存储键、路由前缀、菜单 order、slot 名等常量
29
+ i18n.js zh / en 词条(全部文案都要走 t())
30
+ styles.js 全部 CSS(一次注入,类名前缀 dsrm-)
31
+ apps-catalog.js 应用 id → 名称 / 图标
32
+ dom.js fiber 解析、行/工作区/会话定位
33
+ runtime.js 运行期单例:rootCtx、菜单挂载(beginMenu / closeMenu)、服务查找
34
+ toast.js Toast 宿主
35
+ host-api.js 调宿主 HTTP 路由、剪贴板、cwd 解析
36
+ slot-ownership.js slot 条目归属判断(避免与内置条目重复)
37
+ lib/create-store.js 持久化 store 工厂(localStorage + useSyncExternalStore 样板)
38
+ store/*.js 9 个小状态:items / placements / blank-menu / children / pinned /
39
+ apps / permission / archived / delete-request
40
+ ui/*.js jsx 辅助、菜单定位、子菜单关闭、右键手势标记
41
+ features/*.js 功能块:工作区菜单、会话菜单、空白正文菜单、打开方式、设置页、
42
+ 删除确认框、归档可见性、行右键入口
43
+ ```
44
+
45
+ ## 编码约定
46
+
47
+ - 文件名 kebab-case;React hook 以 `use` 开头,工厂函数以 `create` 开头。
48
+ - 新增持久化状态:用 `lib/create-store.js` 的 `createStore`,不要再手写
49
+ `publish / subscribe / snapshot` 三件套。
50
+ - 菜单挂载:统一用 `runtime.js` 的 `beginMenu(slotName).render(...)`,由 `closeMenu()`
51
+ 统一卸载;不要各自 `createRoot` 挂 `menuHost`。
52
+ - 菜单关闭:菜单级用 `ui/dismiss.js` 的 `useDismissOnOutside`,悬浮子菜单用
53
+ `useSubmenuDismiss` + `useHoverSubmenu`;不要再逐个组件写 mousedown / keydown。
54
+ - i18n:所有用户可见文案进 `i18n.js`,中英各一条,键名用 `域.名字`。
55
+ - 样式:只写 `styles.js`,类名 `dsrm-` 前缀,跟随宿主 CSS 变量(不要硬编码颜色)。
56
+ - 容错:`catch` 不允许完全留空,至少 `console.warn` 或注释说明为什么可以忽略。
57
+ - 宿主服务可能晚于插件就绪:用 `serviceOf(ctx, name)`,缺失时降级而不是抛错。
58
+
59
+ ## 构建与校验
60
+
61
+ ```bash
62
+ npm install # 只装 esbuild
63
+ npm run build:client # src/client/** → lib/client.js
64
+ npm test # pretest 会先构建;然后跑 host / manifest / client 测试
65
+ npm run check # 语法检查 + 构建产物与源码一致性(不一致即失败)
66
+ npm pack # prepack 会构建 + check,产出 .tgz
67
+ ```
68
+
69
+ 改完客户端代码务必 `npm run build:client && npm test`;只改 `lib/host/**` 时也跑一遍
70
+ `npm test`。提交前 `git status` 应包含 `lib/client.js`(产物)与源码改动。
71
+
72
+ ## 发布
73
+
74
+ 1. 升 `package.json` 版本,CHANGELOG 按现有双语格式加一节;
75
+ 2. `npm test` / `npm run check` 全绿;
76
+ 3. 在真实 DSH GUI 里用 CDP 验证受影响的菜单(右键、权限切换、删除确认、设置页);
77
+ 4. 打 tag(`vX.Y.Z`)由 release workflow 打包发布;`lib/client.js` 必须已被构建并提交。
package/CHANGELOG.md CHANGED
@@ -1,5 +1,81 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.11.0 — 2026-11-06
4
+
5
+ ### 中文摘要
6
+
7
+ - **切换「新会话默认权限」失败时不再假装成功。** 网络或宿主出错时,菜单会回滚到原来
8
+ 的勾选项并弹出失败提示;之前乐观更新会留在界面上、实际没写盘,也没有任何反馈。
9
+ 同时修掉了「读设置」晚于「写设置」返回时用旧值覆盖新值的竞态。
10
+ - **一次网络抖动不再让「打开方式 / 置顶应用」条目消失。** 应用列表加载失败时保留上次
11
+ 成功的结果,而不是清空(旧行为会空白 10 秒)。
12
+ - **批量删除会话更快、计数更准。** 一次批量(含删除工作区)只解析一次会话索引,不再
13
+ 每个会话扫一遍全部会话(O(N²)),并改为 4 路并发执行;若目标会话已全部不存在,
14
+ 提示改为「会话已不存在,无需删除」,不再报「已删除 0 个」。
15
+ - **Windows 上从 `.cmd` / `.bat` 启动编辑器时路径不再被拆词。** 含空格或 `& ^ ( )` 的
16
+ 目录不会再传错,也堵掉了命令注入面。
17
+ - **权限设置文件更安全。** 写入加文件锁(避免并发读-改-写丢更新),读到损坏的 JSON 时
18
+ 报错而不是整表覆盖;从工作区子目录启动的会话也能命中该工作区的默认权限。
19
+ - **内部重构:客户端拆模块 + 打包。** `lib/client.js` 改为由 `src/client/**` 经 esbuild
20
+ 打包的产物,9 套重复的 localStorage store、5 处子菜单关闭逻辑、菜单挂载外壳都收敛
21
+ 成共用实现;仓库约定见 `AGENTS.md`。行为与 UI 不变。
22
+
23
+ ### Changed
24
+
25
+ - **A failed "default permission for new sessions" change no longer pretends to
26
+ succeed.** On a network or host error the menu rolls back to the previous checkmark
27
+ and shows a failure toast; the optimistic update used to stay on screen even though
28
+ nothing was persisted, with no feedback at all. A read that resolves after a write
29
+ can no longer overwrite the newer value either.
30
+ - **One network hiccup no longer empties the "Open with / pinned apps" entries.** The
31
+ app list keeps its last successful result on failure instead of clearing for 10
32
+ seconds.
33
+ - **Batch session deletion is faster and counts correctly.** A batch (including
34
+ deleting a whole workspace) resolves the session index once instead of listing every
35
+ session per id (O(N²)) and runs with 4-way concurrency; when all targets were already
36
+ gone the toast says "the sessions were already gone" instead of "deleted 0".
37
+ - **Windows `.cmd` / `.bat` editor launchers no longer split paths.** Directories with
38
+ spaces or `& ^ ( )` are quoted correctly, closing the command-injection surface.
39
+ - **Safer permission store.** Writes take a file lock (no lost updates on concurrent
40
+ read-modify-write) and a corrupt JSON file now fails loudly instead of being
41
+ overwritten; sessions started from a workspace subdirectory resolve that workspace's
42
+ default permission too.
43
+ - **Internal refactor: the client is now split into modules and bundled.**
44
+ `lib/client.js` is a build artifact of `src/client/**` via esbuild; nine duplicated
45
+ localStorage stores, five copies of submenu dismissal and the menu-mount shell now
46
+ share one implementation. Conventions live in `AGENTS.md`; behaviour and UI are
47
+ unchanged.
48
+
49
+ ## 1.10.0 — 2026-11-06
50
+
51
+ ### 中文摘要
52
+
53
+ - **工作区可以单独设置新会话的默认权限。** 工作区右键菜单新增「新会话默认权限」二级
54
+ 菜单,列出宿主原生的权限预设(仅可查看 / 工作区内修改 / 完全权限),当前生效的一项
55
+ 右侧打勾;选择后只影响该工作区之后新建的会话,已有会话不动。「跟随全局默认」清除
56
+ 本工作区的设置。设置存在 `$DSH_HOME/storages/dsh-sidebar-row-menu/permission-defaults.json`,
57
+ 由宿主插件在会话创建时通过 `permissionPresets` 原生接口落成会话事件,不靠客户端改默认值。
58
+ - **菜单条目可以只放进「…」菜单。** 设置 →「行菜单」→「菜单显示位置」把原来
59
+ 「同时放进「…」菜单」的开关改成三态:右键菜单 / 「…」菜单 / 两边,每个条目至少保留
60
+ 一边;旧配置自动迁移(开 → 两边,关 → 右键菜单)。
61
+
62
+ ### Changed
63
+
64
+ - **A workspace can now set the default permission for its new sessions.** The
65
+ workspace context menu gains a "Default permission for new sessions" submenu listing
66
+ the host's native presets (read-only / workspace-write / danger-full-access) with the
67
+ effective one checked. The choice applies only to sessions created afterwards in
68
+ that workspace and never touches existing sessions; "Follow global default" clears
69
+ it. The mapping is stored in
70
+ `$DSH_HOME/storages/dsh-sidebar-row-menu/permission-defaults.json`, and the host
71
+ plugin applies it through the native `permissionPresets` service when a session is
72
+ created, so it is recorded as a normal permission event rather than by mutating the
73
+ client default.
74
+ - **Entries can live only in the "…" menu.** Settings → "Row menu" → "Menu placement"
75
+ replaces the old "Also show in the '…' menu" switches with a tri-state choice per
76
+ entry (right-click / "…" / both), always keeping at least one side; existing
77
+ settings migrate automatically (on → both, off → right-click).
78
+
3
79
  ## 1.9.3 — 2026-11-06
4
80
 
5
81
  ### 中文摘要
package/README.en.md CHANGED
@@ -27,13 +27,14 @@ Session menu content comes from the `sidebar.workspaces.session.menu.item` slot
27
27
  built-in actions and other plugins' actions are all there. This plugin only appends
28
28
  its own entries; it does not copy or hardcode any built-in action.
29
29
 
30
- > The "…" button menu stays exactly as DSH ships it. The plugin's entries appear
31
- > only when the menu is opened by right-click — on 0.2.2 that holds for workspace
32
- > and session rows alike, and each entry can be opted into the "…" menu in Settings.
30
+ > The plugin's entries appear in the right-click menu by default. Settings → "Row
31
+ > menu" → "Menu placement" can move each entry to the "…" menu only, or show it in
32
+ > both (at least one side always stays on).
33
33
  > On 0.2.2 both gestures share
34
34
  > one host menu, so the plugin tells them apart by the gesture itself: a right-click
35
35
  > sets a mark that is released once the menu panel has actually mounted, while the
36
- > pointerdown on the "…" button clears it immediately. A right-click menu does not
36
+ > pointerdown on the "…" button clears it immediately. The "…" menu therefore only
37
+ > carries entries placed there (or on both sides). A right-click menu does not
37
38
  > close when the pointer leaves it; it closes only on an outside click, an item
38
39
  > selection, or Escape.
39
40
 
@@ -45,9 +46,10 @@ On 0.2.2 the host renders this menu, and the plugin's items interleave with the
45
46
  built-in Rename (100), Reveal in file manager (200) and Delete workspace (300),
46
47
  using the same grouping rules as the session menu: New session 90, Open in new
47
48
  tab 110, archived-session toggle 130, Copy workspace path 150, pinned app 210,
48
- Open with 220, Delete workspace and all sessions 310. These items are right-click only by default; the row's "…"
49
- menu keeps its native content unless you opt entries in under Settings → "Also
50
- show in the "…" menu → workspace". On 0.2.1 the plugin draws the whole menu and
49
+ Open with 220, default permission for new sessions 225, Delete workspace and all
50
+ sessions 310. These items are right-click only by default; the row's "…"
51
+ menu keeps its native content unless you place entries under Settings → "Row
52
+ menu" → "Menu placement". On 0.2.1 the plugin draws the whole menu and
51
53
  runs the official dialogs for Rename and Delete workspace.
52
54
 
53
55
  | Item | Behavior |
@@ -60,6 +62,7 @@ runs the official dialogs for Rename and Delete workspace.
60
62
  | Reveal in file manager (built-in) | Opens the workspace directory in Explorer; provided by the host on 0.2.2, absent on 0.2.1 |
61
63
  | Pinned open-with app | A pinned app shows directly at the top level (e.g. "Open with Visual Studio Code") and opens the workspace directory |
62
64
  | Open with | A submenu of external apps (editors, terminals, file managers) sharing the session menu's catalog, pinning and toggles; hover expands it. Host menu items draw no submenu caret, so the plugin renders the label as text plus a right-aligned chevron, matching the session menu; the 0.2.1 self-drawn menu uses an arrow icon |
65
+ | Default permission for new sessions | A submenu of the host's native permission presets (read-only / workspace-write / danger-full-access). The chosen preset becomes the default for **sessions created afterwards in this workspace**; the effective one is checked on the right, and "Follow global default" clears the per-workspace value. Existing sessions are never changed |
63
66
  | Delete workspace (built-in) | Opens the official delete confirmation; removes only the workspace registration and leaves its sessions under "Ungrouped". On 0.2.1 the plugin renders this item itself |
64
67
  | Delete workspace and all sessions | Permanently deletes every session and transcript under the workspace first, then the workspace registration. If any session fails, the workspace is kept so the user can retry. The directory itself is not deleted |
65
68
 
@@ -91,25 +94,19 @@ Both delete entries are served by this plugin's own host route. They leave
91
94
  unarchived sessions alone by default; the opt-in switch extends the single-session
92
95
  entry to them. Deletion never cascades into forked child sessions.
93
96
 
94
- These entries are **right-click only by default**: they do not mount when the "…"
95
- button opens the menu. There is no time window any more; the plugin sets a
96
- right-click gesture mark and releases it only once the menu panel has actually
97
- appeared (0.2.2 mounts the menu asynchronously), and the pointerdown on the "…"
98
- button clears it at once. While the mark is live the entries render normally;
99
- otherwise they render `null`.
97
+ These entries appear in the **right-click menu** by default. To use them from the
98
+ row's "…" button, set each one under Settings → "Row menu" → "Menu placement":
100
99
 
101
- To use them from the row's "…" button, enable them one by one under Settings →
102
- "Row menu" → "Also show in the "…" menu":
100
+ - "…" menu: the entry appears only in the row's "…" menu and leaves the
101
+ right-click menu;
102
+ - "Both": it appears on both sides;
103
+ - "Right-click menu": the default, right-click only.
103
104
 
104
- - session entries mount into the native "…" menu;
105
- - workspace entries on 0.2.2: the host menu is data-driven, so an enabled entry skips
106
- the gesture check and appears in the "…" menu; with everything off that menu keeps
107
- only the native items;
108
- - workspace entries on 0.2.1: enabling any of them makes this plugin take over the
109
- row's "…" button and render the official "Rename / Delete workspace" items
110
- alongside the enabled plugin entries.
111
-
112
- With everything off, the "…" button behaves exactly like DSH's own.
105
+ Every entry must keep at least one side, so the switch for the last active side is
106
+ locked. Session entries and workspace entries (0.2.2) share the same
107
+ `contextMenus` providers and use the gesture mark to tell which menu is being
108
+ rendered; on 0.2.1 the plugin takes over the row's "…" button and draws the
109
+ workspace menu itself with the same placement rules.
113
110
 
114
111
  Blank draft sessions have no right-click menu and are left alone. Rows that are
115
112
  neither a workspace nor a session (e.g. "Show more") are not intercepted.
@@ -167,24 +164,27 @@ Settings → "Row menu" toggles every new entry:
167
164
  | Allow deleting unarchived sessions | Off by default; also shows "Delete session permanently" on unarchived rows. Requires the previous switch |
168
165
  | Delete all archived sessions | Hides the bulk-delete entry |
169
166
  | Show archived sessions | Hides the workspace "Show / hide archived sessions" entry (on by default; the entry itself only appears when that workspace has archived sessions) |
170
- | Also show in the "…" menu → workspace | Adds workspace right-click entries one by one to the row's "…" menu: New session, Open with, Copy workspace path, Open in new tab, archived-session toggle, Delete workspace and all sessions. On 0.2.1 enabling any of them makes this plugin render the "…" menu |
171
- | Also show in the "…" menu → session | Adds session right-click entries one by one to the native "…" menu: Open with, Delete session, Delete all archived sessions |
167
+ | Menu placement → workspace | Per-entry choice between the right-click menu, the "…" menu, or both: New session, Default permission for new sessions, Open with, Copy workspace path, Open in new tab, archived-session toggle, Delete workspace and all sessions |
168
+ | Menu placement → session | Per-entry choice for session-row entries: Open with, Delete session, Delete all archived sessions |
172
169
  | Enable blank-space context menu | Master switch for the conversation-body right-click menu (blank space, thinking blocks, replies; on by default); when off, that area keeps the browser/host native menu |
173
170
  | Pin "More" to the far right | On by default; keeps a "More actions" style overflow trigger as the last row regardless of its current toolbar position |
174
171
  | Show top-left status widgets | Off by default; also lists everything in the header's `conversation.session.header.actions` slot (Session cost, background-job counter, …) in the conversation-body menu |
175
172
 
176
- The "Also show in the "…" menu" switches are all off by default; until one is
177
- enabled the "…" menu is exactly DSH's own.
173
+ Every placement defaults to "Right-click menu", so untouched installs keep the "…"
174
+ menu exactly as DSH ships it.
178
175
 
179
- On DSH 0.2.2 and later the "…" button and right-click share one host menu, yet the
180
- plugin's entries still appear only on right-click; flipping a switch also puts that
181
- entry into the "…" menu. On 0.2.1 workspace rows have no host menu to contribute to,
182
- so the plugin takes over the "…" button and draws the menu itself.
176
+ On DSH 0.2.2 and later the "…" button and right-click share one host menu; the
177
+ plugin tells them apart by the gesture mark, and only entries placed in "…" or
178
+ "Both" join that menu. On 0.2.1 workspace rows have no host menu to contribute to,
179
+ so the plugin takes over the "…" button and draws the menu itself with the same
180
+ placement rules.
183
181
 
184
182
  Switches live in browser `localStorage` (`dsh.sidebar-row-menu.items.v1`,
185
183
  `dsh.sidebar-row-menu.open-with.v1`, `dsh.sidebar-row-menu.open-with.pinned.v1`,
186
- `dsh.sidebar-row-menu.ellipsis.v1` and `dsh.sidebar-row-menu.blank-menu.v1`)
187
- and apply immediately. Before the first explicit pin, the plugin derives one: the
184
+ `dsh.sidebar-row-menu.placement.v1` and `dsh.sidebar-row-menu.blank-menu.v1`)
185
+ and apply immediately. The legacy `dsh.sidebar-row-menu.ellipsis.v1` booleans are
186
+ migrated on first load (true → "Both", false → "Right-click menu"). Before the
187
+ first explicit pin, the plugin derives one: the
188
188
  first detected editor (usually VS Code), or the first catalog app when no editor
189
189
  exists.
190
190
 
@@ -366,11 +366,19 @@ pnpm install (npm or GitHub)" transition.
366
366
  ## Development
367
367
 
368
368
  ```powershell
369
- npm run test # normal: node --test, one child process per file
370
- npm run test:local # restricted terminals / sandboxes: --test-isolation=none
371
- npm run build # npm pack (prepack runs check first), writes the .tgz
369
+ npm install # installs esbuild, the only devDependency
370
+ npm run build:client # src/client/** → lib/client.js (build artifact)
371
+ npm run test # builds first, then node --test, one process per file
372
+ npm run test:local # restricted terminals / sandboxes: --test-isolation=none
373
+ npm run check # syntax check + artifact freshness
374
+ npm run build # npm pack (prepack builds and checks first), writes the .tgz
372
375
  ```
373
376
 
377
+ The client half lives in `src/client/**` (ESM, multiple files) and is bundled into the
378
+ single file `lib/client.js` by esbuild. **Do not edit `lib/client.js` by hand** — change
379
+ the sources and rebuild. Layout rules, naming and the release checklist are in
380
+ [AGENTS.md](AGENTS.md).
381
+
374
382
  The suite covers host pure functions (path validation, `vscode://file` encoding,
375
383
  loopback checks, route status codes) and the client module (module id, exports,
376
384
  dependency whitelist, listener install/uninstall, slot registry). Integration
@@ -389,7 +397,10 @@ lib/host/launch.js VS Code / Explorer launching
389
397
  lib/host/route.js /open, /session/delete and /workspace/delete routes, HTTP status codes
390
398
  lib/host/session-delete.js transcript verification and deletion (detach / rm / re-check)
391
399
  lib/host/workspace-delete.js workspace + all sessions deletion, workspace kept on failure
392
- lib/client.js browser half: fiber lookup, slot registration, menu mount
400
+ lib/client.js browser half build artifact (bundled from src/client/**, do not edit)
401
+ src/client/ browser sources: i18n, constants, styles, stores, ui, features
402
+ scripts/build-client.mjs esbuild bundler (--check verifies the artifact is fresh)
403
+ AGENTS.md repository conventions: layering, naming, build and release
393
404
  cordis.patch.yml bundle patch inserting the plugin into the profile tree
394
405
  scripts/install.ps1 pnpm-free install / uninstall
395
406
  test/*.test.mjs node:test
@@ -486,8 +497,8 @@ mark only lives until "the panel for this gesture appeared", never into the next
486
497
  Workspace rows on 0.2.2 take a different path: the host menu is built from each
487
498
  `ctx.contextMenus.register` provider's `resolve`, which has no React entry to read the
488
499
  mark, so the provider checks the same mark while resolving. The "…" pointerdown
489
- clears it before the host resolves, hence the entries show on right-click only; the
490
- "Also show in the "…" menu → workspace" switches make the provider skip that check.
500
+ clears it before the host resolves, so the provider returns entries for the
501
+ right-click side or the "…" side according to each entry's "Menu placement".
491
502
 
492
503
  ### Why the conversation-body menu sorts by screen position
493
504
 
@@ -593,12 +604,12 @@ menus to a data-driven service, and the plugin picks the path by host capability
593
604
 
594
605
  - **0.2.2+**: workspace entries are registered with `ctx.contextMenus.register`
595
606
  (New session 90, Open in new tab 110, Copy workspace path 150, pinned app 210,
596
- Open with 220, Delete workspace and all sessions 310), interleaved by
607
+ Open with 220, default permission for new sessions 225, Delete workspace and all
608
+ sessions 310), interleaved by
597
609
  order with the native Rename (100), Reveal (200) and Delete workspace (300) and
598
610
  grouped with the same separators as the session menu.
599
- Right-click and the "…" button share the host's single menu; providers answer only
600
- during a right-click gesture (or when the matching "Also show in the "…" menu"
601
- switch is on).
611
+ Right-click and the "…" button share the host's single menu; providers answer per
612
+ entry according to the right-click gesture and that entry's "Menu placement".
602
613
  - **0.2.1**: no `contextMenus` service, so the plugin keeps drawing its own
603
614
  workspace menu.
604
615
  - **Session rows**: 0.2.2 still supports the
package/README.md CHANGED
@@ -22,10 +22,10 @@ DSH 0.2.2 把侧栏右键改成数据驱动:工作区菜单由宿主统一渲
22
22
  `sidebar.workspaces.session.menu.item` slot —— 内置动作和其他插件的动作都在里面,
23
23
  本插件只追加自己的条目,不复制、不硬编码任何内置动作。
24
24
 
25
- > 点行尾「…」按钮看到的菜单保持 DSH 原样;本插件的条目默认只在右键展开时出现(0.2.2 上
26
- > 工作区行与会话行都是如此,可以在设置里逐项开启)。
25
+ > 本插件的条目默认只在右键展开时出现;设置 →「行菜单」→「菜单显示位置」可以逐项改成
26
+ > 只进「…」菜单、或两边都进(至少保留一边)。
27
27
  > 0.2.2 上右键与「…」共用同一份宿主菜单,插件按“右键手势”区分:右键时打标,等菜单面板
28
- > 真正挂载后才收尾;点「…」的按下事件会立刻清标,所以那个菜单不带右键条目。
28
+ > 真正挂载后才收尾;点「…」的按下事件会立刻清标,所以那个菜单只带设为「…」或「两边」的条目。
29
29
  > 右键展开的菜单不会因为鼠标移出而关闭,只有点击外部、选中条目或按 Esc 才关闭。
30
30
 
31
31
  ## 功能
@@ -34,10 +34,10 @@ DSH 0.2.2 把侧栏右键改成数据驱动:工作区菜单由宿主统一渲
34
34
 
35
35
  0.2.2 上这份菜单由宿主渲染,本插件条目与内置「重命名」(100)、「在文件夹显示」(200)、
36
36
  「删除工作区」(300)按 order 交错,分组规则与会话菜单一致:新会话 90、在新标签打开 110、
37
- 归档会话开关 130、复制工作区路径 150、置顶应用 210、打开方式 220、删除工作区及所有会话 310。这些条目默认
38
- 只出现在右键菜单;行尾「…」里的菜单保持原生,想让它也带上插件条目,可在设置里逐项开启
39
- 「同时放进「…」菜单 → 工作区」。0.2.1 上整份菜单由本插件自绘,「重命名」「删除工作区」
40
- 由插件调用官方对话框。
37
+ 归档会话开关 130、复制工作区路径 150、置顶应用 210、打开方式 220、新会话默认权限 225、
38
+ 删除工作区及所有会话 310。这些条目默认只出现在右键菜单;行尾「…」里的菜单保持原生,
39
+ 想让它也带上插件条目,可在设置 →「行菜单」→「菜单显示位置」里逐项选择「…」或「两边」。
40
+ 0.2.1 上整份菜单由本插件自绘,「重命名」「删除工作区」由插件调用官方对话框。
41
41
 
42
42
  | 菜单项 | 行为 |
43
43
  | --- | --- |
@@ -49,6 +49,7 @@ DSH 0.2.2 把侧栏右键改成数据驱动:工作区菜单由宿主统一渲
49
49
  | 在文件夹显示(内置) | 在资源管理器里打开工作区目录;0.2.2 由宿主提供,0.2.1 上不出现 |
50
50
  | 置顶的打开方式 | 被置顶的应用直接显示在一级菜单(例如「用 Visual Studio Code 打开」),点击后用该应用打开工作区目录 |
51
51
  | 打开方式 | 二级菜单列出宿主提供的外部应用(编辑器、终端、文件管理器);与会话菜单共用同一套应用列表、置顶与开关设置,鼠标悬浮自动展开。宿主的菜单项不画二级箭头,本插件把标签渲染成「文字 + 右对齐箭头」,与会话菜单同一位置;0.2.1 自绘菜单画的是箭头图标 |
52
+ | 新会话默认权限 | 二级菜单列出宿主原生权限预设(仅可查看 / 工作区内修改 / 完全权限)。选中的预设成为**该工作区下以后新建会话**的默认权限,当前生效项右侧打勾;「跟随全局默认」清除本工作区的设置。只改新会话,不动已有会话 |
52
53
  | 删除工作区(内置) | 打开官方删除确认对话框;只删除工作区登记,会话保留并归入「未分组」。0.2.1 上由本插件代为渲染 |
53
54
  | 删除工作区及所有会话 | 先永久删除该工作区下的每个会话及其日志,全部成功后再删除工作区登记;任一会话删除失败则保留工作区,便于重试。工作区目录本身不删除 |
54
55
 
@@ -77,17 +78,16 @@ DSH 0.2.2 把侧栏右键改成数据驱动:工作区菜单由宿主统一渲
77
78
  一级菜单。两个删除入口由本插件自己的宿主路由完成,默认不会删除未归档会话;
78
79
  开启「允许删除未归档会话」后才会对普通会话开放。删除不会级联删除 fork 子会话。
79
80
 
80
- 这些条目默认是**右键专属**:点行尾「…」按钮时它们不挂载。实现上不再用时间窗口,而是
81
- 「右键手势标记」:右键时打标,等菜单面板真正出现(0.2.2 的挂载是异步的)后才释放;点
82
- 「…」的按下事件会立刻清标。标记有效期内的条目正常渲染,其余情况渲染直接返回 `null`。
81
+ 这些条目默认出现在**右键菜单**。想直接使用行尾「…」菜单,可以在设置 →「行菜单」→
82
+ 「菜单显示位置」里逐项选择:
83
83
 
84
- 如果想直接使用行尾「…」菜单,可以在设置 →「行菜单」→「同时放进「…」菜单」里逐个开启:
84
+ - 「…」菜单:条目只出现在行尾「…」打开的菜单里,右键菜单不再显示;
85
+ - 「两边」:右键和「…」都显示;
86
+ - 「右键菜单」:默认值,只在右键菜单里出现。
85
87
 
86
- - 会话条目:开启后挂进原生「…」菜单;
87
- - 工作区条目(0.2.2):宿主菜单本身是数据驱动的,开启后 provider 解析时不再看手势,
88
- 条目直接进入「…」菜单;全部关闭时「…」里只有原生项;
89
- - 工作区条目(0.2.1):开启任意一项后,本插件接管行尾「…」按钮,用同一套菜单展示
90
- 官方「重命名 / 删除工作区」加上已开启的插件条目。
88
+ 每个条目至少要保留一边,最后一边的开关会被锁住。会话条目与工作区条目(0.2.2)都走同一套
89
+ `contextMenus` provider,按手势标记决定当前是哪份菜单;工作区条目在 0.2.1 上由本插件接管
90
+ 行尾「…」按钮自绘,规则一致。
91
91
 
92
92
  草稿会话(还没有任何消息的空白会话)没有右键菜单,本插件同样不接管。
93
93
  非工作区 / 非会话的侧边栏行(如“显示更多”)也不会被拦截。
@@ -134,22 +134,23 @@ DSH 0.2.2 把侧栏右键改成数据驱动:工作区菜单由宿主统一渲
134
134
  | 允许删除未归档会话 | 默认关闭;开启后未归档会话的右键菜单也会出现「永久删除会话」,需先开启上一项 |
135
135
  | 删除所有已归档会话 | 关闭后不再出现批量删除入口 |
136
136
  | 显示已归档会话 | 关闭后工作区菜单里不再出现「显示 / 隐藏已归档会话」(默认开启;该项只在当前工作区存在已归档会话时出现) |
137
- | 同时放进「…」菜单 → 工作区 | 逐项把工作区行右键条目也放进行尾「…」菜单:新会话、打开方式、复制工作区路径、在新标签中打开、归档会话开关、删除工作区及所有会话。0.2.1 上开启任意一项后「…」按钮改由本插件渲染 |
138
- | 同时放进「…」菜单 → 会话 | 逐项把会话行右键条目加进原生「…」菜单:打开方式、永久删除会话、删除所有已归档会话 |
137
+ | 菜单显示位置 → 工作区 | 逐项选择工作区行条目出现在右键菜单、「…」菜单,或两边:新会话、新会话默认权限、打开方式、复制工作区路径、在新标签中打开、归档会话开关、删除工作区及所有会话 |
138
+ | 菜单显示位置 → 会话 | 逐项选择会话行条目的显示位置:打开方式、永久删除会话、删除所有已归档会话 |
139
139
  | 启用空白处右键菜单 | 会话正文(空白处、思考过程、回复)的右键菜单总开关(默认开启);关闭后该区域恢复浏览器 / 宿主的原生菜单 |
140
140
  | 「更多」固定在最右 | 默认开启;把「更多操作」一类的溢出入口固定在菜单最后一项,不受工具条当前顺序影响 |
141
141
  | 显示左上角状态组件 | 默认关闭;打开后会话栏左上角 `conversation.session.header.actions` 槽位里的组件(费用明细、后台任务计数等)也会列进正文右键菜单 |
142
142
 
143
- 「同时放进「…」菜单」下的开关默认全部关闭,`…` 菜单在未开启时与 DSH 完全一致。
143
+ 「菜单显示位置」默认全部是「右键菜单」,所以未改动时 `…` 菜单与 DSH 完全一致。
144
144
 
145
- DSH 0.2.2 起行尾「…」与右键共用同一份宿主菜单,但插件条目仍按手势只出现在右键菜单里;
146
- 上表的开关打开后,对应条目才会同时进入「…」菜单。0.2.1 上工作区行没有宿主菜单可贡献,
147
- 改由插件接管「…」按钮自绘。
145
+ DSH 0.2.2 起行尾「…」与右键共用同一份宿主菜单,插件条目按手势标记区分当前是哪份菜单;
146
+ 设为「…」或「两边」的条目才会进入「…」菜单。0.2.1 上工作区行没有宿主菜单可贡献,
147
+ 改由插件接管「…」按钮自绘,显示规则相同。
148
148
 
149
149
  开关存在浏览器 `localStorage`(`dsh.sidebar-row-menu.items.v1`、
150
150
  `dsh.sidebar-row-menu.open-with.v1`、`dsh.sidebar-row-menu.open-with.pinned.v1`、
151
- `dsh.sidebar-row-menu.ellipsis.v1` 与 `dsh.sidebar-row-menu.blank-menu.v1`),
152
- 改完立即生效,不需要重启。置顶项在第一次配置前是自动推导的:优先置顶检测到的第一个
151
+ `dsh.sidebar-row-menu.placement.v1` 与 `dsh.sidebar-row-menu.blank-menu.v1`),
152
+ 改完立即生效,不需要重启。旧版的 `dsh.sidebar-row-menu.ellipsis.v1`(布尔值)会在首次加载时
153
+ 自动迁移成「两边 / 右键菜单」。置顶项在第一次配置前是自动推导的:优先置顶检测到的第一个
153
154
  编辑器(通常是 VS Code),没有编辑器时置顶应用列表里的第一个。
154
155
 
155
156
  ### 与 dsh-session-menu 共存
@@ -312,11 +313,18 @@ cmd /c rmdir "$env:USERPROFILE\.dsh\profiles\desktop\node_modules\dsh-sidebar-ro
312
313
  ## 开发
313
314
 
314
315
  ```powershell
315
- npm run test # 常规:node --test,逐文件子进程
316
- npm run test:local # 受限终端 / 沙箱:--test-isolation=none,同进程跑
317
- npm run build # npm pack(prepack 会先跑 check),产出 .tgz
316
+ npm install # 只装 esbuild(唯一的 devDependency)
317
+ npm run build:client # src/client/** → lib/client.js(构建产物)
318
+ npm run test # 先构建再跑 node --test,逐文件子进程
319
+ npm run test:local # 受限终端 / 沙箱:--test-isolation=none,同进程跑
320
+ npm run check # 语法检查 + 产物与源码是否一致
321
+ npm run build # npm pack(prepack 会先构建 + check),产出 .tgz
318
322
  ```
319
323
 
324
+ 客户端源码在 `src/client/**`(ESM,多文件),由 esbuild 打包成单文件 `lib/client.js`;
325
+ **不要手改 `lib/client.js`**,改完源码要重新构建。目录分层、命名、构建与发布约定见
326
+ [AGENTS.md](AGENTS.md)。
327
+
320
328
  测试覆盖宿主侧纯函数(路径校验、`vscode://file` 编码、loopback 判定、路由状态码)
321
329
  和客户端模块(模块 id、导出、依赖白名单、监听器安装/卸载、slot 注册表),并用伪造的
322
330
  `__reactFiber$`、DOM 与 `Menu` 宿主做集成测试:工作区右键的菜单项顺序、会话右键是否
@@ -334,7 +342,10 @@ lib/host/launch.js VS Code / 资源管理器启动
334
342
  lib/host/route.js /open、/session/delete 与 /workspace/delete 路由、HTTP 状态码
335
343
  lib/host/session-delete.js 会话日志目录的校验与删除(detach / rm / 复查)
336
344
  lib/host/workspace-delete.js 删除工作区及其所有会话;会话失败时保留工作区
337
- lib/client.js 客户端半边:fiber 解析、slot 注册、菜单挂载
345
+ lib/client.js 客户端半边构建产物(由 src/client/** 打包,勿手改)
346
+ src/client/ 客户端源码:i18n、常量、样式、store、ui、features
347
+ scripts/build-client.mjs esbuild 打包脚本(--check 校验产物是否最新)
348
+ AGENTS.md 仓库约定:分层、命名、构建与发布
338
349
  cordis.patch.yml bundle patch:把插件插进 profile 插件树
339
350
  scripts/install.ps1 无 pnpm 的安装 / 卸载脚本
340
351
  test/*.test.mjs node:test
@@ -412,8 +423,9 @@ DSH 的归档可见性只有一个全局筛选(`archivedFilter`:隐藏已归
412
423
 
413
424
  工作区行在 0.2.2 上走的是另一条路:宿主菜单的数据来自 `ctx.contextMenus.register` 的
414
425
  `resolve`,没有 React 条目可以读标记,所以 provider 在解析时直接判断同一枚手势标记,
415
- 并且行尾「…」的 `pointerdown` 清标发生在宿主解析之前 —— 右键时条目进菜单,点「…」时
416
- 不进。「同时放进「…」菜单 → 工作区」的开关则让 provider 跳过手势判断,条目不依赖右键。
426
+ 并且行尾「…」的 `pointerdown` 清标发生在宿主解析之前 —— 右键时按「右键菜单 / 两边」
427
+ 返回条目,点「…」时按「… 菜单 / 两边」返回。「菜单显示位置」的开关让 provider 分别
428
+ 跳过一侧的手势判断。
417
429
 
418
430
  ### 会话正文右键为什么按视觉顺序
419
431
 
@@ -508,10 +520,11 @@ DSH 宿主按该范围拒绝安装不兼容的宿主,插件市场也按它显
508
520
  与 `contextMenus` 实现适配。DSH 0.2.2 起把侧栏右键菜单改成数据驱动,本包按宿主能力自动选路:
509
521
 
510
522
  - **0.2.2 及以上**:工作区行改用 `ctx.contextMenus.register` 贡献动作
511
- (新会话 90、新标签 110、复制路径 150、置顶应用 210、打开方式 220、删除工作区及所有会话 310),
523
+ (新会话 90、新标签 110、复制路径 150、置顶应用 210、打开方式 220、新会话默认权限 225、
524
+ 删除工作区及所有会话 310),
512
525
  与原生「重命名」「在文件夹显示」「删除工作区」按 order 交错排列;分组分隔线与会话菜单同一
513
526
  套规则。右键与「…」共用同一份宿主菜单,插件不再自绘,也不再接管「…」按钮。provider 解析时
514
- 按右键手势(或设置里「同时放进「…」菜单」的开关)决定是否返回条目。会话行由宿主自己把原生
527
+ 按右键手势与每项的「菜单显示位置」决定是否返回条目。会话行由宿主自己把原生
515
528
  菜单开在指针处,本包条目继续从 `sidebar.workspaces.session.menu.item` 渲染;
516
529
  - **0.2.1**:没有 `contextMenus` 服务,保持原来的自绘工作区菜单;会话右键仍由插件把
517
530
  行尾「…」菜单搬到指针。