@deepseek-ai/dsh-client-ui-commands 0.1.5-rc.2 → 0.1.6-alpha.2
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 +361 -115
- package/lib/types/client/contract.d.ts +22 -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/popup.d.ts +8 -2
- 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 -16
- package/package.json +16 -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: f8cba29179f9960042a4a9e67e86496591a76d08
|
|
6
|
+
README.zh.md: 1cea3ae7aea857f2b2d219abf0a7192c2f222792
|
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 popup holds composer focus: typing filters the loaded rows locally, `↑`/`↓` walk them, Enter and `Tab` accept the highlighted row, and Escape and `Shift+Tab` return to the composer. The highlight opens on the row the options mark as the session's current value, so accepting on a freshly opened panel confirms it. 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 焦点:键入即在已加载的行上本地筛选,`↑`/`↓` 在行间移动,回车与 `Tab` 接受高亮行,Escape 与 `Shift+Tab` 把焦点还给 composer。高亮落在选项标记为会话当前值的行上,因此在刚打开的弹窗上接受即确认当前值。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,181 @@ 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 ../../util/values/src/index.ts
|
|
13
|
+
/**
|
|
14
|
+
* Weak-key lookup with a strongly retained iterable set of associated values.
|
|
15
|
+
*
|
|
16
|
+
* Each value must belong to only one key. The container performs no automatic
|
|
17
|
+
* cleanup; owners delete associations or clear the container at lifecycle end.
|
|
18
|
+
*/
|
|
19
|
+
var WeakMapWithValues = class {
|
|
20
|
+
keys = /* @__PURE__ */ new WeakMap();
|
|
21
|
+
valueSet = /* @__PURE__ */ new Set();
|
|
22
|
+
/** Live strongly retained values in insertion order. */
|
|
23
|
+
values = this.valueSet;
|
|
24
|
+
/**
|
|
25
|
+
* Read the value associated with a key.
|
|
26
|
+
* @param key - weakly held lookup key.
|
|
27
|
+
* @returns the associated value, or absence.
|
|
28
|
+
*/
|
|
29
|
+
get(key) {
|
|
30
|
+
return this.keys.get(key);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Test whether a key has an association.
|
|
34
|
+
* @param key - weakly held lookup key.
|
|
35
|
+
* @returns whether the key is present.
|
|
36
|
+
*/
|
|
37
|
+
has(key) {
|
|
38
|
+
return this.keys.has(key);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Associate one key with one caller-unique value.
|
|
42
|
+
* @param key - weakly held lookup key.
|
|
43
|
+
* @param value - strongly retained value that belongs to no other key.
|
|
44
|
+
* @returns this container.
|
|
45
|
+
*/
|
|
46
|
+
set(key, value) {
|
|
47
|
+
if (this.keys.has(key)) {
|
|
48
|
+
const previous = this.keys.get(key);
|
|
49
|
+
if (previous === value) return this;
|
|
50
|
+
this.valueSet.delete(previous);
|
|
51
|
+
}
|
|
52
|
+
this.keys.set(key, value);
|
|
53
|
+
this.valueSet.add(value);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Remove one association and its strongly retained value.
|
|
58
|
+
* @param key - weakly held lookup key.
|
|
59
|
+
* @returns whether an association was removed.
|
|
60
|
+
*/
|
|
61
|
+
delete(key) {
|
|
62
|
+
if (!this.keys.has(key)) return false;
|
|
63
|
+
const value = this.keys.get(key);
|
|
64
|
+
const deleted = this.keys.delete(key);
|
|
65
|
+
this.valueSet.delete(value);
|
|
66
|
+
return deleted;
|
|
67
|
+
}
|
|
68
|
+
/** Remove every association and strongly retained value. */
|
|
69
|
+
clear() {
|
|
70
|
+
this.keys = /* @__PURE__ */ new WeakMap();
|
|
71
|
+
this.valueSet.clear();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region lib/types/client/locales.js
|
|
76
|
+
/**
|
|
77
|
+
* `command` namespace dictionaries: the composer menu's section headings,
|
|
78
|
+
* the client face (title, description, claim token) of the built-in Host
|
|
79
|
+
* commands whose catalog descriptors carry English text only, and the
|
|
80
|
+
* popupSelect shell's copy.
|
|
81
|
+
*/
|
|
82
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
83
|
+
const zh = {
|
|
84
|
+
"section.add": "添加",
|
|
85
|
+
"section.commands": "指令",
|
|
86
|
+
"label.goal": "目标",
|
|
87
|
+
"label.plan": "计划",
|
|
88
|
+
"label.feedback": "反馈",
|
|
89
|
+
"label.compact": "压缩",
|
|
90
|
+
"label.permission": "权限",
|
|
91
|
+
"label.export": "下载日志",
|
|
92
|
+
"description.goal": "设置或查看长期任务目标",
|
|
93
|
+
"description.plan": "进入或退出计划模式",
|
|
94
|
+
"description.feedback": "发送关于当前会话的反馈",
|
|
95
|
+
"description.compact": "压缩以上对话内容",
|
|
96
|
+
"description.permission": "切换权限预设(沙箱模式与审批策略)",
|
|
97
|
+
"description.export": "将当前会话内容导出为 ZIP",
|
|
98
|
+
"token.goal": "目标",
|
|
99
|
+
"token.plan": "计划",
|
|
100
|
+
"token.feedback": "反馈",
|
|
101
|
+
"token.compact": "压缩",
|
|
102
|
+
"token.permission": "权限",
|
|
103
|
+
"token.export": "导出",
|
|
104
|
+
"search.placeholder": "搜索…",
|
|
105
|
+
"search.aria": "筛选选项",
|
|
106
|
+
"status.loading": "正在加载选项…",
|
|
107
|
+
"status.applying": "正在应用…",
|
|
108
|
+
"status.empty": "无选项",
|
|
109
|
+
"overlay.aria": "/{command} 选项",
|
|
110
|
+
"listbox.aria": "/{command} 匹配项",
|
|
111
|
+
"notice.attachmentsUnsupported": "/{command} 不接受附件,请先移除附件"
|
|
112
|
+
};
|
|
113
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
114
|
+
const en = {
|
|
115
|
+
"section.add": "Add",
|
|
116
|
+
"section.commands": "Commands",
|
|
117
|
+
"label.goal": "Goal",
|
|
118
|
+
"label.plan": "Plan",
|
|
119
|
+
"label.feedback": "Feedback",
|
|
120
|
+
"label.compact": "Compact",
|
|
121
|
+
"label.permission": "Permission",
|
|
122
|
+
"label.export": "Export",
|
|
123
|
+
"description.goal": "Set or view the goal for a long-running task",
|
|
124
|
+
"description.plan": "Enter or leave plan mode",
|
|
125
|
+
"description.feedback": "Record feedback about this session",
|
|
126
|
+
"description.compact": "Compact older conversation history",
|
|
127
|
+
"description.permission": "Switch the permission preset (sandbox mode + approval policy)",
|
|
128
|
+
"description.export": "Download this Session log as a ZIP archive",
|
|
129
|
+
"token.goal": "goal",
|
|
130
|
+
"token.plan": "plan",
|
|
131
|
+
"token.feedback": "feedback",
|
|
132
|
+
"token.compact": "compact",
|
|
133
|
+
"token.permission": "permission",
|
|
134
|
+
"token.export": "export",
|
|
135
|
+
"search.placeholder": "Search…",
|
|
136
|
+
"search.aria": "Filter options",
|
|
137
|
+
"status.loading": "Loading options…",
|
|
138
|
+
"status.applying": "Applying…",
|
|
139
|
+
"status.empty": "No options",
|
|
140
|
+
"overlay.aria": "/{command} options",
|
|
141
|
+
"listbox.aria": "/{command} matches",
|
|
142
|
+
"notice.attachmentsUnsupported": "/{command} does not accept attachments; remove them first"
|
|
143
|
+
};
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region lib/types/client/resolution.js
|
|
146
|
+
const BUILTINS = {
|
|
147
|
+
goal: "@deepseek-ai/dsh-command-goal",
|
|
148
|
+
plan: "@deepseek-ai/dsh-plan-mode",
|
|
149
|
+
feedback: "@deepseek-ai/dsh-command-feedback",
|
|
150
|
+
compact: "@deepseek-ai/dsh-command-compact",
|
|
151
|
+
permission: "@deepseek-ai/dsh-permission-presets",
|
|
152
|
+
export: "@deepseek-ai/dsh-session-log-export"
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Identify a first-party definition without interpreting its display copy.
|
|
156
|
+
* @param descriptor - effective Host descriptor after scoped shadowing.
|
|
157
|
+
* @returns its first-party name, or undefined for another definition.
|
|
158
|
+
*/
|
|
159
|
+
function builtinCommandName(descriptor) {
|
|
160
|
+
return Object.keys(BUILTINS).find((name) => descriptor.definitionId === BUILTINS[name]);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Select the input spelling for a menu-picked command.
|
|
164
|
+
* @param descriptor - effective Host descriptor.
|
|
165
|
+
* @param t - command-namespace translator.
|
|
166
|
+
* @returns localized spelling for a known definition, otherwise its registered name.
|
|
167
|
+
*/
|
|
168
|
+
function claimToken(descriptor, t) {
|
|
169
|
+
const name = builtinCommandName(descriptor);
|
|
170
|
+
return name === void 0 ? descriptor.name : t(`token.${name}`);
|
|
171
|
+
}
|
|
172
|
+
const TOKEN_ALIASES = new Map(Object.keys(BUILTINS).flatMap((name) => [zh[`token.${name}`], en[`token.${name}`]].map((token) => [token, name])));
|
|
173
|
+
/**
|
|
174
|
+
* Resolve typed spelling against the current Session's effective definitions.
|
|
175
|
+
* @param token - typed name without its leading slash.
|
|
176
|
+
* @param descriptors - effective descriptors in the Session's ready catalog.
|
|
177
|
+
* @returns the matching descriptor; aliases never select an unrelated scoped override.
|
|
178
|
+
*/
|
|
179
|
+
function resolveCommand(token, descriptors) {
|
|
180
|
+
const exact = descriptors.find((descriptor) => descriptor.name === token);
|
|
181
|
+
if (exact !== void 0) return exact;
|
|
182
|
+
const name = TOKEN_ALIASES.get(token);
|
|
183
|
+
if (name === void 0) return void 0;
|
|
184
|
+
return descriptors.find((descriptor) => descriptor.definitionId === BUILTINS[name]);
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
12
187
|
//#region lib/types/client/directory.js
|
|
13
188
|
/** One session key's cache cell. */
|
|
14
189
|
var Entry = class {
|
|
@@ -35,15 +210,15 @@ window.__ModuleLoader__.load({
|
|
|
35
210
|
return this.entries.get(sessionId)?.state ?? "cold";
|
|
36
211
|
}
|
|
37
212
|
/**
|
|
38
|
-
* Synchronous
|
|
213
|
+
* Synchronous command lookup over one Session's ready catalog; exact names precede localized aliases.
|
|
39
214
|
* @param sessionId - session key.
|
|
40
|
-
* @param name - command
|
|
215
|
+
* @param name - typed command spelling without the leading slash.
|
|
41
216
|
* @returns the descriptor, or undefined when absent or the entry is not ready.
|
|
42
217
|
*/
|
|
43
218
|
resolve(sessionId, name) {
|
|
44
219
|
const entry = this.entries.get(sessionId);
|
|
45
220
|
if (entry === void 0 || entry.state !== "ready") return void 0;
|
|
46
|
-
return entry.commands
|
|
221
|
+
return resolveCommand(name, entry.commands);
|
|
47
222
|
}
|
|
48
223
|
/** Soft invalidation (commands-changed): background repull on every touched key; ready snapshots keep serving. */
|
|
49
224
|
invalidateAll() {
|
|
@@ -159,43 +334,6 @@ window.__ModuleLoader__.load({
|
|
|
159
334
|
return signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("command directory wait aborted");
|
|
160
335
|
}
|
|
161
336
|
//#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
337
|
//#region lib/types/client/popup.js
|
|
200
338
|
/**
|
|
201
339
|
* Headless popupSelect shell state: one controller per client
|
|
@@ -232,6 +370,19 @@ window.__ModuleLoader__.load({
|
|
|
232
370
|
if (query === "") return options;
|
|
233
371
|
return options.filter((o) => o.label.toLowerCase().includes(query) || (o.detail?.toLowerCase().includes(query) ?? false));
|
|
234
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Highlight index for a freshly loaded row list: the row marked as the current
|
|
375
|
+
* value when the live search still shows it, else the top row. Opening parks
|
|
376
|
+
* the highlight on the value the session already uses, so an accept gesture
|
|
377
|
+
* made without looking confirms that value instead of the topmost row.
|
|
378
|
+
* @param options - the loaded rows.
|
|
379
|
+
* @param search - the shell's live filter text (non-empty after a retry).
|
|
380
|
+
* @returns index into the filtered rows.
|
|
381
|
+
*/
|
|
382
|
+
function currentIndex(options, search) {
|
|
383
|
+
const at = filterOptions(options, search).findIndex((option) => option.active === true);
|
|
384
|
+
return at === -1 ? 0 : at;
|
|
385
|
+
}
|
|
235
386
|
/** The shell's error-strip line for a settlement failure. */
|
|
236
387
|
function errorText(error) {
|
|
237
388
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -283,11 +434,12 @@ window.__ModuleLoader__.load({
|
|
|
283
434
|
load(binding) {
|
|
284
435
|
binding.spec.options(binding.context, binding.abort.signal).then((options) => {
|
|
285
436
|
if (this.binding !== binding) return;
|
|
437
|
+
const current = this.state.getSnapshot();
|
|
286
438
|
this.state.set({
|
|
287
|
-
...
|
|
439
|
+
...current,
|
|
288
440
|
status: "ready",
|
|
289
441
|
options,
|
|
290
|
-
active:
|
|
442
|
+
active: currentIndex(options, current.search),
|
|
291
443
|
error: null
|
|
292
444
|
});
|
|
293
445
|
}, (error) => {
|
|
@@ -316,7 +468,8 @@ window.__ModuleLoader__.load({
|
|
|
316
468
|
}
|
|
317
469
|
/**
|
|
318
470
|
* Replace the local search text (pure local filter — the provider is never
|
|
319
|
-
* re-queried) and rebase the highlight
|
|
471
|
+
* re-queried) and rebase the highlight to the top of the new filtered list:
|
|
472
|
+
* typing searches for something other than the current value.
|
|
320
473
|
* @param search - the shell search input's text.
|
|
321
474
|
*/
|
|
322
475
|
setSearch(search) {
|
|
@@ -464,15 +617,92 @@ window.__ModuleLoader__.load({
|
|
|
464
617
|
}
|
|
465
618
|
};
|
|
466
619
|
//#endregion
|
|
620
|
+
//#region lib/types/client/presentation.js
|
|
621
|
+
/** Row names per section, highest usage first; rows outside both lists close the Commands section in catalog order. */
|
|
622
|
+
const SECTION_ROWS = {
|
|
623
|
+
add: [
|
|
624
|
+
"file",
|
|
625
|
+
"goal",
|
|
626
|
+
"plan",
|
|
627
|
+
"feedback"
|
|
628
|
+
],
|
|
629
|
+
commands: [
|
|
630
|
+
"compact",
|
|
631
|
+
"permission",
|
|
632
|
+
"model",
|
|
633
|
+
"export"
|
|
634
|
+
]
|
|
635
|
+
};
|
|
636
|
+
/** One built-in Host command's face, keyed by its dictionary entries. */
|
|
637
|
+
function hostFace(name, icon) {
|
|
638
|
+
return [name, {
|
|
639
|
+
label: `label.${name}`,
|
|
640
|
+
description: `description.${name}`,
|
|
641
|
+
icon
|
|
642
|
+
}];
|
|
643
|
+
}
|
|
644
|
+
/** Built-in Host commands whose client face this package owns. */
|
|
645
|
+
const HOST_FACES = new Map([
|
|
646
|
+
hostFace("goal", _deepseek_ai_dsh_client_ui_primitives.IconGoalOutline16),
|
|
647
|
+
hostFace("plan", _deepseek_ai_dsh_client_ui_primitives.IconPlanOutline14),
|
|
648
|
+
hostFace("feedback", _deepseek_ai_dsh_client_ui_primitives.IconPaperPlaneOutline14),
|
|
649
|
+
hostFace("compact", _deepseek_ai_dsh_client_ui_primitives.IconCompactOutline16),
|
|
650
|
+
hostFace("permission", _deepseek_ai_dsh_client_ui_primitives.IconShieldOutline16),
|
|
651
|
+
hostFace("export", _deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16)
|
|
652
|
+
]);
|
|
653
|
+
/**
|
|
654
|
+
* The localized menu face of a catalog row.
|
|
655
|
+
* @param descriptor - effective Host command descriptor.
|
|
656
|
+
* @param t - the `command` namespace translator.
|
|
657
|
+
* @returns title, description, and glyph for a built-in command; undefined
|
|
658
|
+
* for any other row, which keeps its catalog description.
|
|
659
|
+
*/
|
|
660
|
+
function builtinRowFace(descriptor, t) {
|
|
661
|
+
const name = builtinCommandName(descriptor);
|
|
662
|
+
const face = name === void 0 ? void 0 : HOST_FACES.get(name);
|
|
663
|
+
return face === void 0 ? void 0 : {
|
|
664
|
+
label: t(face.label),
|
|
665
|
+
description: t(face.description),
|
|
666
|
+
icon: face.icon
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Arrange the empty-query menu: the Add section, then the Commands section,
|
|
671
|
+
* each in usage order, with unlisted rows closing Commands in their input
|
|
672
|
+
* order; each row carries its section heading.
|
|
673
|
+
* @param rows - the visible candidates in catalog-then-contribution order.
|
|
674
|
+
* @param t - the `command` namespace translator.
|
|
675
|
+
* @returns the sectioned rows.
|
|
676
|
+
*/
|
|
677
|
+
function sectionRows(rows, t) {
|
|
678
|
+
const listed = new Set([...SECTION_ROWS.add, ...SECTION_ROWS.commands]);
|
|
679
|
+
const byName = new Map(rows.map((row) => [row.name, row]));
|
|
680
|
+
const pick = (names) => names.flatMap((name) => {
|
|
681
|
+
const row = byName.get(name);
|
|
682
|
+
return row === void 0 ? [] : [row];
|
|
683
|
+
});
|
|
684
|
+
const add = pick(SECTION_ROWS.add).map((row) => ({
|
|
685
|
+
...row,
|
|
686
|
+
section: t("section.add")
|
|
687
|
+
}));
|
|
688
|
+
const commands = [...pick(SECTION_ROWS.commands), ...rows.filter((row) => !listed.has(row.name))].map((row) => ({
|
|
689
|
+
...row,
|
|
690
|
+
section: t("section.commands")
|
|
691
|
+
}));
|
|
692
|
+
return [...add, ...commands];
|
|
693
|
+
}
|
|
694
|
+
//#endregion
|
|
467
695
|
//#region lib/types/client/service.js
|
|
468
696
|
/**
|
|
469
697
|
* CommandUiRuntime (`ctx.commandUi`): the '/' command source over the
|
|
470
698
|
* session-keyed directory, the client-contribution registry, and the
|
|
471
699
|
* per-session popupSelect controllers. Candidate synthesis merges the host
|
|
472
|
-
* catalog with contributions by availability,
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
700
|
+
* catalog with contributions by availability, gives built-in Host rows their
|
|
701
|
+
* localized face (presentation.ts), then position-filters; an empty query
|
|
702
|
+
* lists the Add and Commands sections in usage order, a typed query ranks
|
|
703
|
+
* every row by the `/` menu's shared name-and-label ranking (ui-primitives
|
|
704
|
+
* `rankByName`). A host/contribution name collision fails loud. Every
|
|
705
|
+
* execute addresses the session's agent by sessionId — sessions are always
|
|
476
706
|
* agent-backed.
|
|
477
707
|
*/
|
|
478
708
|
/** Recover the command name from a line the Host confirmed as executed. */
|
|
@@ -481,15 +711,6 @@ window.__ModuleLoader__.load({
|
|
|
481
711
|
const separator = trimmed.search(/\s/u);
|
|
482
712
|
return (separator === -1 ? trimmed : trimmed.slice(0, separator)).slice(1);
|
|
483
713
|
}
|
|
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
714
|
/** Command surface: session-keyed directory + '/' source + contribution registry + per-session popups. */
|
|
494
715
|
var CommandUiRuntime = class extends _deepseek_ai_cordis.Service {
|
|
495
716
|
static inject = [
|
|
@@ -502,7 +723,7 @@ window.__ModuleLoader__.load({
|
|
|
502
723
|
live = {
|
|
503
724
|
contributions: /* @__PURE__ */ new Map(),
|
|
504
725
|
decorations: /* @__PURE__ */ new Map(),
|
|
505
|
-
popups:
|
|
726
|
+
popups: new WeakMapWithValues()
|
|
506
727
|
};
|
|
507
728
|
/** `command`-namespace translator (composer refusal notices). */
|
|
508
729
|
t;
|
|
@@ -583,21 +804,32 @@ window.__ModuleLoader__.load({
|
|
|
583
804
|
};
|
|
584
805
|
}
|
|
585
806
|
/**
|
|
807
|
+
* Close every open popup for a command whose options have become stale.
|
|
808
|
+
* Pending loads and confirmations lose their binding; drafts stay intact.
|
|
809
|
+
* @param name - command name without the leading slash.
|
|
810
|
+
*/
|
|
811
|
+
dismiss(name) {
|
|
812
|
+
for (const popup of this.live.popups.values) if (popup.state.getSnapshot().command === name) popup.dismiss({ focusComposer: true });
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
586
815
|
* Resolve the per-session popup controller (lazy; dies with the session
|
|
587
816
|
* scope). The controller's consume callback dispatches the scoped
|
|
588
817
|
* consume-token event back to this session; focusComposer reaches the
|
|
589
|
-
* composer through the
|
|
818
|
+
* session's composer through the conversation input face.
|
|
590
819
|
* @param actx - session-scope ctx.
|
|
591
820
|
* @returns the resident controller.
|
|
821
|
+
* @throws when the Context no longer belongs to a retained Session generation.
|
|
592
822
|
*/
|
|
593
823
|
popupFor(actx) {
|
|
594
|
-
const
|
|
595
|
-
|
|
824
|
+
const sessions = this.sessions();
|
|
825
|
+
const session = sessions.sessionOf(actx);
|
|
826
|
+
const binding = session === void 0 ? void 0 : sessions.binding(session.sessionId);
|
|
827
|
+
if (binding === void 0 || binding.session !== session) throw new Error("command.popupFor requires a retained Session scope");
|
|
596
828
|
const { popups } = this.live;
|
|
597
|
-
const existing = popups.get(
|
|
829
|
+
const existing = popups.get(binding);
|
|
598
830
|
if (existing !== void 0) return existing;
|
|
599
831
|
const controller = new PopupSelectController({
|
|
600
|
-
consume: (segment) =>
|
|
832
|
+
consume: (segment) => binding.ctx.bail(binding.ctx, "slash/input-consume-token", { guard: segment.via === "menu" ? {
|
|
601
833
|
kind: "span",
|
|
602
834
|
span: segment.span
|
|
603
835
|
} : {
|
|
@@ -605,32 +837,21 @@ window.__ModuleLoader__.load({
|
|
|
605
837
|
token: segment.token
|
|
606
838
|
} }) === true,
|
|
607
839
|
focusComposer: () => {
|
|
608
|
-
|
|
840
|
+
binding.ctx.get("conversation")?.input.for(binding.ctx).focus();
|
|
609
841
|
}
|
|
610
842
|
});
|
|
611
|
-
popups.set(
|
|
612
|
-
|
|
843
|
+
popups.set(binding, controller);
|
|
844
|
+
binding.ctx.effect(() => () => {
|
|
613
845
|
controller.dispose();
|
|
614
|
-
popups.delete(
|
|
615
|
-
this.focusHooks.delete(id);
|
|
846
|
+
popups.delete(binding);
|
|
616
847
|
}, "command: session popup");
|
|
617
848
|
return controller;
|
|
618
849
|
}
|
|
619
|
-
/** Composer focus hooks by session (the overlay wiring binds the textarea focus here). */
|
|
620
|
-
focusHooks = /* @__PURE__ */ new Map();
|
|
621
850
|
/**
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
* @returns the unbind disposer.
|
|
851
|
+
* Menu candidates: host catalog + contribution availability, built-in rows
|
|
852
|
+
* localized, then position filtering; sections for an empty query, the
|
|
853
|
+
* shared name-and-label ranking for a typed one.
|
|
626
854
|
*/
|
|
627
|
-
bindComposerFocus(id, focus) {
|
|
628
|
-
this.focusHooks.set(id, focus);
|
|
629
|
-
return () => {
|
|
630
|
-
if (this.focusHooks.get(id) === focus) this.focusHooks.delete(id);
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
/** Menu candidates: host catalog + contribution availability, then position filtering and the shared name ranking. */
|
|
634
855
|
async candidates(session, req) {
|
|
635
856
|
const list = await this.directory.ensureReady(session.sessionId, req.signal);
|
|
636
857
|
const rows = [];
|
|
@@ -639,7 +860,7 @@ window.__ModuleLoader__.load({
|
|
|
639
860
|
seen.add(c.name);
|
|
640
861
|
rows.push({
|
|
641
862
|
name: c.name,
|
|
642
|
-
description:
|
|
863
|
+
...builtinRowFace(c, this.t) ?? { description: c.description },
|
|
643
864
|
...c.input !== void 0 ? { hint: c.input.hint } : {}
|
|
644
865
|
});
|
|
645
866
|
}
|
|
@@ -648,15 +869,13 @@ window.__ModuleLoader__.load({
|
|
|
648
869
|
if (seen.has(contribution.name)) throw new Error(`ui-commands: contribution /${contribution.name} collides with a host command`);
|
|
649
870
|
rows.push({
|
|
650
871
|
name: contribution.name,
|
|
651
|
-
|
|
872
|
+
...contribution.label === void 0 ? {} : { label: contribution.label() },
|
|
873
|
+
...contribution.description === void 0 ? {} : { description: contribution.description() },
|
|
874
|
+
...contribution.icon === void 0 ? {} : { icon: contribution.icon }
|
|
652
875
|
});
|
|
653
876
|
}
|
|
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;
|
|
877
|
+
const visible = rows.filter((c) => req.position === "leading" || c.hint === void 0);
|
|
878
|
+
return req.query === "" ? sectionRows(visible, this.t) : (0, _deepseek_ai_dsh_client_ui_primitives.rankByName)(visible, req.query);
|
|
660
879
|
}
|
|
661
880
|
/** Decision table, menu column: contribution/decorated-host → popup or action; host input → claim; host bare → detached execute. */
|
|
662
881
|
dispatch(pick) {
|
|
@@ -679,7 +898,7 @@ window.__ModuleLoader__.load({
|
|
|
679
898
|
});
|
|
680
899
|
return "handled";
|
|
681
900
|
}
|
|
682
|
-
if (desc.input !== void 0) return { claim: this.leadingClaim(desc, pick.session) };
|
|
901
|
+
if (desc.input !== void 0) return { claim: this.leadingClaim(desc, pick.session, claimToken(desc, this.t)) };
|
|
683
902
|
this.consumeVia(pick.session.sessionId, {
|
|
684
903
|
via: "menu",
|
|
685
904
|
span: pick.span
|
|
@@ -690,11 +909,10 @@ window.__ModuleLoader__.load({
|
|
|
690
909
|
/** Decision table, space column: hot-key sync check; only host leadingInput claims. */
|
|
691
910
|
matchSpace(session, token) {
|
|
692
911
|
if (!token.startsWith("/")) return void 0;
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const desc = this.directory.resolve(session.sessionId, name);
|
|
912
|
+
if (this.live.contributions.has(token.slice(1))) return void 0;
|
|
913
|
+
const desc = this.directory.resolve(session.sessionId, token.slice(1));
|
|
696
914
|
if (desc === void 0 || desc.input === void 0) return void 0;
|
|
697
|
-
return { claim: this.leadingClaim(desc, session) };
|
|
915
|
+
return { claim: this.leadingClaim(desc, session, token.slice(1)) };
|
|
698
916
|
}
|
|
699
917
|
/**
|
|
700
918
|
* Decision table, enter column. Strong-waits the session's catalog (a
|
|
@@ -708,6 +926,9 @@ window.__ModuleLoader__.load({
|
|
|
708
926
|
* refusal so the machine surfaces one composer notice and the draft and
|
|
709
927
|
* attachments stay in place; nothing executes and nothing is dropped. An
|
|
710
928
|
* action submits nothing and runs regardless.
|
|
929
|
+
*
|
|
930
|
+
* A typed token is resolved through the localized claim tokens, so a line
|
|
931
|
+
* written as `/计划` reaches the `plan` descriptor and executes as `/plan`.
|
|
711
932
|
*/
|
|
712
933
|
async matchEnter(session, line, signal, envelope) {
|
|
713
934
|
const trimmed = line.trim();
|
|
@@ -715,24 +936,26 @@ window.__ModuleLoader__.load({
|
|
|
715
936
|
const ws = trimmed.search(/\s/);
|
|
716
937
|
const token = ws === -1 ? trimmed : trimmed.slice(0, ws);
|
|
717
938
|
const bare = ws === -1;
|
|
718
|
-
const
|
|
719
|
-
if (
|
|
939
|
+
const typedName = token.slice(1);
|
|
940
|
+
if (typedName === "") return void 0;
|
|
720
941
|
const refuseAttachments = () => {
|
|
721
|
-
throw new Error(this.t("notice.attachmentsUnsupported", { command:
|
|
942
|
+
throw new Error(this.t("notice.attachmentsUnsupported", { command: typedName }));
|
|
722
943
|
};
|
|
723
|
-
const contribution = this.live.contributions.get(
|
|
944
|
+
const contribution = this.live.contributions.get(typedName);
|
|
724
945
|
if (contribution !== void 0 && contribution.available(session)) {
|
|
725
946
|
if (!bare) return void 0;
|
|
726
947
|
if (envelope.attachments > 0 && contribution.ui.kind !== "action") refuseAttachments();
|
|
727
|
-
this.invoke(
|
|
948
|
+
this.invoke(typedName, contribution.ui, session, {
|
|
728
949
|
via: "enter",
|
|
729
950
|
token
|
|
730
951
|
});
|
|
731
952
|
return "handled";
|
|
732
953
|
}
|
|
733
954
|
await this.directory.ensureReady(session.sessionId, signal);
|
|
734
|
-
const desc = this.directory.resolve(session.sessionId,
|
|
955
|
+
const desc = this.directory.resolve(session.sessionId, typedName);
|
|
735
956
|
if (desc === void 0) return void 0;
|
|
957
|
+
const name = desc.name;
|
|
958
|
+
const canonical = `/${name}${trimmed.slice(token.length)}`;
|
|
736
959
|
if (bare) {
|
|
737
960
|
const decoration = this.live.decorations.get(name);
|
|
738
961
|
if (decoration !== void 0 && decoration.available(session)) {
|
|
@@ -746,7 +969,7 @@ window.__ModuleLoader__.load({
|
|
|
746
969
|
}
|
|
747
970
|
if (desc.input !== void 0) {
|
|
748
971
|
if (envelope.attachments > 0 && desc.input.attachments !== true) refuseAttachments();
|
|
749
|
-
return { claim: this.leadingClaim(desc, session) };
|
|
972
|
+
return { claim: this.leadingClaim(desc, session, token.slice(1)) };
|
|
750
973
|
}
|
|
751
974
|
if (!bare) return void 0;
|
|
752
975
|
if (envelope.attachments > 0) refuseAttachments();
|
|
@@ -754,7 +977,7 @@ window.__ModuleLoader__.load({
|
|
|
754
977
|
via: "enter",
|
|
755
978
|
token
|
|
756
979
|
});
|
|
757
|
-
this.runDetached(desc, session,
|
|
980
|
+
this.runDetached(desc, session, canonical);
|
|
758
981
|
return "handled";
|
|
759
982
|
}
|
|
760
983
|
/**
|
|
@@ -771,14 +994,22 @@ window.__ModuleLoader__.load({
|
|
|
771
994
|
if (actx === void 0) return;
|
|
772
995
|
this.popupFor(actx).open(name, ui, session, segment);
|
|
773
996
|
}
|
|
774
|
-
/**
|
|
775
|
-
|
|
776
|
-
|
|
997
|
+
/**
|
|
998
|
+
* Build the leadingInput claim. The composer keeps the claimed token in
|
|
999
|
+
* the draft and reads the arguments after it, so the token is the spelling
|
|
1000
|
+
* the draft will carry: the locale's token for a menu pick, the typed
|
|
1001
|
+
* spelling for Space and Enter. The command.execute submit transaction
|
|
1002
|
+
* always sends the catalog name.
|
|
1003
|
+
*/
|
|
1004
|
+
leadingClaim(desc, session, shown) {
|
|
1005
|
+
const token = `/${shown} `;
|
|
1006
|
+
const line = `/${desc.name} `;
|
|
777
1007
|
return {
|
|
1008
|
+
name: desc.name,
|
|
778
1009
|
token,
|
|
779
1010
|
...desc.input !== void 0 ? { hint: desc.input.hint } : {},
|
|
780
1011
|
...desc.input?.attachments === true ? { attachments: true } : {},
|
|
781
|
-
submit: (args, _actx, attachments) => this.execute(session,
|
|
1012
|
+
submit: (args, _actx, attachments) => this.execute(session, line + args, attachments)
|
|
782
1013
|
};
|
|
783
1014
|
}
|
|
784
1015
|
/**
|
|
@@ -890,7 +1121,7 @@ window.__ModuleLoader__.load({
|
|
|
890
1121
|
}
|
|
891
1122
|
//#endregion
|
|
892
1123
|
//#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;
|
|
1124
|
+
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
1125
|
const tagId = "@deepseek-ai/dsh-client-ui-commands/PopupSelectView.module.css";
|
|
895
1126
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
896
1127
|
const tag = document.createElement("style");
|
|
@@ -900,12 +1131,14 @@ window.__ModuleLoader__.load({
|
|
|
900
1131
|
document.head.appendChild(tag);
|
|
901
1132
|
}
|
|
902
1133
|
var PopupSelectView_module_css_default = {
|
|
1134
|
+
"badge": "mufS8W_badge",
|
|
903
1135
|
"card": "mufS8W_card",
|
|
904
1136
|
"check": "mufS8W_check",
|
|
905
1137
|
"detail": "mufS8W_detail",
|
|
906
1138
|
"error": "mufS8W_error",
|
|
907
1139
|
"errorText": "mufS8W_errorText",
|
|
908
1140
|
"label": "mufS8W_label",
|
|
1141
|
+
"labelText": "mufS8W_labelText",
|
|
909
1142
|
"retry": "mufS8W_retry",
|
|
910
1143
|
"row": "mufS8W_row",
|
|
911
1144
|
"rowActive": "mufS8W_rowActive",
|
|
@@ -920,11 +1153,12 @@ window.__ModuleLoader__.load({
|
|
|
920
1153
|
* store into the conversation.input.overlay anchor. Unlike the slash menu
|
|
921
1154
|
* (combobox — textarea keeps focus), this shell HOLDS focus while open: the
|
|
922
1155
|
* inner search input takes focus, plain typing filters the loaded options
|
|
923
|
-
* locally, Enter
|
|
924
|
-
*
|
|
925
|
-
* caret. Any pointer interaction outside the
|
|
926
|
-
* target takes focus). Closed state renders
|
|
927
|
-
* mounted. The card height clamps to the space
|
|
1156
|
+
* locally, Enter and Tab accept the filtered highlight, ↑↓ walk it (wrapping,
|
|
1157
|
+
* scrolled into view), and Escape and Shift+Tab dismiss back to the composer.
|
|
1158
|
+
* ←→ keep the search input's native caret. Any pointer interaction outside the
|
|
1159
|
+
* box dismisses (the click's own target takes focus). Closed state renders
|
|
1160
|
+
* null; the overlay slot stays mounted. The card height clamps to the space
|
|
1161
|
+
* above the composer.
|
|
928
1162
|
*/
|
|
929
1163
|
/** Design cap on the card height (same MenuDropdown family as the slash menu). */
|
|
930
1164
|
const MAX_HEIGHT = 320;
|
|
@@ -978,6 +1212,12 @@ window.__ModuleLoader__.load({
|
|
|
978
1212
|
ev.preventDefault();
|
|
979
1213
|
popup.select(state.active);
|
|
980
1214
|
return;
|
|
1215
|
+
case "Tab":
|
|
1216
|
+
if (!ev.shiftKey && (state.status !== "ready" || rows.length === 0)) return;
|
|
1217
|
+
ev.preventDefault();
|
|
1218
|
+
if (ev.shiftKey) popup.dismiss({ focusComposer: true });
|
|
1219
|
+
else popup.select(state.active);
|
|
1220
|
+
return;
|
|
981
1221
|
case "Escape":
|
|
982
1222
|
ev.preventDefault();
|
|
983
1223
|
popup.dismiss({ focusComposer: true });
|
|
@@ -1038,6 +1278,7 @@ window.__ModuleLoader__.load({
|
|
|
1038
1278
|
children: rows.map((option, index) => (0, react_jsx_runtime.jsxs)("div", {
|
|
1039
1279
|
role: "option",
|
|
1040
1280
|
"aria-selected": index === state.active,
|
|
1281
|
+
"aria-label": option.badge === void 0 ? void 0 : `${option.label} ${option.badge}`,
|
|
1041
1282
|
className: clsx(PopupSelectView_module_css_default.row, index === state.active && PopupSelectView_module_css_default.rowActive),
|
|
1042
1283
|
onClick: () => {
|
|
1043
1284
|
popup.select(index);
|
|
@@ -1046,9 +1287,15 @@ window.__ModuleLoader__.load({
|
|
|
1046
1287
|
popup.highlight(index);
|
|
1047
1288
|
},
|
|
1048
1289
|
children: [
|
|
1049
|
-
(0, react_jsx_runtime.
|
|
1290
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
1050
1291
|
className: PopupSelectView_module_css_default.label,
|
|
1051
|
-
children:
|
|
1292
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1293
|
+
className: PopupSelectView_module_css_default.labelText,
|
|
1294
|
+
children: option.label
|
|
1295
|
+
}), option.badge !== void 0 && (0, react_jsx_runtime.jsx)("sup", {
|
|
1296
|
+
className: PopupSelectView_module_css_default.badge,
|
|
1297
|
+
children: option.badge
|
|
1298
|
+
})]
|
|
1052
1299
|
}),
|
|
1053
1300
|
option.detail !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
1054
1301
|
className: PopupSelectView_module_css_default.detail,
|
|
@@ -1095,8 +1342,7 @@ window.__ModuleLoader__.load({
|
|
|
1095
1342
|
"locale"
|
|
1096
1343
|
];
|
|
1097
1344
|
/**
|
|
1098
|
-
*
|
|
1099
|
-
* into the input overlay once its declarer is up.
|
|
1345
|
+
* Mount the command service and its per-session popupSelect overlay.
|
|
1100
1346
|
* @param ctx - client root context.
|
|
1101
1347
|
*/
|
|
1102
1348
|
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,7 +19,15 @@ 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;
|
|
25
|
+
/**
|
|
26
|
+
* The row the shell's highlight parks on when the panel opens, so an accept
|
|
27
|
+
* gesture made without looking confirms the value in use. A business package
|
|
28
|
+
* that marks a row `active` for presentation alone would make that row the
|
|
29
|
+
* default pick.
|
|
30
|
+
*/
|
|
21
31
|
readonly active?: boolean;
|
|
22
32
|
/** Optional in-page risk gate owned by the shared popup shell. */
|
|
23
33
|
readonly confirmation?: SelectConfirmation;
|
|
@@ -35,8 +45,7 @@ export interface PopupSelectSpec {
|
|
|
35
45
|
}
|
|
36
46
|
/**
|
|
37
47
|
* 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
|
|
48
|
+
* consumes the trigger token and runs one client-side callback. It submits nothing, so an
|
|
40
49
|
* attachment-carrying draft never refuses it.
|
|
41
50
|
*/
|
|
42
51
|
export interface ActionSpec {
|
|
@@ -53,13 +62,18 @@ export type CommandUiSpec = PopupSelectSpec | ActionSpec;
|
|
|
53
62
|
* One client-owned command contribution: a slash-menu entry whose behavior
|
|
54
63
|
* lives entirely on the client (no host descriptor). Merged with the host
|
|
55
64
|
* catalog by name — a collision with a host command fails loud at candidate
|
|
56
|
-
* synthesis, never shadows.
|
|
65
|
+
* synthesis, never shadows. Row copy is read on every candidate pass, so a
|
|
66
|
+
* locale change reaches the next menu open without re-registration.
|
|
57
67
|
*/
|
|
58
68
|
export interface CommandContribution {
|
|
59
69
|
/** Command name without the leading slash (unique across contributions). */
|
|
60
70
|
readonly name: string;
|
|
61
|
-
/**
|
|
62
|
-
|
|
71
|
+
/** Localized menu row title; the name itself when absent. */
|
|
72
|
+
label?(): string;
|
|
73
|
+
/** Localized menu row description; the row shows none when absent. */
|
|
74
|
+
description?(): string;
|
|
75
|
+
/** Menu row glyph from the shared icon set. */
|
|
76
|
+
readonly icon?: ComponentType<IconProps>;
|
|
63
77
|
/** Capability filter, called with a fresh projection per candidate pass. */
|
|
64
78
|
available(session: ClientSessionContext): boolean;
|
|
65
79
|
/** The command's UI behavior. */
|
|
@@ -94,6 +108,8 @@ export interface CommandUiContract {
|
|
|
94
108
|
* Duplicate names throw at registration.
|
|
95
109
|
*/
|
|
96
110
|
decorate(decoration: CommandDecoration): () => void;
|
|
111
|
+
/** Close this command's open popups and confirmations without consuming composer drafts. */
|
|
112
|
+
dismiss(name: string): void;
|
|
97
113
|
/** Resolve the per-session popup controller for one session scope (wiring/overlay layer). */
|
|
98
114
|
popupFor(actx: ClientContext): unknown;
|
|
99
115
|
}
|
|
@@ -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;
|
|
@@ -51,7 +51,12 @@ export interface PopupState {
|
|
|
51
51
|
readonly options: readonly SelectOption[];
|
|
52
52
|
/** Local filter text over the loaded options. */
|
|
53
53
|
readonly search: string;
|
|
54
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Highlight index into the filtered row list: 0 until options land; afterwards
|
|
56
|
+
* the row the loaded list marks as the current value
|
|
57
|
+
* ({@link SelectOption.active}), else 0. A search rebases it to the top of the
|
|
58
|
+
* filtered rows.
|
|
59
|
+
*/
|
|
55
60
|
readonly active: number;
|
|
56
61
|
/** A select() settlement is in flight: further select/search/highlight no-op until it settles. */
|
|
57
62
|
readonly submitting: boolean;
|
|
@@ -101,7 +106,8 @@ export declare class PopupSelectController<TCtx = unknown> {
|
|
|
101
106
|
retry(): void;
|
|
102
107
|
/**
|
|
103
108
|
* Replace the local search text (pure local filter — the provider is never
|
|
104
|
-
* re-queried) and rebase the highlight
|
|
109
|
+
* re-queried) and rebase the highlight to the top of the new filtered list:
|
|
110
|
+
* typing searches for something other than the current value.
|
|
105
111
|
* @param search - the shell search input's text.
|
|
106
112
|
*/
|
|
107
113
|
setSearch(search: string): void;
|
|
@@ -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,28 +58,28 @@ 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
|
|
62
70
|
* consume-token event back to this session; focusComposer reaches the
|
|
63
|
-
* composer through the
|
|
71
|
+
* session's composer through the conversation input face.
|
|
64
72
|
* @param actx - session-scope ctx.
|
|
65
73
|
* @returns the resident controller.
|
|
74
|
+
* @throws when the Context no longer belongs to a retained Session generation.
|
|
66
75
|
*/
|
|
67
76
|
popupFor(actx: ClientContext): PopupSelectController<ClientSessionContext>;
|
|
68
|
-
/** Composer focus hooks by session (the overlay wiring binds the textarea focus here). */
|
|
69
|
-
private readonly focusHooks;
|
|
70
77
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* @returns the unbind disposer.
|
|
78
|
+
* Menu candidates: host catalog + contribution availability, built-in rows
|
|
79
|
+
* localized, then position filtering; sections for an empty query, the
|
|
80
|
+
* shared name-and-label ranking for a typed one.
|
|
75
81
|
*/
|
|
76
|
-
bindComposerFocus(id: SessionId, focus: () => void): () => void;
|
|
77
|
-
/** Menu candidates: host catalog + contribution availability, then position filtering and the shared name ranking. */
|
|
78
82
|
private candidates;
|
|
79
|
-
/** Translate exact built-in Host copy while preserving scoped or third-party descriptors verbatim. */
|
|
80
|
-
private hostDescription;
|
|
81
83
|
/** Decision table, menu column: contribution/decorated-host → popup or action; host input → claim; host bare → detached execute. */
|
|
82
84
|
private dispatch;
|
|
83
85
|
/** Decision table, space column: hot-key sync check; only host leadingInput claims. */
|
|
@@ -94,6 +96,9 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
94
96
|
* refusal so the machine surfaces one composer notice and the draft and
|
|
95
97
|
* attachments stay in place; nothing executes and nothing is dropped. An
|
|
96
98
|
* action submits nothing and runs regardless.
|
|
99
|
+
*
|
|
100
|
+
* A typed token is resolved through the localized claim tokens, so a line
|
|
101
|
+
* written as `/计划` reaches the `plan` descriptor and executes as `/plan`.
|
|
97
102
|
*/
|
|
98
103
|
private matchEnter;
|
|
99
104
|
/**
|
|
@@ -101,7 +106,13 @@ export declare class CommandUiRuntime extends Service implements CommandUiContra
|
|
|
101
106
|
* session's popup, or consume the token and run the action.
|
|
102
107
|
*/
|
|
103
108
|
private invoke;
|
|
104
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Build the leadingInput claim. The composer keeps the claimed token in
|
|
111
|
+
* the draft and reads the arguments after it, so the token is the spelling
|
|
112
|
+
* the draft will carry: the locale's token for a menu pick, the typed
|
|
113
|
+
* spelling for Space and Enter. The command.execute submit transaction
|
|
114
|
+
* always sends the catalog name.
|
|
115
|
+
*/
|
|
105
116
|
private leadingClaim;
|
|
106
117
|
/**
|
|
107
118
|
* 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.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -44,21 +44,22 @@
|
|
|
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-connection": "^0.1.
|
|
49
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
50
|
-
"@deepseek-ai/dsh-client-test-runtime": "^0.1.
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.
|
|
52
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.
|
|
53
|
-
"@deepseek-ai/dsh-client-ui-
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
47
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.2",
|
|
48
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.6-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.6-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-commands": "^0.1.6-alpha.2",
|
|
55
56
|
"@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-
|
|
60
|
-
"@deepseek-ai/dsh-client-ui-
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-session": "^0.1.
|
|
57
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-client-store": "^0.1.6-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-util-values": "^0.1.6-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.2",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.2"
|
|
62
63
|
},
|
|
63
64
|
"files": [
|
|
64
65
|
"lib/index.js",
|