From 7e5a4d1e4b517ffd97d74d90201daec2306bb372 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Thu, 24 Sep 2026 23:56:45 +0800 Subject: [PATCH 01/23] fix(docs): align publication runner and scoped-event heap budgets --- .github/workflows/docs-pages.yml | 2 +- package.json | 4 ++-- scripts/gen-scoped-events.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs-pages.yml b/.github/workflows/docs-pages.yml index 8bac404b2d..6cd25b059d 100644 --- a/.github/workflows/docs-pages.yml +++ b/.github/workflows/docs-pages.yml @@ -35,7 +35,7 @@ env: jobs: build: - runs-on: ubuntu-latest + runs-on: blacksmith-8vcpu-ubuntu-2404 permissions: contents: read pages: read diff --git a/package.json b/package.json index 81a370d4c4..4317f7dc94 100644 --- a/package.json +++ b/package.json @@ -181,8 +181,8 @@ "gen-third-party-notices": "tsx scripts/gen-third-party-notices.ts", "verify-third-party-notices": "tsx scripts/gen-third-party-notices.ts --check", "gen-module-graph": "tsx scripts/gen-module-graph.ts", - "gen-scoped-events": "tsx scripts/gen-scoped-events.ts", - "verify-scoped-events": "tsx scripts/gen-scoped-events.ts --check", + "gen-scoped-events": "node --max-old-space-size=4096 --import tsx/esm scripts/gen-scoped-events.ts", + "verify-scoped-events": "node --max-old-space-size=4096 --import tsx/esm scripts/gen-scoped-events.ts --check", "verify-module-graph": "tsx scripts/gen-module-graph.ts --check", "constraints": "tsx scripts/check-workspace-constraints.ts", "doc-sync": "tsx scripts/run-gates.ts doc-sync", diff --git a/scripts/gen-scoped-events.ts b/scripts/gen-scoped-events.ts index 9d070def72..eecf684d8b 100644 --- a/scripts/gen-scoped-events.ts +++ b/scripts/gen-scoped-events.ts @@ -9,8 +9,8 @@ * `Events` parameter tuple. Zero matches require `@dshScopeScan unsupported`; * multiple matches are ambiguous and always fail loud. * - * `tsx scripts/gen-scoped-events.ts` -> write the generated source - * `tsx scripts/gen-scoped-events.ts --check` -> exit 1 when it is stale + * `pnpm run gen-scoped-events` -> write the generated source + * `pnpm run verify-scoped-events` -> exit 1 when it is stale */ import { existsSync, readFileSync, writeFileSync } from 'node:fs' From 638842895a18ee14e9b3a3c4ccab91f77a266180 Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Fri, 25 Sep 2026 14:00:53 +0800 Subject: [PATCH 02/23] docs(desktop): describe keyboard typecheck ownership --- apps/desktop/README.i18n.yaml | 4 ++-- apps/desktop/README.md | 2 +- apps/desktop/README.zh.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/desktop/README.i18n.yaml b/apps/desktop/README.i18n.yaml index 039d4701b1..00a0668e87 100644 --- a/apps/desktop/README.i18n.yaml +++ b/apps/desktop/README.i18n.yaml @@ -21,8 +21,8 @@ en: 0d11af8639977a6d zh: 86b086b12d2210d3 /deepseek-harness-desktop/develop: - en: 9b9b362a9865e763 - zh: c748901168e2db8c + en: 40d2ad4014bf445b + zh: 6a08de06df9ed042 /deepseek-harness-desktop/develop/startup-onboarding: en: 5e2d62207a603d81 zh: 5fb863d4906f4a67 diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 0f579c249b..f1de0eba4c 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -130,7 +130,7 @@ pnpm run start:desktop The Web counterparts are `pnpm run dev:web` and `pnpm run start:web`, documented in the [development guide](../../docs/development.md). Workspace development runs the current CLI and private Desktop Host packages under Electron RunAsNode. Plugin management and recovery use `$DSH_HOME/profiles/desktop`, separate from the disposable workspace runtime. The Host uses runtime module resolution in both development and packaged builds without creating official-package fallback links; developer-installed packages, including links, retain native priority. Use an unpacked application to exercise Electron RunAsNode, bundled pnpm, bundled dsh resources, plugin installation and repair paths. -The [native/renderer keyboard tests](tests/keyboard.spec.ts) compile in [their own Client test project](../../tsconfig.desktop-keyboard-tests.json), reached by the repository Client typecheck. Its Desktop imports are limited to Cordis-free input, persistence, IPC, browser-guest, and overlay modules. +The [native/renderer keyboard tests](tests/keyboard.spec.ts) compile as part of the repository Client typecheck. Their Desktop imports are limited to Cordis-free input, persistence, IPC, browser-guest, and overlay modules. ### Startup onboarding diff --git a/apps/desktop/README.zh.md b/apps/desktop/README.zh.md index 62e34fad48..c2f7ffbcfb 100644 --- a/apps/desktop/README.zh.md +++ b/apps/desktop/README.zh.md @@ -132,7 +132,7 @@ pnpm run start:desktop Web 侧的对应命令是 `pnpm run dev:web` 与 `pnpm run start:web`,见[开发指南](../../docs/development.zh.md)。Workspace 开发使用 Electron RunAsNode 运行当前 CLI 与私有 Desktop Host 包,插件管理和恢复使用 `$DSH_HOME/profiles/desktop`,与一次性工作区运行时分离。Host 在开发与打包构建中都使用 runtime 模块解析,不创建官方包的 fallback 链接;开发者安装的包(包括链接)保留原生优先级。需要验证 Electron RunAsNode、内置 pnpm、内置 dsh 资源、插件安装和修复时,应运行未封装安装器的应用目录。 -[原生输入与渲染进程键盘测试](tests/keyboard.spec.ts)在[独立的 Client 测试项目](../../tsconfig.desktop-keyboard-tests.json)中编译,由仓库 Client 类型检查纳入。它只导入不依赖 Cordis 的 Desktop 输入、持久化、IPC、浏览器 guest 和蒙层模块。 +[原生输入与渲染进程键盘测试](tests/keyboard.spec.ts)直接纳入仓库 Client 类型检查。它只导入不依赖 Cordis 的 Desktop 输入、持久化、IPC、浏览器 guest 和蒙层模块。 ### 启动引导 From a08472e9828922406ea9b6c6128a86a6e248720f Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Fri, 25 Sep 2026 14:01:08 +0800 Subject: [PATCH 03/23] fix(build): fold desktop keyboard tests into client typecheck --- tsconfig.client.json | 9 ++++++++- tsconfig.desktop-keyboard-tests.json | 24 ------------------------ 2 files changed, 8 insertions(+), 25 deletions(-) delete mode 100644 tsconfig.desktop-keyboard-tests.json diff --git a/tsconfig.client.json b/tsconfig.client.json index 0471bfa13b..2e27307ddb 100644 --- a/tsconfig.client.json +++ b/tsconfig.client.json @@ -14,6 +14,13 @@ "types": ["node"] }, "include": [ + // The native/renderer keyboard fixture imports only these Cordis-free Desktop modules. + "apps/desktop/tests/keyboard.spec.ts", + "apps/desktop/src/keyboard.ts", + "apps/desktop/src/keybindings.ts", + "apps/desktop/src/browser-guests.ts", + "apps/desktop/src/ipc.ts", + "apps/desktop/src/update-overlay.ts", "apps/desktop/tests/**/*.client.spec.tsx", // Source-subpath test imports can arrive through workspace links whose // realpath semantics vary by host. Load package CSS declarations directly. @@ -43,8 +50,8 @@ "packages/client/*/tests/**/*.host.spec.ts" ], "references": [ - { "path": "./tsconfig.desktop-keyboard-tests.json" }, { "path": "./apps/desktop/tsconfig.client.json" }, + { "path": "./packages/util/atomic-write" }, { "path": "./packages/experimental/client-ui-voice-input" }, { "path": "./packages/client/ui-sidebar-terminal" }, diff --git a/tsconfig.desktop-keyboard-tests.json b/tsconfig.desktop-keyboard-tests.json deleted file mode 100644 index 28cdd1bf9c..0000000000 --- a/tsconfig.desktop-keyboard-tests.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - // The native/renderer integration fixture imports only Cordis-free Desktop modules. - "extends": "./tsconfig.base.client.json", - "compilerOptions": { - "rootDir": ".", - "outDir": "lib/desktop-keyboard-test-types", - "emitDeclarationOnly": true, - "types": ["node"] - }, - "files": [ - "apps/desktop/tests/keyboard.spec.ts", - "apps/desktop/src/keyboard.ts", - "apps/desktop/src/keybindings.ts", - "apps/desktop/src/browser-guests.ts", - "apps/desktop/src/ipc.ts", - "apps/desktop/src/update-overlay.ts" - ], - "include": [], - "references": [ - { "path": "./packages/client/shortcuts/tsconfig.client.json" }, - { "path": "./packages/client/ui-sidebar-browser/tsconfig.client.json" }, - { "path": "./packages/util/atomic-write" } - ] -} From 197355bb12584f99595cd4ac5f2268f231e9f176 Mon Sep 17 00:00:00 2001 From: ZiyaZhang <199893125+ZiyaZhang@users.noreply.github.com> Date: Thu, 24 Sep 2026 23:08:48 -0700 Subject: [PATCH 04/23] fix(schedule-bundle): name the Plugins row Automation tasks with an alarm clock icon The row now carries the name of the sidebar entry it adds and a plain-language description, and its icon is an alarm clock drawn in the Official group's 36px style. The README and the user guide quote the new title; the README names the sidebar entry instead of a right-sidebar page. --- .../tests/expected/plugin-config/official.expected.md | 6 +++--- .../expected/plugin-manager/live-enabled.expected.md | 6 +++--- .../tests/expected/plugin-manager/manager.expected.md | 6 +++--- docs/user/guide/schedule.i18n.yaml | 4 ++-- docs/user/guide/schedule.md | 2 +- docs/user/guide/schedule.zh.md | 2 +- packages/experimental/schedule-bundle/README.i18n.yaml | 4 ++-- packages/experimental/schedule-bundle/README.md | 2 +- packages/experimental/schedule-bundle/README.zh.md | 2 +- packages/experimental/schedule-bundle/icon.svg | 9 +++++---- packages/experimental/schedule-bundle/locale/en.json | 4 ++-- packages/experimental/schedule-bundle/locale/zh.json | 4 ++-- 12 files changed, 26 insertions(+), 25 deletions(-) diff --git a/apps/web/tests/expected/plugin-config/official.expected.md b/apps/web/tests/expected/plugin-config/official.expected.md index 247d6ac308..dddc056ff2 100644 --- a/apps/web/tests/expected/plugin-config/official.expected.md +++ b/apps/web/tests/expected/plugin-config/official.expected.md @@ -14,9 +14,9 @@ - text: 实验性 提供自动审查权限模式,由模型在每次工具调用前判断是否授权。 - switch "启用 自动授权审查" - listitem: - - button "查看 定时与时间上下文": 定时与时间上下文 - - text: 实验性 启用持久提醒、自动化任务页面与逐步时钟上下文。 - - switch "启用 定时与时间上下文" + - button "查看 自动化任务": 自动化任务 + - text: 实验性 按设定的时间或周期,在会话中自动执行任务。 + - switch "启用 自动化任务" - listitem: - button "查看 语音输入": 语音输入 - text: 实验性 在本机使用 SenseVoice 转写录音;首次使用需安装依赖 diff --git a/apps/web/tests/expected/plugin-manager/live-enabled.expected.md b/apps/web/tests/expected/plugin-manager/live-enabled.expected.md index 4bd8ed064e..086bfee63c 100644 --- a/apps/web/tests/expected/plugin-manager/live-enabled.expected.md +++ b/apps/web/tests/expected/plugin-manager/live-enabled.expected.md @@ -14,9 +14,9 @@ - text: 实验性 提供自动审查权限模式,由模型在每次工具调用前判断是否授权。 - switch "启用 自动授权审查" - listitem: - - button "查看 定时与时间上下文": 定时与时间上下文 - - text: 实验性 启用持久提醒、自动化任务页面与逐步时钟上下文。 - - switch "启用 定时与时间上下文" + - button "查看 自动化任务": 自动化任务 + - text: 实验性 按设定的时间或周期,在会话中自动执行任务。 + - switch "启用 自动化任务" - listitem: - button "查看 语音输入": 语音输入 - text: 实验性 在本机使用 SenseVoice 转写录音;首次使用需安装依赖 diff --git a/apps/web/tests/expected/plugin-manager/manager.expected.md b/apps/web/tests/expected/plugin-manager/manager.expected.md index b188be8787..90f137a80e 100644 --- a/apps/web/tests/expected/plugin-manager/manager.expected.md +++ b/apps/web/tests/expected/plugin-manager/manager.expected.md @@ -14,9 +14,9 @@ - text: 实验性 提供自动审查权限模式,由模型在每次工具调用前判断是否授权。 - switch "启用 自动授权审查" - listitem: - - button "查看 定时与时间上下文": 定时与时间上下文 - - text: 实验性 启用持久提醒、自动化任务页面与逐步时钟上下文。 - - switch "启用 定时与时间上下文" + - button "查看 自动化任务": 自动化任务 + - text: 实验性 按设定的时间或周期,在会话中自动执行任务。 + - switch "启用 自动化任务" - listitem: - button "查看 语音输入": 语音输入 - text: 实验性 在本机使用 SenseVoice 转写录音;首次使用需安装依赖 diff --git a/docs/user/guide/schedule.i18n.yaml b/docs/user/guide/schedule.i18n.yaml index 1bf88dec7c..1aa4d2c185 100644 --- a/docs/user/guide/schedule.i18n.yaml +++ b/docs/user/guide/schedule.i18n.yaml @@ -12,8 +12,8 @@ en: b3b2a6e0ce02bdee zh: 2ddb168de79fd19c /schedule-reminders/create-reminders: - en: c76ee99f59529942 - zh: be1d75c226231dd7 + en: 75d55fa3e4fc01d5 + zh: 2fb76166a9106f5f /schedule-reminders/inspect-and-delete-tasks: en: a44fc1aabc2ab957 zh: 31f444d4c7fd5c7a diff --git a/docs/user/guide/schedule.md b/docs/user/guide/schedule.md index abb86b7f8e..e248cccdb5 100644 --- a/docs/user/guide/schedule.md +++ b/docs/user/guide/schedule.md @@ -17,7 +17,7 @@ Create reminders in a conversation, then inspect active and inactive tasks and e ## Create reminders -The shipped Web profile carries the `time-context`, `schedule`, and `ui-schedule` rows disabled. Open Plugins and enable `Scheduling and time context` in the Official group; the optional experimental bundle `@deepseek-ai/dsh-experimental-schedule-bundle` then mounts Schedule with the clock context that gives the model the current time and the browser's zone. Configure a model provider before asking it to create reminders. +The shipped Web profile carries the `time-context`, `schedule`, and `ui-schedule` rows disabled. Open Plugins and enable `Automation tasks` in the Official group; the optional experimental bundle `@deepseek-ai/dsh-experimental-schedule-bundle` then mounts Schedule with the clock context that gives the model the current time and the browser's zone. Configure a model provider before asking it to create reminders. Ask the model to create, list, edit, or delete reminders. It uses `schedule_create`, `schedule_list`, `schedule_update`, and `schedule_delete` (update changes one reminder in place and keeps its id and saved delivery records); the Automation tasks page's New action opens a New Session for a creation instead, with the request already written in its composer. Supported choices are a one-time delay in positive whole seconds, an absolute date and time, a fixed interval of at least one minute, a daily local time with an IANA time zone, a weekly local time with an IANA time zone and ISO weekdays from Monday 1 through Sunday 7, or a five-field cron expression with an explicit IANA time zone, stored in canonical form. diff --git a/docs/user/guide/schedule.zh.md b/docs/user/guide/schedule.zh.md index bd10a06fe5..4b2a1827f3 100644 --- a/docs/user/guide/schedule.zh.md +++ b/docs/user/guide/schedule.zh.md @@ -17,7 +17,7 @@ ## 创建提醒 -发布的 Web profile 把 `time-context`、`schedule` 与 `ui-schedule` 三行保持禁用。打开插件管理页,在 Official 分组中启用“定时与时间上下文”;可选实验性 bundle `@deepseek-ai/dsh-experimental-schedule-bundle` 随即挂载 Schedule 及其时钟上下文,后者把当前时间和浏览器时区交给模型。在请求模型创建提醒前,先配置模型 provider。 +发布的 Web profile 把 `time-context`、`schedule` 与 `ui-schedule` 三行保持禁用。打开插件管理页,在“官方”分组中启用“自动化任务”;可选实验性 bundle `@deepseek-ai/dsh-experimental-schedule-bundle` 随即挂载 Schedule 及其时钟上下文,后者把当前时间和浏览器时区交给模型。在请求模型创建提醒前,先配置模型 provider。 向模型提出创建、列出、修改或删除提醒的请求。模型使用 `schedule_create`、`schedule_list`、`schedule_update` 和 `schedule_delete`(更新原地修改一条提醒,保留其 id 与已保存的投递记录);「自动化任务」页面的「新建」操作则为创建打开一个新会话,其 composer 已写好该请求。支持正整数秒的一次性延迟、绝对日期时间、至少一分钟的固定间隔、带 IANA 时区的每日本地时间、带 IANA 时区和 ISO 星期(周一 1 至周日 7)的每周本地时间,以及带显式 IANA 时区的五字段 cron 表达式(表达式以规范化形式保存)。 diff --git a/packages/experimental/schedule-bundle/README.i18n.yaml b/packages/experimental/schedule-bundle/README.i18n.yaml index 98d86454bc..f7efa8d5ab 100644 --- a/packages/experimental/schedule-bundle/README.i18n.yaml +++ b/packages/experimental/schedule-bundle/README.i18n.yaml @@ -15,8 +15,8 @@ en: d152484eb41ac6b4 zh: 364c80ee00a374ff /deepseek-ai-dsh-experimental-schedule-bundle/use-this-package: - en: ffaa4b18e10a1df9 - zh: b675fd8195ebb057 + en: a5fcc3d26604ff4c + zh: bc358d33b0b0c1d2 /deepseek-ai-dsh-experimental-schedule-bundle/understand-the-implementation: en: 90beaeda691bd0a7 zh: d81683d7bb1f6903 diff --git a/packages/experimental/schedule-bundle/README.md b/packages/experimental/schedule-bundle/README.md index 6e3abd4eea..6bd93ce427 100644 --- a/packages/experimental/schedule-bundle/README.md +++ b/packages/experimental/schedule-bundle/README.md @@ -25,7 +25,7 @@ This optional bundle switches on the three Schedule rows that `@deepseek-ai/dsh- ## Use this package -Open Plugins in the Web sidebar and enable Scheduling and time context, marked by a clock icon. A live root Agent then receives `schedule_create`, `schedule_list`, `schedule_update`, and `schedule_delete`, the Session header shows its reminder catalog, the right sidebar shows the Automation tasks page, and each eligible step appends one clock reading with the current time, the browser zone attached to the open request, and the elapsed time since the preceding model-visible message. Disabling the bundle restores the shipped composition; stored tasks remain on disk. +Open Plugins in the Web sidebar and enable Automation tasks, marked by an alarm clock icon. A live root Agent then receives `schedule_create`, `schedule_list`, `schedule_update`, and `schedule_delete`, the Session header shows its reminder catalog, the sidebar shows the Automation tasks entry, and each eligible step appends one clock reading with the current time, the browser zone attached to the open request, and the elapsed time since the preceding model-visible message. Disabling the bundle restores the shipped composition; stored tasks remain on disk. ----- diff --git a/packages/experimental/schedule-bundle/README.zh.md b/packages/experimental/schedule-bundle/README.zh.md index 747137bc57..d72e8434a3 100644 --- a/packages/experimental/schedule-bundle/README.zh.md +++ b/packages/experimental/schedule-bundle/README.zh.md @@ -25,7 +25,7 @@ kind: "package-bundle" ## 使用此包 -打开 Web 侧栏的插件管理页并启用带时钟图标的“定时与时间上下文”。此后,活跃的根智能体获得 `schedule_create`、`schedule_list`、`schedule_update` 与 `schedule_delete`,会话头部显示提醒目录,右侧栏显示自动化任务页面,每个符合条件的步骤追加一条时钟读数,包含当前时间、打开请求所带的浏览器时区,以及距上一条模型可见消息的经过时间。禁用此 Bundle 会恢复随包组合;已存储的任务保留在磁盘上。 +打开 Web 侧栏的插件管理页并启用带闹钟图标的“自动化任务”。此后,活跃的根智能体获得 `schedule_create`、`schedule_list`、`schedule_update` 与 `schedule_delete`,会话头部显示提醒目录,侧栏出现“自动化任务”入口,每个符合条件的步骤追加一条时钟读数,包含当前时间、打开请求所带的浏览器时区,以及距上一条模型可见消息的经过时间。禁用此 Bundle 会恢复随包组合;已存储的任务保留在磁盘上。 ----- diff --git a/packages/experimental/schedule-bundle/icon.svg b/packages/experimental/schedule-bundle/icon.svg index 4299a86395..91a374ce9f 100644 --- a/packages/experimental/schedule-bundle/icon.svg +++ b/packages/experimental/schedule-bundle/icon.svg @@ -1,9 +1,10 @@ - - - + + + + - + diff --git a/packages/experimental/schedule-bundle/locale/en.json b/packages/experimental/schedule-bundle/locale/en.json index f748924183..8766ee2a4c 100644 --- a/packages/experimental/schedule-bundle/locale/en.json +++ b/packages/experimental/schedule-bundle/locale/en.json @@ -1,6 +1,6 @@ { "meta": { - "title": "Scheduling and time context", - "description": "Enable durable reminders, the Automation tasks page, and per-step clock context." + "title": "Automation tasks", + "description": "Run tasks in your sessions at a set time or on a repeating schedule." } } diff --git a/packages/experimental/schedule-bundle/locale/zh.json b/packages/experimental/schedule-bundle/locale/zh.json index 47abb9b169..14e171d352 100644 --- a/packages/experimental/schedule-bundle/locale/zh.json +++ b/packages/experimental/schedule-bundle/locale/zh.json @@ -1,6 +1,6 @@ { "meta": { - "title": "定时与时间上下文", - "description": "启用持久提醒、自动化任务页面与逐步时钟上下文。" + "title": "自动化任务", + "description": "按设定的时间或周期,在会话中自动执行任务。" } } From 92690a5a3bd96bc36c281eb3d7d347ee971d3e61 Mon Sep 17 00:00:00 2001 From: ZiyaZhang <199893125+ZiyaZhang@users.noreply.github.com> Date: Fri, 25 Sep 2026 00:14:40 -0700 Subject: [PATCH 05/23] fix(ui-settings-plugin-inventory): align and simplify plugin cards Cards in one grid row now share a height, with title rows and descriptions starting at the same offsets. The entry id shows only when it differs from the title, as a code chip in the card's bottom-left corner; tags mark only states other than plainly enabled; descriptions clamp to two lines until the card expands; the preset group starts open; loading shows skeleton cards. Group headers share one height, the subtitle separator is evenly spaced, the title weight drops to 500, 12.5px text moves to 12px, and a container query on the tab switches the grid to one column. --- .../README.i18n.yaml | 16 +-- .../ui-settings-plugin-inventory/README.md | 10 +- .../ui-settings-plugin-inventory/README.zh.md | 10 +- .../PluginInventorySettingsTab.module.css | 135 +++++++++++++----- .../src/client/PluginInventorySettingsTab.tsx | 57 +++++--- .../src/client/locales.ts | 2 +- .../tests/components.client.spec.tsx | 83 ++++++----- 7 files changed, 204 insertions(+), 109 deletions(-) diff --git a/packages/client/ui-settings-plugin-inventory/README.i18n.yaml b/packages/client/ui-settings-plugin-inventory/README.i18n.yaml index 6b7ff6a2bf..b69016100b 100644 --- a/packages/client/ui-settings-plugin-inventory/README.i18n.yaml +++ b/packages/client/ui-settings-plugin-inventory/README.i18n.yaml @@ -9,8 +9,8 @@ en: 463eecf002ec0f54 zh: b8dc6003ddccd14a /deepseek-ai-dsh-client-ui-settings-plugin-inventory/summary: - en: 56f5349b6e502525 - zh: 691a0fe90b739e9d + en: f52353cf99816eb8 + zh: e172c1751289882f /deepseek-ai-dsh-client-ui-settings-plugin-inventory/table-of-contents: en: d152484eb41ac6b4 zh: 09388d293f9be9cb @@ -18,14 +18,14 @@ en: 67637808e7940157 zh: d4d0716ea4d33e4a /deepseek-ai-dsh-client-ui-settings-plugin-inventory/use-this-package/reading-a-card: - en: e615aaa891b5055f - zh: 38769e763c8e54ca + en: 6ccd8a53e3205f24 + zh: 1d0ad59bfb7786e4 /deepseek-ai-dsh-client-ui-settings-plugin-inventory/use-this-package/the-preset-switcher: en: 84e5938329199a22 zh: 6f5cfaac56433e8b /deepseek-ai-dsh-client-ui-settings-plugin-inventory/use-this-package/retrying-a-failed-read: - en: 2859449aaae5ab59 - zh: 06380059ae429026 + en: 673c7574e14078be + zh: b093e897f0341973 /deepseek-ai-dsh-client-ui-settings-plugin-inventory/understand-the-implementation: en: cab883d26642c932 zh: 54e7321bd781b400 @@ -33,8 +33,8 @@ en: e4792880c5916ce0 zh: 4c8171a446dd153f /deepseek-ai-dsh-client-ui-settings-plugin-inventory/understand-the-implementation/rendering: - en: b9aa850e5f101b68 - zh: 8a29025f19f6ce37 + en: 779146e3d3732803 + zh: 0dfafa328dfeda75 /deepseek-ai-dsh-client-ui-settings-plugin-inventory/further-exploration: en: ae3819f0c5da95bd zh: 40972ab96d5c95e2 diff --git a/packages/client/ui-settings-plugin-inventory/README.md b/packages/client/ui-settings-plugin-inventory/README.md index 7353f14d11..d6127c9ed9 100644 --- a/packages/client/ui-settings-plugin-inventory/README.md +++ b/packages/client/ui-settings-plugin-inventory/README.md @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -The **Plugin list** tab lets Web users inspect plugins without changing their configuration. It lists agent presets, then the global inventory; both start collapsed and open on demand or during a search. Cards show localized titles and descriptions when available, identify instances by stable entry id, and expose enablement, source details, runtime status, disabled conditions, and discovery failures; preset-provided global entries name their presets. Search covers both groups and points to matches in other presets. The tab handles loading, empty, no-match, failure, and retry states without exposing transport details, and still shows the global inventory without a preset roster. +The **Plugin list** tab lets Web users inspect plugins without changing their configuration. It lists agent presets, open by default, then the collapsed global inventory; a search opens both. Cards show localized titles and descriptions when available, identify instances by stable entry id, and expose enablement, source details, runtime status, disabled conditions, and discovery failures; preset-provided global entries name their presets. Search covers both groups and points to matches in other presets. The tab handles loading, empty, no-match, failure, and retry states without exposing transport details, and still shows the global inventory without a preset roster. ## Table of Contents @@ -31,9 +31,9 @@ A failed installation with pending pnpm build permissions offers **Allow these s ### Reading a card -Each collapsed card uses the plugin's localized title and description when available. Settings shortens literal package-name or module-name fallbacks by removing the npm scope and the `cordis:`, `cordis-plugin-`, or `dsh-`/`dsh-host-`/`dsh-client-` prefix; locale titles remain unchanged. The Host supplies locale and package-field fallbacks in `meta`; the Client selects the current language. Full module specifiers remain available in details and search. Missing descriptions are omitted, and metadata diagnostics appear with the card. This compact naming applies only to Settings, not the sidebar's Plugins page. +Each collapsed card uses the plugin's localized title and description when available; a description shows at most two lines until its card is expanded. Settings shortens literal package-name or module-name fallbacks by removing the npm scope and the `cordis:`, `cordis-plugin-`, or `dsh-`/`dsh-host-`/`dsh-client-` prefix; locale titles remain unchanged. The Host supplies locale and package-field fallbacks in `meta`; the Client selects the current language. Full module specifiers remain available in details and search. Missing descriptions are omitted, and metadata diagnostics appear with the card. This compact naming applies only to Settings, not the sidebar's Plugins page. -The stable entry id appears underneath the title, alongside a small enablement tag; a colored root-fiber status dot marks only the phases the tag cannot state: `pending` maps to idle, while `loading` and `unloading` map to ongoing. An `active` or `failed` fiber shows its tag without a dot. A composition-generated subtitle omits its leading `include:` marker, while hover, search, the accessible name, and expanded details retain the complete id. Long entry ids truncate in the row and remain available on hover. Expanding one card reveals the declared entry id, the full module specifier, and the state facts: a preset row names the preset it comes from, its runtime status when the composition is live, and its disable condition when it carries one; a preset-provided global row explains that agent presets provide it per session, names the presets that enable it, and offers a jump into the preset group. Preset names resolve through the shared `presetDisplayText` fold (`dsh-agent-preset-registry/display`) over [`ui-agent-preset`](../ui-agent-preset/README.md)'s dictionaries: shipped presets follow the active locale while user-authored ones keep their own metadata, so an English surface never echoes the preset declarations' Chinese names. Search filters both groups by localized title and description, module name, and entry id. +A small enablement tag marks only the states that differ from plainly enabled: disabled, conditional, provided by presets, and failed; a plainly enabled row carries no tag. A colored root-fiber status dot marks the live phases no tag states: `pending` maps to idle, while `loading` and `unloading` map to ongoing; an `active` or `failed` fiber shows no dot. The stable entry id appears in a code chip at the card's bottom-left, in line with the title, only when it differs from the title: a composition-generated id drops its leading `include:` marker there, and an id that then repeats the title is omitted. The chip's hover text, search, and expanded details retain the complete id, and so does the accessible name unless the id equals the title. Long entry ids truncate in the chip and remain available on hover. Expanding one card reveals its full description, the declared entry id, the full module specifier, and the state facts: a preset row names the preset it comes from, its runtime status when the composition is live, and its disable condition when it carries one; a preset-provided global row explains that agent presets provide it per session, names the presets that enable it, and offers a jump into the preset group. Preset names resolve through the shared `presetDisplayText` fold (`dsh-agent-preset-registry/display`) over [`ui-agent-preset`](../ui-agent-preset/README.md)'s dictionaries: shipped presets follow the active locale while user-authored ones keep their own metadata, so an English surface never echoes the preset declarations' Chinese names. Search filters both groups by localized title and description, module name, and entry id. ### The preset switcher @@ -41,7 +41,7 @@ The switcher is the same selector-pill-plus-menu control the General settings ro ### Retrying a failed read -A failed read renders the shared error marker inside the tab; loading and current-page synchronization use the shared ongoing loader. Retrying re-runs the lazy `list()` call without exposing transport details. +A failed read renders the shared error marker inside the tab. Loading shows skeleton cards in the card grid, and current-page synchronization uses the shared ongoing loader. Retrying re-runs the lazy `list()` call without exposing transport details. The Plugin list also shows synchronization failures on the current page. Its retry reapplies the latest client graph without changing Host enablement or refreshing the page. @@ -61,7 +61,7 @@ The browser plugin registers one localized `settings.plugins.tab` contribution w ### Rendering -Row keys are scope-qualified (`global:`, `preset::`), so one module appearing in both scopes keeps distinct disclosure state; a declared entry id appears in expanded details and supplies the collapsed subtitle after removal of a leading composition `include:` marker, while a row without one stays unlabeled. The preset-provided marking is derived client-side: a global entry carries it when it is disabled there while at least one preset row for the same module specifier is actually enabled, so a module every preset gates off (or declares only conditionally) stays plainly disabled rather than over-claiming provision. +Row keys are scope-qualified (`global:`, `preset::`), so one module appearing in both scopes keeps distinct disclosure state; a declared entry id appears in expanded details and, after removal of a leading composition `include:` marker, supplies the collapsed id chip when the result differs from the title, while a row without one stays unlabeled. The preset-provided marking is derived client-side: a global entry carries it when it is disabled there while at least one preset row for the same module specifier is actually enabled, so a module every preset gates off (or declares only conditionally) stays plainly disabled rather than over-claiming provision. diff --git a/packages/client/ui-settings-plugin-inventory/README.zh.md b/packages/client/ui-settings-plugin-inventory/README.zh.md index 9078f8074a..a084d93919 100644 --- a/packages/client/ui-settings-plugin-inventory/README.zh.md +++ b/packages/client/ui-settings-plugin-inventory/README.zh.md @@ -9,7 +9,7 @@ kind: "package-reference" ## 概述 -**插件列表**标签页让 Web 用户查看插件,而不改变其配置。它先列 agent(智能体)预设分组,再列全局清单;两个分组默认都收起,按需展开,搜索期间保持展开。卡片显示可用的本地化标题与描述,以稳定的条目 id 标识实例,并展示启停状态、出处、运行状态、禁用条件与发现失败;由预设提供的全局条目会列出对应预设。搜索覆盖两个分组,并指出其他预设中的匹配。标签页处理加载、空结果、无匹配、失败与重试状态,且不暴露传输细节;没有预设 roster 时仍会展示全局清单。 +**插件列表**标签页让 Web 用户查看插件,而不改变其配置。它先列 agent(智能体)预设分组,再列全局清单;预设分组默认展开,全局分组默认收起,搜索期间两组都展开。卡片显示可用的本地化标题与描述,以稳定的条目 id 标识实例,并展示启停状态、出处、运行状态、禁用条件与发现失败;由预设提供的全局条目会列出对应预设。搜索覆盖两个分组,并指出其他预设中的匹配。标签页处理加载、空结果、无匹配、失败与重试状态,且不暴露传输细节;没有预设 roster 时仍会展示全局清单。 ## 目录 @@ -31,9 +31,9 @@ kind: "package-reference" ### 阅读卡片 -每张收起的卡片使用插件可用的本地化标题与描述。设置页会缩短字面的包名或模块名回退值,移除 npm scope 以及 `cordis:`、`cordis-plugin-` 或 `dsh-`/`dsh-host-`/`dsh-client-` 前缀;locale 标题保持原样。Host 在 `meta` 中提供 locale 与包字段的回退结果,Client 选择当前语言。详情与搜索仍保留完整模块标识。缺失的描述不显示,元信息诊断随卡片展示。这种短名显示仅用于设置页,不影响侧栏的插件页。 +每张收起的卡片使用插件可用的本地化标题与描述;描述最多显示两行,展开卡片后显示全文。设置页会缩短字面的包名或模块名回退值,移除 npm scope 以及 `cordis:`、`cordis-plugin-` 或 `dsh-`/`dsh-host-`/`dsh-client-` 前缀;locale 标题保持原样。Host 在 `meta` 中提供 locale 与包字段的回退结果,Client 选择当前语言。详情与搜索仍保留完整模块标识。缺失的描述不显示,元信息诊断随卡片展示。这种短名显示仅用于设置页,不影响侧栏的插件页。 -稳定的条目 id 显示在标题下方,卡片带有表示启停状态的小标签;彩色根 fiber 状态圆点只标记标签无法表达的阶段:`pending` 映射为 idle,`loading` 与 `unloading` 映射为 ongoing。`active` 或 `failed` fiber 只显示标签,不显示圆点。组合生成的次标题省略开头的 `include:` 标记;悬停、搜索、无障碍名称与展开详情仍保留完整 id。长条目 id 会在行内截断,悬停时仍可查看完整值。展开卡片后会显示声明的条目 id、完整模块标识与状态事实:预设行说明它来自哪个预设、组合存活时的运行状态,以及它携带的禁用条件;被预设提供的全局行说明它由 Agent 预设按会话提供、列出启用它的预设,并提供跳转到预设组的入口。预设名经共享的 `presetDisplayText` 纯函数(`dsh-agent-preset-registry/display`)叠在 [`ui-agent-preset`](../ui-agent-preset/README.zh.md) 的字典上解析:内置预设走当前语言,用户自建预设保留自己的元数据,因此英文界面不会回显预设声明里的中文名。搜索按本地化标题与描述、模块名称及条目 id 过滤两组。 +表示启停状态的小标签只标记与普通启用不同的状态:已停用、条件启用、由预设提供与启动失败;普通启用的行不带标签。彩色根 fiber 状态圆点标记没有标签表达的存活阶段:`pending` 映射为 idle,`loading` 与 `unloading` 映射为 ongoing;`active` 或 `failed` fiber 不显示圆点。稳定的条目 id 仅在与标题不同时显示,位于卡片左下角的代码标签中,与标题左对齐:组合生成的 id 在此省略开头的 `include:` 标记,省略后与标题相同的 id 不再重复显示。代码标签的悬停提示、搜索与展开详情保留完整 id;除非 id 与标题相同,无障碍名称也保留完整 id。长条目 id 会在代码标签内截断,悬停时仍可查看完整值。展开卡片后会显示完整描述、声明的条目 id、完整模块标识与状态事实:预设行说明它来自哪个预设、组合存活时的运行状态,以及它携带的禁用条件;被预设提供的全局行说明它由 Agent 预设按会话提供、列出启用它的预设,并提供跳转到预设组的入口。预设名经共享的 `presetDisplayText` 纯函数(`dsh-agent-preset-registry/display`)叠在 [`ui-agent-preset`](../ui-agent-preset/README.zh.md) 的字典上解析:内置预设走当前语言,用户自建预设保留自己的元数据,因此英文界面不会回显预设声明里的中文名。搜索按本地化标题与描述、模块名称及条目 id 过滤两组。 ### 预设切换器 @@ -41,7 +41,7 @@ kind: "package-reference" ### 重试失败的读取 -读取失败会在标签页内渲染共享 error 标记;加载与当前页面同步使用共享 ongoing loading。重试会重新执行懒 `list()` 调用,且不会暴露传输细节。 +读取失败会在标签页内渲染共享 error 标记。加载时在卡片网格中显示骨架卡片,当前页面同步使用共享 ongoing loading。重试会重新执行懒 `list()` 调用,且不会暴露传输细节。 插件列表还显示当前页面的同步失败。重试会重新应用最新客户端图,不改变 Host 启用状态,也不刷新页面。 @@ -61,7 +61,7 @@ kind: "package-reference" ### 渲染 -行 key 按作用域限定(`global:`、`preset::`),因此同一模块出现在两个作用域时保持各自的展开状态;声明的条目 id 出现在展开详情中,并在去掉开头的组合 `include:` 标记后作为收起次标题,没有 id 的行不显示次级标签。预设提供标记在客户端推导:一个全局条目在全局被停用、且至少一个预设行对同一模块标识实际启用时才携带它,因此被所有预设关掉(或仅条件声明)的模块保持单纯的已停用,而不是夸大提供关系。 +行 key 按作用域限定(`global:`、`preset::`),因此同一模块出现在两个作用域时保持各自的展开状态;声明的条目 id 出现在展开详情中;去掉开头的组合 `include:` 标记后若与标题不同,则作为收起卡片的 id 代码标签,没有 id 的行不显示次级标签。预设提供标记在客户端推导:一个全局条目在全局被停用、且至少一个预设行对同一模块标识实际启用时才携带它,因此被所有预设关掉(或仅条件声明)的模块保持单纯的已停用,而不是夸大提供关系。 diff --git a/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.module.css b/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.module.css index 87b29d442f..1d732ae93d 100644 --- a/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.module.css +++ b/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.module.css @@ -1,4 +1,6 @@ +/* The tab's own width, not the window's, decides the card columns below. */ .section { + container: plugin-inventory / inline-size; display: flex; flex-direction: column; gap: 14px; @@ -7,7 +9,6 @@ color: var(--dsw-alias-label-primary); } -.catalogHeading h3, .status, .failure p { margin: 0; @@ -85,37 +86,21 @@ box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)) 18%, transparent); } -.catalogHeading { - display: flex; - align-items: baseline; - gap: 7px; - padding: 0 2px; -} - -.catalogHeading h3 { - font-size: 13px; - line-height: 20px; - font-weight: 600; -} - -.catalogHeading span { - font-size: 12px; - line-height: 18px; - color: var(--dsw-alias-label-tertiary); - font-variant-numeric: tabular-nums; -} - .cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - align-items: start; gap: 10px; margin: 0; padding: 0; list-style: none; } +/* Cards stretch to their grid row's height. The button fills a stretched card: + the title row and description stay at the top, in line with the other card's, + and the spare height sits above the entry id chip or at the bottom. */ .card { + display: flex; + flex-direction: column; min-width: 0; overflow: hidden; border: 0.5px solid var(--dsw-alias-settings-card-stroke); @@ -127,11 +112,21 @@ border-color: var(--dsw-alias-border-l3); } +/* The other card in an expanded card's row keeps its own height instead of + stretching alongside the details. With two columns each row starts at an odd + position; in one column the matched card sits alone in its row, unaffected. */ +.card:nth-child(odd)[data-open='true'] + .card, +.card:nth-child(odd):has(+ .card[data-open='true']) { + align-self: start; +} + .cardContent { box-sizing: border-box; display: flex; + flex: 1 1 auto; flex-direction: column; align-items: stretch; + gap: 2px; width: 100%; min-height: 52px; border: 0; @@ -173,11 +168,35 @@ min-width: 0; font-size: 14px; line-height: 20px; - font-weight: 600; + font-weight: 500; } -/* The Loader entry id keeps one size in the card subtitle and the row it expands into; - the collapsed subtitle stays on the quieter label tier, while the expanded row carries +/* Two lines at most while collapsed; the expanded card shows the full text. */ +.cardDescription { + display: -webkit-box; + overflow: hidden; + color: var(--dsw-alias-label-tertiary); + font-size: 12px; + line-height: 18px; + text-wrap: pretty; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.card[data-open='true'] .cardDescription { + display: block; +} + +/* The entry id is metadata, not prose: a code chip in the card's bottom-left + corner whose left edge lines up with the title. */ +.cardMeta { + display: flex; + margin-top: auto; + padding-top: 6px; +} + +/* The Loader entry id keeps one size in the card chip and the row it expands into; + the collapsed chip stays on the quieter label tier, while the expanded row carries the card's primary value. */ .cardIdentity, .entryValue { @@ -188,15 +207,19 @@ .cardIdentity { display: block; - width: 100%; - color: var(--dsw-alias-label-tertiary); + box-sizing: border-box; + max-width: 100%; + border-radius: var(--dsw-radius-xs); + padding: 1px 6px; + background: var(--dsw-alias-bg-module-platform); + color: var(--dsw-alias-label-secondary); } .cardTrailing { display: inline-flex; flex: none; align-items: center; - gap: 7px; + gap: 8px; color: var(--dsw-alias-label-tertiary); } @@ -213,11 +236,13 @@ gap: 10px; } +/* Every group header takes the preset switcher's 36px height, so each subtitle + sits the same distance below its title. */ .groupTitleRow { display: flex; align-items: center; gap: 8px; - min-height: 32px; + min-height: 36px; } .group + .group { @@ -266,7 +291,7 @@ .groupSub { display: flex; flex-wrap: wrap; - gap: 4px 8px; + row-gap: 4px; margin: -6px 0 0 20px; color: var(--dsw-alias-label-tertiary); font-size: 12px; @@ -274,6 +299,13 @@ font-variant-numeric: tabular-nums; } +/* A middle dot with equal space on both sides separates the subtitle's parts. */ +.groupSub > * + *::before { + content: '·'; + padding: 0 6px; + color: var(--dsw-alias-label-tertiary); +} + .failedCount { color: var(--dsw-alias-state-error-primary); } @@ -330,7 +362,7 @@ padding: 8px 10px; background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent); color: var(--dsw-alias-state-error-primary); - font-size: 12.5px; + font-size: 12px; line-height: 18px; overflow-wrap: anywhere; white-space: pre-line; @@ -343,7 +375,7 @@ gap: 4px 8px; margin: 0; color: var(--dsw-alias-label-tertiary); - font-size: 12.5px; + font-size: 12px; line-height: 18px; } @@ -353,10 +385,35 @@ background: transparent; color: var(--dsw-alias-state-business-primary); font: inherit; - font-size: 12.5px; + font-size: 12px; cursor: pointer; } +/* Loading placeholder in the cards grid, sized like a card with one description + line; its bars pulse through the shared skeleton token as the input-trigger + menu's pending rows do. */ +.skeletonCard { + display: flex; + flex-direction: column; + gap: 8px; + border: 0.5px solid var(--dsw-alias-settings-card-stroke); + border-radius: var(--dsw-radius-xl); + padding: 15px 14px; + background: var(--dsw-alias-settings-card-fill); +} + +.skeletonBar { + width: 40%; + height: 14px; + border-radius: var(--dsw-radius-xs); + background: var(--dsw-alias-bg-skeleton); +} + +.skeletonBar + .skeletonBar { + width: 80%; + height: 12px; +} + .enabledIn { display: flex; @@ -430,9 +487,19 @@ .chevron { transition: transform 140ms var(--ds-ease-in-out); } + + .skeletonBar { + animation: plugin-inventory-skeleton 2s cubic-bezier(0.36, 0, 0.64, 1) infinite; + } } -@media (max-width: 680px) { +@keyframes plugin-inventory-skeleton { + 0% { opacity: 1; } + 40% { opacity: 0.6; } + 80%, 100% { opacity: 1; } +} + +@container plugin-inventory (max-width: 520px) { .cards { grid-template-columns: minmax(0, 1fr); } diff --git a/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.tsx b/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.tsx index c5064e5441..a86672f79f 100644 --- a/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.tsx +++ b/packages/client/ui-settings-plugin-inventory/src/client/PluginInventorySettingsTab.tsx @@ -58,6 +58,9 @@ const PHASE_KEYS = { unloading: 'unloading', } satisfies Record, PluginInventoryLocaleKey> +/** Placeholder cards the loading skeleton lays out in the cards grid. */ +const SKELETON_CARDS = [0, 1, 2, 3] as const + /** Localized accessible label for one root Fiber phase. */ function phaseLabel(phase: PluginFiberPhase, t: Translate): string { return phase === null ? t('unobserved') : t(PHASE_KEYS[phase]) @@ -77,6 +80,11 @@ function entrySubtitle(entryId: string): string { return entryId.replace(/^include:/, '') } +/** Accessible card name: the title, the entry id unless it repeats the title, then the enablement state. */ +function cardLabel(title: string, entryId: string | null, state: string): string { + return [title, ...entryId === null || entryId === title ? [] : [entryId], state].join(', ') +} + /** Preserve translated titles and shorten literal package or module name fallbacks in Settings. */ function pluginText(row: PluginInventoryEntry | AgentPresetRow, resolveText: PluginInventorySettingsTabInjected['resolveText']) { const title = row.meta?.title @@ -127,6 +135,7 @@ function PluginCard({ const open = expanded === rowKey const detailId = `plugin-details-${encodeURIComponent(rowKey)}` const descriptionId = useId() + const subtitle = entryId === null ? null : entrySubtitle(entryId) return (