diff --git a/docs/plans/2026-08-14-add-plugins-modal-design.md b/docs/plans/2026-08-14-add-plugins-modal-design.md index 9cb21df..458fab9 100644 --- a/docs/plans/2026-08-14-add-plugins-modal-design.md +++ b/docs/plans/2026-08-14-add-plugins-modal-design.md @@ -27,7 +27,7 @@ ### 3.1 数据层:两个目录文件 + 共享词汇 - `src/client/plugins-shared.ts`:`PluginEntry { id, name, url, description, install }` + `PLUGIN_TOPIC_URL`。 -- `src/client/plugins-tabs.ts`:`builtinTabPlugins`(当前为空,空态展示)。 +- `src/client/plugins-tabs.ts`:`builtinTabPlugins`(收录 dsh-sentinel——条件驱动的 agent 唤醒系统,注册 `dsh-sentinel:watches` Tab;install 用官方 github: 源一键命令)。 - `src/client/plugins-viewers.ts`:`builtinViewerPlugins`(种子:office 插件,install = `cd ~/.dsh && dsh plugin --profile web add @huanlin/dsh-plugin-better-sidebar-plugin-office`)。 ### 3.2 UI 层:`src/client/add-plugin-modal.tsx` + `SideCardSection` @@ -47,7 +47,7 @@ ## 4. 边界情况与失败模式 - **复制失败**:`writeClipboard` 尽力而为(navigator.clipboard → execCommand 兜底),失败静默;按钮反馈照常闪现。无会话/无终端/配额等一切前置条件均不涉及——复制与它们无关。 -- **空目录**:Tab 目录当前为空,弹窗渲染空态 + topic 按钮。 +- **空目录**:目录可为空(弹窗渲染空态 + topic 按钮);当前 Tab/Viewer 目录各有 1 条。 - **版本号**:v0.12.0(`package.json` / `dsh.plugin.json` / `service.ts` 的 `SIDEBAR_SERVICE_VERSION` 三处一致,manifest-consistency 测试守护)。 ## 5. 实施偏差记录 diff --git a/src/client/locales.ts b/src/client/locales.ts index 6d5915c..448171a 100644 --- a/src/client/locales.ts +++ b/src/client/locales.ts @@ -218,6 +218,7 @@ export const zh = { openPlugin: '跳转', copyInstall: '复制安装命令', pluginOfficeDesc: '为 better-sidebar 编辑器提供 Office 三件套预览(.docx / .xlsx / .pptx),把重型 Office 渲染库拆出主包、按需安装', + pluginSentinelDesc: '条件驱动的 agent 唤醒系统:文件/进程/端口/HTTP/命令/webhook 传感器,条件达成自动唤醒休眠会话;注册「哨兵」Tab 展示服务器全局监控表', } /** The en dictionary (key-set-equal to zh, enforced by the type annotation). */ @@ -429,6 +430,7 @@ export const en: Record = { openPlugin: 'Open', copyInstall: 'Copy install command', pluginOfficeDesc: 'Office-suite preview (.docx / .xlsx / .pptx) for the better-sidebar editor, keeping the heavy Office render libraries out of the core bundle', + pluginSentinelDesc: 'Condition-driven agent wakeup: file/process/port/http/command/webhook sensors wake dormant sessions when conditions fire; registers a "Sentinel" tab with the server-wide watch table', } /** diff --git a/src/client/plugins-tabs.ts b/src/client/plugins-tabs.ts index 372e5a4..a9bd73d 100644 --- a/src/client/plugins-tabs.ts +++ b/src/client/plugins-tabs.ts @@ -3,13 +3,25 @@ * shown in the "add tab plugin" modal (Side card settings → 侧边栏内容 grid * → the dashed card). Adding an entry: append one object here (unique * `id` = npm package name, `url` = GitHub repo, `description` = - * i18n-friendly, `install` = the full shell command pre-filled into the - * install terminal — it starts with `cd ~/.dsh` so the install runs with - * the DSH home as the working directory). Data integrity is guarded by - * `tests/plugin-list.spec.ts`. + * i18n-friendly (add a `pluginXxxDesc` key in locales.ts), `install` = the + * full one-line install script — it starts with `cd ~/.dsh` so the install + * runs with the DSH home as the working directory). Data integrity is + * guarded by `tests/plugin-list.spec.ts`. */ +import { t } from './locales.ts' import type { PluginEntry } from './plugins-shared.ts' -/** Tab-registration plugins (empty until a real one exists — the modal - * renders the empty state and points at the GitHub topic). */ -export const builtinTabPlugins: readonly PluginEntry[] = [] +/** Tab-registration plugins (alphabetical order). */ +export const builtinTabPlugins: readonly PluginEntry[] = [ + { + id: '@dsh-external/dsh-sentinel', + name: 'dsh-sentinel 唤醒系统', + url: 'https://github.com/fuhefei/dsh-sentinel', + description: () => t('pluginSentinelDesc'), + // The official one-line bundle-channel install (git source, build + // artifacts committed — no build step needed). The `github:…` form is + // the upstream's documented command, `cd ~/.dsh` keeps the profile + // context consistent with the other entries. + install: 'cd ~/.dsh && dsh plugin --profile web add "github:fuhefei/dsh-sentinel#v0.7.0"', + }, +] diff --git a/tests/add-plugin-modal.spec.tsx b/tests/add-plugin-modal.spec.tsx index 7cb5e7b..b1ec09b 100644 --- a/tests/add-plugin-modal.spec.tsx +++ b/tests/add-plugin-modal.spec.tsx @@ -48,11 +48,12 @@ describe('PluginListBody (render)', () => { expect(html).toContain('Copy') }) - it('tab kind renders its own empty state (no office entry)', () => { + it('tab kind renders the sentinel entry (its own catalog, no office entry)', () => { const store = createSidebarStore() const service = createBetterSidebarService(store) const html = renderToString(createElement(PluginListBody, { service, kind: 'tab' })) - expect(html).toContain('No plugins curated yet') + expect(html).toContain('dsh-sentinel 唤醒系统') + expect(html).toContain('github:fuhefei/dsh-sentinel') expect(html).not.toContain(officeEntry.name) }) }) @@ -75,7 +76,7 @@ describe('AddPluginModal wiring', () => { if (kind === 'viewer') { expect(html).toContain(officeEntry.name) } else { - expect(html).toContain('No plugins curated yet') + expect(html).toContain('dsh-sentinel 唤醒系统') } act(() => { root.unmount() }) container.remove() diff --git a/tests/plugin-list.spec.ts b/tests/plugin-list.spec.ts index d14909f..4677405 100644 --- a/tests/plugin-list.spec.ts +++ b/tests/plugin-list.spec.ts @@ -24,9 +24,10 @@ const catalogs: Array<[string, readonly PluginEntry[]]> = [ ] describe('builtin plugin catalogs', () => { - it('the viewer catalog has the seeded office plugin, the tab catalog does not', () => { + it('the viewer catalog has the office plugin, the tab catalog has the sentinel plugin', () => { const ids = (list: readonly PluginEntry[]): string[] => list.map(p => p.id) expect(ids(builtinViewerPlugins)).toContain('@huanlin/dsh-plugin-better-sidebar-plugin-office') + expect(ids(builtinTabPlugins)).toContain('@dsh-external/dsh-sentinel') expect(ids(builtinTabPlugins)).not.toContain('@huanlin/dsh-plugin-better-sidebar-plugin-office') })