@deepseek-ai/dsh-client-ui-commands 0.1.5-rc.2 → 0.1.6-alpha.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/README.i18n.yaml +2 -2
- package/README.md +10 -6
- package/README.zh.md +14 -10
- package/lib/client.js +257 -84
- package/lib/types/client/contract.d.ts +16 -6
- package/lib/types/client/directory.d.ts +2 -2
- package/lib/types/client/index.d.ts +2 -3
- package/lib/types/client/locales.d.ts +42 -9
- package/lib/types/client/presentation.d.ts +23 -0
- package/lib/types/client/resolution.d.ts +35 -0
- package/lib/types/client/service.d.ts +27 -8
- package/package.json +15 -15
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/ui-commands/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: f802179e08009cd4db8a3b786e52e81b26bb21da
|
|
6
|
+
README.zh.md: 1dee58ab42d67a62099d57d4e631e440d3349f21
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
Typing a `/` command
|
|
12
|
+
Typing a `/` command opens a registered popup, a client action, a host command's input, or direct execution; a command line is never silently downgraded to a plain prompt. Business packages register popupSelect specs (`/model`, `/permission`) or actions through `ctx.commandUi`, or decorate existing host commands with either kind while preserving their catalog rows and argument claims. Space and Enter resolve the line against the session's directory: a host descriptor with `input` is `leadingInput`, a registered `CommandUiSpec` is `popupSelect` or `action`, and everything else is `execute`.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -25,11 +25,15 @@ Typing a `/` command in the composer opens the matching surface — a registered
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## Use this package
|
|
27
27
|
|
|
28
|
-
Mount this plugin alongside `ui-input-trigger` and `ui-conversation`; the `/` source then appears in the trigger menu, and business packages register their command surfaces through `ctx.commandUi`. Typing `/model` opens the registered popup; a host command with an argument claim opens its input or executes directly.
|
|
28
|
+
Mount this plugin alongside `ui-input-trigger` and `ui-conversation`; the `/` source then appears in the trigger menu, and business packages register their command surfaces through `ctx.commandUi`. Typing `/model` opens the registered popup; a host command with an argument claim opens its input or executes directly. The composer's `+` button and a typed `/` open the same menu: an Add section (File, Goal, Plan, Feedback) and a Commands section (Compact, Permission, Model, Export) in usage order, each row with a glyph, a localized title and description, and the command name as an alias where the localized title differs from it.
|
|
29
29
|
|
|
30
30
|
### Kinds and decorations
|
|
31
31
|
|
|
32
|
-
A contribution is a client-owned command
|
|
32
|
+
A contribution is a client-owned command; a host-name collision fails loudly. Its UI is a popupSelect spec or an action: a callback a bare invocation runs after the trigger token is consumed, without submitting a message. Business packages own their actions and availability; the composer registers File through this same API. A decoration adds a bare-invocation popup or action to an existing host command while preserving its catalog row, argument claim, and lifecycle logging; it never fires without a matching host row. Menu queries fuzzy-match ordered, case-insensitive subsequences of command names and titles, with prefixes first and no section headings.
|
|
33
|
+
|
|
34
|
+
### Built-in row faces
|
|
35
|
+
|
|
36
|
+
First-party command definitions carry stable `definitionId` values. The client selects their localized titles, descriptions, icons, and input spellings by identity; changing a Host description cannot change that selection. Same-name overrides without the matching identity keep their own copy and receive no first-party aliases. Chinese and English spellings resolve through the same effective Session catalog in every locale, preserving the typed spelling in the draft and submitting the registered Host name. Contributions supply their own `label`, `description`, and `icon`, read on every candidate pass. Empty-query section order follows names, with unlisted rows closing Commands.
|
|
33
37
|
|
|
34
38
|
### Attachment-carrying submissions
|
|
35
39
|
|
|
@@ -43,7 +47,7 @@ When the composer submits with images or generic files, only a host command decl
|
|
|
43
47
|
<details>
|
|
44
48
|
<summary>Implementation internals — click to expand</summary>
|
|
45
49
|
|
|
46
|
-
`src/client/contract.ts`
|
|
50
|
+
`src/client/contract.ts` defines contribution and decoration registration, plus `dismiss(name)`, which closes that command's open popups and confirmations: dismissal aborts pending option loads, prevents their late results from reopening the popup, and preserves composer drafts. `CommandDirectory` owns the per-session wire cache and resolves typed commands through `resolution.ts`; that module owns first-party identity matching and localized input spellings. `matchSpace` reads the ready cache synchronously, while `matchEnter` waits for readiness and rejects on warmup failure or cancellation. Forwarded catalog and connection events invalidate the cache. After a matched Host execution, this browser emits `command/executed`; other clients observe only the durable command events. `PopupSelectController` owns popup state, and `PopupSelectView` occupies the input overlay. `presentation.ts` owns row labels, icons, and sections; its helpers and the resolution helpers stay internal to the plugin.
|
|
47
51
|
|
|
48
52
|
</details>
|
|
49
53
|
|
|
@@ -63,7 +67,7 @@ Read these pages when the command surface is not enough. They move from the comm
|
|
|
63
67
|
<a id="model-experience"></a>
|
|
64
68
|
## Model Experience
|
|
65
69
|
|
|
66
|
-
Indirectly, through the host `command.execute` RPC
|
|
70
|
+
Indirectly, through the host `command.execute` RPC they trigger, each command handler's host package owns any model-visible effect (the `/plan` handler flips plan mode, whose owning package injects its policy section), while the command line, the detached result, and every menu and notice rendering stay client-side and never enter the session log.
|
|
67
71
|
|
|
68
72
|
#### KV Cache effect
|
|
69
73
|
|
|
@@ -88,4 +92,4 @@ None.
|
|
|
88
92
|
|
|
89
93
|
</details>
|
|
90
94
|
|
|
91
|
-
**Runtime invariant:** No companion is published.
|
|
95
|
+
**Runtime invariant:** No companion is published. This browser-side source uses the wire command directory; it emits no Cordis events and owns no cross-plugin mutable state. Its dispatch and cache behavior are asserted by this package's specs.
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
键入 `/` 命令会打开已注册的弹窗、运行客户端动作、进入宿主命令的输入或直接执行,命令行不会被静默降级为普通提示词。业务包通过 `ctx.commandUi` 注册 popupSelect(`/model`、`/permission`)或 action,也可用这两种方式装饰既有宿主命令,同时保留其目录行与参数声明。空格与回车根据会话目录解析命令行:带 `input` 的宿主描述符是 `leadingInput`,注册了 `CommandUiSpec` 的是 `popupSelect` 或 `action`,其余是 `execute`。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -25,15 +25,19 @@ kind: "package-reference"
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## 使用本包
|
|
27
27
|
|
|
28
|
-
与 `ui-input-trigger` 及 `ui-conversation` 一起挂载本插件;`/` source 随即出现在触发菜单中,业务包经 `ctx.commandUi` 注册自己的命令表面。键入 `/model` 打开已注册的弹窗;带参数声明的宿主命令打开其输入或直接执行。
|
|
28
|
+
与 `ui-input-trigger` 及 `ui-conversation` 一起挂载本插件;`/` source 随即出现在触发菜单中,业务包经 `ctx.commandUi` 注册自己的命令表面。键入 `/model` 打开已注册的弹窗;带参数声明的宿主命令打开其输入或直接执行。composer 的 `+` 按钮与键入的 `/` 打开同一个菜单:「添加」小节(文件、目标、计划、反馈)与「指令」小节(压缩、权限、模型、下载日志)按使用频次排列,每行带图标、本地化的标题与说明,本地化标题与命令名不同时还显示命令名作为别名。
|
|
29
29
|
|
|
30
30
|
### 种类与装饰
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
贡献项是客户端自有命令,与宿主命令同名会明确报错。它的 UI 是 popupSelect 规格或动作:裸调用消费触发 token 后运行回调,不提交消息。业务包负责自己的动作及可用性,输入框通过同一 API 注册「文件」。装饰为已有宿主命令添加裸调用弹窗或动作,并保留其目录行、参数认领与生命周期记录;没有匹配的宿主行时不触发。菜单查询按顺序、不区分大小写地模糊匹配命令名与标题的子序列,前缀优先,不显示小节标题。
|
|
33
|
+
|
|
34
|
+
### 内置行的展示面
|
|
35
|
+
|
|
36
|
+
内置命令定义携带稳定的 `definitionId`。客户端按标识选择本地化标题、说明、图标和输入写法,修改宿主说明不会改变选择结果。没有匹配标识的同名覆盖保留自己的文案,也不获得内置别名。在任何界面语言下,中英文写法都通过同一个会话有效目录解析,草稿保留手输写法,提交使用宿主注册名。贡献项提供自己的 `label`、`description` 和 `icon`,每次生成候选项时读取。空查询按名称确定小节顺序,未列出的行排在「指令」末尾。
|
|
33
37
|
|
|
34
38
|
### 带附件提交
|
|
35
39
|
|
|
36
|
-
composer 携带图片或通用文件提交时,只有声明了 `input.attachments` 的宿主命令继续。其余命令路径都会抛出本地化的 `attachmentsUnsupported` 拒绝,以瞬态 toast
|
|
40
|
+
composer 携带图片或通用文件提交时,只有声明了 `input.attachments` 的宿主命令继续。其余命令路径都会抛出本地化的 `attachmentsUnsupported` 拒绝,以瞬态 toast 呈现,草稿与附件卡保持原位。处理器出错时保留相同草稿状态供用户重试。
|
|
37
41
|
|
|
38
42
|
-----
|
|
39
43
|
|
|
@@ -43,7 +47,7 @@ composer 携带图片或通用文件提交时,只有声明了 `input.attachmen
|
|
|
43
47
|
<details>
|
|
44
48
|
<summary>实现细节——点击展开</summary>
|
|
45
49
|
|
|
46
|
-
`src/client/contract.ts`
|
|
50
|
+
`src/client/contract.ts` 定义贡献项和装饰的注册接口,以及 `dismiss(name)`:它关闭该命令已打开的弹窗与确认对话框,中止待完成的选项加载,阻止晚到结果重新打开弹窗,并保留 composer 草稿。`CommandDirectory` 负责会话级协议缓存,并通过 `resolution.ts` 解析输入命令;该模块负责内置命令标识匹配和本地化输入写法。`matchSpace` 同步读取就绪缓存,`matchEnter` 等待缓存就绪,预热失败或取消时拒绝。转发的目录和连接事件使缓存失效。宿主执行匹配的命令后,本浏览器发布 `command/executed`,其他客户端只观察持久命令事件。`PopupSelectController` 负责弹窗状态,`PopupSelectView` 占据输入浮层。`presentation.ts` 负责行标题、图标和分节,展示与解析辅助函数均留在插件内部。
|
|
47
51
|
|
|
48
52
|
</details>
|
|
49
53
|
|
|
@@ -52,10 +56,10 @@ composer 携带图片或通用文件提交时,只有声明了 `input.attachmen
|
|
|
52
56
|
<a id="further-exploration"></a>
|
|
53
57
|
## 进一步探索
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
如果仅了解命令交互还不够,请阅读以下页面。它们从命令 API 进入触发流水线与宿主命令注册表。
|
|
56
60
|
|
|
57
61
|
- [ui-input-trigger](../ui-input-trigger/README.zh.md)——`/` source 注册进的流水线。
|
|
58
|
-
- [ui-conversation](../ui-conversation/README.zh.md)
|
|
62
|
+
- [ui-conversation](../ui-conversation/README.zh.md)——声明输入浮层 slot 并拥有 composer。
|
|
59
63
|
- [客户端包映射](../README.zh.md)——相邻的浏览器 UI 包。
|
|
60
64
|
|
|
61
65
|
-----
|
|
@@ -63,7 +67,7 @@ composer 携带图片或通用文件提交时,只有声明了 `input.attachmen
|
|
|
63
67
|
<a id="model-experience"></a>
|
|
64
68
|
## 模型体验
|
|
65
69
|
|
|
66
|
-
|
|
70
|
+
派发路径通过其触发的宿主 `command.execute` RPC 间接影响模型:每个命令 handler 的宿主包拥有任何模型可见效果(`/plan` 的 handler 翻转 plan 模式,其归属包注入 policy 段),而命令行、分离结果与所有菜单和 notice 渲染都留在客户端,永不进入会话日志。
|
|
67
71
|
|
|
68
72
|
#### KV Cache 影响
|
|
69
73
|
|
|
@@ -74,7 +78,7 @@ composer 携带图片或通用文件提交时,只有声明了 `input.attachmen
|
|
|
74
78
|
<a id="known-limitations-and-deferred-work"></a>
|
|
75
79
|
|
|
76
80
|
|
|
77
|
-
|
|
81
|
+
这些限制界定了当前命令交互方式。它们是当前包约束,不是通用命令行对比或任务积压。
|
|
78
82
|
|
|
79
83
|
- **脱离会话后,分离结果 notice 回退到 console**——fire-and-forget 路径经 `SessionInput.notify` 把结果送到触发会话的 composer;会话销毁后,console 输出行是仅剩的呈现面。
|
|
80
84
|
|
|
@@ -88,4 +92,4 @@ composer 携带图片或通用文件提交时,只有声明了 `input.attachmen
|
|
|
88
92
|
|
|
89
93
|
</details>
|
|
90
94
|
|
|
91
|
-
**运行时不变式:** 不发布伴生入口。这是基于 wire
|
|
95
|
+
**运行时不变式:** 不发布伴生入口。这是基于 wire 命令目录的浏览器侧 source,不发出 Cordis 事件,也不持有跨插件可变状态;dispatch 与 cache 行为由包测试覆盖。
|
package/lib/client.js
CHANGED
|
@@ -9,6 +9,118 @@ window.__ModuleLoader__.load({
|
|
|
9
9
|
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
11
|
let react = require("react");
|
|
12
|
+
//#region lib/types/client/locales.js
|
|
13
|
+
/**
|
|
14
|
+
* `command` namespace dictionaries: the composer menu's section headings,
|
|
15
|
+
* the client face (title, description, claim token) of the built-in Host
|
|
16
|
+
* commands whose catalog descriptors carry English text only, and the
|
|
17
|
+
* popupSelect shell's copy.
|
|
18
|
+
*/
|
|
19
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
20
|
+
const zh = {
|
|
21
|
+
"section.add": "添加",
|
|
22
|
+
"section.commands": "指令",
|
|
23
|
+
"label.goal": "目标",
|
|
24
|
+
"label.plan": "计划",
|
|
25
|
+
"label.feedback": "反馈",
|
|
26
|
+
"label.compact": "压缩",
|
|
27
|
+
"label.permission": "权限",
|
|
28
|
+
"label.export": "下载日志",
|
|
29
|
+
"description.goal": "设置或查看长期任务目标",
|
|
30
|
+
"description.plan": "进入或退出计划模式",
|
|
31
|
+
"description.feedback": "发送关于当前会话的反馈",
|
|
32
|
+
"description.compact": "压缩以上对话内容",
|
|
33
|
+
"description.permission": "切换权限预设(沙箱模式与审批策略)",
|
|
34
|
+
"description.export": "将当前会话内容导出为 ZIP",
|
|
35
|
+
"token.goal": "目标",
|
|
36
|
+
"token.plan": "计划",
|
|
37
|
+
"token.feedback": "反馈",
|
|
38
|
+
"token.compact": "压缩",
|
|
39
|
+
"token.permission": "权限",
|
|
40
|
+
"token.export": "导出",
|
|
41
|
+
"search.placeholder": "搜索…",
|
|
42
|
+
"search.aria": "筛选选项",
|
|
43
|
+
"status.loading": "正在加载选项…",
|
|
44
|
+
"status.applying": "正在应用…",
|
|
45
|
+
"status.empty": "无选项",
|
|
46
|
+
"overlay.aria": "/{command} 选项",
|
|
47
|
+
"listbox.aria": "/{command} 匹配项",
|
|
48
|
+
"notice.attachmentsUnsupported": "/{command} 不接受附件,请先移除附件"
|
|
49
|
+
};
|
|
50
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
51
|
+
const en = {
|
|
52
|
+
"section.add": "Add",
|
|
53
|
+
"section.commands": "Commands",
|
|
54
|
+
"label.goal": "Goal",
|
|
55
|
+
"label.plan": "Plan",
|
|
56
|
+
"label.feedback": "Feedback",
|
|
57
|
+
"label.compact": "Compact",
|
|
58
|
+
"label.permission": "Permission",
|
|
59
|
+
"label.export": "Export",
|
|
60
|
+
"description.goal": "Set or view the goal for a long-running task",
|
|
61
|
+
"description.plan": "Enter or leave plan mode",
|
|
62
|
+
"description.feedback": "Record feedback about this session",
|
|
63
|
+
"description.compact": "Compact older conversation history",
|
|
64
|
+
"description.permission": "Switch the permission preset (sandbox mode + approval policy)",
|
|
65
|
+
"description.export": "Download this Session log as a ZIP archive",
|
|
66
|
+
"token.goal": "goal",
|
|
67
|
+
"token.plan": "plan",
|
|
68
|
+
"token.feedback": "feedback",
|
|
69
|
+
"token.compact": "compact",
|
|
70
|
+
"token.permission": "permission",
|
|
71
|
+
"token.export": "export",
|
|
72
|
+
"search.placeholder": "Search…",
|
|
73
|
+
"search.aria": "Filter options",
|
|
74
|
+
"status.loading": "Loading options…",
|
|
75
|
+
"status.applying": "Applying…",
|
|
76
|
+
"status.empty": "No options",
|
|
77
|
+
"overlay.aria": "/{command} options",
|
|
78
|
+
"listbox.aria": "/{command} matches",
|
|
79
|
+
"notice.attachmentsUnsupported": "/{command} does not accept attachments; remove them first"
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region lib/types/client/resolution.js
|
|
83
|
+
const BUILTINS = {
|
|
84
|
+
goal: "@deepseek-ai/dsh-command-goal",
|
|
85
|
+
plan: "@deepseek-ai/dsh-plan-mode",
|
|
86
|
+
feedback: "@deepseek-ai/dsh-command-feedback",
|
|
87
|
+
compact: "@deepseek-ai/dsh-command-compact",
|
|
88
|
+
permission: "@deepseek-ai/dsh-permission-presets",
|
|
89
|
+
export: "@deepseek-ai/dsh-session-log-export"
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Identify a first-party definition without interpreting its display copy.
|
|
93
|
+
* @param descriptor - effective Host descriptor after scoped shadowing.
|
|
94
|
+
* @returns its first-party name, or undefined for another definition.
|
|
95
|
+
*/
|
|
96
|
+
function builtinCommandName(descriptor) {
|
|
97
|
+
return Object.keys(BUILTINS).find((name) => descriptor.definitionId === BUILTINS[name]);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Select the input spelling for a menu-picked command.
|
|
101
|
+
* @param descriptor - effective Host descriptor.
|
|
102
|
+
* @param t - command-namespace translator.
|
|
103
|
+
* @returns localized spelling for a known definition, otherwise its registered name.
|
|
104
|
+
*/
|
|
105
|
+
function claimToken(descriptor, t) {
|
|
106
|
+
const name = builtinCommandName(descriptor);
|
|
107
|
+
return name === void 0 ? descriptor.name : t(`token.${name}`);
|
|
108
|
+
}
|
|
109
|
+
const TOKEN_ALIASES = new Map(Object.keys(BUILTINS).flatMap((name) => [zh[`token.${name}`], en[`token.${name}`]].map((token) => [token, name])));
|
|
110
|
+
/**
|
|
111
|
+
* Resolve typed spelling against the current Session's effective definitions.
|
|
112
|
+
* @param token - typed name without its leading slash.
|
|
113
|
+
* @param descriptors - effective descriptors in the Session's ready catalog.
|
|
114
|
+
* @returns the matching descriptor; aliases never select an unrelated scoped override.
|
|
115
|
+
*/
|
|
116
|
+
function resolveCommand(token, descriptors) {
|
|
117
|
+
const exact = descriptors.find((descriptor) => descriptor.name === token);
|
|
118
|
+
if (exact !== void 0) return exact;
|
|
119
|
+
const name = TOKEN_ALIASES.get(token);
|
|
120
|
+
if (name === void 0) return void 0;
|
|
121
|
+
return descriptors.find((descriptor) => descriptor.definitionId === BUILTINS[name]);
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
12
124
|
//#region lib/types/client/directory.js
|
|
13
125
|
/** One session key's cache cell. */
|
|
14
126
|
var Entry = class {
|
|
@@ -35,15 +147,15 @@ window.__ModuleLoader__.load({
|
|
|
35
147
|
return this.entries.get(sessionId)?.state ?? "cold";
|
|
36
148
|
}
|
|
37
149
|
/**
|
|
38
|
-
* Synchronous
|
|
150
|
+
* Synchronous command lookup over one Session's ready catalog; exact names precede localized aliases.
|
|
39
151
|
* @param sessionId - session key.
|
|
40
|
-
* @param name - command
|
|
152
|
+
* @param name - typed command spelling without the leading slash.
|
|
41
153
|
* @returns the descriptor, or undefined when absent or the entry is not ready.
|
|
42
154
|
*/
|
|
43
155
|
resolve(sessionId, name) {
|
|
44
156
|
const entry = this.entries.get(sessionId);
|
|
45
157
|
if (entry === void 0 || entry.state !== "ready") return void 0;
|
|
46
|
-
return entry.commands
|
|
158
|
+
return resolveCommand(name, entry.commands);
|
|
47
159
|
}
|
|
48
160
|
/** Soft invalidation (commands-changed): background repull on every touched key; ready snapshots keep serving. */
|
|
49
161
|
invalidateAll() {
|
|
@@ -159,43 +271,6 @@ window.__ModuleLoader__.load({
|
|
|
159
271
|
return signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("command directory wait aborted");
|
|
160
272
|
}
|
|
161
273
|
//#endregion
|
|
162
|
-
//#region lib/types/client/locales.js
|
|
163
|
-
/** `command` namespace dictionaries (the popupSelect shell's copy). */
|
|
164
|
-
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
165
|
-
const zh = {
|
|
166
|
-
"description.compact": "压缩以上对话内容",
|
|
167
|
-
"description.export": "将当前会话内容导出为 ZIP",
|
|
168
|
-
"description.feedback": "发送关于当前会话的反馈",
|
|
169
|
-
"description.goal": "设置或查看长期任务目标",
|
|
170
|
-
"description.permission": "切换权限预设(沙箱模式与审批策略)",
|
|
171
|
-
"description.plan": "进入或退出计划模式",
|
|
172
|
-
"search.placeholder": "搜索…",
|
|
173
|
-
"search.aria": "筛选选项",
|
|
174
|
-
"status.loading": "正在加载选项…",
|
|
175
|
-
"status.applying": "正在应用…",
|
|
176
|
-
"status.empty": "无选项",
|
|
177
|
-
"overlay.aria": "/{command} 选项",
|
|
178
|
-
"listbox.aria": "/{command} 匹配项",
|
|
179
|
-
"notice.attachmentsUnsupported": "/{command} 不接受附件,请先移除附件"
|
|
180
|
-
};
|
|
181
|
-
/** English dictionary, checked complete against the zh key set. */
|
|
182
|
-
const en = {
|
|
183
|
-
"description.compact": "Compact older conversation history",
|
|
184
|
-
"description.export": "Download this Session log as a ZIP archive",
|
|
185
|
-
"description.feedback": "record feedback about this session",
|
|
186
|
-
"description.goal": "set or view the goal for a long-running task",
|
|
187
|
-
"description.permission": "Switch the permission preset (sandbox mode + approval policy)",
|
|
188
|
-
"description.plan": "Enter or leave plan mode",
|
|
189
|
-
"search.placeholder": "Search…",
|
|
190
|
-
"search.aria": "Filter options",
|
|
191
|
-
"status.loading": "Loading options…",
|
|
192
|
-
"status.applying": "Applying…",
|
|
193
|
-
"status.empty": "No options",
|
|
194
|
-
"overlay.aria": "/{command} options",
|
|
195
|
-
"listbox.aria": "/{command} matches",
|
|
196
|
-
"notice.attachmentsUnsupported": "/{command} does not accept attachments; remove them first"
|
|
197
|
-
};
|
|
198
|
-
//#endregion
|
|
199
274
|
//#region lib/types/client/popup.js
|
|
200
275
|
/**
|
|
201
276
|
* Headless popupSelect shell state: one controller per client
|
|
@@ -464,15 +539,92 @@ window.__ModuleLoader__.load({
|
|
|
464
539
|
}
|
|
465
540
|
};
|
|
466
541
|
//#endregion
|
|
542
|
+
//#region lib/types/client/presentation.js
|
|
543
|
+
/** Row names per section, highest usage first; rows outside both lists close the Commands section in catalog order. */
|
|
544
|
+
const SECTION_ROWS = {
|
|
545
|
+
add: [
|
|
546
|
+
"file",
|
|
547
|
+
"goal",
|
|
548
|
+
"plan",
|
|
549
|
+
"feedback"
|
|
550
|
+
],
|
|
551
|
+
commands: [
|
|
552
|
+
"compact",
|
|
553
|
+
"permission",
|
|
554
|
+
"model",
|
|
555
|
+
"export"
|
|
556
|
+
]
|
|
557
|
+
};
|
|
558
|
+
/** One built-in Host command's face, keyed by its dictionary entries. */
|
|
559
|
+
function hostFace(name, icon) {
|
|
560
|
+
return [name, {
|
|
561
|
+
label: `label.${name}`,
|
|
562
|
+
description: `description.${name}`,
|
|
563
|
+
icon
|
|
564
|
+
}];
|
|
565
|
+
}
|
|
566
|
+
/** Built-in Host commands whose client face this package owns. */
|
|
567
|
+
const HOST_FACES = new Map([
|
|
568
|
+
hostFace("goal", _deepseek_ai_dsh_client_ui_primitives.IconGoalOutline16),
|
|
569
|
+
hostFace("plan", _deepseek_ai_dsh_client_ui_primitives.IconPlanOutline14),
|
|
570
|
+
hostFace("feedback", _deepseek_ai_dsh_client_ui_primitives.IconPaperPlaneOutline14),
|
|
571
|
+
hostFace("compact", _deepseek_ai_dsh_client_ui_primitives.IconCompactOutline16),
|
|
572
|
+
hostFace("permission", _deepseek_ai_dsh_client_ui_primitives.IconShieldOutline16),
|
|
573
|
+
hostFace("export", _deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16)
|
|
574
|
+
]);
|
|
575
|
+
/**
|
|
576
|
+
* The localized menu face of a catalog row.
|
|
577
|
+
* @param descriptor - effective Host command descriptor.
|
|
578
|
+
* @param t - the `command` namespace translator.
|
|
579
|
+
* @returns title, description, and glyph for a built-in command; undefined
|
|
580
|
+
* for any other row, which keeps its catalog description.
|
|
581
|
+
*/
|
|
582
|
+
function builtinRowFace(descriptor, t) {
|
|
583
|
+
const name = builtinCommandName(descriptor);
|
|
584
|
+
const face = name === void 0 ? void 0 : HOST_FACES.get(name);
|
|
585
|
+
return face === void 0 ? void 0 : {
|
|
586
|
+
label: t(face.label),
|
|
587
|
+
description: t(face.description),
|
|
588
|
+
icon: face.icon
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Arrange the empty-query menu: the Add section, then the Commands section,
|
|
593
|
+
* each in usage order, with unlisted rows closing Commands in their input
|
|
594
|
+
* order; each row carries its section heading.
|
|
595
|
+
* @param rows - the visible candidates in catalog-then-contribution order.
|
|
596
|
+
* @param t - the `command` namespace translator.
|
|
597
|
+
* @returns the sectioned rows.
|
|
598
|
+
*/
|
|
599
|
+
function sectionRows(rows, t) {
|
|
600
|
+
const listed = new Set([...SECTION_ROWS.add, ...SECTION_ROWS.commands]);
|
|
601
|
+
const byName = new Map(rows.map((row) => [row.name, row]));
|
|
602
|
+
const pick = (names) => names.flatMap((name) => {
|
|
603
|
+
const row = byName.get(name);
|
|
604
|
+
return row === void 0 ? [] : [row];
|
|
605
|
+
});
|
|
606
|
+
const add = pick(SECTION_ROWS.add).map((row) => ({
|
|
607
|
+
...row,
|
|
608
|
+
section: t("section.add")
|
|
609
|
+
}));
|
|
610
|
+
const commands = [...pick(SECTION_ROWS.commands), ...rows.filter((row) => !listed.has(row.name))].map((row) => ({
|
|
611
|
+
...row,
|
|
612
|
+
section: t("section.commands")
|
|
613
|
+
}));
|
|
614
|
+
return [...add, ...commands];
|
|
615
|
+
}
|
|
616
|
+
//#endregion
|
|
467
617
|
//#region lib/types/client/service.js
|
|
468
618
|
/**
|
|
469
619
|
* CommandUiRuntime (`ctx.commandUi`): the '/' command source over the
|
|
470
620
|
* session-keyed directory, the client-contribution registry, and the
|
|
471
621
|
* per-session popupSelect controllers. Candidate synthesis merges the host
|
|
472
|
-
* catalog with contributions by availability,
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
622
|
+
* catalog with contributions by availability, gives built-in Host rows their
|
|
623
|
+
* localized face (presentation.ts), then position-filters; an empty query
|
|
624
|
+
* lists the Add and Commands sections in usage order, a typed query ranks
|
|
625
|
+
* every row by the `/` menu's shared name-and-label ranking (ui-primitives
|
|
626
|
+
* `rankByName`). A host/contribution name collision fails loud. Every
|
|
627
|
+
* execute addresses the session's agent by sessionId — sessions are always
|
|
476
628
|
* agent-backed.
|
|
477
629
|
*/
|
|
478
630
|
/** Recover the command name from a line the Host confirmed as executed. */
|
|
@@ -481,15 +633,6 @@ window.__ModuleLoader__.load({
|
|
|
481
633
|
const separator = trimmed.search(/\s/u);
|
|
482
634
|
return (separator === -1 ? trimmed : trimmed.slice(0, separator)).slice(1);
|
|
483
635
|
}
|
|
484
|
-
/** Locale keys for the canonical first-party Host command descriptions. */
|
|
485
|
-
const HOST_DESCRIPTION_KEYS = new Map([
|
|
486
|
-
["compact", "description.compact"],
|
|
487
|
-
["export", "description.export"],
|
|
488
|
-
["feedback", "description.feedback"],
|
|
489
|
-
["goal", "description.goal"],
|
|
490
|
-
["permission", "description.permission"],
|
|
491
|
-
["plan", "description.plan"]
|
|
492
|
-
]);
|
|
493
636
|
/** Command surface: session-keyed directory + '/' source + contribution registry + per-session popups. */
|
|
494
637
|
var CommandUiRuntime = class extends _deepseek_ai_cordis.Service {
|
|
495
638
|
static inject = [
|
|
@@ -583,6 +726,14 @@ window.__ModuleLoader__.load({
|
|
|
583
726
|
};
|
|
584
727
|
}
|
|
585
728
|
/**
|
|
729
|
+
* Close every open popup for a command whose options have become stale.
|
|
730
|
+
* Pending loads and confirmations lose their binding; drafts stay intact.
|
|
731
|
+
* @param name - command name without the leading slash.
|
|
732
|
+
*/
|
|
733
|
+
dismiss(name) {
|
|
734
|
+
for (const popup of this.live.popups.values()) if (popup.state.getSnapshot().command === name) popup.dismiss();
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
586
737
|
* Resolve the per-session popup controller (lazy; dies with the session
|
|
587
738
|
* scope). The controller's consume callback dispatches the scoped
|
|
588
739
|
* consume-token event back to this session; focusComposer reaches the
|
|
@@ -630,7 +781,11 @@ window.__ModuleLoader__.load({
|
|
|
630
781
|
if (this.focusHooks.get(id) === focus) this.focusHooks.delete(id);
|
|
631
782
|
};
|
|
632
783
|
}
|
|
633
|
-
/**
|
|
784
|
+
/**
|
|
785
|
+
* Menu candidates: host catalog + contribution availability, built-in rows
|
|
786
|
+
* localized, then position filtering; sections for an empty query, the
|
|
787
|
+
* shared name-and-label ranking for a typed one.
|
|
788
|
+
*/
|
|
634
789
|
async candidates(session, req) {
|
|
635
790
|
const list = await this.directory.ensureReady(session.sessionId, req.signal);
|
|
636
791
|
const rows = [];
|
|
@@ -639,7 +794,7 @@ window.__ModuleLoader__.load({
|
|
|
639
794
|
seen.add(c.name);
|
|
640
795
|
rows.push({
|
|
641
796
|
name: c.name,
|
|
642
|
-
description:
|
|
797
|
+
...builtinRowFace(c, this.t) ?? { description: c.description },
|
|
643
798
|
...c.input !== void 0 ? { hint: c.input.hint } : {}
|
|
644
799
|
});
|
|
645
800
|
}
|
|
@@ -648,15 +803,13 @@ window.__ModuleLoader__.load({
|
|
|
648
803
|
if (seen.has(contribution.name)) throw new Error(`ui-commands: contribution /${contribution.name} collides with a host command`);
|
|
649
804
|
rows.push({
|
|
650
805
|
name: contribution.name,
|
|
651
|
-
|
|
806
|
+
...contribution.label === void 0 ? {} : { label: contribution.label() },
|
|
807
|
+
...contribution.description === void 0 ? {} : { description: contribution.description() },
|
|
808
|
+
...contribution.icon === void 0 ? {} : { icon: contribution.icon }
|
|
652
809
|
});
|
|
653
810
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
/** Translate exact built-in Host copy while preserving scoped or third-party descriptors verbatim. */
|
|
657
|
-
hostDescription(command) {
|
|
658
|
-
const key = HOST_DESCRIPTION_KEYS.get(command.name);
|
|
659
|
-
return key !== void 0 && command.description === en[key] ? this.t(key) : command.description;
|
|
811
|
+
const visible = rows.filter((c) => req.position === "leading" || c.hint === void 0);
|
|
812
|
+
return req.query === "" ? sectionRows(visible, this.t) : (0, _deepseek_ai_dsh_client_ui_primitives.rankByName)(visible, req.query);
|
|
660
813
|
}
|
|
661
814
|
/** Decision table, menu column: contribution/decorated-host → popup or action; host input → claim; host bare → detached execute. */
|
|
662
815
|
dispatch(pick) {
|
|
@@ -679,7 +832,7 @@ window.__ModuleLoader__.load({
|
|
|
679
832
|
});
|
|
680
833
|
return "handled";
|
|
681
834
|
}
|
|
682
|
-
if (desc.input !== void 0) return { claim: this.leadingClaim(desc, pick.session) };
|
|
835
|
+
if (desc.input !== void 0) return { claim: this.leadingClaim(desc, pick.session, claimToken(desc, this.t)) };
|
|
683
836
|
this.consumeVia(pick.session.sessionId, {
|
|
684
837
|
via: "menu",
|
|
685
838
|
span: pick.span
|
|
@@ -690,11 +843,10 @@ window.__ModuleLoader__.load({
|
|
|
690
843
|
/** Decision table, space column: hot-key sync check; only host leadingInput claims. */
|
|
691
844
|
matchSpace(session, token) {
|
|
692
845
|
if (!token.startsWith("/")) return void 0;
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const desc = this.directory.resolve(session.sessionId, name);
|
|
846
|
+
if (this.live.contributions.has(token.slice(1))) return void 0;
|
|
847
|
+
const desc = this.directory.resolve(session.sessionId, token.slice(1));
|
|
696
848
|
if (desc === void 0 || desc.input === void 0) return void 0;
|
|
697
|
-
return { claim: this.leadingClaim(desc, session) };
|
|
849
|
+
return { claim: this.leadingClaim(desc, session, token.slice(1)) };
|
|
698
850
|
}
|
|
699
851
|
/**
|
|
700
852
|
* Decision table, enter column. Strong-waits the session's catalog (a
|
|
@@ -708,6 +860,9 @@ window.__ModuleLoader__.load({
|
|
|
708
860
|
* refusal so the machine surfaces one composer notice and the draft and
|
|
709
861
|
* attachments stay in place; nothing executes and nothing is dropped. An
|
|
710
862
|
* action submits nothing and runs regardless.
|
|
863
|
+
*
|
|
864
|
+
* A typed token is resolved through the localized claim tokens, so a line
|
|
865
|
+
* written as `/计划` reaches the `plan` descriptor and executes as `/plan`.
|
|
711
866
|
*/
|
|
712
867
|
async matchEnter(session, line, signal, envelope) {
|
|
713
868
|
const trimmed = line.trim();
|
|
@@ -715,24 +870,26 @@ window.__ModuleLoader__.load({
|
|
|
715
870
|
const ws = trimmed.search(/\s/);
|
|
716
871
|
const token = ws === -1 ? trimmed : trimmed.slice(0, ws);
|
|
717
872
|
const bare = ws === -1;
|
|
718
|
-
const
|
|
719
|
-
if (
|
|
873
|
+
const typedName = token.slice(1);
|
|
874
|
+
if (typedName === "") return void 0;
|
|
720
875
|
const refuseAttachments = () => {
|
|
721
|
-
throw new Error(this.t("notice.attachmentsUnsupported", { command:
|
|
876
|
+
throw new Error(this.t("notice.attachmentsUnsupported", { command: typedName }));
|
|
722
877
|
};
|
|
723
|
-
const contribution = this.live.contributions.get(
|
|
878
|
+
const contribution = this.live.contributions.get(typedName);
|
|
724
879
|
if (contribution !== void 0 && contribution.available(session)) {
|
|
725
880
|
if (!bare) return void 0;
|
|
726
881
|
if (envelope.attachments > 0 && contribution.ui.kind !== "action") refuseAttachments();
|
|
727
|
-
this.invoke(
|
|
882
|
+
this.invoke(typedName, contribution.ui, session, {
|
|
728
883
|
via: "enter",
|
|
729
884
|
token
|
|
730
885
|
});
|
|
731
886
|
return "handled";
|
|
732
887
|
}
|
|
733
888
|
await this.directory.ensureReady(session.sessionId, signal);
|
|
734
|
-
const desc = this.directory.resolve(session.sessionId,
|
|
889
|
+
const desc = this.directory.resolve(session.sessionId, typedName);
|
|
735
890
|
if (desc === void 0) return void 0;
|
|
891
|
+
const name = desc.name;
|
|
892
|
+
const canonical = `/${name}${trimmed.slice(token.length)}`;
|
|
736
893
|
if (bare) {
|
|
737
894
|
const decoration = this.live.decorations.get(name);
|
|
738
895
|
if (decoration !== void 0 && decoration.available(session)) {
|
|
@@ -746,7 +903,7 @@ window.__ModuleLoader__.load({
|
|
|
746
903
|
}
|
|
747
904
|
if (desc.input !== void 0) {
|
|
748
905
|
if (envelope.attachments > 0 && desc.input.attachments !== true) refuseAttachments();
|
|
749
|
-
return { claim: this.leadingClaim(desc, session) };
|
|
906
|
+
return { claim: this.leadingClaim(desc, session, token.slice(1)) };
|
|
750
907
|
}
|
|
751
908
|
if (!bare) return void 0;
|
|
752
909
|
if (envelope.attachments > 0) refuseAttachments();
|
|
@@ -754,7 +911,7 @@ window.__ModuleLoader__.load({
|
|
|
754
911
|
via: "enter",
|
|
755
912
|
token
|
|
756
913
|
});
|
|
757
|
-
this.runDetached(desc, session,
|
|
914
|
+
this.runDetached(desc, session, canonical);
|
|
758
915
|
return "handled";
|
|
759
916
|
}
|
|
760
917
|
/**
|
|
@@ -771,14 +928,22 @@ window.__ModuleLoader__.load({
|
|
|
771
928
|
if (actx === void 0) return;
|
|
772
929
|
this.popupFor(actx).open(name, ui, session, segment);
|
|
773
930
|
}
|
|
774
|
-
/**
|
|
775
|
-
|
|
776
|
-
|
|
931
|
+
/**
|
|
932
|
+
* Build the leadingInput claim. The composer keeps the claimed token in
|
|
933
|
+
* the draft and reads the arguments after it, so the token is the spelling
|
|
934
|
+
* the draft will carry: the locale's token for a menu pick, the typed
|
|
935
|
+
* spelling for Space and Enter. The command.execute submit transaction
|
|
936
|
+
* always sends the catalog name.
|
|
937
|
+
*/
|
|
938
|
+
leadingClaim(desc, session, shown) {
|
|
939
|
+
const token = `/${shown} `;
|
|
940
|
+
const line = `/${desc.name} `;
|
|
777
941
|
return {
|
|
942
|
+
name: desc.name,
|
|
778
943
|
token,
|
|
779
944
|
...desc.input !== void 0 ? { hint: desc.input.hint } : {},
|
|
780
945
|
...desc.input?.attachments === true ? { attachments: true } : {},
|
|
781
|
-
submit: (args, _actx, attachments) => this.execute(session,
|
|
946
|
+
submit: (args, _actx, attachments) => this.execute(session, line + args, attachments)
|
|
782
947
|
};
|
|
783
948
|
}
|
|
784
949
|
/**
|
|
@@ -890,7 +1055,7 @@ window.__ModuleLoader__.load({
|
|
|
890
1055
|
}
|
|
891
1056
|
//#endregion
|
|
892
1057
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-commands/src/client/PopupSelectView.module.css.mjs
|
|
893
|
-
const css = ".mufS8W_card{z-index:100;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);background:var(--dsw-specific-menu);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);min-width:min(220px,100%);max-width:100%;max-height:320px;box-shadow:var(--dsw-elevation-prominent);border:0;border-radius:20px;outline:none;flex-direction:column;padding:4px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;overflow:hidden}.mufS8W_viewport{flex-direction:column;min-height:0;display:flex;overflow-y:auto}.mufS8W_row{cursor:pointer;color:var(--dsw-alias-label-primary);border-radius:8px;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:flex}.mufS8W_rowActive{background:var(--dsw-alias-interactive-bg-hover)}.mufS8W_label{white-space:nowrap;text-overflow:ellipsis;
|
|
1058
|
+
const css = ".mufS8W_card{z-index:100;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);background:var(--dsw-specific-menu);--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);min-width:min(220px,100%);max-width:100%;max-height:320px;box-shadow:var(--dsw-elevation-prominent);border:0;border-radius:20px;outline:none;flex-direction:column;padding:4px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;overflow:hidden}.mufS8W_viewport{flex-direction:column;min-height:0;display:flex;overflow-y:auto}.mufS8W_row{cursor:pointer;color:var(--dsw-alias-label-primary);border-radius:8px;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:flex}.mufS8W_rowActive{background:var(--dsw-alias-interactive-bg-hover)}.mufS8W_label{flex:0 auto;align-items:baseline;gap:4px;min-width:0;display:flex}.mufS8W_labelText{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}.mufS8W_badge{color:var(--dsw-alias-label-tertiary);letter-spacing:.2px;flex:none;align-self:flex-start;margin-top:-1px;font-size:8px;font-weight:600;line-height:10px}.mufS8W_detail{min-width:0;color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-overflow:ellipsis;flex:1;font-size:12px;overflow:hidden}.mufS8W_check{color:var(--dsw-alias-label-primary);flex:none;margin-left:auto;display:inline-flex}.mufS8W_status{color:var(--dsw-alias-label-tertiary);padding:8px 10px;font-size:13px}.mufS8W_search{border:.5px solid var(--dsw-alias-border-inverted);color:var(--dsw-alias-label-primary);background:0 0;border-radius:8px;outline:none;margin:2px 2px 4px;padding:6px 8px;font-size:13px}.mufS8W_error{color:var(--dsw-alias-state-error-primary);align-items:center;gap:8px;padding:6px 8px;font-size:12px;display:flex}.mufS8W_errorText{text-overflow:ellipsis;flex:1;overflow:hidden}.mufS8W_retry{border:.5px solid var(--dsw-alias-border-inverted);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:6px;padding:2px 8px;font-size:12px}";
|
|
894
1059
|
const tagId = "@deepseek-ai/dsh-client-ui-commands/PopupSelectView.module.css";
|
|
895
1060
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
896
1061
|
const tag = document.createElement("style");
|
|
@@ -900,12 +1065,14 @@ window.__ModuleLoader__.load({
|
|
|
900
1065
|
document.head.appendChild(tag);
|
|
901
1066
|
}
|
|
902
1067
|
var PopupSelectView_module_css_default = {
|
|
1068
|
+
"badge": "mufS8W_badge",
|
|
903
1069
|
"card": "mufS8W_card",
|
|
904
1070
|
"check": "mufS8W_check",
|
|
905
1071
|
"detail": "mufS8W_detail",
|
|
906
1072
|
"error": "mufS8W_error",
|
|
907
1073
|
"errorText": "mufS8W_errorText",
|
|
908
1074
|
"label": "mufS8W_label",
|
|
1075
|
+
"labelText": "mufS8W_labelText",
|
|
909
1076
|
"retry": "mufS8W_retry",
|
|
910
1077
|
"row": "mufS8W_row",
|
|
911
1078
|
"rowActive": "mufS8W_rowActive",
|
|
@@ -1038,6 +1205,7 @@ window.__ModuleLoader__.load({
|
|
|
1038
1205
|
children: rows.map((option, index) => (0, react_jsx_runtime.jsxs)("div", {
|
|
1039
1206
|
role: "option",
|
|
1040
1207
|
"aria-selected": index === state.active,
|
|
1208
|
+
"aria-label": option.badge === void 0 ? void 0 : `${option.label} ${option.badge}`,
|
|
1041
1209
|
className: clsx(PopupSelectView_module_css_default.row, index === state.active && PopupSelectView_module_css_default.rowActive),
|
|
1042
1210
|
onClick: () => {
|
|
1043
1211
|
popup.select(index);
|
|
@@ -1046,9 +1214,15 @@ window.__ModuleLoader__.load({
|
|
|
1046
1214
|
popup.highlight(index);
|
|
1047
1215
|
},
|
|
1048
1216
|
children: [
|
|
1049
|
-
(0, react_jsx_runtime.
|
|
1217
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
1050
1218
|
className: PopupSelectView_module_css_default.label,
|
|
1051
|
-
children:
|
|
1219
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1220
|
+
className: PopupSelectView_module_css_default.labelText,
|
|
1221
|
+
children: option.label
|
|
1222
|
+
}), option.badge !== void 0 && (0, react_jsx_runtime.jsx)("sup", {
|
|
1223
|
+
className: PopupSelectView_module_css_default.badge,
|
|
1224
|
+
children: option.badge
|
|
1225
|
+
})]
|
|
1052
1226
|
}),
|
|
1053
1227
|
option.detail !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
1054
1228
|
className: PopupSelectView_module_css_default.detail,
|
|
@@ -1095,8 +1269,7 @@ window.__ModuleLoader__.load({
|
|
|
1095
1269
|
"locale"
|
|
1096
1270
|
];
|
|
1097
1271
|
/**
|
|
1098
|
-
*
|
|
1099
|
-
* into the input overlay once its declarer is up.
|
|
1272
|
+
* Mount the command service and its per-session popupSelect overlay.
|
|
1100
1273
|
* @param ctx - client root context.
|
|
1101
1274
|
*/
|
|
1102
1275
|
function apply(ctx) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Frozen contract of the client command surface. Types only. The
|
|
3
3
|
* CommandUiRuntime (`ctx.commandUi`) implements this face; business packages
|
|
4
|
-
* consume
|
|
4
|
+
* consume its registration and dismissal operations.
|
|
5
5
|
*/
|
|
6
|
+
import type { ComponentType } from 'react';
|
|
6
7
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
7
8
|
import type { ClientSessionContext } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
|
|
9
|
+
import type { IconProps } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
8
10
|
/** Copy for an option that must be acknowledged before onSelect can run. */
|
|
9
11
|
export interface SelectConfirmation {
|
|
10
12
|
readonly title: string;
|
|
@@ -17,6 +19,8 @@ export interface SelectConfirmation {
|
|
|
17
19
|
export interface SelectOption {
|
|
18
20
|
readonly id: string;
|
|
19
21
|
readonly label: string;
|
|
22
|
+
/** Optional short marker rendered as a superscript beside the label. */
|
|
23
|
+
readonly badge?: string;
|
|
20
24
|
readonly detail?: string;
|
|
21
25
|
readonly active?: boolean;
|
|
22
26
|
/** Optional in-page risk gate owned by the shared popup shell. */
|
|
@@ -35,8 +39,7 @@ export interface PopupSelectSpec {
|
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
41
|
* Business registration for the action command kind: a bare invocation
|
|
38
|
-
* consumes the trigger token and runs one client-side callback
|
|
39
|
-
* row opens the feedback dialog). It submits nothing, so an
|
|
42
|
+
* consumes the trigger token and runs one client-side callback. It submits nothing, so an
|
|
40
43
|
* attachment-carrying draft never refuses it.
|
|
41
44
|
*/
|
|
42
45
|
export interface ActionSpec {
|
|
@@ -53,13 +56,18 @@ export type CommandUiSpec = PopupSelectSpec | ActionSpec;
|
|
|
53
56
|
* One client-owned command contribution: a slash-menu entry whose behavior
|
|
54
57
|
* lives entirely on the client (no host descriptor). Merged with the host
|
|
55
58
|
* catalog by name — a collision with a host command fails loud at candidate
|
|
56
|
-
* synthesis, never shadows.
|
|
59
|
+
* synthesis, never shadows. Row copy is read on every candidate pass, so a
|
|
60
|
+
* locale change reaches the next menu open without re-registration.
|
|
57
61
|
*/
|
|
58
62
|
export interface CommandContribution {
|
|
59
63
|
/** Command name without the leading slash (unique across contributions). */
|
|
60
64
|
readonly name: string;
|
|
61
|
-
/**
|
|
62
|
-
|
|
65
|
+
/** Localized menu row title; the name itself when absent. */
|
|
66
|
+
label?(): string;
|
|
67
|
+
/** Localized menu row description; the row shows none when absent. */
|
|
68
|
+
description?(): string;
|
|
69
|
+
/** Menu row glyph from the shared icon set. */
|
|
70
|
+
readonly icon?: ComponentType<IconProps>;
|
|
63
71
|
/** Capability filter, called with a fresh projection per candidate pass. */
|
|
64
72
|
available(session: ClientSessionContext): boolean;
|
|
65
73
|
/** The command's UI behavior. */
|
|
@@ -94,6 +102,8 @@ export interface CommandUiContract {
|
|
|
94
102
|
* Duplicate names throw at registration.
|
|
95
103
|
*/
|
|
96
104
|
decorate(decoration: CommandDecoration): () => void;
|
|
105
|
+
/** Close this command's open popups and confirmations without consuming composer drafts. */
|
|
106
|
+
dismiss(name: string): void;
|
|
97
107
|
/** Resolve the per-session popup controller for one session scope (wiring/overlay layer). */
|
|
98
108
|
popupFor(actx: ClientContext): unknown;
|
|
99
109
|
}
|
|
@@ -28,9 +28,9 @@ export declare class CommandDirectory {
|
|
|
28
28
|
*/
|
|
29
29
|
status(sessionId: SessionId): DirectoryStatus;
|
|
30
30
|
/**
|
|
31
|
-
* Synchronous
|
|
31
|
+
* Synchronous command lookup over one Session's ready catalog; exact names precede localized aliases.
|
|
32
32
|
* @param sessionId - session key.
|
|
33
|
-
* @param name - command
|
|
33
|
+
* @param name - typed command spelling without the leading slash.
|
|
34
34
|
* @returns the descriptor, or undefined when absent or the entry is not ready.
|
|
35
35
|
*/
|
|
36
36
|
resolve(sessionId: SessionId, name: string): CommandDescriptor | undefined;
|
|
@@ -23,15 +23,14 @@ declare module '@deepseek-ai/cordis' {
|
|
|
23
23
|
}
|
|
24
24
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
25
25
|
interface LocaleNamespaceMap {
|
|
26
|
-
/** The popupSelect shell's copy. */
|
|
26
|
+
/** The menu rows' and the popupSelect shell's copy. */
|
|
27
27
|
command: CommandKey;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
/** Required services: the '/' source registry, session scopes, commands Remote, and locale registry. */
|
|
31
31
|
export declare const inject: string[];
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
* into the input overlay once its declarer is up.
|
|
33
|
+
* Mount the command service and its per-session popupSelect overlay.
|
|
35
34
|
* @param ctx - client root context.
|
|
36
35
|
*/
|
|
37
36
|
export declare function apply(ctx: ClientContext): void;
|
|
@@ -1,12 +1,31 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* `command` namespace dictionaries: the composer menu's section headings,
|
|
3
|
+
* the client face (title, description, claim token) of the built-in Host
|
|
4
|
+
* commands whose catalog descriptors carry English text only, and the
|
|
5
|
+
* popupSelect shell's copy.
|
|
6
|
+
*/
|
|
2
7
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
8
|
export declare const zh: {
|
|
4
|
-
'
|
|
5
|
-
'
|
|
6
|
-
'
|
|
9
|
+
'section.add': string;
|
|
10
|
+
'section.commands': string;
|
|
11
|
+
'label.goal': string;
|
|
12
|
+
'label.plan': string;
|
|
13
|
+
'label.feedback': string;
|
|
14
|
+
'label.compact': string;
|
|
15
|
+
'label.permission': string;
|
|
16
|
+
'label.export': string;
|
|
7
17
|
'description.goal': string;
|
|
8
|
-
'description.permission': string;
|
|
9
18
|
'description.plan': string;
|
|
19
|
+
'description.feedback': string;
|
|
20
|
+
'description.compact': string;
|
|
21
|
+
'description.permission': string;
|
|
22
|
+
'description.export': string;
|
|
23
|
+
'token.goal': string;
|
|
24
|
+
'token.plan': string;
|
|
25
|
+
'token.feedback': string;
|
|
26
|
+
'token.compact': string;
|
|
27
|
+
'token.permission': string;
|
|
28
|
+
'token.export': string;
|
|
10
29
|
'search.placeholder': string;
|
|
11
30
|
'search.aria': string;
|
|
12
31
|
'status.loading': string;
|
|
@@ -20,12 +39,26 @@ export declare const zh: {
|
|
|
20
39
|
export type CommandKey = keyof typeof zh;
|
|
21
40
|
/** English dictionary, checked complete against the zh key set. */
|
|
22
41
|
export declare const en: {
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
42
|
+
'section.add': string;
|
|
43
|
+
'section.commands': string;
|
|
44
|
+
'label.goal': string;
|
|
45
|
+
'label.plan': string;
|
|
46
|
+
'label.feedback': string;
|
|
47
|
+
'label.compact': string;
|
|
48
|
+
'label.permission': string;
|
|
49
|
+
'label.export': string;
|
|
26
50
|
'description.goal': string;
|
|
27
|
-
'description.permission': string;
|
|
28
51
|
'description.plan': string;
|
|
52
|
+
'description.feedback': string;
|
|
53
|
+
'description.compact': string;
|
|
54
|
+
'description.permission': string;
|
|
55
|
+
'description.export': string;
|
|
56
|
+
'token.goal': string;
|
|
57
|
+
'token.plan': string;
|
|
58
|
+
'token.feedback': string;
|
|
59
|
+
'token.compact': string;
|
|
60
|
+
'token.permission': string;
|
|
61
|
+
'token.export': string;
|
|
29
62
|
'search.placeholder': string;
|
|
30
63
|
'search.aria': string;
|
|
31
64
|
'status.loading': string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { InputTriggerCandidate } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
|
|
2
|
+
import type { TranslateNS } from '@deepseek-ai/dsh-client-locale/client';
|
|
3
|
+
import type { CommandDescriptor } from '@deepseek-ai/dsh-commands/types';
|
|
4
|
+
/** The menu's two sections. */
|
|
5
|
+
export type MenuSection = 'add' | 'commands';
|
|
6
|
+
/**
|
|
7
|
+
* The localized menu face of a catalog row.
|
|
8
|
+
* @param descriptor - effective Host command descriptor.
|
|
9
|
+
* @param t - the `command` namespace translator.
|
|
10
|
+
* @returns title, description, and glyph for a built-in command; undefined
|
|
11
|
+
* for any other row, which keeps its catalog description.
|
|
12
|
+
*/
|
|
13
|
+
export declare function builtinRowFace(descriptor: CommandDescriptor, t: TranslateNS<'command'>): Pick<InputTriggerCandidate, 'label' | 'description' | 'icon'> | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Arrange the empty-query menu: the Add section, then the Commands section,
|
|
16
|
+
* each in usage order, with unlisted rows closing Commands in their input
|
|
17
|
+
* order; each row carries its section heading.
|
|
18
|
+
* @param rows - the visible candidates in catalog-then-contribution order.
|
|
19
|
+
* @param t - the `command` namespace translator.
|
|
20
|
+
* @returns the sectioned rows.
|
|
21
|
+
*/
|
|
22
|
+
export declare function sectionRows(rows: readonly InputTriggerCandidate[], t: TranslateNS<'command'>): readonly InputTriggerCandidate[];
|
|
23
|
+
//# sourceMappingURL=presentation.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Command identity and localized input spelling over the effective Host catalog. */
|
|
2
|
+
import type { CommandDescriptor } from '@deepseek-ai/dsh-commands/types';
|
|
3
|
+
import type { TranslateNS } from '@deepseek-ai/dsh-client-locale/client';
|
|
4
|
+
declare const BUILTINS: {
|
|
5
|
+
readonly goal: "@deepseek-ai/dsh-command-goal";
|
|
6
|
+
readonly plan: "@deepseek-ai/dsh-plan-mode";
|
|
7
|
+
readonly feedback: "@deepseek-ai/dsh-command-feedback";
|
|
8
|
+
readonly compact: "@deepseek-ai/dsh-command-compact";
|
|
9
|
+
readonly permission: "@deepseek-ai/dsh-permission-presets";
|
|
10
|
+
readonly export: "@deepseek-ai/dsh-session-log-export";
|
|
11
|
+
};
|
|
12
|
+
/** Names whose first-party definitions have localized client presentation. */
|
|
13
|
+
export type BuiltinCommandName = keyof typeof BUILTINS;
|
|
14
|
+
/**
|
|
15
|
+
* Identify a first-party definition without interpreting its display copy.
|
|
16
|
+
* @param descriptor - effective Host descriptor after scoped shadowing.
|
|
17
|
+
* @returns its first-party name, or undefined for another definition.
|
|
18
|
+
*/
|
|
19
|
+
export declare function builtinCommandName(descriptor: CommandDescriptor): BuiltinCommandName | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Select the input spelling for a menu-picked command.
|
|
22
|
+
* @param descriptor - effective Host descriptor.
|
|
23
|
+
* @param t - command-namespace translator.
|
|
24
|
+
* @returns localized spelling for a known definition, otherwise its registered name.
|
|
25
|
+
*/
|
|
26
|
+
export declare function claimToken(descriptor: CommandDescriptor, t: TranslateNS<'command'>): string;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve typed spelling against the current Session's effective definitions.
|
|
29
|
+
* @param token - typed name without its leading slash.
|
|
30
|
+
* @param descriptors - effective descriptors in the Session's ready catalog.
|
|
31
|
+
* @returns the matching descriptor; aliases never select an unrelated scoped override.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveCommand(token: string, descriptors: readonly CommandDescriptor[]): CommandDescriptor | undefined;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=resolution.d.ts.map
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
* CommandUiRuntime (`ctx.commandUi`): the '/' command source over the
|
|
3
3
|
* session-keyed directory, the client-contribution registry, and the
|
|
4
4
|
* per-session popupSelect controllers. Candidate synthesis merges the host
|
|
5
|
-
* catalog with contributions by availability,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* catalog with contributions by availability, gives built-in Host rows their
|
|
6
|
+
* localized face (presentation.ts), then position-filters; an empty query
|
|
7
|
+
* lists the Add and Commands sections in usage order, a typed query ranks
|
|
8
|
+
* every row by the `/` menu's shared name-and-label ranking (ui-primitives
|
|
9
|
+
* `rankByName`). A host/contribution name collision fails loud. Every
|
|
10
|
+
* execute addresses the session's agent by sessionId — sessions are always
|
|
9
11
|
* agent-backed.
|
|
10
12
|
*/
|
|
11
13
|
import { Service } from '@deepseek-ai/cordis';
|
|
@@ -56,6 +58,12 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
56
58
|
* @returns the disposer removing the registration.
|
|
57
59
|
*/
|
|
58
60
|
decorate(decoration: CommandDecoration): () => void;
|
|
61
|
+
/**
|
|
62
|
+
* Close every open popup for a command whose options have become stale.
|
|
63
|
+
* Pending loads and confirmations lose their binding; drafts stay intact.
|
|
64
|
+
* @param name - command name without the leading slash.
|
|
65
|
+
*/
|
|
66
|
+
dismiss(name: string): void;
|
|
59
67
|
/**
|
|
60
68
|
* Resolve the per-session popup controller (lazy; dies with the session
|
|
61
69
|
* scope). The controller's consume callback dispatches the scoped
|
|
@@ -74,10 +82,12 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
74
82
|
* @returns the unbind disposer.
|
|
75
83
|
*/
|
|
76
84
|
bindComposerFocus(id: SessionId, focus: () => void): () => void;
|
|
77
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Menu candidates: host catalog + contribution availability, built-in rows
|
|
87
|
+
* localized, then position filtering; sections for an empty query, the
|
|
88
|
+
* shared name-and-label ranking for a typed one.
|
|
89
|
+
*/
|
|
78
90
|
private candidates;
|
|
79
|
-
/** Translate exact built-in Host copy while preserving scoped or third-party descriptors verbatim. */
|
|
80
|
-
private hostDescription;
|
|
81
91
|
/** Decision table, menu column: contribution/decorated-host → popup or action; host input → claim; host bare → detached execute. */
|
|
82
92
|
private dispatch;
|
|
83
93
|
/** Decision table, space column: hot-key sync check; only host leadingInput claims. */
|
|
@@ -94,6 +104,9 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
94
104
|
* refusal so the machine surfaces one composer notice and the draft and
|
|
95
105
|
* attachments stay in place; nothing executes and nothing is dropped. An
|
|
96
106
|
* action submits nothing and runs regardless.
|
|
107
|
+
*
|
|
108
|
+
* A typed token is resolved through the localized claim tokens, so a line
|
|
109
|
+
* written as `/计划` reaches the `plan` descriptor and executes as `/plan`.
|
|
97
110
|
*/
|
|
98
111
|
private matchEnter;
|
|
99
112
|
/**
|
|
@@ -101,7 +114,13 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
101
114
|
* session's popup, or consume the token and run the action.
|
|
102
115
|
*/
|
|
103
116
|
private invoke;
|
|
104
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Build the leadingInput claim. The composer keeps the claimed token in
|
|
119
|
+
* the draft and reads the arguments after it, so the token is the spelling
|
|
120
|
+
* the draft will carry: the locale's token for a menu pick, the typed
|
|
121
|
+
* spelling for Space and Enter. The command.execute submit transaction
|
|
122
|
+
* always sends the catalog name.
|
|
123
|
+
*/
|
|
105
124
|
private leadingClaim;
|
|
106
125
|
/**
|
|
107
126
|
* The command.execute transaction, addressed to the session's agent — pure
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-commands",
|
|
3
3
|
"description": "Client command surface: global directory cache, '/' source, three command UI kinds, popupSelect registry",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -44,21 +44,21 @@
|
|
|
44
44
|
"@types/react": "~18.3.1",
|
|
45
45
|
"react": "^18.2.0",
|
|
46
46
|
"clsx": "^2.0.0",
|
|
47
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.
|
|
48
|
-
"@deepseek-ai/dsh-client-
|
|
49
|
-
"@deepseek-ai/dsh-client-
|
|
50
|
-
"@deepseek-ai/dsh-client-
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-
|
|
52
|
-
"@deepseek-ai/dsh-client-ui-
|
|
53
|
-
"@deepseek-ai/dsh-client-
|
|
54
|
-
"@deepseek-ai/dsh-commands": "^0.1.
|
|
47
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.1",
|
|
48
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.1",
|
|
49
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.1",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.1",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.1",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.1",
|
|
53
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.6-alpha.1",
|
|
54
|
+
"@deepseek-ai/dsh-commands": "^0.1.6-alpha.1",
|
|
55
55
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
56
|
-
"@deepseek-ai/dsh-api-session-controller": "^0.1.
|
|
57
|
-
"@deepseek-ai/dsh-client-store": "^0.1.
|
|
58
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
59
|
-
"@deepseek-ai/dsh-client-ui-
|
|
60
|
-
"@deepseek-ai/dsh-client-ui-
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-
|
|
56
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.1",
|
|
57
|
+
"@deepseek-ai/dsh-client-store": "^0.1.6-alpha.1",
|
|
58
|
+
"@deepseek-ai/dsh-session": "^0.1.6-alpha.1",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.6-alpha.1",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.1",
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.1"
|
|
62
62
|
},
|
|
63
63
|
"files": [
|
|
64
64
|
"lib/index.js",
|