@deepseek-ai/dsh-client-ui-context-menu 0.0.0-stage → 0.2.2-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
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.
@@ -0,0 +1,40 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/client/ui-context-menu/README.md
5
+ /:
6
+ en: c7a386986f8aa0d7
7
+ zh: a4bace64648d0a89
8
+ /deepseek-ai-dsh-client-ui-context-menu:
9
+ en: f165f99dfbea7ed2
10
+ zh: 0f2835e7726dd0f5
11
+ /deepseek-ai-dsh-client-ui-context-menu/summary:
12
+ en: 7d22e21395a76909
13
+ zh: e9f98068a4c5fde7
14
+ /deepseek-ai-dsh-client-ui-context-menu/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 725b47765ee8f30b
17
+ /deepseek-ai-dsh-client-ui-context-menu/use-this-package:
18
+ en: c84c65c7d24d51c3
19
+ zh: 98c8f40a326a8f0b
20
+ /deepseek-ai-dsh-client-ui-context-menu/use-this-package/register-actions:
21
+ en: 11bea09005de2678
22
+ zh: d56e25d2d93022aa
23
+ /deepseek-ai-dsh-client-ui-context-menu/understand-the-implementation:
24
+ en: 5b7717fad8457145
25
+ zh: 19a06fca8060cf62
26
+ /deepseek-ai-dsh-client-ui-context-menu/further-exploration:
27
+ en: 6d538e70a88f070b
28
+ zh: f90577fe9c8d1799
29
+ /deepseek-ai-dsh-client-ui-context-menu/model-experience:
30
+ en: bb3787b3201a6724
31
+ zh: e51cbb66f42a433d
32
+ /deepseek-ai-dsh-client-ui-context-menu/model-experience/kv-cache-effect:
33
+ en: e5fe67ca0631f67a
34
+ zh: b26e6107f2638589
35
+ /deepseek-ai-dsh-client-ui-context-menu/known-limitations-and-deferred-work:
36
+ en: ac1e5554e7f3109f
37
+ zh: c2df38df3b9ea4f2
38
+ /deepseek-ai-dsh-client-ui-context-menu/known-limitations-and-deferred-work/dev-note:
39
+ en: e67f633dc6fe7773
40
+ zh: 6c5f9788c6536776
package/README.md CHANGED
@@ -1,3 +1,100 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "Add actions to right-click menus without importing a React component"
3
+ kind: "package-reference"
4
+ ---
2
5
 
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.
6
+ # @deepseek-ai/dsh-client-ui-context-menu
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ Add actions to right-click menus without importing a React component. Targets retain the clicked Session, Workspace, file, directory, URL, image, or pane even when selection changes. Contributions can discover metadata asynchronously and disappear safely when their plugin unloads.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ The Web application bundle mounts this Client plugin. External Client plugins declare the package in `dsh.client.inject`, import its `/client` types, and inject the `contextMenus` service. `resolutionTimeoutMs` bounds each provider’s preparation (1000ms by default); timed-out providers are aborted and omitted with visible failure feedback.
29
+
30
+ The plugin injects `useContextMenu` into every Slot scope through `ctx.slots.provideRoot`. Call `const { getTriggerProps } = useContextMenu()` once in the component and spread `getTriggerProps(target, handlers?)` onto each trigger. The factory is an ordinary function and can run in loops. It handles right-click, Shift+F10, the ContextMenu key, viewport coordinates, and focus without scrolling. Existing handlers run first; `preventDefault()` keeps their ownership and prevents duplicate handling by composed or nested bindings. Repeated, composing, and unrelated key events remain untouched. Pass `undefined` to retain native behavior, or a target resolver to read business state when the gesture begins. The resolved object is captured before focus changes. Plain children receive event props or a factory through props or an existing delegate; no additional React Context is needed. Special editors and canvases can call `ctx.contextMenus.open()` directly.
31
+
32
+ ### Register actions
33
+
34
+ Call `ctx.effect(() => ctx.contextMenus.register({ id, target, order, resolve }))` in the contributing plugin. `resolve` receives the captured target and its cancellation signal; return localized actions with stable IDs, optional icons, disabled state, submenus, and execution callbacks. Contributions sort by `order`, then ID. Duplicate contribution IDs within one target family fail registration; duplicate action paths reject that provider’s result. IDs are encoded by segment before publication.
35
+
36
+ An action’s optional `commandId` associates it with an existing shortcut command. The overlay subscribes to `ctx.shortcuts.catalog`, including configured, removed, and reset bindings; nested and footer actions use the same presentation. This association never dispatches the command: selection invokes only the action’s captured `run` callback. The menu plugin requires the `shortcuts` service.
37
+
38
+ A contribution can declare `observe(target)` sources for permissions, membership, or localized labels. Source updates re-resolve that contribution against the same captured target and replace its callbacks; outdated asynchronous preparations are cancelled. Dismissal and plugin unload release every subscription.
39
+
40
+ Extend `ContextMenuTargetMap` through declaration merging on `@deepseek-ai/dsh-client-ui-context-menu/client` for a new target family; `ContextMenuTarget` derives the `{ kind, value }` union from that map. Open a target with `ctx.contextMenus.open(target, { x, y })`. Keep target values as immutable JSON-compatible identifiers and metadata. Execution callbacks capture that value; they must recheck mutable resources before acting. New registrations participate in the next opening. Unregistering removes visible actions and cancels pending metadata.
41
+
42
+ `ctx.contextMenus.resolve(target)` gives a separate occurrence for another renderer. Subscribe to its immutable action views, select a published ID, and dispose it when its owner closes. Views contain presentation fields only. Callbacks remain in the registry and execute at most once. Dismissal and replacement cancel metadata, but an action already selected owns its own execution and cancellation. Rejected providers and actions produce a persistent overlay toast; an action’s localized `failureLabel` supplies operation-specific recovery guidance.
43
+
44
+ `ctx.contextMenus.notify(text, success, action?)` can attach a localized text action through the shared Toast. The action provides `label`, `run`, and `failureLabel`; clicking dismisses the toast before execution, and rejection reports its failure label. A dismissed or replaced toast cannot execute its action again.
45
+
46
+ Shared popups wait for every contribution to settle or time out before appearing. A repeated gesture within four CSS pixels closes the same target; a farther gesture replaces it with a separate fade. `ready` gives custom renderers the same preparation point. Structured action icons align within each divider group; component contributions retain their own layout. Normal dismissal retains the popup for 80ms, replacement for 10ms; opening waits at least 20ms and fades in over 40ms. Reduced motion suppresses the fade.
47
+
48
+ Actions carry `icon?: { key: string; src?: string }`. Register the matching glyph component with `ctx.slots.inject('contextMenu.icon', () => ctx.slots.register({ name: 'contextMenu.icon', key: 'my-plugin.action' }, Icon))`. The overlay dispatches `renderSlot` by that key for ordinary, nested, and footer actions; the component receives the captured `icon` metadata, including an optional image URL. Registrations follow the contributing plugin and menu Slot declaration lifetimes. Action views contain no JSX nodes.
49
+
50
+ The optional keyed `contextMenu.content` Slot hosts component contributions. Existing Session and tab Slot names remain owned by their feature adapters. Keyboard context keys and Shift+F10 use the focused trigger’s lower edge. `afterContent` places a data action after those rows; ordinary actions render before them.
51
+
52
+ -----
53
+
54
+ <a id="understand-the-implementation"></a>
55
+ ## Understand the implementation
56
+
57
+ <details>
58
+ <summary>Implementation internals — click to expand</summary>
59
+
60
+ The registry separates resolution and callback lifetimes from presentation. The shared overlay uses the existing Menu primitive for keyboard movement, focus return, dismissal, submenus, and viewport positioning. [Registry](src/client/registry.ts) owns cancellation and ordering; [target and action types](src/client/types.ts) own the public fields.
61
+
62
+ </details>
63
+
64
+ -----
65
+
66
+ <a id="further-exploration"></a>
67
+ ## Further Exploration
68
+
69
+ - [Slots](../ui-slots/README.md) — component contributions.
70
+ - [File actions](../ui-file-actions/README.md) — original content transfer.
71
+ - [Open in app](../ui-open-in-app/README.md) — shipped resource actions.
72
+
73
+ -----
74
+
75
+ <a id="model-experience"></a>
76
+ ## Model Experience
77
+
78
+ None, as popup presentation and registration do not construct model input.
79
+
80
+ #### KV Cache effect
81
+
82
+ None; contributed feature actions own any subsequent model interaction.
83
+
84
+ ## Known Limitations and Deferred Work
85
+
86
+ <a id="known-limitations-and-deferred-work"></a>
87
+
88
+ - The shipped renderer is the shared Web Menu, including inside Desktop. Native menu rendering is not supplied.
89
+ - Component-only legacy actions do not appear in the plain-data resolution API.
90
+ - Menus are window-local and transient; they are not persisted in Session data.
91
+
92
+ <a id="dev-note"></a>
93
+ ### Dev Note
94
+
95
+ <details>
96
+ <summary>Working context for maintainers — click to expand</summary>
97
+
98
+ None.
99
+
100
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,100 @@
1
+ ---
2
+ description: "无需导入 React 组件即可向右键菜单添加操作"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-client-ui-context-menu
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 无需导入 React 组件即可向右键菜单添加操作。即使选中项变化,目标仍保留被点击的会话、工作区、文件、文件夹、链接、图片或窗格。贡献项可以异步查询元数据,并在插件卸载时安全移除。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延后工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ Web 应用 bundle 挂载此客户端插件。外部客户端插件在 `dsh.client.inject` 声明本包,导入 `/client` 类型,并注入 `contextMenus` 服务。`resolutionTimeoutMs` 限制每个提供方的准备时长(默认 1000ms);超时后取消并忽略该提供方,显示失败提示。
29
+
30
+ 插件通过 `ctx.slots.provideRoot` 向每个 Slot 作用域注入 `useContextMenu`。组件调用一次 `const { getTriggerProps } = useContextMenu()`,再将 `getTriggerProps(target, handlers?)` 展开到各个触发元素上。该工厂是普通函数,可以在循环中调用;它统一处理右键、Shift+F10、ContextMenu 键、视口坐标及无滚动聚焦。已有处理器先运行,`preventDefault()` 保留其处理权,并防止组合或嵌套绑定重复处理。重复、输入法组合及无关按键保持原状。传入 `undefined` 保留原生行为,也可传入目标解析函数,在手势开始时读取业务状态。解析后的对象会在焦点变化前捕获。普通子组件通过 props 或现有 delegate 接收事件 props 或工厂,无需额外 React Context。特殊编辑器和 Canvas 可直接调用 `ctx.contextMenus.open()`。
31
+
32
+ ### 注册操作
33
+
34
+ 在贡献插件中调用 `ctx.effect(() => ctx.contextMenus.register({ id, target, order, resolve }))`。`resolve` 接收捕获的目标和取消信号;返回本地化操作,包含稳定 ID、可选图标、禁用状态、子菜单和执行回调。贡献项先按 `order`、再按 ID 排序。同类目标的贡献 ID 重复会导致注册失败;操作路径重复会拒绝该提供方的结果。发布前会逐段编码 ID。
35
+
36
+ 动作可通过可选 `commandId` 关联已有快捷键命令。浮层订阅 `ctx.shortcuts.catalog`,跟随已配置、移除和重置的绑定更新;子菜单和底部动作采用相同展示。此关联不会调度命令:选择菜单项只调用该动作捕获的 `run` 回调。菜单插件依赖 `shortcuts` 服务。
37
+
38
+ 贡献可通过 `observe(target)` 声明权限、成员状态或本地化标签的数据源。数据源更新时,针对同一捕获目标重新解析该贡献并替换回调;过期的异步准备会被取消。关闭菜单和卸载插件都会释放所有订阅。
39
+
40
+ 通过对 `@deepseek-ai/dsh-client-ui-context-menu/client` 的声明合并扩展 `ContextMenuTargetMap`,即可增加目标类别;`ContextMenuTarget` 从该映射派生 `{ kind, value }` 联合类型。通过 `ctx.contextMenus.open(target, { x, y })` 打开目标。目标值使用不可变、可 JSON 序列化的标识与元数据。执行回调捕获该值,并在操作前重新检查可变资源。新注册项在下次打开时生效。注销会移除可见操作并取消待处理的元数据查询。
41
+
42
+ `ctx.contextMenus.resolve(target)` 为其他渲染器提供独立菜单实例。订阅不可变操作视图,选择已发布的 ID,并在所属界面关闭时释放。视图仅包含展示字段。回调保留在注册表中,最多执行一次。关闭和替换菜单会取消元数据查询,已选中操作则自行管理执行与取消。提供方或操作拒绝时由持久的覆盖层提示;操作的本地化 `failureLabel` 提供具体的恢复建议。
43
+
44
+ `ctx.contextMenus.notify(text, success, action?)` 可通过共享 Toast 附加本地化文字操作。操作提供 `label`、`run` 和 `failureLabel`;点击后先关闭提示再执行,失败时显示对应文案。已关闭或被替换的提示不能再次执行其操作。
45
+
46
+ 共享弹窗等待全部贡献项完成或超时后才显示。同一目标在四个 CSS 像素以内重复触发会关闭菜单;更远的触发以独立淡入淡出替换菜单。`ready` 向自定义渲染器提供同一个准备完成时点。结构化操作的图标按分隔线组对齐;组件贡献保留自己的布局。普通关闭保留弹窗 80ms,替换保留 10ms;打开至少等待 20ms,再用 40ms 淡入。减少动态效果偏好会禁用淡入淡出。
47
+
48
+ 操作携带 `icon?: { key: string; src?: string }`。通过 `ctx.slots.inject('contextMenu.icon', () => ctx.slots.register({ name: 'contextMenu.icon', key: 'my-plugin.action' }, Icon))` 注册匹配的图标组件。覆盖层对普通项、子菜单和底部操作都按该 key 调用 `renderSlot`;组件接收已捕获的 `icon` 元数据,包括可选图片 URL。注册随贡献插件和菜单 Slot 声明的生命周期清理。操作视图不包含 JSX 节点。
49
+
50
+ 可选的 keyed Slot `contextMenu.content` 承载组件贡献。现有会话和标签菜单 Slot 名称仍由各自功能适配器维护。键盘菜单键和 Shift+F10 使用焦点触发元素的下边缘定位。`afterContent` 将数据操作放在这些行之后;普通操作排在之前。
51
+
52
+ -----
53
+
54
+ <a id="understand-the-implementation"></a>
55
+ ## 理解实现
56
+
57
+ <details>
58
+ <summary>实现细节 — 点击展开</summary>
59
+
60
+ 注册表将查询和回调生命周期与展示分离。共享覆盖层复用现有 Menu 原语,处理键盘移动、焦点返回、关闭、子菜单和视口定位。[注册表](src/client/registry.ts) 管理取消与排序;[目标和操作类型](src/client/types.ts) 定义公开字段。
61
+
62
+ </details>
63
+
64
+ -----
65
+
66
+ <a id="further-exploration"></a>
67
+ ## 进一步探索
68
+
69
+ - [Slots](../ui-slots/README.zh.md) — 组件贡献。
70
+ - [文件操作](../ui-file-actions/README.zh.md) — 原始内容传输。
71
+ - [本地打开](../ui-open-in-app/README.zh.md) — 内置资源操作。
72
+
73
+ -----
74
+
75
+ <a id="model-experience"></a>
76
+ ## 模型体验
77
+
78
+ 无,因为弹出菜单展示与注册不会构造模型输入。
79
+
80
+ #### KV 缓存影响
81
+
82
+ 无;贡献操作所属功能负责后续可能产生的模型交互。
83
+
84
+ ## 已知限制与延后工作
85
+
86
+ <a id="known-limitations-and-deferred-work"></a>
87
+
88
+ - 内置渲染器是共享 Web Menu,Desktop 也使用它;未提供原生菜单渲染器。
89
+ - 仅有组件形式的旧操作不会出现在纯数据查询 API 中。
90
+ - 菜单是窗口内的临时状态,不持久化到会话数据。
91
+
92
+ <a id="dev-note"></a>
93
+ ### 开发备注
94
+
95
+ <details>
96
+ <summary>维护者工作背景 — 点击展开</summary>
97
+
98
+ 无。
99
+
100
+ </details>