@kesike/dsh-exit 0.1.3 → 0.2.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.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![dsh web plugin](https://img.shields.io/badge/dsh-web%20plugin-4f46e5?style=for-the-badge)](https://npmjs.com/package/@deepseek-ai/dsh)
8
8
  [![npm](https://img.shields.io/npm/v/%40kesike%2Fdsh-exit?style=for-the-badge&label=npm)](https://www.npmjs.com/package/@kesike/dsh-exit)
9
9
  [![Platform](https://img.shields.io/badge/platform-web-0ea5e9?style=for-the-badge)](https://github.com/KeS1Ke/dsh-exit)
10
- [![Version](https://img.shields.io/badge/version-0.1.3-64748b?style=for-the-badge)](package.json)
10
+ [![Version](https://img.shields.io/badge/version-0.2.1-64748b?style=for-the-badge)](package.json)
11
11
  [![Dependencies](https://img.shields.io/badge/runtime%20dependencies-none-16a34a?style=for-the-badge)](package.json)
12
12
 
13
13
  [简体中文](https://github.com/KeS1Ke/dsh-exit/blob/main/README.zh-CN.md)
@@ -19,9 +19,10 @@
19
19
 
20
20
  ## ✨ What it does
21
21
 
22
- `dsh-exit` adds a compact, floating **Exit** button to the lower-right corner of the entire dsh web interface.
22
+ `dsh-exit` adds a dedicated **Exit** section to the dsh settings panel. It uses the highest settings order so newly installed settings plugins appear above it.
23
23
 
24
24
  - Opens a confirmation modal before doing anything destructive.
25
+ - Keeps the **Exit** section at the bottom of the settings navigation.
25
26
  - Calls the typed `dshExit/exit` Remote method only after confirmation.
26
27
  - Gives the gateway a short acknowledgement window, then ends the host process.
27
28
  - Releases every port held by that process, including the usual `127.0.0.1:3080`.
@@ -46,7 +47,7 @@ The animation, video, and screenshot below are repository showcase assets. They
46
47
 
47
48
  ## 📸 Real dsh UI
48
49
 
49
- The capture below comes from a running dsh Web profile with `dsh-exit` installed. The red power button is the actual plugin control rendered at the lower-right corner.
50
+ The capture below comes from a running dsh Web profile with `dsh-exit` installed. The Exit control is now provided by the final section in the settings navigation.
50
51
 
51
52
  <p align="center">
52
53
  <img src="docs/dsh-exit-real.png" alt="Running dsh Web UI with the dsh-exit power button in the lower-right corner" width="720">
@@ -57,7 +58,7 @@ The capture below comes from a running dsh Web profile with `dsh-exit` installed
57
58
  | Layer | File | Responsibility |
58
59
  | --- | --- | --- |
59
60
  | Host | [`lib/index.js`](lib/index.js) | Registers the `dshExit` Cordis service and the typed `exit()` Remote method. |
60
- | Client | [`lib/client.js`](lib/client.js) | Self-contained browser bundle: button, CSS, modal, keyboard handling, and Remote mounting. |
61
+ | Client | [`lib/client.js`](lib/client.js) | Self-contained browser bundle: settings section, exit button, CSS, modal, keyboard handling, and Remote mounting. |
61
62
  | Bundle patch | [`cordis.patch.yml`](cordis.patch.yml) | Inserts the plugin row into the dsh profile. |
62
63
  | Package metadata | [`package.json`](package.json) | Declares the host entry, web client, and bundle patch. |
63
64
 
package/README.zh-CN.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![dsh Web 插件](https://img.shields.io/badge/dsh-Web%20%E6%8F%92%E4%BB%B6-4f46e5?style=for-the-badge)](https://npmjs.com/package/@deepseek-ai/dsh)
8
8
  [![npm](https://img.shields.io/npm/v/%40kesike%2Fdsh-exit?style=for-the-badge&label=npm)](https://www.npmjs.com/package/@kesike/dsh-exit)
9
9
  [![平台](https://img.shields.io/badge/%E5%B9%B3%E5%8F%B0-Web-0ea5e9?style=for-the-badge)](https://github.com/KeS1Ke/dsh-exit)
10
- [![版本](https://img.shields.io/badge/%E7%89%88%E6%9C%AC-0.1.3-64748b?style=for-the-badge)](package.json)
10
+ [![版本](https://img.shields.io/badge/%E7%89%88%E6%9C%AC-0.2.1-64748b?style=for-the-badge)](package.json)
11
11
  [![依赖](https://img.shields.io/badge/%E8%BF%90%E8%A1%8C%E6%97%B6%E4%BE%9D%E8%B5%96-%E6%97%A0-16a34a?style=for-the-badge)](package.json)
12
12
 
13
13
  **简体中文** | [English](https://github.com/KeS1Ke/dsh-exit/blob/main/README.md)
@@ -19,9 +19,10 @@
19
19
 
20
20
  ## ✨ 功能概览
21
21
 
22
- `dsh-exit` 在 dsh Web 界面右下角增加一个紧凑的悬浮**退出**按钮。
22
+ `dsh-exit` 在 dsh 设置面板中增加一个独立的**退出**栏目,并使用最高排序值,确保新安装的设置类插件仍排在它前面。
23
23
 
24
24
  - 先弹出确认弹窗,确认前不会执行退出动作。
25
+ - **退出**栏目始终位于设置左侧导航的最底部。
25
26
  - 确认后调用类型化的 `dshExit/exit` Remote 方法。
26
27
  - 先返回调用应答,再等待短暂窗口结束宿主进程。
27
28
  - 释放该进程占用的全部端口,包括常见的 `127.0.0.1:3080`。
@@ -46,7 +47,7 @@
46
47
 
47
48
  ## 📸 真实 dsh 界面
48
49
 
49
- 下图来自已经安装 `dsh-exit` 的真实 dsh Web profile;右下角的红色电源按钮就是插件实际渲染的控件。
50
+ 下图来自已经安装 `dsh-exit` 的真实 dsh Web profile;退出操作现在位于设置左侧导航的最后一栏。
50
51
 
51
52
  <p align="center">
52
53
  <img src="docs/dsh-exit-real.png" alt="运行中的 dsh Web 界面与右下角 dsh-exit 电源按钮" width="720">
@@ -57,7 +58,7 @@
57
58
  | 层次 | 文件 | 职责 |
58
59
  | --- | --- | --- |
59
60
  | 宿主侧 | [`lib/index.js`](lib/index.js) | 注册 `dshExit` Cordis 服务和类型化 `exit()` Remote 方法。 |
60
- | 客户端 | [`lib/client.js`](lib/client.js) | 自包含浏览器 bundle:按钮、样式、弹窗、键盘交互和 Remote 挂载。 |
61
+ | 客户端 | [`lib/client.js`](lib/client.js) | 自包含浏览器 bundle:设置栏目、退出按钮、样式、弹窗、键盘交互和 Remote 挂载。 |
61
62
  | Bundle 补丁 | [`cordis.patch.yml`](cordis.patch.yml) | 向 dsh profile 插入插件行。 |
62
63
  | 包元数据 | [`package.json`](package.json) | 声明宿主入口、Web 客户端和 Bundle 补丁。 |
63
64
 
package/lib/client.js CHANGED
@@ -1,19 +1,21 @@
1
1
  //#region dsh-exit 浏览器客户端 bundle
2
2
  /**
3
3
  * dsh-exit 客户端:
4
- * - 在整个界面右下角挂载紧凑的退出按钮
5
- * - 退出按钮首次点击弹确认弹窗;确认后调用 remote.dshExit.exit(),
4
+ * - 在 dsh 设置菜单中注册一个始终排在最后的“退出”栏目
5
+ * - 栏目中的退出按钮首次点击弹确认弹窗;确认后调用 remote.dshExit.exit(),
6
6
  * 服务端结束宿主进程并释放端口,不操作终端窗口或浏览器标签页
7
7
  *
8
- * 无外部依赖:全部逻辑在本工厂内完成,不 require 任何模块。
8
+ * 页面使用 dsh 已提供的 React、slots 和 locale 运行时,不引入新的运行时依赖。
9
9
  */
10
10
  window.__ModuleLoader__.load({
11
11
  // The loader key follows the resolved npm package name, including its scope.
12
12
  id: "@kesike/dsh-exit",
13
- factory: () => {
13
+ factory: (require) => {
14
14
  var module = { exports: {} };
15
15
  var exports = module.exports;
16
16
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
17
+ const React = require("react");
18
+ const h = React.createElement;
17
19
 
18
20
  /** 与 archive-manager「删除会话」相同的错误红(设计令牌)。 */
19
21
  const ERROR_RED = "var(--dsw-alias-state-error-primary)";
@@ -22,11 +24,23 @@ window.__ModuleLoader__.load({
22
24
  const ICON_SVG = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2v10"/><path d="M18.4 6.6a9 9 0 1 1-12.77.04"/></svg>';
23
25
 
24
26
  const CSS_TEXT = [
25
- ".dshExitBtn{position:fixed;right:12px;bottom:12px;left:auto;z-index:2147482000;box-sizing:border-box;width:32px;height:32px;padding:0;display:grid;place-items:center;cursor:pointer;border:1px solid " + ERROR_RED + ";border-radius:50%;background:var(--dsw-alias-button-elevated-fill);color:" + ERROR_RED + ";box-shadow:0 2px 8px rgba(0,0,0,.24);transition:background .12s ease,transform .12s ease}",
26
- ".dshExitBtn svg{width:16px;height:16px;flex:none}",
27
- ".dshExitBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);transform:translateY(-1px)}",
28
- ".dshExitBtn:focus-visible{outline:2px solid " + ERROR_RED + ";outline-offset:2px}",
29
- ".dshExitBtn:disabled{cursor:default;opacity:.7}",
27
+ ".dshExitSettings{box-sizing:border-box;max-width:720px;padding:28px 32px 40px;color:var(--dsw-alias-label-primary)}",
28
+ ".dshExitSettingsHeader{display:flex;align-items:flex-start;gap:14px;padding-bottom:22px;border-bottom:1px solid var(--dsw-alias-border-l2)}",
29
+ ".dshExitSettingsIcon{box-sizing:border-box;width:38px;height:38px;display:grid;place-items:center;flex:none;border:1px solid " + ERROR_RED + ";border-radius:12px;color:" + ERROR_RED + ";background:var(--dsw-alias-button-elevated-fill)}",
30
+ ".dshExitSettingsIcon svg{width:20px;height:20px}",
31
+ ".dshExitSettingsTitle{margin:0;font-size:20px;line-height:28px;font-weight:650;color:var(--dsw-alias-label-primary)}",
32
+ ".dshExitSettingsDescription{margin:6px 0 0;max-width:560px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}",
33
+ ".dshExitSettingsCard{display:flex;align-items:center;justify-content:space-between;gap:24px;margin-top:24px;padding:20px;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;background:var(--dsw-alias-bg-layer-2)}",
34
+ ".dshExitSettingsWarning{margin:0;max-width:430px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}",
35
+ ".dshExitSettingsButton{display:inline-flex;align-items:center;justify-content:center;gap:8px;flex:none;min-width:112px;height:36px;padding:0 16px;cursor:pointer;border:0;border-radius:10px;background:" + ERROR_RED + ";color:#fff;font-size:13px;font-weight:650;transition:filter .12s ease,opacity .12s ease}",
36
+ ".dshExitSettingsButton svg{width:16px;height:16px}",
37
+ ".dshExitSettingsButton:hover:not(:disabled){filter:brightness(1.08)}",
38
+ ".dshExitSettingsButton:focus-visible{outline:2px solid " + ERROR_RED + ";outline-offset:2px}",
39
+ ".dshExitSettingsButton:disabled{cursor:default;opacity:.6}",
40
+ ".dshExitNavCell{color:" + ERROR_RED + " !important}",
41
+ ".dshExitNavCell .dshExitNavLabel{color:" + ERROR_RED + " !important;font-weight:650}",
42
+ ".dshExitNavIcon{box-sizing:border-box;width:16px;height:16px;display:grid;place-items:center;flex:none;color:" + ERROR_RED + "}",
43
+ ".dshExitNavIcon svg{width:16px;height:16px}",
30
44
  ".dshExitOverlay{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;background:rgba(15,20,25,.45);animation:dshExitFade .12s ease-out}",
31
45
  "@keyframes dshExitFade{from{opacity:0}}",
32
46
  ".dshExitModal{box-sizing:border-box;width:min(380px,calc(100vw - 48px));border-radius:16px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-elevated-fill);box-shadow:0 24px 64px rgba(0,0,0,.35);padding:24px;display:flex;flex-direction:column;gap:12px;animation:dshExitPop .14s ease-out}",
@@ -71,8 +85,37 @@ window.__ModuleLoader__.load({
71
85
  ]
72
86
  };
73
87
 
88
+ const NS = "dsh-exit";
89
+ const DICT = {
90
+ zh: {
91
+ nav: "退出",
92
+ title: "退出 dsh",
93
+ description: "从这里安全结束 dsh 宿主进程,并释放它占用的端口。",
94
+ warning: "确认后将结束所有运行中的会话并停止后台服务,3080 端口会随之释放。",
95
+ button: "确认退出",
96
+ dialogTitle: "退出 DeepSeek Harness?",
97
+ dialogBody: "退出后将结束所有运行中的会话并停止后台服务,3080 端口会随之释放。",
98
+ cancel: "取消",
99
+ confirm: "确认退出",
100
+ exiting: "正在退出…",
101
+ errorPrefix: "退出失败:"
102
+ },
103
+ en: {
104
+ nav: "Exit",
105
+ title: "Exit dsh",
106
+ description: "Safely terminate the dsh host process and release the ports it owns.",
107
+ warning: "Confirming ends all running sessions and stops the background service, releasing port 3080.",
108
+ button: "Confirm exit",
109
+ dialogTitle: "Exit DeepSeek Harness?",
110
+ dialogBody: "This ends all running sessions and stops the background service, releasing port 3080.",
111
+ cancel: "Cancel",
112
+ confirm: "Confirm exit",
113
+ exiting: "Exiting…",
114
+ errorPrefix: "Exit failed: "
115
+ }
116
+ };
117
+
74
118
  let exiting = false;
75
- let exitBtnEl = null;
76
119
  let overlayEl = null;
77
120
  let escHandler = null;
78
121
 
@@ -84,32 +127,74 @@ window.__ModuleLoader__.load({
84
127
  document.head.appendChild(tag);
85
128
  }
86
129
 
87
- function createExitButton() {
88
- const exitBtn = document.createElement("button");
89
- exitBtn.className = "dshExitBtn";
90
- exitBtn.type = "button";
91
- exitBtn.disabled = false;
92
- exitBtn.setAttribute("aria-label", "退出");
93
- exitBtn.title = "退出 DeepSeek Harness";
94
- exitBtn.dataset.dshExitButton = "true";
95
- exitBtn.innerHTML = ICON_SVG;
96
- return exitBtn;
130
+ function textOf(t, key) {
131
+ return typeof t === "function" ? t(key) : DICT.zh[key] ?? key;
97
132
  }
98
133
 
99
- function mount(ctx) {
100
- const existing = document.querySelector("[data-dsh-exit-button='true']");
101
- if (existing) {
102
- exitBtnEl = existing;
103
- return;
134
+ /** dsh 的设置壳层为未知 section 使用齿轮图标;给本插件的导航项换成 power 图标。 */
135
+ function decorateExitNav(t) {
136
+ const labels = new Set(["退出", "Exit", textOf(t, "nav")]);
137
+ for (const cell of document.querySelectorAll("nav button")) {
138
+ const label = [...cell.querySelectorAll("span")].find((candidate) => labels.has(candidate.textContent.trim()));
139
+ if (label === undefined) continue;
140
+ cell.dataset.dshExitNav = "true";
141
+ cell.classList.add("dshExitNavCell");
142
+ label.classList.add("dshExitNavLabel");
143
+ if (cell.querySelector(".dshExitNavIcon") !== null) continue;
144
+ const originalIcon = cell.firstElementChild;
145
+ const icon = document.createElement("span");
146
+ icon.className = "dshExitNavIcon";
147
+ icon.innerHTML = ICON_SVG;
148
+ if (originalIcon !== null && originalIcon !== label) {
149
+ originalIcon.dataset.dshExitOriginalIcon = "true";
150
+ originalIcon.style.display = "none";
151
+ cell.insertBefore(icon, originalIcon);
152
+ } else {
153
+ cell.insertBefore(icon, label);
154
+ }
104
155
  }
105
- if (!document.body) return;
106
- const exitBtn = createExitButton();
107
- exitBtn.addEventListener("click", (event) => {
108
- event.stopPropagation();
109
- openModal(ctx, exitBtn);
110
- });
111
- document.body.appendChild(exitBtn);
112
- exitBtnEl = exitBtn;
156
+ }
157
+
158
+ function clearDecoratedExitNav() {
159
+ for (const cell of document.querySelectorAll("[data-dsh-exit-nav='true']")) {
160
+ cell.querySelector(".dshExitNavIcon")?.remove();
161
+ cell.querySelector("[data-dsh-exit-original-icon='true']")?.style.removeProperty("display");
162
+ cell.querySelector(".dshExitNavLabel")?.classList.remove("dshExitNavLabel");
163
+ cell.classList.remove("dshExitNavCell");
164
+ delete cell.dataset.dshExitNav;
165
+ }
166
+ }
167
+
168
+ function ExitSettingsSection({ ctx, t }) {
169
+ return h("section", {
170
+ className: "dshExitSettings",
171
+ "aria-labelledby": "dsh-exit-settings-title"
172
+ }, [
173
+ h("div", { className: "dshExitSettingsHeader", key: "header" }, [
174
+ h("span", {
175
+ className: "dshExitSettingsIcon",
176
+ dangerouslySetInnerHTML: { __html: ICON_SVG },
177
+ key: "icon"
178
+ }),
179
+ h("div", { key: "copy" }, [
180
+ h("h2", { className: "dshExitSettingsTitle", id: "dsh-exit-settings-title", key: "title" }, textOf(t, "title")),
181
+ h("p", { className: "dshExitSettingsDescription", key: "description" }, textOf(t, "description"))
182
+ ])
183
+ ]),
184
+ h("div", { className: "dshExitSettingsCard", key: "card" }, [
185
+ h("p", { className: "dshExitSettingsWarning", key: "warning" }, textOf(t, "warning")),
186
+ h("button", {
187
+ type: "button",
188
+ className: "dshExitSettingsButton",
189
+ "aria-label": textOf(t, "button"),
190
+ onClick: (event) => openModal(ctx, event.currentTarget, t),
191
+ key: "button"
192
+ }, [
193
+ h("span", { dangerouslySetInnerHTML: { __html: ICON_SVG }, key: "icon" }),
194
+ textOf(t, "button")
195
+ ])
196
+ ])
197
+ ]);
113
198
  }
114
199
 
115
200
  function closeModal() {
@@ -123,7 +208,8 @@ window.__ModuleLoader__.load({
123
208
  }
124
209
  }
125
210
 
126
- function openModal(ctx, exitBtn) {
211
+ function openModal(ctx, exitBtn, t) {
212
+ const text = (key) => textOf(t, key);
127
213
  closeModal();
128
214
  overlayEl = document.createElement("div");
129
215
  overlayEl.className = "dshExitOverlay";
@@ -131,13 +217,13 @@ window.__ModuleLoader__.load({
131
217
  panel.className = "dshExitModal";
132
218
  panel.setAttribute("role", "dialog");
133
219
  panel.setAttribute("aria-modal", "true");
134
- panel.setAttribute("aria-label", "退出 DeepSeek Harness");
220
+ panel.setAttribute("aria-label", text("dialogTitle"));
135
221
  const title = document.createElement("h3");
136
222
  title.className = "dshExitTitle";
137
- title.textContent = "退出 DeepSeek Harness?";
223
+ title.textContent = text("dialogTitle");
138
224
  const body = document.createElement("p");
139
225
  body.className = "dshExitBody";
140
- body.textContent = "退出后将结束所有运行中的会话并停止后台服务,3080 端口会随之释放。";
226
+ body.textContent = text("dialogBody");
141
227
  const errorEl = document.createElement("p");
142
228
  errorEl.className = "dshExitError";
143
229
  const actions = document.createElement("div");
@@ -145,11 +231,11 @@ window.__ModuleLoader__.load({
145
231
  const cancel = document.createElement("button");
146
232
  cancel.type = "button";
147
233
  cancel.className = "dshExitCancel";
148
- cancel.textContent = "取消";
234
+ cancel.textContent = text("cancel");
149
235
  const confirm = document.createElement("button");
150
236
  confirm.type = "button";
151
237
  confirm.className = "dshExitConfirm";
152
- confirm.textContent = "确认退出";
238
+ confirm.textContent = text("confirm");
153
239
  actions.append(cancel, confirm);
154
240
  panel.append(title, body, errorEl, actions);
155
241
  overlayEl.appendChild(panel);
@@ -170,7 +256,7 @@ window.__ModuleLoader__.load({
170
256
  cancel.disabled = true;
171
257
  errorEl.textContent = "";
172
258
  exitBtn.disabled = true;
173
- exitBtn.setAttribute("aria-label", "正在退出…");
259
+ exitBtn.setAttribute("aria-label", text("exiting"));
174
260
  Promise.resolve()
175
261
  .then(() => {
176
262
  const service = ctx.get("remote.dshExit");
@@ -189,7 +275,7 @@ window.__ModuleLoader__.load({
189
275
  cancel.disabled = false;
190
276
  exitBtn.disabled = false;
191
277
  exitBtn.setAttribute("aria-label", "退出");
192
- errorEl.textContent = "退出失败:" + (reason instanceof Error ? reason.message : String(reason));
278
+ errorEl.textContent = text("errorPrefix") + (reason instanceof Error ? reason.message : String(reason));
193
279
  });
194
280
  });
195
281
  document.body.appendChild(overlayEl);
@@ -197,25 +283,32 @@ window.__ModuleLoader__.load({
197
283
 
198
284
  async function apply(ctx) {
199
285
  injectCss();
200
- mount(ctx);
201
- const observer = new MutationObserver(() => {
202
- if (!exiting) mount(ctx);
203
- });
204
- observer.observe(document.documentElement, { childList: true, subtree: true });
286
+ ctx.effect(() => ctx.locale.register(NS, DICT), "dsh-exit: settings dictionaries");
287
+ const t = ctx.locale.bind(NS);
288
+ const decorate = () => decorateExitNav(t);
289
+ decorate();
290
+ const navObserver = document.body === null ? null : new MutationObserver(decorate);
291
+ navObserver?.observe(document.body, { childList: true, subtree: true, characterData: true });
292
+ const SettingsSection = (props) => h(ExitSettingsSection, { ...props, ctx });
293
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
294
+ name: "settings.section",
295
+ id: "dsh-exit",
296
+ order: Number.MAX_SAFE_INTEGER,
297
+ label: () => t("nav"),
298
+ locale: NS
299
+ }, SettingsSection));
205
300
  let disposeRemote = () => {};
206
301
  const remote = ctx.get("remote");
207
302
  if (remote !== undefined) disposeRemote = await remote.$mount(EXIT_REMOTE);
208
303
  return async () => {
209
- observer.disconnect();
304
+ navObserver?.disconnect();
305
+ clearDecoratedExitNav();
210
306
  closeModal();
211
- if (exitBtnEl) {
212
- exitBtnEl.remove();
213
- exitBtnEl = null;
214
- }
215
307
  disposeRemote();
216
308
  };
217
309
  }
218
310
 
311
+ exports.inject = ["slots", "locale"];
219
312
  exports.apply = apply;
220
313
  return module.exports;
221
314
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kesike/dsh-exit",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "在 dsh web 界面提供退出按钮:确认后结束宿主进程并释放端口",
6
6
  "keywords": [
@@ -43,6 +43,11 @@
43
43
  "patch": "./cordis.patch.yml"
44
44
  },
45
45
  "client": {
46
+ "inject": [
47
+ "@deepseek-ai/dsh-client-runtime",
48
+ "@deepseek-ai/dsh-client-ui-slots",
49
+ "@deepseek-ai/dsh-client-locale"
50
+ ],
46
51
  "platform": "web"
47
52
  }
48
53
  }