@tianyulinfeng1/dsh-plugin-deepseek-chat 1.1.6 → 1.1.7

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/CHANGELOG.md CHANGED
@@ -2,12 +2,71 @@
2
2
 
3
3
  All notable changes to this project are documented here.
4
4
 
5
+ ## 1.1.7 — 2026-11-12
6
+
7
+ ### 新增右边栏卡片(dsh-better-sidebar)与设置页开关/命名
8
+
9
+ - **最简便配置:软依赖、零新增依赖**。不改 `package.json` 的
10
+ `dsh.client.inject`(那是 client module 图加载顺序,不是服务声明)、
11
+ 不改 `exports.inject`(仍 `["slots"]`)、不 value-import 任何外部包。
12
+ 运行时用宿主 guard 允许的 `ctx.get("betterSidebar")` 软探测:
13
+ 装了 dsh-better-sidebar 就注册,没装则静默跳过,左侧栏 / 顶栏 / 设置页照常。
14
+ - **`registerTab` 一张右边栏卡片**:`CardView` 直接复用 `ChatPanelShell`
15
+ (内嵌 chat.deepseek.com webview、走同一 guest 租约与固定持久分区),
16
+ `id = "@tianyulinfeng1/dsh-plugin-deepseek-chat:card"`(包前缀避免冲突,
17
+ `order: 60`、`single: true`、`dedupeKey` 返回同一 id),`icon` 复用 `ChatGlyph`。
18
+ `title: () => cardLabel()` 用 thunk——dsh-better-sidebar 每次渲染都重新求值,
19
+ 改名即时生效、无需重新注册。
20
+ - **开关与生命周期**:`showCard` 关 → dispose 旧 `registerTab`;开 → 重新注册;
21
+ disposer 由 `ctx.effect` 持有,插件卸载(HMR / 禁用)时注销,
22
+ 不留孤儿 TabDescriptor、下次激活不会抛 `already registered`。
23
+ - **设置页新增两项**(与顶栏标签栏的开关/名称彼此独立):
24
+ 「显示右边栏卡片」复选框、「右边栏卡片名称」输入框(默认「DeepSeek 聊天」,
25
+ 灰色占位提示、留空恢复默认)。`DEFAULTS` / `readConfig` / `writeConfig` 由
26
+ 两项扩为四项(`showTab` / `tabLabel` / `showCard` / `cardLabel`),
27
+ 归一化抽成 `normalizeLabel(value, fallback)`。
28
+ - **Verification**:`node --check lib/client.js` 通过;`npm test` 全部通过
29
+ (新增 better-sidebar 软探测缺席/在位、descriptor 字段、title thunk 改名、
30
+ 开关注销/重注册、effect cleanup 等断言;元信息版本号随 `package.json`
31
+ 同步到 `1.1.7`)。
32
+ - **Packaging**:版本 `1.1.7`;新增中文 `docs/RELEASE_NOTES_v1.1.7.md`
33
+ 并加入 `package.json` 的 `files` 列表。
34
+
35
+ ### CI 与文档(未发布,随 1.1.7 一并暂存)
36
+
37
+ - **设置页美化(v1.1.7)**:按「顶部标签栏 / 右边栏卡片 / 关于与检查更新」
38
+ 三个逻辑分组各包一张卡片,卡片用 `--dsw-alias-border-l3` 细边框、
39
+ `--dsw-radius-sm` 圆角与 `--dsw-alias-bg-layer-1` 浅底;输入框、按钮、
40
+ 状态行改用 `--dsw-alias-bg-base` / `--dsw-alias-bg-layer-2` 拉开层次。
41
+ 全部沿用 DSH 主题别名令牌,明暗主题自动适配,不写死任何颜色。
42
+ - **设置页对齐微调(v1.1.7)**:卡片上留白增大让「关于本插件」标题不再紧贴边框;
43
+ 基础信息 5 行改用严格两栏网格(标签列固定 88px、值列自适应、baseline 首行对齐、
44
+ 每行等距 5px),标签不换行——多行「功能简介」不再把整列挤得参差不齐。
45
+ - **README 插图**:在面板截图下方新增已登录的网页聊天界面截图
46
+ `docs/screenshot-chat.png`(图1),并加入 `package.json` 的 `files`。
47
+ - **CI 全面升到 Node 24(Active LTS)**:`.github/workflows/test.yml`
48
+ 矩阵改为 `[24]`、`publish.yml` 的 `node-version` 改为 `24`;
49
+ `checkout` / `setup-node` 由 `@v4` 升到 `@v7`(v7 的 Action 自身运行时为
50
+ `node24`,消除 GitHub「Node.js 20 已弃用、被强制在 Node.js 24 运行」的警告)。
51
+ `engines.node` 改为 `>=24`,
52
+ README 中英、CHANGELOG、各版本发布说明里的旧 Node 版本字样统一为 24。
53
+ - **删除 setup-node 的无效输入**:`publish.yml` 去掉 `registry-url`
54
+ (OIDC 模式不需要,且会写出无效的 `always-auth` 与
55
+ `NODE_AUTH_TOKEN: XXXXX` 占位符并触发 `npm warn Unknown user config "always-auth"`)。
56
+ npm 默认 registry 仍是 npmjs.org,OIDC 走 `id-token: write` 不受影响。
57
+ - **消除良性 E409 并发抢注**:`publish.yml` 的 `concurrency.group` 由
58
+ `npm-publish-${{ github.ref }}`(按 ref 分组)改为
59
+ `npm-publish-${{ github.repository }}`(按仓库串行)。同一 commit 被
60
+ `push main` 与 `push tag` 触发两次时,两个运行排队;先跑的发布成功后,
61
+ 后跑的 `npm view` 会看到已发布而跳过,避免两个 `npm publish` 并发抢注
62
+ 不可变版本导致 `409 Conflict`(先跑者已发成功、非鉴权失败)。
63
+
5
64
  ## 1.1.6 — 2026-11-11
6
65
 
7
66
  ### OIDC 可信发布(Trusted Publishing)链路测试
8
67
 
9
68
  - **`.github/workflows/publish.yml` 改为 OIDC 模式**:新增
10
- `permissions.id-token: write`(OIDC 必需)、`node-version: 22`(≥ 22.14.0)、
69
+ `permissions.id-token: write`(OIDC 必需)、`node-version: 24`(Node 24 Active LTS)、
11
70
  `npm install -g npm@latest`(npm CLI ≥ 11.5.1)、
12
71
  `npm publish --provenance --access public`;删除 `NODE_AUTH_TOKEN` 环境变量、
13
72
  `environment: npm-更新` 声明与 `NPM_TOKEN` 校验步骤;保留
package/README.en.md CHANGED
@@ -2,23 +2,30 @@
2
2
 
3
3
  English | [中文](README.md)
4
4
 
5
- > **Current version: v1.1.6** | Targets DSH **0.2.2-rc.1**
5
+ > **Current version: v1.1.7** | Targets DSH **0.2.2-rc.1**
6
6
 
7
- Adds **DeepSeek Chat** to the **DeepSeek Harness (DSH) desktop**: a sidebar icon entry and a conversation-header tab (default name **DeepSeek Chat**, configurable). Clicking either opens `chat.deepseek.com` filling the conversation area and remembers the last conversation. A **settings page** (with plugin metadata and a "Check for updates" button) lets you show/hide the header tab and rename it. The panel is **pure web chat** — no account status strip.
7
+ Adds **DeepSeek Chat** to the **DeepSeek Harness (DSH) desktop**: a sidebar icon entry, a conversation-header tab (default name **DeepSeek Chat**, configurable), and an optional **right-sidebar card** registered through `dsh-better-sidebar` when installed (default name **DeepSeek Chat**, independently configurable). Clicking any of them opens `chat.deepseek.com` filling the conversation area and remembers the last conversation. A **settings page** (with plugin metadata and a "Check for updates" button) lets you show/hide each surface and rename them. The panel is **pure web chat** — no account status strip.
8
8
 
9
9
  > Repository: [GitHub](https://github.com/sweet560/dsh-plugin-deepseek-chat) | [Gitee mirror](https://gitee.com/wtt520wyl/dsh-plugin-deepseek-chat)
10
10
 
11
11
  ![panel screenshot](docs/screenshot.png)
12
12
 
13
+ ![Chat page (signed in)](docs/screenshot-chat.png)
14
+
15
+ > The image above is the signed-in `chat.deepseek.com` new-chat home ("早上好,今天有什么计划?") as rendered inside the plugin's embedded webview; the image below shows where the panel sits in the DSH desktop app.
16
+
13
17
  ## Features
14
18
 
15
- - **Two entries coexist**:
19
+ - **Three entries coexist**:
16
20
  - A sidebar icon, placed between **Plugins** and **Scheduled tasks**, opening the full main area;
17
- - A tab in the conversation-header tab row ("Dialogue / Trajectory / Context / Compression / Team / Stats"), registered through the official `conversation.view` slot (`id: deepseek-chat-view`, `order: 100`), **default label "DeepSeek Chat"**.
18
- - **Settings page (v1.1.4)**: registered through the official `settings.section` slot (a "DeepSeek Chat" page in the Settings dialog). Two settings:
21
+ - A tab in the conversation-header tab row ("Dialogue / Trajectory / Context / Compression / Team / Stats"), registered through the official `conversation.view` slot (`id: deepseek-chat-view`, `order: 100`), **default label "DeepSeek Chat"**;
22
+ - **Right-sidebar card (v1.1.7)**: when `dsh-better-sidebar` is installed, a right-sidebar Tab is registered through `ctx.betterSidebar.registerTab` (`id: @tianyulinfeng1/dsh-plugin-deepseek-chat:card`, `order: 60`, single instance), **default label "DeepSeek Chat"**, reusing the web-chat webview. This is a **soft dependency**: with better-sidebar absent it is silently skipped and the other entries still work — no dependency or inject declaration changes.
23
+ - **Settings page (v1.1.4, extended to four settings in v1.1.7)**: registered through the official `settings.section` slot (a "DeepSeek Chat" page in the Settings dialog). Four settings (header tab and right-sidebar card are independent):
19
24
  - **Show header tab** (default on): turning it off removes the header tab, leaving only the sidebar entry;
20
- - **Header tab name** (default `DeepSeek Chat`): the field is **empty by default** and shows the default name only as a grey placeholder; click into it and type a new name directly (no need to delete the default text first). Empty falls back to the default, capped at 40 characters.
21
- - Settings are persisted in `localStorage` under `dsh-plugin-deepseek-chat:config` and take effect immediately (the tab row re-registers).
25
+ - **Header tab name** (default `DeepSeek Chat`): the field is **empty by default** and shows the default name only as a grey placeholder; click into it and type a new name directly (no need to delete the default text first). Empty falls back to the default, capped at 40 characters;
26
+ - **Show right-sidebar card** (v1.1.7, default on): turning it off unregisters the better-sidebar Tab;
27
+ - **Right-sidebar card name** (v1.1.7, default `DeepSeek Chat`): same grey-placeholder behaviour, empty restores the default, capped at 40 characters; `title` is a thunk so renaming takes effect immediately, independent of the header tab name.
28
+ - Settings are persisted in `localStorage` under `dsh-plugin-deepseek-chat:config` (`showTab` / `tabLabel` / `showCard` / `cardLabel`) and take effect immediately (tab row / card are registered or unregistered).
22
29
  - **About this plugin (v1.1.5)**: the Settings page shows the plugin version, author (`sweet560`), a short description, the open-source license (`MIT`), and two `https` links to the npm package page and the GitHub repository (opened by the desktop shell in the system browser).
23
30
  - **Check for updates (v1.1.5)**: the Settings page adds a "Check for updates" button that reads the latest published version from two public JSON endpoints — **npm first, GitHub as a fallback** (npm `dist-tags.latest`, `raw.githubusercontent.com` `package.json`). Versions are compared segment-by-segment numerically: up to date / new version available / check failed all produce a text status with npm/GitHub jump links. Repeated clicks are ignored while checking; network failures and both-source failures are handled gracefully. No Host route is added.
24
31
  - **Fills the conversation area (v1.1.4)**: while the header tab is selected, the web chat now fills the whole conversation area and covers DSH's own message list and bottom composer. Switching back to "Dialogue" restores them. The header entry uses an opaque absolutely-positioned overlay (`inset: 0`, `z-index: 50`) over the conversation body; the tab row in the header stays above it and remains clickable.
@@ -37,7 +44,11 @@ Adds **DeepSeek Chat** to the **DeepSeek Harness (DSH) desktop**: a sidebar icon
37
44
 
38
45
  > **v1.1.4**: adds a settings page (show/hide the header tab + a custom tab name, default "DeepSeek Chat"); fixes the header tab not filling the conversation area (DSH's composer was still visible below the web chat). The red-boxed title chip in the user's second screenshot is a chat.deepseek.com page element; per the user's confirmation it is left untouched — the plugin does not modify the third-party page.
39
46
 
40
- > **v1.1.5 (this release)**: adds an About block to Settings (version / author / description / license / npm & GitHub links) and a "Check for updates" button (npm first, GitHub fallback, numeric semver comparison, text status + jump links, timeout and failure fallback). The empty Host half is untouched.
47
+ > **v1.1.5**: adds an About block to Settings (version / author / description / license / npm & GitHub links) and a "Check for updates" button (npm first, GitHub fallback, numeric semver comparison, text status + jump links, timeout and failure fallback). The empty Host half is untouched.
48
+
49
+ > **v1.1.6**: a pure OIDC trusted-publishing pipeline test; same features as v1.1.5.
50
+
51
+ > **v1.1.7 (this release)**: adds a right-sidebar card following the `dsh-better-sidebar` model (soft `ctx.get('betterSidebar')` probe + `registerTab`, thunk `title` for instant renaming, disposer held by `ctx.effect`), plus a "Show right-sidebar card" switch and a "Right-sidebar card name" field in Settings (default "DeepSeek Chat", independent of the header tab). Zero new dependencies and no inject-declaration changes. See [docs/RELEASE_NOTES_v1.1.7.md](docs/RELEASE_NOTES_v1.1.7.md).
41
52
 
42
53
  ## Requirements
43
54
 
@@ -116,8 +127,9 @@ dsh plugin --profile desktop add file:"<absolute path to this repository>"
116
127
  - Within one app run, switching panels or sessions keeps you signed in (fixed partition since v1.1.1).
117
128
  - **Sign in again after restarting DSH**: DSH currently gives embedded browsers a random in-memory partition (`dsh-sidebar-browser-<uuid>`, no `persist:` prefix), so cookies are dropped when the process exits. This plugin does not modify DSH's installation files; to sign in once and stay signed in, apply the equal-length byte patch described in **[docs/login-persistence.md](docs/login-persistence.md)**.
118
129
  - The conversation URL is stored under the fixed key `dsh-plugin-deepseek-chat:last-url`, so the last conversation is restored after a restart (while signed in and while it still exists).
119
- - The header tab (default **"DeepSeek Chat"**) and the sidebar icon are the same web chat; switching to another tab unmounts this plugin's guest and releases the lease, and switching back reloads it (same partition, login state and conversation memory preserved).
120
- - **Settings page**: open the DSH **Settings** dialog → "DeepSeek Chat" in the left navigation. Toggle **Show header tab** and edit **Header tab name**. The name field is empty by default, showing "DeepSeek Chat" only as a grey placeholder; click into it and type directly without deleting any default text. Turning the tab off removes the header entry but keeps the sidebar icon; leaving the name empty restores the default "DeepSeek Chat". Settings apply immediately and are stored locally.
130
+ - The header tab (default **"DeepSeek Chat"**), the sidebar icon and the right-sidebar card are the same web chat; switching to another tab unmounts this plugin's guest and releases the lease, and switching back reloads it (same partition, login state and conversation memory preserved).
131
+ - **Settings page**: open the DSH **Settings** dialog → "DeepSeek Chat" in the left navigation. Independently toggle **Show header tab** / **Show right-sidebar card** and edit **Header tab name** / **Right-sidebar card name**. Both name fields are empty by default, showing "DeepSeek Chat" only as a grey placeholder; click into one and type directly without deleting any default text. Turning a surface off removes its entry but keeps the sidebar icon; leaving a name empty restores the default "DeepSeek Chat". Settings apply immediately and are stored locally.
132
+ - **Right-sidebar card**: requires `dsh-better-sidebar` installed and enabled (0.30.1 on this machine). It appears under Settings → Side cards → "+ Add Tab plugin". With better-sidebar absent the card is not shown and the other entries still work. Its name is a thunk, so renaming updates the card title immediately.
121
133
  - While the header tab is selected the web chat fills the conversation area and covers DSH's own messages/composer; clicking back to "Dialogue" restores them without losing conversation content.
122
134
 
123
135
  ## Persistent login (optional)
@@ -150,7 +162,8 @@ The current implementation guards every guest call behind an `isConnected` check
150
162
 
151
163
  | Version | Changes |
152
164
  |---|---|
153
- | **1.1.6** | **OIDC trusted-publishing pipeline test**: `.github/workflows/publish.yml` now enables `permissions.id-token: write`, Node 22, `npm@latest`, `npm publish --provenance`, and removes `NODE_AUTH_TOKEN` and the `environment` declaration. Version bumped to `1.1.6` and pushed to GitHub to test whether GitHub Actions can publish via npm Trusted Publisher. No functional change from 1.1.5. |
165
+ | **1.1.7** | **Adds a right-sidebar card (dsh-better-sidebar)**: soft `ctx.get('betterSidebar')` probe + `registerTab` (`id: @tianyulinfeng1/dsh-plugin-deepseek-chat:card`, `order: 60`, single instance, reuses the web-chat webview), thunk `title` for instant renaming, disposer held by `ctx.effect`; Settings gains a "Show right-sidebar card" switch and a "Right-sidebar card name" field (default "DeepSeek Chat", independent of the header tab). Zero new dependencies, no inject-declaration changes. Offline self-check passes fully (`RESULT: PASS`). See [docs/RELEASE_NOTES_v1.1.7.md](docs/RELEASE_NOTES_v1.1.7.md). |
166
+ | **1.1.6** | **OIDC trusted-publishing pipeline test**: `.github/workflows/publish.yml` now enables `permissions.id-token: write`, Node 24 (Active LTS), `npm@latest`, `npm publish --provenance`, and removes `NODE_AUTH_TOKEN` and the `environment` declaration. Version bumped to `1.1.6` and pushed to GitHub to test whether GitHub Actions can publish via npm Trusted Publisher. No functional change from 1.1.5. |
154
167
  | **1.1.5** | **Adds About info + Check for updates to Settings**: plugin version / author / description / license / npm & GitHub links, plus a "Check for updates" button (npm `dist-tags` first, GitHub `raw` fallback, numeric segment-by-segment version comparison, text status + jump links, 8-second timeout and failure fallbacks). Metadata constants are asserted equal to `package.json`; offline self-check passes fully (`RESULT: PASS`). |
155
168
  | **1.1.4** | **Adds a settings page + makes the header tab configurable + fills the conversation area**: a `settings.section` page (show/hide header tab, custom tab name default "DeepSeek Chat", localStorage persistence, immediate re-registration for the tab row); the header entry uses an opaque absolute overlay to fill the conversation body and cover the composer. The red-boxed in-page title chip is left untouched per user confirmation. Four slot registrations, offline self-check passes fully (`RESULT: PASS`). |
156
169
  | **1.1.3** | **Removes the account status display + adds a conversation-header tab**: deletes the account status strip, probe, 5-second polling and account-sync store; adds a `conversation.view` "Web Chat" header tab sharing the component and fixed partition with the sidebar/full-area entries; three slot registrations now use independent try/catch blocks. Offline self-check passes fully (`RESULT: PASS`). |
@@ -167,11 +180,11 @@ cd dsh-plugin-deepseek-chat
167
180
  node test/check-client.mjs
168
181
  ```
169
182
 
170
- `test/check-client.mjs` loads `lib/client.js` in a `node:vm` sandbox with fake React/DOM/bridge objects and checks four slot registrations (sidebar / full-area panel / header tab / settings page), the config layer (defaults, type validation, persistence, subscription, dynamic re-injection), the header overlay's fill styling, factory id equals package name, Web-surface inertness, shared fixed partition across both entries, pure web chat with no account strip, URL allowlisting, conversation memory, lease release, popup allowlisting and crash containment; a mock fetch verifies metadata constants against `package.json`, semver comparison, update-payload parsing, npm/GitHub fallback, all update-state branches, and the Settings-page click flow.
183
+ `test/check-client.mjs` loads `lib/client.js` in a `node:vm` sandbox with fake React/DOM/bridge objects and checks four slot registrations (sidebar / full-area panel / header tab / settings page), the **better-sidebar soft probe** (absent → silent skip; present → one card registered, thunk renaming, switch unregister/re-register, effect cleanup unregistration), the config layer (four settings: defaults, type validation, persistence, subscription, dynamic re-injection), the header overlay's fill styling, factory id equals package name, Web-surface inertness, shared fixed partition across both entries, pure web chat with no account strip, URL allowlisting, conversation memory, lease release, popup allowlisting and crash containment; a mock fetch verifies metadata constants against `package.json`, semver comparison, update-payload parsing, npm/GitHub fallback, all update-state branches, and the Settings-page click flow.
171
184
 
172
185
  ```
173
186
  lib/index.js host half (empty; it only gives the client bundle a Loader entry)
174
- lib/client.js browser half: entries, web-chat panel, full-area header overlay, settings page + config layer, about info / check-for-updates, webview lifecycle, crash containment
187
+ lib/client.js browser half: entries, web-chat panel, full-area header overlay, right-sidebar card (better-sidebar soft probe), settings page + config layer, about info / check-for-updates, webview lifecycle, crash containment
175
188
  cordis.patch.yml bundle patch that inserts this plugin's Loader row
176
189
  locale/{zh,en}.json Plugins-page title/description
177
190
  icon.svg Plugins-page icon
package/README.md CHANGED
@@ -2,23 +2,30 @@
2
2
 
3
3
  [English](README.en.md) | 中文
4
4
 
5
- > **当前版本:v1.1.6** |适配 DSH **0.2.2-rc.1**
5
+ > **当前版本:v1.1.7** |适配 DSH **0.2.2-rc.1**
6
6
 
7
- 在 **DeepSeek Harness(DSH)桌面版**加入 **DeepSeek 聊天**:左侧栏图标入口、会话顶栏标签页(默认名「DeepSeek 聊天」,可在设置页开关 / 自定义),点开后 `chat.deepseek.com` 铺满会话区,记住上次所在的会话;提供**设置页面**(含插件基础信息与「检查更新」按钮),面板为**纯网页聊天**,不显示账号状态。
7
+ 在 **DeepSeek Harness(DSH)桌面版**加入 **DeepSeek 聊天**:左侧栏图标入口、会话顶栏标签页(默认名「DeepSeek 聊天」,可在设置页开关 / 自定义),以及通过 **dsh-better-sidebar**(若已安装)注册的**右边栏卡片**(默认名「DeepSeek 聊天」,独立开关 / 自定义),点开后 `chat.deepseek.com` 铺满会话区,记住上次所在的会话;提供**设置页面**(含插件基础信息与「检查更新」按钮),面板为**纯网页聊天**,不显示账号状态。
8
8
 
9
9
  > 仓库:[GitHub](https://github.com/sweet560/dsh-plugin-deepseek-chat) | [Gitee 镜像](https://gitee.com/wtt520wyl/dsh-plugin-deepseek-chat)
10
10
 
11
11
  ![面板截图](docs/screenshot.png)
12
12
 
13
+ ![网页聊天界面(已登录)](docs/screenshot-chat.png)
14
+
15
+ > 上图为 `chat.deepseek.com` 已登录后的新对话首页(「早上好,今天有什么计划?」),即插件内嵌网页的实际呈现;下图为插件在 DSH 桌面版里的面板位置。
16
+
13
17
  ## 功能
14
18
 
15
- - **两个入口并存**:
19
+ - **三个入口并存**:
16
20
  - 左侧栏图标入口,排在「插件」和「自动化任务」之间,点开后占满主内容区;
17
- - 会话顶栏标签行(「对话 / 轨迹 / 上下文 / 压缩 / 编队 / 统计」那一行)新增标签,通过官方 `conversation.view` 槽注册(`id: deepseek-chat-view`、`order: 100`),**默认显示名「DeepSeek 聊天」**。
18
- - **设置页面(v1.1.4)**:通过官方 `settings.section` 槽注册「DeepSeek 聊天」设置页(左侧「设置」弹窗导航里)。两项设置:
21
+ - 会话顶栏标签行(「对话 / 轨迹 / 上下文 / 压缩 / 编队 / 统计」那一行)新增标签,通过官方 `conversation.view` 槽注册(`id: deepseek-chat-view`、`order: 100`),**默认显示名「DeepSeek 聊天」**;
22
+ - **右边栏卡片(v1.1.7)**:若已安装 `dsh-better-sidebar`,通过其 `ctx.betterSidebar.registerTab` 注册一张右边栏 Tab(`id: @tianyulinfeng1/dsh-plugin-deepseek-chat:card`、`order: 60`、单实例),**默认显示名「DeepSeek 聊天」**、直接复用网页聊天 webview。这是**软依赖**:没装 better-sidebar 时静默跳过、其余入口照常,无需改依赖或注入声明。
23
+ - **设置页面(v1.1.4,v1.1.7 扩为四项)**:通过官方 `settings.section` 槽注册「DeepSeek 聊天」设置页(左侧「设置」弹窗导航里)。四项设置(顶栏标签与右边栏卡片彼此独立):
19
24
  - **显示顶部标签栏**(默认开启):关闭后移除顶栏标签,只保留左侧栏入口;
20
- - **顶部标签栏名称**(默认 `DeepSeek 聊天`):输入框默认**空白**,以灰色占位提示显示默认名;点进去不用删、直接输入新名字即可,留空自动恢复默认,最长 40 字。
21
- - 配置存 `localStorage` 的 `dsh-plugin-deepseek-chat:config`,改完即时生效(标签行会重新注册)。
25
+ - **顶部标签栏名称**(默认 `DeepSeek 聊天`):输入框默认**空白**,以灰色占位提示显示默认名;点进去不用删、直接输入新名字即可,留空自动恢复默认,最长 40 字;
26
+ - **显示右边栏卡片**(v1.1.7,默认开启):关闭后注销 better-sidebar 的 Tab 注册;
27
+ - **右边栏卡片名称**(v1.1.7,默认 `DeepSeek 聊天`):同样是灰色占位提示、留空恢复默认、最长 40 字;`title` 用 thunk,改名即时生效,与顶栏名称互不影响。
28
+ - 配置存 `localStorage` 的 `dsh-plugin-deepseek-chat:config`(`showTab` / `tabLabel` / `showCard` / `cardLabel` 四项),改完即时生效(标签行 / 卡片会注册或注销)。
22
29
  - **关于本插件(v1.1.5)**:设置页展示插件版本号、作者(`sweet560`)、功能简介、开源许可证(`MIT`),以及 npm 插件页面 / GitHub 仓库两个 `https` 外链(点开由桌面壳用系统浏览器打开)。
23
30
  - **检查更新(v1.1.5)**:设置页新增「检查更新」按钮,从 **npm 优先、GitHub 兜底** 两个公开 JSON 接口读取最新发布版本号(npm `dist-tags.latest`、`raw.githubusercontent.com` 上的 `package.json`),按数字逐段比较版本:已是最新 / 发现新版本 / 检查失败都给出文字状态,并附 npm / GitHub 跳转链接。检查中禁用重复点击,网络不可用或两源都失败都有兜底,不新增 Host 路由。
24
31
  - **图3 修复:网页铺满会话区(v1.1.4)**:选中顶栏标签时,网页现在撑满整个会话区、盖住 DSH 自己的对话消息与底部输入框;切回「对话」标签即恢复。顶栏入口用不透明绝对定位浮层(`inset: 0`、`z-index: 50`)覆盖会话 body,header 里的标签行仍在上方、随时可点回。
@@ -37,7 +44,11 @@
37
44
 
38
45
  > **v1.1.4**:新增设置页(开关顶部标签栏 + 自定义标签名,默认「DeepSeek 聊天」);修复选中顶栏标签时网页没有铺满会话区、DSH 输入框仍露在下方的问题;图2 红框(网页内自带会话标题胶囊)经用户确认「先不处理」,插件不改动第三方网页。
39
46
 
40
- > **v1.1.5(本版)**:设置页新增「关于本插件」基础信息(版本号 / 作者 / 功能简介 / 许可证 / npm·GitHub 链接)与「检查更新」按钮(npm 优先、GitHub 兜底、语义版本比较、文字状态 + 跳转链接、失败兜底);不改动 Host 半侧。
47
+ > **v1.1.5**:设置页新增「关于本插件」基础信息(版本号 / 作者 / 功能简介 / 许可证 / npm·GitHub 链接)与「检查更新」按钮(npm 优先、GitHub 兜底、语义版本比较、文字状态 + 跳转链接、失败兜底);不改动 Host 半侧。
48
+
49
+ > **v1.1.6**:纯发布链路测试(OIDC 可信发布),功能与 1.1.5 相同。
50
+
51
+ > **v1.1.7(本版)**:按 `dsh-better-sidebar` 原理新增右边栏卡片(软探测 `ctx.get('betterSidebar')` + `registerTab`、`title` thunk 即时改名、`ctx.effect` 持有 disposer),设置页新增「显示右边栏卡片」开关与「右边栏卡片名称」输入框(默认「DeepSeek 聊天」,与顶栏独立);零新增依赖、不改注入声明。详见 [docs/RELEASE_NOTES_v1.1.7.md](docs/RELEASE_NOTES_v1.1.7.md)。
41
52
 
42
53
  ## 环境要求
43
54
 
@@ -116,8 +127,9 @@ dsh plugin --profile desktop add file:"<本仓库绝对路径>"
116
127
  - 本次 App 运行期间,切换面板/会话**不需要**重新登录(v1.1.1 起分区固定)。
117
128
  - **重启 DSH 后仍需要重新登录一次**:DSH 目前给内嵌浏览器的是随机内存分区(`dsh-sidebar-browser-<uuid>`,没有 `persist:` 前缀),进程退出即清空 cookie。本插件不修改 DSH 的安装文件;想做到「登录一次、长期免登录」,可按 **[docs/login-persistence.md](docs/login-persistence.md)** 做一次等长字节替换(含脚本、验证与回滚)。
118
129
  - 会话链接记在 App 的 localStorage(固定 key `dsh-plugin-deepseek-chat:last-url`),重启后仍会打开上次的会话(前提是已登录且该会话仍存在)。
119
- - 会话顶栏标签(默认 **「DeepSeek 聊天」**)与左侧栏图标是同一份网页聊天;切到别的标签时本插件的 guest 会被卸载并释放租约,切回来重新加载(分区不变,登录态与会话记忆保留)。
120
- - **设置页面**:打开 DSH「设置」弹窗 → 左侧导航「DeepSeek 聊天」,可勾选 / 取消「显示顶部标签栏」、修改「顶部标签栏名称」。名称输入框默认是空白的,只用灰色文字提示「DeepSeek 聊天」;点进输入框即可直接输入新名字,无需先删除默认文字。关闭标签栏后顶栏入口消失,但左侧栏图标仍可用;名字留空自动恢复默认「DeepSeek 聊天」。设置即时生效并保存在本机。
130
+ - 会话顶栏标签(默认 **「DeepSeek 聊天」**)、左边栏图标与右边栏卡片是同一份网页聊天;切到别的标签时本插件的 guest 会被卸载并释放租约,切回来重新加载(分区不变,登录态与会话记忆保留)。
131
+ - **设置页面**:打开 DSH「设置」弹窗 → 左侧导航「DeepSeek 聊天」,可分别勾选 / 取消「显示顶部标签栏」与「显示右边栏卡片」、分别修改「顶部标签栏名称」与「右边栏卡片名称」(互不影响)。两个名称输入框默认是空白的,只用灰色文字提示「DeepSeek 聊天」;点进输入框即可直接输入新名字,无需先删除默认文字。关闭标签栏后顶栏入口消失、关闭卡片后右边栏 Tab 被注销,但左侧栏图标始终可用;名字留空自动恢复默认「DeepSeek 聊天」。设置即时生效并保存在本机。
132
+ - **右边栏卡片**:需已安装并启用 `dsh-better-sidebar`(本机即 0.30.1)。在「设置 → 侧边卡片 → + 添加 Tab 插件」里能看到本插件注册的卡片;未安装 better-sidebar 时该卡片不出现,其它入口照常。卡片名称用 thunk,改名即时反映到卡片标题。
121
133
  - **关于与检查更新**:同页下方展示版本号、作者、功能简介、许可证与 npm / GitHub 链接;点「检查更新」会从 npm(失败则 GitHub)读取最新发布版本号。本地未发版的新版本会领先远端,显示「已是最新版本」;远端更高则显示「发现新版本 x.y.z」,结果下方附有跳转链接;网络不可用会显示失败原因。
122
134
  - 选中顶栏标签时网页铺满整个会话区,DSH 自己的对话消息与输入框被盖住;点回「对话」标签即恢复,不会丢失会话内容。
123
135
 
@@ -158,7 +170,8 @@ DSH 目前给内嵌浏览器分配的是**内存分区**(`dsh-sidebar-browser-
158
170
 
159
171
  | 版本 | 内容 |
160
172
  |---|---|
161
- | **1.1.6** | **OIDC 可信发布链路测试**:`.github/workflows/publish.yml` 启用 `permissions.id-token: write`、Node 22、`npm@latest`、`npm publish --provenance`,移除 `NODE_AUTH_TOKEN` 与 `environment` 声明;版本号升到 `1.1.6` 推送 GitHub,实测 GitHub Actions 能否经 npm Trusted Publisher 自动发布。功能与 1.1.5 相同,仅发布链路变更。 |
173
+ | **1.1.7** | **新增右边栏卡片(dsh-better-sidebar)**:`ctx.get('betterSidebar')` 软探测 + `registerTab`(`id: @tianyulinfeng1/dsh-plugin-deepseek-chat:card`、`order: 60`、复用网页聊天 webview),`title` thunk 即时改名、`ctx.effect` 持有 disposer;设置页新增「显示右边栏卡片」开关与「右边栏卡片名称」输入框(默认「DeepSeek 聊天」,与顶栏独立);零新增依赖、不改注入声明。离线自检全部通过(`RESULT: PASS`)。详见 [docs/RELEASE_NOTES_v1.1.7.md](docs/RELEASE_NOTES_v1.1.7.md)。 |
174
+ | **1.1.6** | **OIDC 可信发布链路测试**:`.github/workflows/publish.yml` 启用 `permissions.id-token: write`、Node 24(Active LTS)、`npm@latest`、`npm publish --provenance`,移除 `NODE_AUTH_TOKEN` 与 `environment` 声明;版本号升到 `1.1.6` 推送 GitHub,实测 GitHub Actions 能否经 npm Trusted Publisher 自动发布。功能与 1.1.5 相同,仅发布链路变更。 |
162
175
  | **1.1.5** | **设置页新增关于信息 + 检查更新**:新增插件版本号 / 作者 / 功能简介 / 许可证 / npm·GitHub 链接,以及「检查更新」按钮(npm `dist-tags` 优先、GitHub `raw` 兜底、数字逐段版本比较、文字状态 + 跳转链接、8 秒超时与失败兜底);元信息常量与 `package.json` 一致性断言、离线自检全部通过(`RESULT: PASS`)。 |
163
176
  | **1.1.4** | **新增设置页 + 顶栏标签可配置 + 网页铺满会话区**:新增 `settings.section` 设置页(开关顶部标签栏、自定义标签名默认「DeepSeek 聊天」,localStorage 持久化、即时重注入刷新标签行);顶栏标签用不透明绝对定位浮层铺满会话 body、盖住 composer 输入框;图2 网页内标题胶囊按用户确认不处理。四处槽位注册、离线自检全部通过(`RESULT: PASS`)。 |
164
177
  | **1.1.3** | **移除账号状态显示 + 新增会话顶栏标签页**:彻底删除账号状态条、账号探测、5 秒轮询、账号同步 store;新增 `conversation.view` 顶栏「网页聊天」标签(与左侧栏/全屏面板共用组件与固定分区);三处槽位注册改为各自独立 try/catch。离线自检全部通过(`RESULT: PASS`)。 |
@@ -175,13 +188,13 @@ cd dsh-plugin-deepseek-chat
175
188
  node test/check-client.mjs
176
189
  ```
177
190
 
178
- `test/check-client.mjs` 是离线自检:在 `node:vm` 里加载 `lib/client.js`,用假的 React/DOM/租约桥验证四处槽位注册(左侧栏 / 全屏主面板 / 顶栏标签页 / 设置页)、配置层(默认值、类型校验、持久化、订阅通知、动态重注入)、顶栏浮层的铺满样式、工厂 id 与包名一致、Web 表层 inert、两入口共用固定分区、纯网页聊天无账号条、URL 白名单、会话记忆、租约释放、弹窗白名单和崩溃隔离;并用 mock fetch 验证基础信息常量与 `package.json` 一致、语义版本比较、更新载荷解析、npm/GitHub 兜底、检查更新各状态分支与设置页点击联动。
191
+ `test/check-client.mjs` 是离线自检:在 `node:vm` 里加载 `lib/client.js`,用假的 React/DOM/租约桥验证四处槽位注册(左侧栏 / 全屏主面板 / 顶栏标签页 / 设置页)、**better-sidebar 软探测**(未装时静默跳过、已装时注册一张右边栏卡片、title thunk 改名、开关注销/重注册、effect cleanup 注销)、配置层(四项默认值、类型校验、持久化、订阅通知、动态重注入)、顶栏浮层的铺满样式、工厂 id 与包名一致、Web 表层 inert、两入口共用固定分区、纯网页聊天无账号条、URL 白名单、会话记忆、租约释放、弹窗白名单和崩溃隔离;并用 mock fetch 验证基础信息常量与 `package.json` 一致、语义版本比较、更新载荷解析、npm/GitHub 兜底、检查更新各状态分支与设置页点击联动。
179
192
 
180
193
  目录:
181
194
 
182
195
  ```
183
196
  lib/index.js 宿主半侧(空实现,只为让 client bundle 有 Loader 入口)
184
- lib/client.js 浏览器半侧:入口、网页聊天面板、顶栏铺满浮层、设置页与配置层、关于信息/检查更新、webview 生命周期、崩溃隔离
197
+ lib/client.js 浏览器半侧:入口、网页聊天面板、顶栏铺满浮层、右边栏卡片(better-sidebar 软探测)、设置页与配置层、关于信息/检查更新、webview 生命周期、崩溃隔离
185
198
  cordis.patch.yml bundle 补丁:插入本插件的 Loader 行
186
199
  locale/zh.json 插件页显示名称/描述(中文)
187
200
  locale/en.json 插件页显示名称/描述(英文)
@@ -3,7 +3,7 @@
3
3
  **DeepSeek Chat panel for DeepSeek Harness Desktop — stable login session, DSH account status, and full ASAR integrity support for the restart-free login patch.**
4
4
 
5
5
  - Requires DSH Desktop **0.2.2-rc.1** or newer.
6
- - Requires Node >= 20.
6
+ - Requires Node >= 24 (Node 24 Active LTS).
7
7
 
8
8
  ## What's fixed
9
9
 
@@ -9,7 +9,7 @@
9
9
  | 项 | 要求 |
10
10
  |---|---|
11
11
  | DSH Desktop | `>= 0.2.2-rc.1`(含 `0.2.2-rc.1`) |
12
- | Node.js | `>= 20` |
12
+ | Node.js | `>= 24`(Node 24 Active LTS) |
13
13
  | 平台 | Windows / macOS / Linux 桌面壳 |
14
14
 
15
15
  ## ⚠️ 重点:修复 `crash-2026-11-09T08-27-01-314Z-web-boot.log` 中的 web boot 启动失败
@@ -9,7 +9,7 @@
9
9
  | 项 | 要求 |
10
10
  |---|---|
11
11
  | DSH Desktop | `>= 0.2.2-rc.1`(含 `0.2.2-rc.1`) |
12
- | Node.js | `>= 20` |
12
+ | Node.js | `>= 24`(Node 24 Active LTS) |
13
13
  | 平台 | Windows / macOS / Linux 桌面壳 |
14
14
 
15
15
  ## 版本更迭(1.1.2 → 1.1.5)
@@ -0,0 +1,178 @@
1
+ # dsh-plugin-deepseek-chat v1.1.7 发布说明
2
+
3
+ **发布日期**:2026-11-12
4
+ **npm 包名**:`@tianyulinfeng1/dsh-plugin-deepseek-chat`
5
+ **版本**:`1.1.7`(在 1.1.6 的 OIDC 可信发布链路测试之后,新增右边栏卡片能力)
6
+
7
+ ---
8
+
9
+ ## 一句话
10
+
11
+ 按 **dsh-better-sidebar** 的接入原理,新增一张**右边栏卡片**(复用网页聊天 webview),并在「DeepSeek 聊天设置」页新增**卡片开关**与**卡片名称**两项设置;接入做成**可选软依赖**,没装 better-sidebar 时其余入口照常、零新增依赖、零报错。
12
+
13
+ ---
14
+
15
+ ## 需求来源
16
+
17
+ 用户 m01901 的两点要求:
18
+
19
+ 1. 「根据 DSH-better-sidebar 插件的原理,新增一个右边栏卡片,看要怎么配置最为简便」;
20
+ 2. 「在 聊天设置界面,新增 右边栏卡片开关,及右边栏名称修改」。
21
+
22
+ 经中文选择卡片确认的范围:
23
+
24
+ - **卡片内容**:复用网页聊天(右边栏卡片内嵌 `chat.deepseek.com` webview);
25
+ - **默认值**:默认开启、名称「DeepSeek 聊天」(与顶栏标签名称彼此独立);
26
+ - **发布**:升 `1.1.7` 并推送发布,且生成 GitHub / Gitee Releases。
27
+
28
+ ---
29
+
30
+ ## 「最简便配置」是怎么做到的
31
+
32
+ 本版刻意**不改**下面三处,把改动压到最小:
33
+
34
+ | 不改的东西 | 原因 |
35
+ |---|---|
36
+ | `package.json` 的 `dsh.client.inject` | 它是 **client module 图的加载顺序依赖**(按包/行 id 查 `graphRows`),不是 Cordis 服务声明;往里加 `'betterSidebar'` 是错的 |
37
+ | `lib/client.js` 的模块级 `exports.inject`(仍 `["slots"]`) | 它是 `ctx.xxx` 直读服务的白名单;软探测不需要声明 |
38
+ | `dependencies` / `peerDependencies` / 构建配置 | 不 value-import 任何外部包,构建零耦合 |
39
+
40
+ 运行时改用宿主 guard **明确允许**的软探测:
41
+
42
+ ```js
43
+ let betterSidebar;
44
+ try {
45
+ if (typeof ctx.get === "function") betterSidebar = ctx.get("betterSidebar");
46
+ } catch { betterSidebar = void 0; }
47
+ ```
48
+
49
+ - `ctx.get('betterSidebar')` 在未安装时返回 `undefined`(`guard.ts` 的 `readService(name, false)` 不要求 inject 声明);
50
+ - 官方 `providers.ts:42` 原文即「Prefer `ctx.get(name)` with an undefined check; use inject only for hard dependencies.」;
51
+ - dsh-sentinel 是官方指南里同样的「可选软依赖」真实案例。
52
+
53
+ 因此:
54
+
55
+ - **已装** `dsh-better-sidebar`(本机 0.30.1,已确认 `ctx.provide('betterSidebar', service)` 存在)→ 注册一张卡片;
56
+ - **未装** → 静默跳过,左侧栏图标 / 会话顶栏标签 / 设置页 / 关于与检查更新全部照常。
57
+
58
+ ---
59
+
60
+ ## 新增:右边栏卡片
61
+
62
+ 通过 `ctx.betterSidebar.registerTab(descriptor)` 注册,**descriptor 只用了最必要的字段**:
63
+
64
+ | 字段 | 值 | 说明 |
65
+ |---|---|---|
66
+ | `id` | `@tianyulinfeng1/dsh-plugin-deepseek-chat:card` | 包前缀,避免与内置 6 个 tab(editor / git / subagent / sidechat / terminal-bottom / diff)冲突 |
67
+ | `title` | `() => cardLabel()` | **thunk**;better-sidebar 每次渲染都重新求值,改名即时生效,无需重新注册 |
68
+ | `description` | `COPY.label` | 「DeepSeek 聊天」 |
69
+ | `icon` | `CardGlyph`(复用 `ChatGlyph`) | 宿主 icon 默认 16px |
70
+ | `order` | `60` | 排在内置 tab 之后 |
71
+ | `single` / `dedupeKey` | `true` / `() => CARD_ID` | 单实例,避免重复打开 |
72
+ | `component` | `CardView` | 直接 `return react.createElement(ChatPanelShell)`,复用网页聊天 webview 与固定持久分区 |
73
+
74
+ 生命周期要点(对齐官方指南「注册必须包在 `ctx.effect` 里」):
75
+
76
+ - `registerTab` 的返回值是 disposer,由 `ctx.effect` 持有;插件卸载(HMR / 禁用)时自动注销,**不留孤儿 TabDescriptor、下次激活不会抛 `already registered`**;
77
+ - `showCard` 关 → dispose 旧注册;开 → 重新 `registerTab`;
78
+ - 不用 `available` 置灰(它不拦截 `openTab`、也不从 `+` 菜单隐藏),改用「注销 / 重注册」更干净。
79
+
80
+ 宿主承载面(已核对 `docs/host-contracts.md`):原生右侧栏 tab 体由 better-sidebar 的 `.nativeTabHost` 包一层确定高度的 `height:100%` 列 flex,本插件 `ChatPanelShell` 的根 `styles.root` 本就是 `display:flex; flex-direction:column; height:100%; min-height:0`,可直接复用。
81
+
82
+ ---
83
+
84
+ ## 新增:设置页两项设置
85
+
86
+ 设置页仍走官方 `settings.section` 槽。原两项(顶栏标签开关 + 名称)扩为**四项**,顶栏与右边栏彼此独立:
87
+
88
+ 1. **显示顶部标签栏**(`showTab`,默认开)——沿用旧逻辑(dispose + 重新 inject `conversation.view` 触发标签行刷新);
89
+ 2. **顶部标签栏名称**(`tabLabel`,默认「DeepSeek 聊天」)——灰色占位提示、留空恢复默认、最长 40 字;
90
+ 3. **显示右边栏卡片**(`showCard`,默认开)——关闭即注销 better-sidebar Tab;
91
+ 4. **右边栏卡片名称**(`cardLabel`,默认「DeepSeek 聊天」)——同样灰色占位、留空恢复默认、最长 40 字,改名即时生效。
92
+
93
+ 配置层:
94
+
95
+ - `CONFIG_KEY = "dsh-plugin-deepseek-chat:config"`(与会话记忆 key 分离);
96
+ - `DEFAULTS = { showTab: true, tabLabel: DEFAULT_TAB_LABEL, showCard: true, cardLabel: DEFAULT_CARD_LABEL }`;
97
+ - `readConfig()` 按 `typeof` 校验四项字段,损坏 JSON / 错误类型逐项回退默认;
98
+ - 名称归一化抽成 `normalizeLabel(value, fallback)`(`trim`、空回退、`slice(0, 40)`),`normalizeTabLabel` 变成它的别名;
99
+ - `writeConfig(patch)` 合并四项、`localStorage` 持久化、通知 `configListeners`;
100
+ - `apply(ctx)` 用单个 `sync()` 同时驱动 `syncView()` 与 `syncCard()`,由 `subscribeConfig(sync)` 订阅;`ctx.effect` 的清理闭包同时 `unsubscribe()` 并 dispose 顶栏注册与卡片注册。
101
+
102
+ ---
103
+
104
+ ## 版本更迭(历史,保持已修复)
105
+
106
+ - **v1.1.3**:彻底移除账号状态条、账号探测、5 秒轮询与账号同步 store;新增会话顶栏 `conversation.view` 标签;三处注入改各自独立 try/catch。
107
+ - **v1.1.4**:新增设置页(顶栏标签开关 + 自定义名称);顶栏标签用不透明绝对定位浮层(`inset: 0`、`z-index: 50`)铺满会话区、盖住 composer;图2 网页内标题胶囊按用户确认不处理;名称框改灰色占位 + 组件内 `draft` state。
108
+ - **v1.1.5**:设置页新增「关于本插件」(版本号 / 作者 / 功能简介 / 许可证 / npm·GitHub 链接)与「检查更新」按钮(npm 优先、GitHub 兜底、8 秒超时、数字逐段版本比较、文字状态 + 跳转链接)。
109
+ - **v1.1.6**:纯 OIDC 可信发布链路测试(`.github/workflows/publish.yml` 启用 `id-token: write`、Node 24(Active LTS)、`npm@latest`、`--provenance`),功能与 1.1.5 相同。
110
+ - **v1.1.2 的 web boot 崩溃**(历史,保持已修复):工厂 id 仍为完整包名 `@tianyulinfeng1/dsh-plugin-deepseek-chat`,「工厂 id 必须等于 `package.json` 的 `name`」回归断言继续通过。
111
+
112
+ ---
113
+
114
+ ## 逐文件改动
115
+
116
+ | 文件 | 改动 |
117
+ |---|---|
118
+ | `lib/client.js` | 头注释升 1.1.7;`PLUGIN_VERSION = "1.1.7"`;新增 `CARD_ID` / `CARD_ORDER` / `DEFAULT_CARD_LABEL`;COPY 中英双语各新增 4 个设置文案、`infoSummary` 提到右边栏卡片;配置层扩为四项 + `normalizeLabel` / `cardLabel`;新增 `CardView` / `CardGlyph` / `cardDescriptor`;设置页新增两个控件(data 属性 `show-card` / `card-label`);`apply(ctx)` 加软探测与 `syncCard`、`ctx.effect` 清理;`exports.__test__` 导出新符号 |
119
+ | `test/check-client.mjs` | 配置层断言扩为四项;`makeCtx({ betterSidebar })` + 假 `registerTab` 服务;新增「better-sidebar 缺席 / 在位 / descriptor / title thunk 改名 / 开关注销重注册 / effect cleanup」断言;设置页用 data 属性精确定位两对控件 |
120
+ | `package.json` | `version` → `1.1.7`;description 提到「optional dsh-better-sidebar right-sidebar card」;`files` 新增本发布说明 |
121
+ | `locale/zh.json` / `locale/en.json` | 插件页描述提到右边栏卡片(可开关 / 可自定义名称) |
122
+ | `README.md` / `README.en.md` | 当前版本 1.1.7;功能段两入口→三入口;设置页四项;使用段与版本历史、开发段、目录树同步 |
123
+ | `CHANGELOG.md` | 顶部新增 `## 1.1.7 — 2026-11-12` 段 |
124
+ | `docs/RELEASE_NOTES_v1.1.7.md` | 本文件(新建) |
125
+
126
+ 未改动:`lib/index.js`(空宿主半侧)、`cordis.patch.yml`、`icon.svg`、login-persistence 脚本、`scripts/`、`.github/workflows/*`(workflow 本版不动)。
127
+
128
+ ---
129
+
130
+ ## Verification(本版验证)
131
+
132
+ - `node --check lib/client.js` → 通过;
133
+ - `npm test`(`node test/check-client.mjs`)→ 全部通过(`RESULT: PASS`),含:
134
+ - 工厂 id === `package.json` 的 `name`;
135
+ - 元信息常量(版本号等)与 `package.json` 一致;
136
+ - 桌面表层恰好四处槽位注册;Web 表层 inert;
137
+ - **better-sidebar 缺席**:四处表面照常、零卡片注册、零报错;
138
+ - **better-sidebar 在位**:注册 1 张卡片、descriptor 字段正确、title thunk 改名、showCard 开关注销/重注册、effect cleanup 注销;
139
+ - 设置页两对开关与灰色占位提示联动、顶栏标签动态重注入;
140
+ - 原有全部 guest 生命周期(首次访问 / 会话记忆 / SPA / 鉴权 / 根清除 / 卸载释放租约 / 失效回退 / 非法记忆 / DOM 移除后清理 / 崩溃隔离 / 反复开关 / 弹窗白名单 / 顶栏浮层铺满 / 两入口复用分区);
141
+ - 关于信息 + 检查更新(npm 优先 GitHub 兜底、语义版本、各状态分支);
142
+ - `git diff --check` 无空白错误;
143
+ - `npm pack --dry-run` 产物含 `docs/RELEASE_NOTES_v1.1.7.md`。
144
+
145
+ ---
146
+
147
+ ## 升级 / 安装
148
+
149
+ ```powershell
150
+ # npm(推荐)
151
+ dsh plugin --profile desktop add @tianyulinfeng1/dsh-plugin-deepseek-chat
152
+
153
+ # GitHub
154
+ github:sweet560/dsh-plugin-deepseek-chat
155
+
156
+ # Gitee 镜像
157
+ https://gitee.com/wtt520wyl/dsh-plugin-deepseek-chat
158
+
159
+ # 本地 tgz
160
+ dsh plugin --profile desktop add file:"<dsh-plugin-deepseek-chat-1.1.7.tgz 绝对路径>"
161
+ ```
162
+
163
+ **环境要求**:DSH `>= 0.2.2-rc.1`、Node `>= 24`(Node 24 Active LTS)、Windows / macOS / Linux 桌面壳。右边栏卡片额外需要 `dsh-better-sidebar >= 0.30.0` 已安装并启用;不装也能用其余功能。
164
+
165
+ ---
166
+
167
+ ## Assets
168
+
169
+ - `dsh-plugin-deepseek-chat-1.1.7.tgz` —— `npm pack` 产物(`npm pack` 原生名为 `tianyulinfeng1-dsh-plugin-deepseek-chat-1.1.7.tgz`,发布时统一重命名)。
170
+
171
+ ---
172
+
173
+ ## Honest limitation(如实说明)
174
+
175
+ - **纯插件做不到让 `chat.deepseek.com` 复用 DSH 已登录账号**:DSH 桌面版账号凭据在 Host 进程,网页版登录态在 guest session 的 httpOnly cookie,没有兑换入口;首次仍需在网页里扫码登录一次。v1.1.3 起连「账号状态」展示也一并删除。
176
+ - **重启 DSH 后仍需重新登录网页版**(除非按 `docs/login-persistence.md` 打持久分区补丁);本插件不修改 DSH 安装文件。
177
+ - **纯 Web 表层(`dsh web` 在浏览器里打开)inert**:该站点 `frame-ancestors 'none'`,iframe 嵌不了,guest 租约桥也只有桌面壳提供。
178
+ - **右边栏卡片是可选软依赖**:未安装 / 未启用 `dsh-better-sidebar` 时卡片不出现,这是预期行为,不是故障。
Binary file
package/lib/client.js CHANGED
@@ -1,15 +1,17 @@
1
1
  /**
2
- * Browser half of dsh-plugin-deepseek-chat (v1.1.6).
2
+ * Browser half of dsh-plugin-deepseek-chat (v1.1.7).
3
3
  *
4
- * Three contributions, all showing chat.deepseek.com in an Electron <webview>:
4
+ * Four contributions, all showing chat.deepseek.com in an Electron <webview>:
5
5
  * - the global left-sidebar panel (a `sidebar.panellist` icon row + the root
6
6
  * `main` keyed slot), the same mechanism Plugins / Scheduled tasks use;
7
7
  * - a conversation top-bar tab registered in the `conversation.view` list
8
8
  * slot, so the web chat is reachable from the 对话 / 轨迹 / 上下文 … tab row
9
9
  * without leaving the current Session (this one can be switched off);
10
- * - a Settings page (`settings.section`) with two preferences: whether the
11
- * top-bar tab is shown, and the tab's display name (default "DeepSeek 聊天",
12
- * user-customizable).
10
+ * - a right-sidebar card registered through dsh-better-sidebar's
11
+ * `betterSidebar` service (see "Right-sidebar card" below);
12
+ * - a Settings page (`settings.section`) with four preferences: whether the
13
+ * top-bar tab is shown and its display name, and whether the right-sidebar
14
+ * card is shown and its display name (both default "DeepSeek 聊天").
13
15
  *
14
16
  * Filling the conversation area
15
17
  * -----------------------------
@@ -46,6 +48,21 @@
46
48
  * labels when the slot registry changes, so a label thunk alone cannot reflect
47
49
  * a renamed tab. Re-registering also drives the showTab on/off switch.
48
50
  *
51
+ * Right-sidebar card (v1.1.7)
52
+ * ---------------------------
53
+ * dsh-better-sidebar exposes a client-only `betterSidebar` service and renders
54
+ * every `registerTab()` descriptor as a DSH native right-sidebar tab. The
55
+ * cheapest, zero-dependency integration is a SOFT probe: `ctx.get('betterSidebar')`
56
+ * returns undefined when the plugin is not installed (the dynamic Cordis guard
57
+ * allows `ctx.get()` without declaring an inject), and `registerTab` is callable
58
+ * through the guarded service proxy. No package.json `dsh.client.inject`, no
59
+ * peer dependency and no `exports.inject` change is needed; the left-sidebar,
60
+ * top-bar tab and Settings page keep working on machines without better-sidebar.
61
+ * The disposer returned by registerTab is held by `ctx.effect`, so unloading
62
+ * this plugin removes the card. The card title is a thunk (re-evaluated on every
63
+ * render), so renaming it in Settings is immediate; toggling it off disposes
64
+ * the registration and toggling on re-registers it.
65
+ *
49
66
  * About & update check (v1.1.5)
50
67
  * ----------------------------
51
68
  * The Settings page also shows the plugin's metadata (version, author, license,
@@ -56,7 +73,7 @@
56
73
  * both hosts answer with permissive CORS. No Host-side route is added, so the
57
74
  * empty host half stays untouched. compareVersion() does a numeric semver
58
75
  * comparison rather than string equality, because a local unreleased build
59
- * (e.g. 1.1.6) can already be ahead of the published "latest" (1.1.2).
76
+ * (e.g. 1.1.7) can already be ahead of the published "latest" (1.1.2).
60
77
  *
61
78
  * No account awareness (v1.1.3)
62
79
  * -----------------------------
@@ -96,6 +113,10 @@ window.__ModuleLoader__.load({
96
113
  /** Cell key / position of the Settings page (settings.section list). */
97
114
  const SETTINGS_ID = "deepseek-chat-settings";
98
115
  const SETTINGS_ORDER = 48;
116
+ /** Native right-sidebar tab id (package-prefixed to avoid built-in clashes). */
117
+ const CARD_ID = "@tianyulinfeng1/dsh-plugin-deepseek-chat:card";
118
+ /** Position after better-sidebar's built-in tabs (editor 10 … sidechat 35). */
119
+ const CARD_ORDER = 60;
99
120
  const CHAT_URL = "https://chat.deepseek.com/";
100
121
 
101
122
  /**
@@ -104,7 +125,7 @@ window.__ModuleLoader__.load({
104
125
  * the offline self-test — the same pattern the factory id uses.
105
126
  */
106
127
  const PLUGIN_NAME = "@tianyulinfeng1/dsh-plugin-deepseek-chat";
107
- const PLUGIN_VERSION = "1.1.6";
128
+ const PLUGIN_VERSION = "1.1.7";
108
129
  const PLUGIN_AUTHOR = "sweet560";
109
130
  const PLUGIN_LICENSE = "MIT";
110
131
  const NPM_PAGE_URL = "https://www.npmjs.com/package/@tianyulinfeng1/dsh-plugin-deepseek-chat";
@@ -139,12 +160,16 @@ window.__ModuleLoader__.load({
139
160
  settingsTabLabel: "Top-bar tab name",
140
161
  settingsTabLabelHint: "Default: DeepSeek 聊天. Leave the box empty to restore the default.",
141
162
  settingsPlaceholder: "DeepSeek 聊天",
163
+ settingsEnableCard: "Show the right-sidebar card",
164
+ settingsEnableCardHint: "Register a right-sidebar tab through dsh-better-sidebar (if installed); turning it off removes the card.",
165
+ settingsCardLabel: "Right-sidebar card name",
166
+ settingsCardLabelHint: "Default: DeepSeek 聊天. Leave the box empty to restore the default. Independent of the top-bar tab name.",
142
167
  aboutTitle: "About this plugin",
143
168
  infoVersion: "Version",
144
169
  infoAuthor: "Author",
145
170
  infoLicense: "License",
146
171
  infoDescription: "Description",
147
- infoSummary: "Opens chat.deepseek.com from the sidebar and the conversation tab row: a switchable, renameable top-bar tab, a full-area web chat, and last-conversation memory.",
172
+ infoSummary: "Opens chat.deepseek.com from the left sidebar, the conversation tab row and a better-sidebar right-sidebar card: switchable, renameable entries, a full-area web chat, and last-conversation memory.",
148
173
  infoLinks: "Links",
149
174
  linkNpm: "npm package page",
150
175
  linkGithub: "GitHub repository",
@@ -174,12 +199,16 @@ window.__ModuleLoader__.load({
174
199
  settingsTabLabel: "顶部标签栏名称",
175
200
  settingsTabLabelHint: "默认:DeepSeek 聊天;留空即恢复默认。",
176
201
  settingsPlaceholder: "DeepSeek 聊天",
202
+ settingsEnableCard: "显示右边栏卡片",
203
+ settingsEnableCardHint: "通过 dsh-better-sidebar(若已安装)注册一张右边栏卡片;关闭后移除该卡片。",
204
+ settingsCardLabel: "右边栏卡片名称",
205
+ settingsCardLabelHint: "默认:DeepSeek 聊天;留空即恢复默认。与顶部标签栏名称相互独立。",
177
206
  aboutTitle: "关于本插件",
178
207
  infoVersion: "插件版本号",
179
208
  infoAuthor: "作者",
180
209
  infoLicense: "开源许可证",
181
210
  infoDescription: "功能简介",
182
- infoSummary: "在左侧栏与会话顶栏直接打开 chat.deepseek.com 网页聊天;顶栏标签可开关、可改名,网页铺满会话区并记住上次会话。",
211
+ infoSummary: "在左侧栏、会话顶栏与 better-sidebar 右边栏卡片直接打开 chat.deepseek.com 网页聊天;入口可开关、可改名,网页铺满会话区并记住上次会话。",
183
212
  infoLinks: "相关链接",
184
213
  linkNpm: "npm 插件页面",
185
214
  linkGithub: "GitHub 仓库",
@@ -196,10 +225,16 @@ window.__ModuleLoader__.load({
196
225
  updateCurrent: "当前版本"
197
226
  };
198
227
 
199
- /** localStorage key for the two user preferences (separate from last-url memory). */
228
+ /** localStorage key for the four user preferences (separate from last-url memory). */
200
229
  const CONFIG_KEY = "dsh-plugin-deepseek-chat:config";
201
230
  const DEFAULT_TAB_LABEL = "DeepSeek 聊天";
202
- const DEFAULTS = { showTab: true, tabLabel: DEFAULT_TAB_LABEL };
231
+ const DEFAULT_CARD_LABEL = "DeepSeek 聊天";
232
+ const DEFAULTS = {
233
+ showTab: true,
234
+ tabLabel: DEFAULT_TAB_LABEL,
235
+ showCard: true,
236
+ cardLabel: DEFAULT_CARD_LABEL
237
+ };
203
238
  const configListeners = new Set();
204
239
  let config = readConfig();
205
240
 
@@ -212,35 +247,49 @@ window.__ModuleLoader__.load({
212
247
  const parsed = JSON.parse(raw);
213
248
  if (parsed === null || typeof parsed !== "object") return result;
214
249
  if (typeof parsed.showTab === "boolean") result.showTab = parsed.showTab;
215
- if (typeof parsed.tabLabel === "string") result.tabLabel = normalizeTabLabel(parsed.tabLabel);
250
+ if (typeof parsed.tabLabel === "string") result.tabLabel = normalizeLabel(parsed.tabLabel, DEFAULT_TAB_LABEL);
251
+ if (typeof parsed.showCard === "boolean") result.showCard = parsed.showCard;
252
+ if (typeof parsed.cardLabel === "string") result.cardLabel = normalizeLabel(parsed.cardLabel, DEFAULT_CARD_LABEL);
216
253
  } catch {
217
254
  /* storage unavailable or corrupt: keep defaults */
218
255
  }
219
256
  return result;
220
257
  }
221
258
 
222
- /** Trim, bound length, and fall back to the default when empty. */
223
- function normalizeTabLabel(value) {
259
+ /** Trim, bound length, and fall back to the given default when empty. */
260
+ function normalizeLabel(value, fallback) {
224
261
  const trimmed = value.trim();
225
- if (trimmed.length === 0) return DEFAULT_TAB_LABEL;
262
+ if (trimmed.length === 0) return fallback;
226
263
  return trimmed.slice(0, 40);
227
264
  }
228
265
 
266
+ /** Backwards-compatible alias for the top-bar tab name. */
267
+ function normalizeTabLabel(value) {
268
+ return normalizeLabel(value, DEFAULT_TAB_LABEL);
269
+ }
270
+
229
271
  /** Snapshot of the in-memory preferences (authoritative after load). */
230
272
  function getConfig() {
231
273
  return { ...config };
232
274
  }
233
275
 
234
- /** Current tab label (never empty). */
276
+ /** Current top-bar tab label (never empty). */
235
277
  function tabLabel() {
236
278
  return config.tabLabel.length > 0 ? config.tabLabel : DEFAULT_TAB_LABEL;
237
279
  }
238
280
 
281
+ /** Current right-sidebar card name (never empty). */
282
+ function cardLabel() {
283
+ return config.cardLabel.length > 0 ? config.cardLabel : DEFAULT_CARD_LABEL;
284
+ }
285
+
239
286
  /** Merge a patch, persist it, and notify subscribers (Settings → tab re-injection). */
240
287
  function writeConfig(patch) {
241
288
  const next = { ...config };
242
289
  if (typeof patch.showTab === "boolean") next.showTab = patch.showTab;
243
- if (typeof patch.tabLabel === "string") next.tabLabel = normalizeTabLabel(patch.tabLabel);
290
+ if (typeof patch.tabLabel === "string") next.tabLabel = normalizeLabel(patch.tabLabel, DEFAULT_TAB_LABEL);
291
+ if (typeof patch.showCard === "boolean") next.showCard = patch.showCard;
292
+ if (typeof patch.cardLabel === "string") next.cardLabel = normalizeLabel(patch.cardLabel, DEFAULT_CARD_LABEL);
244
293
  config = next;
245
294
  try {
246
295
  window.localStorage.setItem(CONFIG_KEY, JSON.stringify(config));
@@ -636,6 +685,24 @@ window.__ModuleLoader__.load({
636
685
  overflow: "hidden",
637
686
  background: "var(--dsw-alias-bg-base)"
638
687
  },
688
+ // 三组卡片用「细边框 + 圆角 + 浅一档底色」把设置项分组;颜色全部取自
689
+ // DSH 主题别名令牌,明暗主题自动适配,不写死任何颜色。
690
+ settingsCard: {
691
+ boxSizing: "border-box",
692
+ margin: "0 0 14px",
693
+ // 上留白 12px:分组标题不再紧贴卡片上边框;左右 18px 让标签列与值列
694
+ // 都有稳定起点,两栏对齐更整齐。
695
+ padding: "12px 18px 14px",
696
+ border: ".5px solid var(--dsw-alias-border-l3)",
697
+ borderRadius: "var(--dsw-radius-sm)",
698
+ background: "var(--dsw-alias-bg-layer-1)"
699
+ },
700
+ settingsRowLast: {
701
+ display: "flex",
702
+ flexDirection: "column",
703
+ gap: "6px",
704
+ padding: "14px 0"
705
+ },
639
706
  settingsPage: {
640
707
  boxSizing: "border-box",
641
708
  maxWidth: "620px",
@@ -679,28 +746,31 @@ window.__ModuleLoader__.load({
679
746
  padding: "6px 10px",
680
747
  border: ".5px solid var(--dsw-alias-border-l3)",
681
748
  borderRadius: "var(--dsw-radius-sm)",
682
- background: "var(--dsw-alias-bg-layer-1)",
749
+ background: "var(--dsw-alias-bg-base)",
683
750
  color: "var(--dsw-alias-label-primary)",
684
751
  font: "var(--dsw-font-xxs-12)"
685
752
  },
686
753
  aboutSectionTitle: {
687
- margin: "18px 0 8px",
754
+ margin: "18px 0 10px",
688
755
  fontSize: "13px",
689
756
  fontWeight: 600
690
757
  },
758
+ // 严格两栏网格:标签列固定 88px、值列自适应;baseline 让标签与值的
759
+ // 首行基线对齐,多行值也不会把标签挤歪。每行等距 padding,行距均匀。
691
760
  aboutRow: {
692
- display: "flex",
693
- gap: "8px",
694
- padding: "3px 0",
761
+ display: "grid",
762
+ gridTemplateColumns: "88px minmax(0, 1fr)",
763
+ columnGap: "10px",
764
+ alignItems: "baseline",
765
+ padding: "5px 0",
695
766
  fontSize: "12px",
696
767
  lineHeight: "1.6"
697
768
  },
698
769
  aboutLabel: {
699
- flex: "0 0 88px",
770
+ whiteSpace: "nowrap",
700
771
  color: "var(--dsw-alias-label-tertiary)"
701
772
  },
702
773
  aboutValue: {
703
- flex: "1 1 auto",
704
774
  minWidth: "0",
705
775
  wordBreak: "break-word"
706
776
  },
@@ -712,7 +782,7 @@ window.__ModuleLoader__.load({
712
782
  },
713
783
  updateButton: {
714
784
  color: "var(--dsw-alias-label-primary)",
715
- background: "var(--dsw-alias-bg-layer-1)",
785
+ background: "var(--dsw-alias-bg-layer-2)",
716
786
  border: ".5px solid var(--dsw-alias-border-l3)",
717
787
  borderRadius: "var(--dsw-radius-sm)",
718
788
  padding: "6px 14px",
@@ -720,10 +790,10 @@ window.__ModuleLoader__.load({
720
790
  font: "var(--dsw-font-xxs-12)"
721
791
  },
722
792
  updateResult: {
723
- margin: "8px 0 0",
724
- padding: "8px 10px",
793
+ margin: "10px 0 0",
794
+ padding: "10px 12px",
725
795
  borderRadius: "var(--dsw-radius-sm)",
726
- background: "var(--dsw-alias-bg-layer-1)",
796
+ background: "var(--dsw-alias-bg-base)",
727
797
  fontSize: "12px",
728
798
  lineHeight: "1.7",
729
799
  color: "var(--dsw-alias-label-primary)"
@@ -1030,9 +1100,47 @@ window.__ModuleLoader__.load({
1030
1100
  }, react.createElement(PanelBoundary, null, react.createElement(ChatPanel)));
1031
1101
  }
1032
1102
 
1103
+ /**
1104
+ * The right-sidebar card body. better-sidebar wraps native tab bodies in a
1105
+ * `height:100%` column-flex host, so ChatPanelShell (root flex column,
1106
+ * height 100%) fills it just like the left-sidebar panel. The component is
1107
+ * created ONCE and reused; it ignores TabComponentProps (the web chat is
1108
+ * session-independent).
1109
+ */
1110
+ function CardView() {
1111
+ return react.createElement(ChatPanelShell);
1112
+ }
1113
+
1114
+ /** Native icon for the + menu / tab chip; drawn on currentColor. */
1115
+ function CardGlyph(size) {
1116
+ return react.createElement(ChatGlyph, { size });
1117
+ }
1118
+
1119
+ /**
1120
+ * Build the TabDescriptor for better-sidebar. `title` is a thunk so the
1121
+ * guide and tab chip re-read the current name on every render (renaming in
1122
+ * Settings is immediate, no re-registration needed). `single` + a constant
1123
+ * dedupeKey keep the card to one instance.
1124
+ */
1125
+ function cardDescriptor() {
1126
+ return {
1127
+ id: CARD_ID,
1128
+ title: () => cardLabel(),
1129
+ description: COPY.label,
1130
+ icon: CardGlyph,
1131
+ order: CARD_ORDER,
1132
+ single: true,
1133
+ dedupeKey: () => CARD_ID,
1134
+ component: CardView
1135
+ };
1136
+ }
1137
+
1033
1138
  /** One metadata row: a fixed-width label plus a value. */
1034
1139
  function infoRow(label, value) {
1035
- return react.createElement("div", { style: styles.aboutRow },
1140
+ return react.createElement("div", {
1141
+ "data-dsh-plugin-deepseek-chat": "info-row",
1142
+ style: styles.aboutRow
1143
+ },
1036
1144
  react.createElement("span", { style: styles.aboutLabel }, label),
1037
1145
  react.createElement("span", { style: styles.aboutValue }, value));
1038
1146
  }
@@ -1049,7 +1157,11 @@ window.__ModuleLoader__.load({
1049
1157
 
1050
1158
  /** The two jump links shown in the About block and after an update check. */
1051
1159
  function linkRow() {
1052
- return react.createElement("div", { style: styles.aboutRow },
1160
+ // 与 infoRow 共用同一两栏网格 + 标记:整列(含「相关链接」)严格对齐。
1161
+ return react.createElement("div", {
1162
+ "data-dsh-plugin-deepseek-chat": "info-row",
1163
+ style: styles.aboutRow
1164
+ },
1053
1165
  react.createElement("span", { style: styles.aboutLabel }, COPY.infoLinks),
1054
1166
  react.createElement("span", { style: styles.aboutValue },
1055
1167
  externalLink(NPM_PAGE_URL, COPY.linkNpm),
@@ -1084,14 +1196,18 @@ window.__ModuleLoader__.load({
1084
1196
  const rerender = () => state[1]((value) => value + 1);
1085
1197
  const current = getConfig();
1086
1198
  const update = getUpdateState();
1087
- // The default tab label is shown as a grey placeholder, never as a
1088
- // solid value the user must delete first. `draft` is component-local:
1089
- // it stays empty while the config holds the default, and follows what
1090
- // the user types. Clearing it makes writeConfig() fall back to the
1091
- // default again, so the box returns to its empty placeholder state.
1092
- const draftState = react.useState(current.tabLabel === DEFAULT_TAB_LABEL ? "" : current.tabLabel);
1093
- const draft = draftState[0];
1094
- const setDraft = draftState[1];
1199
+ // Both names show their default as a grey placeholder, never as a
1200
+ // solid value the user must delete first. Each `draft` is
1201
+ // component-local: it stays empty while the config holds the
1202
+ // default and follows what the user types. Clearing it makes
1203
+ // writeConfig() fall back to the default again, so the box returns to
1204
+ // its empty placeholder state (real React keeps this component mounted).
1205
+ const tabDraftState = react.useState(current.tabLabel === DEFAULT_TAB_LABEL ? "" : current.tabLabel);
1206
+ const tabDraft = tabDraftState[0];
1207
+ const setTabDraft = tabDraftState[1];
1208
+ const cardDraftState = react.useState(current.cardLabel === DEFAULT_CARD_LABEL ? "" : current.cardLabel);
1209
+ const cardDraft = cardDraftState[0];
1210
+ const setCardDraft = cardDraftState[1];
1095
1211
 
1096
1212
  const onToggle = (event) => {
1097
1213
  writeConfig({ showTab: !!event?.target?.checked });
@@ -1101,11 +1217,21 @@ window.__ModuleLoader__.load({
1101
1217
  const value = String(event?.target?.value ?? "");
1102
1218
  // Whitespace-only input resolves back to the default name, so keep
1103
1219
  // the box empty to let the grey placeholder show even without a
1104
- // remount (real React keeps this component mounted).
1105
- setDraft(value.trim().length === 0 ? "" : value);
1220
+ // remount.
1221
+ setTabDraft(value.trim().length === 0 ? "" : value);
1106
1222
  writeConfig({ tabLabel: value });
1107
1223
  rerender();
1108
1224
  };
1225
+ const onToggleCard = (event) => {
1226
+ writeConfig({ showCard: !!event?.target?.checked });
1227
+ rerender();
1228
+ };
1229
+ const onCardLabelChange = (event) => {
1230
+ const value = String(event?.target?.value ?? "");
1231
+ setCardDraft(value.trim().length === 0 ? "" : value);
1232
+ writeConfig({ cardLabel: value });
1233
+ rerender();
1234
+ };
1109
1235
  const onCheckUpdate = () => {
1110
1236
  void checkForUpdates(rerender);
1111
1237
  };
@@ -1114,51 +1240,85 @@ window.__ModuleLoader__.load({
1114
1240
 
1115
1241
  return react.createElement("div", { style: styles.settingsPage },
1116
1242
  react.createElement("h2", { style: styles.settingsTitle }, COPY.settingsTitle),
1117
- react.createElement("div", { style: styles.settingsRow },
1118
- react.createElement("label", { style: styles.settingsLabelRow },
1243
+
1244
+ // 第一组卡片:顶部标签栏(开关 + 名称)。
1245
+ react.createElement("div", { "data-dsh-plugin-deepseek-chat": "settings-card", style: styles.settingsCard },
1246
+ react.createElement("div", { style: styles.settingsRow },
1247
+ react.createElement("label", { style: styles.settingsLabelRow },
1248
+ react.createElement("input", {
1249
+ type: "checkbox",
1250
+ style: styles.settingsCheckbox,
1251
+ "data-dsh-plugin-deepseek-chat": "show-tab",
1252
+ checked: current.showTab !== false,
1253
+ onChange: onToggle
1254
+ }),
1255
+ COPY.settingsEnableTab),
1256
+ react.createElement("p", { style: styles.settingsHint }, COPY.settingsEnableHint)),
1257
+ react.createElement("div", { style: styles.settingsRowLast },
1258
+ react.createElement("label", { style: styles.settingsLabelRow }, COPY.settingsTabLabel),
1259
+ react.createElement("input", {
1260
+ type: "text",
1261
+ "data-dsh-plugin-deepseek-chat": "tab-label",
1262
+ style: styles.settingsInput,
1263
+ value: tabDraft,
1264
+ placeholder: COPY.settingsPlaceholder,
1265
+ maxLength: 40,
1266
+ onChange: onLabelChange
1267
+ }),
1268
+ react.createElement("p", { style: styles.settingsHint }, COPY.settingsTabLabelHint))),
1269
+
1270
+ // 第二组卡片:右边栏卡片(开关 + 名称)。
1271
+ react.createElement("div", { "data-dsh-plugin-deepseek-chat": "settings-card", style: styles.settingsCard },
1272
+ react.createElement("div", { style: styles.settingsRow },
1273
+ react.createElement("label", { style: styles.settingsLabelRow },
1274
+ react.createElement("input", {
1275
+ type: "checkbox",
1276
+ "data-dsh-plugin-deepseek-chat": "show-card",
1277
+ style: styles.settingsCheckbox,
1278
+ checked: current.showCard !== false,
1279
+ onChange: onToggleCard
1280
+ }),
1281
+ COPY.settingsEnableCard),
1282
+ react.createElement("p", { style: styles.settingsHint }, COPY.settingsEnableCardHint)),
1283
+ react.createElement("div", { style: styles.settingsRowLast },
1284
+ react.createElement("label", { style: styles.settingsLabelRow }, COPY.settingsCardLabel),
1119
1285
  react.createElement("input", {
1120
- type: "checkbox",
1121
- style: styles.settingsCheckbox,
1122
- checked: current.showTab !== false,
1123
- onChange: onToggle
1286
+ type: "text",
1287
+ "data-dsh-plugin-deepseek-chat": "card-label",
1288
+ style: styles.settingsInput,
1289
+ value: cardDraft,
1290
+ placeholder: COPY.settingsPlaceholder,
1291
+ maxLength: 40,
1292
+ onChange: onCardLabelChange
1124
1293
  }),
1125
- COPY.settingsEnableTab),
1126
- react.createElement("p", { style: styles.settingsHint }, COPY.settingsEnableHint)),
1127
- react.createElement("div", { style: styles.settingsRow },
1128
- react.createElement("label", { style: styles.settingsLabelRow }, COPY.settingsTabLabel),
1129
- react.createElement("input", {
1130
- type: "text",
1131
- style: styles.settingsInput,
1132
- value: draft,
1133
- placeholder: COPY.settingsPlaceholder,
1134
- maxLength: 40,
1135
- onChange: onLabelChange
1136
- }),
1137
- react.createElement("p", { style: styles.settingsHint }, COPY.settingsTabLabelHint)),
1138
-
1139
- // 关于本插件:版本号 / 作者 / 功能简介 / 许可证 / 相关链接。
1140
- react.createElement("h3", { style: styles.aboutSectionTitle }, COPY.aboutTitle),
1141
- infoRow(COPY.infoVersion, PLUGIN_VERSION),
1142
- infoRow(COPY.infoAuthor, PLUGIN_AUTHOR),
1143
- infoRow(COPY.infoDescription, COPY.infoSummary),
1144
- infoRow(COPY.infoLicense, PLUGIN_LICENSE),
1145
- linkRow(),
1146
-
1147
- // 检查更新:按钮 + 文字状态 + 跳转链接。
1148
- react.createElement("h3", { style: styles.aboutSectionTitle }, COPY.updateTitle),
1149
- react.createElement("button", {
1150
- type: "button",
1151
- "data-dsh-plugin-deepseek-chat": "check-update",
1152
- style: styles.updateButton,
1153
- disabled: update.status === "checking",
1154
- onClick: onCheckUpdate
1155
- }, COPY.checkUpdate),
1156
- react.createElement("div", {
1157
- "data-dsh-plugin-deepseek-chat": "update-status",
1158
- style: styles.updateResult
1159
- },
1160
- react.createElement("div", null, updateStatusLine(update)),
1161
- showLinks ? linkRow() : null));
1294
+ react.createElement("p", { style: styles.settingsHint }, COPY.settingsCardLabelHint))),
1295
+
1296
+ // 第三组卡片:关于本插件 + 检查更新。
1297
+ react.createElement("div", { "data-dsh-plugin-deepseek-chat": "settings-card", style: styles.settingsCard },
1298
+ // 关于本插件:版本号 / 作者 / 功能简介 / 许可证 / 相关链接。
1299
+ // 第一个分组标题:靠卡片上留白 + 2px 上边距下移,不再贴边框。
1300
+ react.createElement("h3", { style: { ...styles.aboutSectionTitle, margin: "2px 0 10px" } }, COPY.aboutTitle),
1301
+ infoRow(COPY.infoVersion, PLUGIN_VERSION),
1302
+ infoRow(COPY.infoAuthor, PLUGIN_AUTHOR),
1303
+ infoRow(COPY.infoDescription, COPY.infoSummary),
1304
+ infoRow(COPY.infoLicense, PLUGIN_LICENSE),
1305
+ linkRow(),
1306
+
1307
+ // 检查更新:按钮 + 文字状态 + 跳转链接。
1308
+ react.createElement("h3", { style: styles.aboutSectionTitle }, COPY.updateTitle),
1309
+ react.createElement("button", {
1310
+ type: "button",
1311
+ "data-dsh-plugin-deepseek-chat": "check-update",
1312
+ style: styles.updateButton,
1313
+ disabled: update.status === "checking",
1314
+ onClick: onCheckUpdate
1315
+ }, COPY.checkUpdate),
1316
+ react.createElement("div", {
1317
+ "data-dsh-plugin-deepseek-chat": "update-status",
1318
+ style: styles.updateResult
1319
+ },
1320
+ react.createElement("div", null, updateStatusLine(update)),
1321
+ showLinks ? linkRow() : null)));
1162
1322
  };
1163
1323
  }
1164
1324
 
@@ -1171,11 +1331,19 @@ window.__ModuleLoader__.load({
1171
1331
  * there. Each registration is isolated: one bad slot must not undo the
1172
1332
  * others.
1173
1333
  *
1174
- * The conversation.view tab is dynamic: its label comes from the Settings
1175
- * name, and its visibility from the showTab switch. The tab row only
1176
- * recomputes when the slot registry changes, so on any preference change we
1177
- * dispose that single injection and re-inject it (label thunk + showTab
1178
- * check). The main/sidebar/settings entries are registered once.
1334
+ * Two entries are dynamic and driven by the Settings preferences:
1335
+ * - the conversation.view top-bar tab (label from tabLabel(), visibility
1336
+ * from showTab). The tab row only recomputes when the slot registry
1337
+ * changes, so on any preference change we dispose that single injection
1338
+ * and re-inject it (label thunk + showTab check).
1339
+ * - the better-sidebar right-sidebar card (label via a title thunk so
1340
+ * renaming is immediate; visibility from showCard, enforced by
1341
+ * disposing/re-registering the tab descriptor). better-sidebar is an
1342
+ * OPTIONAL soft dependency: ctx.get("betterSidebar") returns undefined
1343
+ * when it is not installed, and the rest of the plugin is unaffected.
1344
+ * The disposer is owned by the fiber (unregister on HMR / unload).
1345
+ *
1346
+ * The main/sidebar/settings entries are registered once.
1179
1347
  * @param ctx - client plugin context.
1180
1348
  */
1181
1349
  function apply(ctx) {
@@ -1200,6 +1368,16 @@ window.__ModuleLoader__.load({
1200
1368
  }
1201
1369
  };
1202
1370
 
1371
+ // Soft-detect the optional better-sidebar service. Never declare it in
1372
+ // inject (that would make the WHOLE plugin fail to activate when the
1373
+ // optional package is absent). ctx.get() is the guard-approved way.
1374
+ let betterSidebar = void 0;
1375
+ try {
1376
+ if (typeof ctx.get === "function") betterSidebar = ctx.get("betterSidebar");
1377
+ } catch {
1378
+ betterSidebar = void 0;
1379
+ }
1380
+
1203
1381
  // Static entries: left-sidebar panel + full main panel + Settings page.
1204
1382
  register("main", { name: "main", key: PANEL_ID }, ChatPanelShell);
1205
1383
  register("sidebar.panellist", {
@@ -1235,12 +1413,55 @@ window.__ModuleLoader__.load({
1235
1413
  label: () => tabLabel()
1236
1414
  }, ViewTabShell);
1237
1415
  };
1238
- syncView();
1239
1416
 
1240
- const unsubscribe = subscribeConfig(syncView);
1417
+ // Dynamic entry: the better-sidebar right-sidebar card. Its title is a
1418
+ // thunk, so renaming needs no re-registration; the showCard switch is
1419
+ // implemented by disposing/re-registering the descriptor (an `available`
1420
+ // flag would only grey the + menu, not remove an open tab).
1421
+ let disposeCard = void 0;
1422
+ const syncCard = () => {
1423
+ if (typeof disposeCard === "function") {
1424
+ try {
1425
+ disposeCard();
1426
+ } catch {
1427
+ /* already gone */
1428
+ }
1429
+ }
1430
+ disposeCard = void 0;
1431
+ if (betterSidebar === void 0 || betterSidebar === null) return;
1432
+ if (typeof betterSidebar.registerTab !== "function") return;
1433
+ if (config.showCard === false) return;
1434
+ try {
1435
+ disposeCard = betterSidebar.registerTab(cardDescriptor());
1436
+ } catch (error) {
1437
+ try {
1438
+ console.error("dsh-plugin-deepseek-chat: right-sidebar card registration failed", error);
1439
+ } catch {
1440
+ /* console unavailable */
1441
+ }
1442
+ disposeCard = void 0;
1443
+ }
1444
+ };
1445
+
1446
+ // One subscription drives both dynamic surfaces. The effect owns both
1447
+ // disposers so an HMR/unload cleans up the better-sidebar tab too.
1448
+ const sync = () => {
1449
+ syncView();
1450
+ syncCard();
1451
+ };
1452
+ sync();
1453
+
1454
+ const unsubscribe = subscribeConfig(sync);
1241
1455
  if (typeof ctx.effect === "function") {
1242
1456
  try {
1243
- ctx.effect(() => unsubscribe, "dsh-plugin-deepseek-chat: settings sync");
1457
+ // ctx.effect(callback) runs the callback and keeps its RETURN value
1458
+ // as the fiber-owned disposer. Return a closure (do not run cleanup
1459
+ // now) so HMR / unload disposes the CURRENT registrations.
1460
+ ctx.effect(() => () => {
1461
+ unsubscribe();
1462
+ if (typeof disposeView === "function") disposeView();
1463
+ if (typeof disposeCard === "function") disposeCard();
1464
+ }, "dsh-plugin-deepseek-chat: settings sync");
1244
1465
  } catch {
1245
1466
  /* effect registration unavailable: the subscriber still lives */
1246
1467
  }
@@ -1257,7 +1478,11 @@ window.__ModuleLoader__.load({
1257
1478
  VIEW_ORDER,
1258
1479
  SETTINGS_ID,
1259
1480
  SETTINGS_ORDER,
1481
+ CARD_ID,
1482
+ CARD_ORDER,
1260
1483
  CONFIG_KEY,
1484
+ DEFAULT_TAB_LABEL,
1485
+ DEFAULT_CARD_LABEL,
1261
1486
  DEFAULTS,
1262
1487
  PLUGIN_NAME,
1263
1488
  PLUGIN_VERSION,
@@ -1273,10 +1498,15 @@ window.__ModuleLoader__.load({
1273
1498
  storageKeyFor,
1274
1499
  readConfig,
1275
1500
  getConfig,
1501
+ normalizeLabel,
1276
1502
  normalizeTabLabel,
1277
1503
  tabLabel,
1504
+ cardLabel,
1278
1505
  writeConfig,
1279
1506
  subscribeConfig,
1507
+ cardDescriptor,
1508
+ CardView,
1509
+ CardGlyph,
1280
1510
  compareVersion,
1281
1511
  versionFromPayload,
1282
1512
  readVersionFrom,
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "DeepSeek Chat",
4
- "description": "Adds a chat.deepseek.com panel to the sidebar and a configurable conversation-header tab (default \"DeepSeek Chat\"): fills the whole conversation area, remembers the last conversation, keeps a stable login session, and offers a settings page to toggle/rename the tab, show plugin metadata (version, author, description, license, npm/GitHub links), and check for updates against npm/GitHub — pure web chat with no account status strip."
4
+ "description": "Adds a chat.deepseek.com panel to the sidebar, a configurable conversation-header tab (default \"DeepSeek Chat\"), and an optional right-sidebar card registered through dsh-better-sidebar when installed (default \"DeepSeek Chat\", independently toggleable and renameable): fills the whole conversation area, remembers the last conversation, keeps a stable login session, and offers a settings page to toggle/rename both surfaces, show plugin metadata (version, author, description, license, npm/GitHub links), and check for updates against npm/GitHub — pure web chat with no account status strip."
5
5
  }
6
6
  }
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "DeepSeek 聊天",
4
- "description": "在左侧栏加入 chat.deepseek.com 聊天面板,并在会话顶栏新增可开关、可自定义名称的标签页(默认「DeepSeek 聊天」);选中时网页铺满会话区、盖住输入框,记住上次会话,固定登录会话不因切换面板而重新扫码;提供设置页(显示插件版本号、作者、功能简介、许可证、npm/GitHub 链接,并可从 npm/GitHub 检查更新),面板为纯网页聊天、不显示账号状态。"
4
+ "description": "在左侧栏加入 chat.deepseek.com 聊天面板、会话顶栏可开关可自定义名称的标签页(默认「DeepSeek 聊天」),以及通过 dsh-better-sidebar(若已安装)注册的可开关可自定义名称的右边栏卡片(默认「DeepSeek 聊天」);选中标签时网页铺满会话区、盖住输入框,记住上次会话,固定登录会话不因切换面板而重新扫码;提供设置页(显示插件版本号、作者、功能简介、许可证、npm/GitHub 链接,并可从 npm/GitHub 检查更新),面板为纯网页聊天、不显示账号状态。"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tianyulinfeng1/dsh-plugin-deepseek-chat",
3
- "version": "1.1.6",
4
- "description": "DeepSeek Chat (chat.deepseek.com) as a DSH sidebar panel and a configurable conversation-header tab: full main-area chat that fills the conversation area, last-conversation memory, a stable login session, crash containment, a settings page (show/hide tab + custom tab name, about info, and a check-for-updates button against npm/GitHub), and pure web chat with no account status strip.",
3
+ "version": "1.1.7",
4
+ "description": "DeepSeek Chat (chat.deepseek.com) as a DSH sidebar panel, a configurable conversation-header tab, and an optional dsh-better-sidebar right-sidebar card: full main-area chat that fills the conversation area, last-conversation memory, a stable login session, crash containment, a settings page (show/hide each surface + custom names, about info, and a check-for-updates button against npm/GitHub), and pure web chat with no account status strip.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "icon": "./icon.svg",
@@ -19,7 +19,9 @@
19
19
  "docs/RELEASE_NOTES_v1.1.1.md",
20
20
  "docs/RELEASE_NOTES_v1.1.2.md",
21
21
  "docs/RELEASE_NOTES_v1.1.5.md",
22
+ "docs/RELEASE_NOTES_v1.1.7.md",
22
23
  "docs/screenshot.png",
24
+ "docs/screenshot-chat.png",
23
25
  "icon.svg",
24
26
  "cordis.patch.yml",
25
27
  "README.md",
@@ -51,7 +53,7 @@
51
53
  },
52
54
  "homepage": "https://github.com/sweet560/dsh-plugin-deepseek-chat#readme",
53
55
  "engines": {
54
- "node": ">=20",
56
+ "node": ">=24",
55
57
  "dsh": ">=0.2.2-rc.1"
56
58
  },
57
59
  "dsh": {