dsh-super-kanban 0.3.0 → 0.4.0

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
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-11-05
11
+
12
+ UI refinement release: put the frequently used toolbar actions back on the
13
+ toolbar and bring back restrained emphasis and status marks without returning
14
+ to the earlier visual clutter.
15
+
16
+ ### Added
17
+
18
+ - **Toolbar actions are visible**: Settings and "open in right sidebar" are
19
+ now persistent icon buttons in the board header and the compact right-panel
20
+ header. The overflow menu keeps only **Export JSON**.
21
+ - **Restrained emphasis and status marks**: cards show a small status dot and a
22
+ state-tinted progress line (backlog neutral, in progress brand, blocked
23
+ amber, done green), colored priority text (P0 error, P1 amber), issue count,
24
+ artifact count, subtask progress and a low-key AI/rules marker. Column
25
+ headers and the summary line carry matching status dots.
26
+ - **Task drawer depth**: colored status/priority text, type icons for key
27
+ facts, severity-colored issues, and a brand-colored session link, while the
28
+ primary button and DSH typography stay neutral.
29
+ - **English README** (`README.en.md`) alongside the Chinese README.
30
+
31
+ ### Changed
32
+
33
+ - Color is now limited to dots, thin progress lines, small icons and short
34
+ labels; there are no colored blocks, gradients or glow effects. Light and
35
+ dark themes, the full board, the compact sidebar and narrow windows were all
36
+ checked against the DSH type scale and spacing.
37
+
10
38
  ## [0.3.0] - 2026-11-05
11
39
 
12
40
  First public release. Super Kanban turns the sessions of a DeepSeek Harness
@@ -55,5 +83,6 @@ workspace into a maintainable task board.
55
83
  with the local DSH account token; tokens are never logged or persisted by the
56
84
  plugin. See [SECURITY.md](./SECURITY.md).
57
85
 
58
- [Unreleased]: https://github.com/topmindspace/dsh-super-kanban/compare/v0.3.0...HEAD
86
+ [Unreleased]: https://github.com/topmindspace/dsh-super-kanban/compare/v0.4.0...HEAD
59
87
  [0.3.0]: https://github.com/topmindspace/dsh-super-kanban/releases/tag/v0.3.0
88
+ [0.4.0]: https://github.com/topmindspace/dsh-super-kanban/releases/tag/v0.4.0
package/README.en.md ADDED
@@ -0,0 +1,287 @@
1
+ # Super Kanban · AI Task Command Center
2
+
3
+ [![CI](https://github.com/topmindspace/dsh-super-kanban/actions/workflows/ci.yml/badge.svg)](https://github.com/topmindspace/dsh-super-kanban/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/dsh-super-kanban.svg)](https://www.npmjs.com/package/dsh-super-kanban)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ An AI task board that **lives inside the DeepSeek Harness UI**. It reads the real DSH sessions across your workspaces and infers each task's goal, progress, completion state and open issues — and you can also maintain the board entirely by hand. Every card can be traced back to a specific session and a specific sentence.
8
+
9
+ - **Where it lives**: "任务看板" (Task board) in the DSH sidebar, or the compact form in the right sidebar of a conversation.
10
+ - **Data**: sessions are scanned read-only on your machine; board data stays on your machine.
11
+ - **Intelligence**: reuses the model quota of the DSH account already signed in on this machine — zero configuration. Turn AI off and it falls back to local rule-based analysis.
12
+
13
+ **English** | [简体中文](README.md)
14
+
15
+ ![Task board (light)](docs/screenshots/board-light.png)
16
+
17
+ ![Board → task details → continue task demo](docs/screenshots/demo.gif)
18
+
19
+ ## Contents
20
+
21
+ - [Why Super Kanban](#why-super-kanban)
22
+ - [Features](#features)
23
+ - [Two layouts](#two-layouts)
24
+ - [Installation](#installation)
25
+ - [Usage](#usage)
26
+ - [AI and privacy](#ai-and-privacy)
27
+ - [Screenshots](#screenshots)
28
+ - [Compatibility](#compatibility)
29
+ - [FAQ](#faq)
30
+ - [Development and contributing](#development-and-contributing)
31
+ - [License](#license)
32
+
33
+ ## Why Super Kanban
34
+
35
+ You keep several workspaces and many sessions going in DSH at the same time, and task state is scattered across conversations: how far did this get? Where is it stuck? Was anything delivered? Did it quietly die?
36
+
37
+ Super Kanban turns those conversations into a maintainable board:
38
+
39
+ | Capability | Description |
40
+ | --- | --- |
41
+ | 🎯 Goal extraction | Derives the task title and goal from the real request in a session instead of using the whole prompt as a title |
42
+ | 📈 Progress estimation | Prefers todo completion inside the session, combined with deliverables and turn state |
43
+ | ✅ Completion detection | Combines the last assistant reply, deliverables (present / file writes) and whether the user asked for more changes |
44
+ | ⚠️ Issue detection | Tool errors, user corrections and unresolved questions are attached to the card as "issues" |
45
+ | 🔗 Traceability | Every card carries its source workspace / session / evidence timeline, with a jump back to the session |
46
+ | 🤝 Session hand-off | "Continue this task" builds a structured prompt and (after your confirmation) puts it in the original or a new session input — never sends automatically |
47
+ | ✅ Subtasks and goals | Subtask checklists (importable from session todos, writes back progress), task goal, and DSH Session Goal tracking |
48
+ | 📦 Artifact tracking | Collects deliverables and written files; click to open them in the DSH document preview |
49
+ | 🧾 Key facts first | Cards show a one-line goal summary; the drawer leads with 3–6 key facts and keeps raw evidence collapsed |
50
+ | ✍️ Manual control | Create, edit, drag between columns, archive, delete, lock fields — AI never overwrites what you locked |
51
+ | 🔄 Continuous maintenance | First scan in seconds, then incremental scans; new sessions join automatically and old tasks keep updating |
52
+ | 🧠 Explainable | Every inference carries a confidence and a one-line reason — no black-box status changes |
53
+
54
+ ## Features
55
+
56
+ ### Two analysis engines: better with AI, always usable offline
57
+
58
+ 1. **Collect (local)**: reads `workspace.json` and each session's `session.v4.jsonl.zstd`, parsing user requests, assistant replies, tool calls, todo snapshots, deliverables, errors and turn-end reasons.
59
+ 2. **Rules engine (always available, zero tokens)**: local heuristics derive status, progress, issues and evidence. When you are signed out, offline, or AI is disabled, the board keeps working.
60
+ 3. **LLM analysis (optional, online)**: sends a compressed session digest to the DeepSeek inference API and receives structured JSON (tasks, status, progress, issues, evidence, confidence, reason).
61
+ 4. **Merge (local)**: merges results back by session anchor; locked fields are never overwritten; every change is recorded in task history.
62
+
63
+ ### Token efficiency
64
+
65
+ - Session digests sent to the model are capped at **≤1.5KB per session**, containing only recent changes and key signals.
66
+ - Structured output is bounded: ≤6 key facts, ≤8 evidence items, `max_tokens` ≤ 2200, with per-session limits.
67
+ - **Incremental gating**: the model is called only when user messages increase, a turn ends, the session is new, or you explicitly re-analyze. Live turns are never re-analyzed, and there is a minimum interval per session.
68
+ - **Controllable budget**: per-scan and daily AI call limits are configurable; exceeding them automatically falls back to the rules engine. The settings page shows today's calls and tokens.
69
+ - Re-scanning already analyzed sessions results in **zero AI calls**.
70
+
71
+ ### Data and privacy
72
+
73
+ - Board data is written only to `~/.dsh/storages/super-kanban/` on your machine. No third-party servers, no telemetry.
74
+ - Online analysis sends **compressed digests only**: never full session text, never file contents, never account credentials.
75
+ - All AI features can be disabled in settings and take effect immediately, falling back to local rules.
76
+
77
+ ## Two layouts
78
+
79
+ The same board can be opened in two ways:
80
+
81
+ | Layout | How to open | Characteristics |
82
+ | --- | --- | --- |
83
+ | **Full-page panel** | "任务看板" in the left sidebar | Full four-column board + filters + stats line, best for focused review |
84
+ | **Right sidebar** | Board icon in the conversation header, or "任务看板" in the right sidebar's "+" tools list | Board next to the chat; width-adaptive: narrow (single column + status segments) / medium / wide (four columns) |
85
+
86
+ - **Right sidebar fullscreen**: the fullscreen control on the right sidebar header expands the board to the whole viewport; you can leave it at any time.
87
+ - **Switching**: the "open in right sidebar" icon in the full-page header, or the "open as full page" icon in the right sidebar board.
88
+ - **Entry preferences**: board settings → "Display and entry" offers "Both (default) / Full page only / Right sidebar only"; refresh the window after changing.
89
+
90
+ ## Installation
91
+
92
+ > Prerequisites: DSH desktop installed and opened at least once (`~/.dsh/profiles/desktop` exists); DSH version `>= 0.2.1-rc.5 < 0.3.0`.
93
+
94
+ ### Option A: Plugin market index (recommended)
95
+
96
+ > "Discover" comes from DSH's experimental plugin market capability: first enable **Plugin market** under **Plugins → Experimental plugins** and reload DSH as prompted. If your DSH build does not offer it, use Option B/C/D.
97
+
98
+ 1. DSH → **Plugins** → **Discover**
99
+ 2. **Add index source** and paste:
100
+ `https://topmindspace.github.io/dsh-super-kanban/index.json`
101
+ 3. Find **Super Kanban** in the list → **Install** → enable
102
+ 4. Press `Cmd+R` in the DSH window (restart DSH if needed)
103
+
104
+ > The market index becomes available after the first release is published to GitHub Pages; its content comes from this repository's `docs/`.
105
+
106
+ ### Option B: npm package
107
+
108
+ DSH → Plugins → Add plugin → enter the package name `dsh-super-kanban` (optionally with a version, e.g. `dsh-super-kanban@<version>`) → install → enable → reload the window.
109
+
110
+ > The npm package is published: `dsh-super-kanban` (https://www.npmjs.com/package/dsh-super-kanban). See [docs/MARKET.md](docs/MARKET.md) for the release and index-refresh flow.
111
+
112
+ ### Option C: Git repository
113
+
114
+ Plugins → Add plugin → paste `https://github.com/topmindspace/dsh-super-kanban` → install (DSH pins the source to a commit) → enable → reload the window.
115
+
116
+ ### Option D: Local directory / source install
117
+
118
+ For developers and users who want to track the source:
119
+
120
+ ```bash
121
+ git clone https://github.com/topmindspace/dsh-super-kanban.git
122
+ cd dsh-super-kanban
123
+ ./install.sh -y # link into the default desktop profile and register the plugin
124
+ ```
125
+
126
+ Or choose "local directory" in the DSH plugin page and point it at this repository root. After installing, reload the window and "任务看板" appears in the left sidebar.
127
+
128
+ ### Upgrade and uninstall
129
+
130
+ - **Upgrade**: market / npm / Git installs update from the plugin page; source installs run `git pull` and reload the window.
131
+ - **Uninstall**: remove it from the plugin page, or run `./uninstall.sh -y` in the source directory.
132
+ - Uninstalling **does not delete** board data; to wipe it, delete `~/.dsh/storages/super-kanban/`.
133
+
134
+ Detailed installation, upgrade and troubleshooting steps are in [docs/INSTALL.md](docs/INSTALL.md).
135
+
136
+ ## Usage
137
+
138
+ 1. Click **任务看板** in the left sidebar (or the board icon in a conversation header for the right-sidebar form).
139
+ 2. **Board**: four columns — Backlog / In progress / Blocked / Done; column headers show counts and status colors.
140
+ 3. **Cards**: status dot, title with an AI/rules marker, a one-line goal summary, source workspace and last-activity time, plus restrained priority, issue count, artifact count, subtask progress and a progress line.
141
+ 4. **Detail drawer** (click a card):
142
+ - One-line status summary (click "Edit" to change fields)
143
+ - **Key facts** (3–6 items: key progress / decisions / issues from the session)
144
+ - **Subtasks**: check / add / delete; "import todos from session"; progress writes back automatically
145
+ - **Issues / next steps**
146
+ - **Session linkage**: continue this task / open session
147
+ - **Artifacts**: click to open in the DSH right-sidebar document preview
148
+ - Evidence and history (collapsed by default)
149
+ 5. **Filters**: workspace, priority, origin, tags, keyword search, show archived.
150
+ 6. **Toolbar** (persistent in the header): search, scan, settings gear, open in right sidebar, new task; the "⋯" menu only contains "Export JSON".
151
+ 7. **Scan**: click the scan button to trigger an incremental scan immediately; progress is shown live.
152
+ 8. **Settings** (gear icon): AI toggle, auto-scan, scan interval, initial scan range, AI budget, display and entry, excluded workspaces.
153
+
154
+ ### How "Continue this task" works
155
+
156
+ 1. Open any card → click **"继续此任务"** in the drawer;
157
+ 2. The dialog contains a structured prompt (task goal / progress / key facts / issues / next steps / source session) that you can edit;
158
+ 3. Choose **original session** or **new session in the same workspace**;
159
+ 4. Click **"放入会话"**: DSH switches to the target session and puts the prompt into the input box — **it is never sent automatically**; review or edit it and press send yourself. You can also "copy only" or "polish with AI".
160
+
161
+ > The prompt is assembled locally from a template — **0 tokens by default**. Only "Polish with AI" spends one model call.
162
+
163
+ ### Manual and automatic side by side
164
+
165
+ - Automatic tasks are anchored to their source session; repeated scans update the same card instead of creating duplicates.
166
+ - Fields you edit and lock (editing locks by default) are never overwritten by AI; unlock them in the drawer to hand them back.
167
+ - Manual tasks (origin = manual) are never changed by automatic scans.
168
+ - New cards do not need a session; once linked, AI fills in the analysis on the next scan.
169
+
170
+ ### Shortcuts
171
+
172
+ | Key | Action |
173
+ | --- | --- |
174
+ | `/` | Focus search |
175
+ | `n` | New task |
176
+ | `Esc` | Close the topmost overlay / drawer |
177
+ | `Cmd+R` | Reload the DSH window (needed after plugin updates) |
178
+
179
+ ## AI and privacy
180
+
181
+ ### Data flow
182
+
183
+ ```
184
+ DSH sessions (local, read-only)
185
+ │ local parsing / rules engine
186
+ ▼
187
+ local board storage ──(optional)digest ≤1.5KB ──► DeepSeek inference API
188
+ │ │
189
+ └──────────── structured JSON ◄────────────────────┘
190
+ ```
191
+
192
+ ### What is never uploaded
193
+
194
+ - Full session transcripts, file contents, attachments
195
+ - Account credentials / tokens (read locally for the request header only, never included in the payload)
196
+ - Anything to third-party servers (no telemetry, no usage reporting)
197
+
198
+ ### Turning AI off
199
+
200
+ Settings gear in the board header → turn off "AI 联网研判" (online AI analysis); it takes effect immediately. The board keeps running on the local rules engine — no missing features, just coarser inference.
201
+
202
+ ### Cost
203
+
204
+ By default incremental scans only analyze **changed sessions**; the per-scan AI call limit defaults to 40 and the default model is `deepseek-flash`. On days without new conversations the cost is close to zero. You can lower the daily limit or disable AI entirely in settings.
205
+
206
+ ## Screenshots
207
+
208
+ | Board (light) | Board (dark) |
209
+ | --- | --- |
210
+ | ![Board light](docs/screenshots/board-light.png) | ![Board dark](docs/screenshots/board-dark.png) |
211
+
212
+ | Task details (light) | Task details (dark) |
213
+ | --- | --- |
214
+ | ![Task details light](docs/screenshots/drawer-light.png) | ![Task details dark](docs/screenshots/drawer-dark.png) |
215
+
216
+ | Continue-task dialog | Prompt placed into the session input |
217
+ | --- | --- |
218
+ | ![Continue task](docs/screenshots/continue-modal.png) | ![Prompt placed](docs/screenshots/continue-placed.png) |
219
+
220
+ | Right sidebar (narrow) | Right sidebar fullscreen |
221
+ | --- | --- |
222
+ | ![Right sidebar](docs/screenshots/side-panel.png) | ![Sidebar fullscreen](docs/screenshots/side-fullscreen.png) |
223
+
224
+ | Artifact preview entries | Settings (persistent gear) |
225
+ | --- | --- |
226
+ | ![Artifacts](docs/screenshots/drawer-artifacts.png) | ![Settings](docs/screenshots/settings-dual.png) |
227
+
228
+ | "⋯" menu (export JSON only) | 1280px window |
229
+ | --- | --- |
230
+ | ![Toolbar menu](docs/screenshots/toolbar-menu.png) | ![1280](docs/screenshots/board-1280.png) |
231
+
232
+ > All screenshots above use a fully fictional demo dataset (invented workspaces / sessions / tasks) and contain no real user or workspace information.
233
+
234
+ ## Compatibility
235
+
236
+ | Item | Requirement |
237
+ | --- | --- |
238
+ | DeepSeek Harness | `>= 0.2.1-rc.5 < 0.3.0` (verified on 0.2.1-rc.5 desktop) |
239
+ | Node.js | `>= 22.19.0` (uses the built-in `node:zlib` zstd API) |
240
+ | Platform | Verified on macOS desktop; other desktop platforms should work but are not individually verified |
241
+ | Third-party dependencies | None (plugin and tooling scripts are dependency-free) |
242
+
243
+ > The plugin relies on DSH's third-party plugin interfaces; major DSH upgrades may break compatibility. If the entry disappears after upgrading DSH, reinstall the plugin first — board data will not be lost.
244
+
245
+ ## FAQ
246
+
247
+ **Q: There is no "任务看板" in the left sidebar.**
248
+ A: Press `Cmd+R` to reload the window; restart DSH if it is still missing. If it remains missing, check the diagnostics on the DSH plugin page (a failed plugin load usually reports a reason).
249
+
250
+ **Q: Cards are not updating / still show an old status.**
251
+ A: Click the scan button in the header to trigger a manual scan, or check that "Auto scan" is enabled in settings.
252
+
253
+ **Q: The AI judgment is wrong.**
254
+ A: Open the card and edit the fields directly (editing locks them), or use "re-analyze" in the card menu to let AI look at the source session again.
255
+
256
+ **Q: I don't want certain workspaces on the board.**
257
+ A: Settings gear in the board header → Excluded workspaces (multi-select).
258
+
259
+ **Q: How much quota will this use?**
260
+ A: An incremental scan calls `deepseek-flash` once per **changed** session; the settings page shows today's calls and tokens. With AI disabled the cost is zero.
261
+
262
+ **Q: Where is the data? Is it lost after uninstalling?**
263
+ A: `~/.dsh/storages/super-kanban/` (`state.json` / `sessions-cache.json` / `backups/`). Uninstalling the plugin does not delete it.
264
+
265
+ **Q: Can I export the board?**
266
+ A: "⋯" in the board header → Export JSON, which downloads `super-kanban-YYYYMMDD.json`.
267
+
268
+ ## Development and contributing
269
+
270
+ - Zero third-party dependencies; tests run with `node --test`; `scripts/` contains validators, the privacy scanner and the market index generator.
271
+ - Common commands:
272
+
273
+ ```bash
274
+ npm test # unit tests
275
+ npm run validate # manifest, cordis patch, files and market index checks
276
+ npm run check:privacy # scan for personal paths / credentials / session ids
277
+ npm run market:index # regenerate docs/index.json
278
+ ```
279
+
280
+ - For the development setup, repository layout and debugging, see [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md).
281
+ - Architecture: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). Interface contract: [docs/CONTRACT.md](docs/CONTRACT.md). Visual spec: [docs/DESIGN.md](docs/DESIGN.md).
282
+ - Release and distribution flow (market index, npm, Git): [docs/MARKET.md](docs/MARKET.md).
283
+ - For security issues, please follow [SECURITY.md](SECURITY.md) and report privately instead of opening a public issue.
284
+
285
+ ## License
286
+
287
+ [MIT](LICENSE) © topmindspace
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Super Kanban · 智能任务指挥台
2
2
 
3
3
  [![CI](https://github.com/topmindspace/dsh-super-kanban/actions/workflows/ci.yml/badge.svg)](https://github.com/topmindspace/dsh-super-kanban/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/dsh-super-kanban.svg)](https://www.npmjs.com/package/dsh-super-kanban)
4
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
6
 
6
7
  一个**生长在 DeepSeek Harness 界面里**的 AI 任务看板:它读取你各个工作区的真实 DSH 会话,自动研判任务的目标、进度、完成情况与问题,也可以完全手动管理。每张任务卡都能追溯到具体会话与具体一句话。
@@ -9,8 +10,12 @@
9
10
  - **数据**:只读扫描本机 DSH 会话;看板数据保存在本机,不出本机
10
11
  - **智能**:复用本机 DSH 已登录账号的模型额度,零配置;关闭后自动降级为本地规则分析
11
12
 
13
+ **简体中文** | [English](README.en.md)
14
+
12
15
  ![任务看板(浅色)](docs/screenshots/board-light.png)
13
16
 
17
+ ![看板 → 任务详情 → 继续任务 演示](docs/screenshots/demo.gif)
18
+
14
19
  ## 目录
15
20
 
16
21
  - [它解决什么](#它解决什么)
@@ -41,7 +46,7 @@ Super Kanban 把这些对话转成一张可持续维护的看板:
41
46
  | 🤝 会话联动 | 「继续此任务」生成结构化提示词,确认后放入原会话或同工作区新会话输入框,不自动发送 |
42
47
  | ✅ 子任务与目标 | 子任务清单(可从会话待办导入、勾选自动回写进度)、任务目标、DSH Session Goal 追踪 |
43
48
  | 📦 产物跟踪 | 自动收集会话交付物与写出的文件,点击在 DSH 文档预览中打开 |
44
- | 🧾 关键事实优先 | 卡片只有 1 行关键事实;抽屉先看 3–6 条关键事实,完整证据默认折叠 |
49
+ | 🧾 关键事实优先 | 卡片先给一句话目标摘要;抽屉先看 3–6 条关键事实,完整证据默认折叠 |
45
50
  | ✍️ 手动维护 | 新建 / 编辑 / 拖拽换列 / 归档 / 删除 / 锁定字段;你改过的字段 AI 不再覆盖 |
46
51
  | 🔄 持续维护 | 首次约数秒完成扫描,之后按间隔增量扫描;新会话自动纳入,旧任务自动更新 |
47
52
  | 🧠 解释得清 | 每条研判带置信度与一句话理由,避免「黑箱改状态」 |
@@ -79,7 +84,7 @@ Super Kanban 把这些对话转成一张可持续维护的看板:
79
84
  | **右侧侧边栏** | 会话右上角看板图标,或会话右侧栏「+」工具列表里的「任务看板」 | 边聊边看板;宽度自适应:窄(单列 + 状态分段)/ 中 / 宽(四列) |
80
85
 
81
86
  - **右侧栏全屏**:右侧栏顶部的全屏按钮,面板铺满视口,仍可随时退出。
82
- - **互相切换**:全页看板「⋯ → 在右侧栏打开」;右侧栏看板「⋯ → 在全页面板打开」。
87
+ - **互相切换**:全页看板页头的「在右侧栏打开」图标;右侧栏看板的「在全页面板打开」图标。
83
88
  - **入口偏好**:看板「设置 → 显示与入口」可选「两者(默认)/ 仅全局面板 / 仅会话右侧栏」,修改后刷新窗口生效。
84
89
 
85
90
  ## 安装
@@ -100,9 +105,9 @@ Super Kanban 把这些对话转成一张可持续维护的看板:
100
105
 
101
106
  ### 方式 B:npm 包名
102
107
 
103
- DSH → 插件 → 添加插件 → 输入包名 `dsh-super-kanban`(可带版本,如 `dsh-super-kanban@0.3.0`)→ 安装 → 启用 → 刷新窗口。
108
+ DSH → 插件 → 添加插件 → 输入包名 `dsh-super-kanban`(可附加版本号,如 `dsh-super-kanban@<version>`)→ 安装 → 启用 → 刷新窗口。
104
109
 
105
- > npm 包在首个 Release 发布后可用。
110
+ > npm 包已发布:`dsh-super-kanban`(https://www.npmjs.com/package/dsh-super-kanban)。发布与索引更新流程见 [docs/MARKET.md](docs/MARKET.md)。
106
111
 
107
112
  ### 方式 C:Git 仓库
108
113
 
@@ -132,7 +137,7 @@ cd dsh-super-kanban
132
137
 
133
138
  1. 左侧栏点 **任务看板**(或会话右上角看板图标走右侧栏形态)。
134
139
  2. **看板**:四列——待办 / 进行中 / 受阻 / 已完成;列头显示数量与状态色。
135
- 3. **卡片**:标题、来源工作区、最近活动时间、优先级、子任务进度。
140
+ 3. **卡片**:状态点、标题与 AI/规则标记、一句话目标摘要、来源工作区与最近活动时间,以及克制的优先级、问题数、产物数、子任务进度与进度线。
136
141
  4. **详情抽屉**(点卡片打开):
137
142
  - 一句话状态摘要(点「编辑」可改字段)
138
143
  - **关键事实**(3–6 条,来自会话的关键进展 / 决策 / 问题)
@@ -142,8 +147,9 @@ cd dsh-super-kanban
142
147
  - **产物**:点击在 DSH 右侧栏文档预览中打开
143
148
  - 证据与历史(默认折叠)
144
149
  5. **筛选**:工作区、优先级、来源、标签、关键词搜索、显示归档。
145
- 6. **扫描**:右上角扫描按钮立即增量扫描;扫描进度实时显示。
146
- 7. **设置**:AI 开关、自动扫描、扫描间隔、初始扫描天数、AI 预算、显示与入口、排除工作区、导出 JSON。
150
+ 6. **工具栏**(页头常驻):搜索、扫描、设置齿轮、在右侧栏打开、新建;「⋯」只保留「导出 JSON」。
151
+ 7. **扫描**:点扫描按钮立即增量扫描;扫描进度实时显示。
152
+ 8. **设置**(齿轮图标):AI 开关、自动扫描、扫描间隔、初始扫描天数、AI 预算、显示与入口、排除工作区。
147
153
 
148
154
  ### 「继续此任务」怎么用
149
155
 
@@ -191,7 +197,7 @@ DSH 会话(本机只读)
191
197
 
192
198
  ### 关闭 AI
193
199
 
194
- 看板右上角「⋯ → 设置」→ 关闭「AI 联网研判」,立即生效。关闭后看板继续用本地规则引擎运行,功能不缺失,只是研判颗粒度更粗。
200
+ 看板页头的设置齿轮 → 关闭「AI 联网研判」,立即生效。关闭后看板继续用本地规则引擎运行,功能不缺失,只是研判颗粒度更粗。
195
201
 
196
202
  ### 成本
197
203
 
@@ -215,13 +221,13 @@ DSH 会话(本机只读)
215
221
  | --- | --- |
216
222
  | ![右侧栏形态](docs/screenshots/side-panel.png) | ![右侧栏全屏](docs/screenshots/side-fullscreen.png) |
217
223
 
218
- | 产物预览入口 | 设置 |
224
+ | 产物预览入口 | 设置(齿轮常驻) |
219
225
  | --- | --- |
220
226
  | ![产物](docs/screenshots/drawer-artifacts.png) | ![设置](docs/screenshots/settings-dual.png) |
221
227
 
222
- | 1280 宽窗口 | |
228
+ | 「⋯」菜单(仅导出 JSON) | 1280 宽窗口 |
223
229
  | --- | --- |
224
- | ![1280](docs/screenshots/board-1280.png) | |
230
+ | ![工具栏菜单](docs/screenshots/toolbar-menu.png) | ![1280](docs/screenshots/board-1280.png) |
225
231
 
226
232
  > 以上截图来自一套完全虚构的演示数据(虚构工作区 / 会话 / 任务),不含任何真实用户或工作区信息。
227
233
 
@@ -248,16 +254,16 @@ A:点右上角扫描按钮手动触发;或检查设置中的「自动扫描
248
254
  A:点开卡片直接改字段(编辑即锁定);也可以在卡片菜单里「重新研判」,让 AI 重新分析该任务的来源会话。
249
255
 
250
256
  **Q:不想让某些工作区进看板?**
251
- A:看板右上角「⋯ → 设置」→ 排除工作区(可多选)。
257
+ A:看板页头设置齿轮 → 排除工作区(可多选)。
252
258
 
253
259
  **Q:会消耗我多少额度?**
254
- A:扫描时对**有变化的**会话各调用一次 `deepseek-flash`;看板设置页可查看今日调用数与 tokens。关闭 AI 后完全零额度。
260
+ A:扫描时对**有变化的**会话各调用一次 `deepseek-flash`;设置页可查看今日调用数与 tokens。关闭 AI 后完全零额度。
255
261
 
256
262
  **Q:数据在哪里?卸载会丢吗?**
257
263
  A:`~/.dsh/storages/super-kanban/`(`state.json` / `sessions-cache.json` / `backups/`)。卸载插件不删除数据。
258
264
 
259
265
  **Q:能导出吗?**
260
- A:看板右上角「⋯ → 设置」→ 导出 JSON,下载 `super-kanban-YYYYMMDD.json`。
266
+ A:看板页头「⋯」→ 导出 JSON,下载 `super-kanban-YYYYMMDD.json`。
261
267
 
262
268
  ## 开发与贡献
263
269
 
package/client.js CHANGED
@@ -1265,6 +1265,73 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
1265
1265
  .sk-page.is-side.sk-tier-narrow .sk-narrow-list{padding-top:10px}
1266
1266
  .sk-page.is-side .sk-slimbar{padding-bottom:10px}
1267
1267
 
1268
+
1269
+ /* ======================= v0.4:克制的强调色 ======================= */
1270
+ .sk-page{--sk-brand-accent:var(--dsw-alias-state-business-primary,#4176e6)}
1271
+ .sk-dot{width:6px;height:6px;border-radius:50%;flex:none;display:inline-block}
1272
+ .sk-dot.is-total{background:color-mix(in srgb,var(--sk-ink) 45%,transparent)}
1273
+ .sk-dot.is-backlog{background:color-mix(in srgb,var(--sk-ink) 40%,transparent)}
1274
+ .sk-dot.is-in_progress{background:var(--sk-brand-accent)}
1275
+ .sk-dot.is-blocked{background:var(--dsw-alias-state-warn-primary,#f59e0b)}
1276
+ .sk-dot.is-done{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22c55e) 55%,transparent)}
1277
+ .sk-stats-group{display:inline-flex;align-items:center;gap:5px}
1278
+ .sk-stats-v3 .sk-status-dot{background:color-mix(in srgb,var(--sk-ink) 40%,transparent);box-shadow:none;width:6px;height:6px;opacity:1}
1279
+ .sk-stats-v3 .sk-status-dot.is-running{background:var(--sk-brand-accent);animation:skPulse 1.6s ease-in-out infinite}
1280
+ .sk-stats-v3 .sk-status-dot.is-error{background:var(--sk-red)}
1281
+ .sk-header-actions .sk-iconbtn-v3{width:28px;height:28px;border-radius:8px;color:color-mix(in srgb,var(--sk-ink) 62%,transparent)}
1282
+ .sk-header-actions .sk-iconbtn-v3:hover{background:color-mix(in srgb,var(--sk-ink) 6%,transparent);color:var(--sk-ink)}
1283
+ .sk-header-actions .sk-iconbtn-v3.is-primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border:1px solid transparent}
1284
+ .sk-header-actions .sk-iconbtn-v3.is-primary:hover{background:var(--dsw-alias-button-primary-hover);color:var(--dsw-alias-label-primary-foreground)}
1285
+ .sk-card-v3{padding:11px 14px 10px}
1286
+ .sk-card-v3.is-done{opacity:.7}
1287
+ .sk-card-v3.is-done .sk-card-title-v3{font-weight:500}
1288
+ .sk-card-v3-head{display:flex;align-items:flex-start;gap:7px;min-width:0}
1289
+ .sk-card-v3-head .sk-dot{margin-top:6px}
1290
+ .sk-card-v3-head .sk-card-title-v3{flex:1;min-width:0}
1291
+ .sk-ai-mark{font-size:11px;line-height:1;font-weight:500;letter-spacing:.02em;flex:none;white-space:nowrap;margin-top:4px}
1292
+ .sk-ai-mark.is-ai{color:color-mix(in srgb,var(--sk-brand-accent) 65%,transparent)}
1293
+ .sk-ai-mark.is-rules{color:color-mix(in srgb,var(--sk-ink) 45%,transparent)}
1294
+ .sk-card-meta-v3 .sk-card-metrics{gap:7px}
1295
+ .sk-card-metric{display:inline-flex;align-items:center;gap:3px;white-space:nowrap;color:color-mix(in srgb,var(--sk-ink) 45%,transparent);font-size:11px}
1296
+ .sk-card-metric .sk-icon{opacity:.9}
1297
+ .sk-card-metric.is-warn{color:var(--dsw-alias-state-warn-primary,#f59e0b)}
1298
+ .sk-card-metric.is-error{color:var(--sk-red)}
1299
+ .sk-card-metric.is-done{color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22c55e) 75%,transparent)}
1300
+ .sk-card-metrics .sk-prio-text{font-size:11px;font-weight:500}
1301
+ .sk-prio-text.is-p0{color:var(--sk-red)}
1302
+ .sk-prio-text.is-p1{color:var(--dsw-alias-state-warn-primary,#f59e0b)}
1303
+ .sk-prio-text.is-rest{color:color-mix(in srgb,var(--sk-ink) 45%,transparent);font-weight:400}
1304
+ .sk-card-progress-v3{height:3px;border-radius:1.5px;background:color-mix(in srgb,var(--sk-ink) 10%,transparent)}
1305
+ .sk-card-progress-v3 i{background:color-mix(in srgb,var(--sk-ink) 20%,transparent);border-radius:1.5px}
1306
+ .sk-card-v3.is-in_progress .sk-card-progress-v3 i{background:color-mix(in srgb,var(--sk-brand-accent) 75%,transparent)}
1307
+ .sk-card-v3.is-blocked .sk-card-progress-v3 i{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 75%,transparent)}
1308
+ .sk-card-v3.is-done .sk-card-progress-v3{display:block}
1309
+ .sk-card-v3.is-done .sk-card-progress-v3 i{background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22c55e) 55%,transparent)}
1310
+ .sk-col-dot{width:6px;height:6px;border-radius:50%;flex:none;margin-right:2px}
1311
+ .sk-col-head .sk-dot{margin-right:2px}
1312
+ .sk-summary-status{font-weight:600}
1313
+ .sk-summary-status.is-in_progress{color:var(--sk-brand-accent)}
1314
+ .sk-summary-status.is-blocked{color:var(--dsw-alias-state-warn-primary,#f59e0b)}
1315
+ .sk-summary-status.is-done{color:color-mix(in srgb,var(--dsw-alias-state-success-primary,#22c55e) 72%,transparent)}
1316
+ .sk-summary-prio{font-weight:500}
1317
+ .sk-summary-prio.is-p0{color:var(--sk-red)}
1318
+ .sk-summary-prio.is-p1{color:var(--dsw-alias-state-warn-primary,#f59e0b)}
1319
+ .sk-summary-prio.is-rest{color:color-mix(in srgb,var(--sk-ink) 55%,transparent);font-weight:400}
1320
+ .sk-fact{display:flex;align-items:flex-start;gap:8px;font-size:13px;line-height:1.5;color:color-mix(in srgb,var(--sk-ink) 78%,transparent);min-width:0}
1321
+ .sk-fact-icon{width:14px;height:14px;flex:none;display:grid;place-items:center;margin-top:2px;color:var(--fact-tone,color-mix(in srgb,var(--sk-ink) 45%,transparent))}
1322
+ .sk-fact-text{flex:1;min-width:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
1323
+ .sk-link-text{color:var(--sk-brand-accent)!important;text-decoration:none}
1324
+ .sk-link-text:hover{color:var(--sk-brand-accent);text-decoration:underline;text-underline-offset:2px}
1325
+ .sk-session-line .sk-session-icon{flex:none;color:color-mix(in srgb,var(--sk-ink) 55%,transparent);margin-top:1px}
1326
+ .sk-duo-item{display:flex;align-items:flex-start;gap:5px;font-size:12px;line-height:1.55;color:color-mix(in srgb,var(--sk-ink) 78%,transparent);overflow-wrap:anywhere}
1327
+ .sk-duo-item .sk-icon{flex:none;margin-top:2px;color:color-mix(in srgb,var(--sk-ink) 40%,transparent)}
1328
+ .sk-duo-item.is-warn{color:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 85%,var(--sk-ink))}
1329
+ .sk-duo-item.is-warn .sk-icon{color:var(--dsw-alias-state-warn-primary,#f59e0b)}
1330
+ .sk-duo-item.is-error{color:var(--sk-red)}
1331
+ .sk-duo-item.is-error .sk-icon{color:var(--sk-red)}
1332
+ .sk-page.is-side.sk-tier-narrow .sk-card-v3-head .sk-dot{margin-top:5px}
1333
+ .sk-page.is-side.sk-tier-narrow .sk-ai-mark{display:none}
1334
+
1268
1335
  `;
1269
1336
 
1270
1337
  function ensureStyle() {
@@ -1417,25 +1484,22 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
1417
1484
  function TaskCard({ task, onOpen, onDragStart, onDragEnd, dragging }) {
1418
1485
  const progress = progressOf(task);
1419
1486
  const subtasks = subtasksOf(task);
1420
- const issues = issueCount(task);
1487
+ const issueList = Array.isArray(task.issues) ? task.issues : [];
1488
+ const issues = issueList.length;
1421
1489
  const artifacts = artifactsOf(task);
1422
1490
  const priority = task.priority || "P2";
1423
1491
  const taskStatus = statusOf(task);
1424
1492
  const done = taskStatus === "done";
1425
- const blocked = taskStatus === "blocked";
1426
- const highIssue = (Array.isArray(task.issues) ? task.issues : []).some((issue) => issue && issue.severity === "high");
1493
+ const highIssue = issueList.some((issue) => issue && issue.severity === "high");
1427
1494
  const subtaskDone = subtasks.filter((item) => item.done).length;
1428
- const metric = subtasks.length
1429
- ? `${subtaskDone}/${subtasks.length}`
1430
- : issues > 0
1431
- ? `问题 ${issues}`
1432
- : artifacts.length
1433
- ? `${artifacts.length} 产物`
1434
- : "";
1495
+ const allSubtasksDone = subtasks.length > 0 && subtaskDone === subtasks.length;
1496
+ const engine = task.ai && task.ai.engine === "llm" ? "AI" : "规则";
1497
+ const showEngine = Boolean(task.ai) || task.origin === "auto" || task.origin === "auto+edited";
1498
+ const prioClass = priority === "P0" ? "is-p0" : priority === "P1" ? "is-p1" : "is-rest";
1435
1499
  return h(
1436
1500
  "article",
1437
1501
  {
1438
- className: cx("sk-card", "sk-card-v3", done && "is-done", dragging && "dragging"),
1502
+ className: cx("sk-card", "sk-card-v3", "is-" + taskStatus, done && "is-done", dragging && "dragging"),
1439
1503
  draggable: true,
1440
1504
  tabIndex: 0,
1441
1505
  onDragStart: (event) => {
@@ -1456,13 +1520,11 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
1456
1520
  h(
1457
1521
  "div",
1458
1522
  { className: "sk-card-v3-head" },
1459
- blocked || highIssue
1460
- ? h("span", {
1461
- className: cx("sk-card-warn-dot", highIssue && !blocked && "is-error"),
1462
- title: blocked ? "受阻" : "存在高优先级问题",
1463
- })
1464
- : null,
1523
+ h("span", { className: cx("sk-dot", "is-" + taskStatus), title: STATUS_META[taskStatus].label }),
1465
1524
  h("h3", { className: "sk-card-title-v3" }, task.title),
1525
+ showEngine
1526
+ ? h("span", { className: cx("sk-ai-mark", engine === "AI" ? "is-ai" : "is-rules"), title: engine === "AI" ? "AI 研判" : "规则推断" }, engine)
1527
+ : null,
1466
1528
  ),
1467
1529
  h(
1468
1530
  "div",
@@ -1473,11 +1535,19 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
1473
1535
  h(
1474
1536
  "span",
1475
1537
  { className: "sk-card-metrics" },
1476
- h("span", { className: cx("sk-prio-text", priority === "P0" && "is-p0") }, priority),
1477
- metric ? h("span", null, metric) : null,
1538
+ h("span", { className: cx("sk-prio-text", prioClass), title: "优先级" }, priority),
1539
+ issues > 0
1540
+ ? h("span", { className: cx("sk-card-metric", "is-warn", highIssue && "is-error"), title: `${issues} 个问题${highIssue ? "(含高优先级)" : ""}` }, h(Icon, { name: "alert", size: 10 }), issues)
1541
+ : null,
1542
+ artifacts.length > 0
1543
+ ? h("span", { className: "sk-card-metric", title: `${artifacts.length} 个产物` }, h(Icon, { name: "file", size: 10 }), artifacts.length)
1544
+ : null,
1545
+ subtasks.length > 0
1546
+ ? h("span", { className: cx("sk-card-metric", allSubtasksDone && "is-done"), title: `${subtaskDone}/${subtasks.length} 子任务` }, h(Icon, { name: "check", size: 10 }), `${subtaskDone}/${subtasks.length}`)
1547
+ : null,
1478
1548
  ),
1479
1549
  ),
1480
- done ? null : h("div", { className: "sk-card-progress-v3" }, h("i", { style: { width: `${progress}%` } })),
1550
+ h("div", { className: "sk-card-progress-v3" }, h("i", { style: { width: `${progress}%` } })),
1481
1551
  );
1482
1552
  }
1483
1553
 
@@ -1506,6 +1576,7 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
1506
1576
  h(
1507
1577
  "header",
1508
1578
  { className: "sk-col-head" },
1579
+ h("span", { className: cx("sk-dot", "is-" + status) }),
1509
1580
  h("span", { className: "sk-col-title" }, meta.label),
1510
1581
  h("span", { className: "sk-col-count" }, tasks.length),
1511
1582
  h("span", { className: "sk-col-head-spacer" }),
@@ -2128,9 +2199,10 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
2128
2199
  : h(
2129
2200
  "div",
2130
2201
  { className: "sk-summary-line" },
2131
- h("span", null, STATUS_META[statusOf(task)].label),
2202
+ h("span", { className: cx("sk-dot", "is-" + statusOf(task)) }),
2203
+ h("span", { className: cx("sk-summary-status", "is-" + statusOf(task)) }, STATUS_META[statusOf(task)].label),
2132
2204
  h("span", { className: "sk-sep" }, "·"),
2133
- h("span", null, task.priority || "P2"),
2205
+ h("span", { className: cx("sk-summary-prio", task.priority === "P0" ? "is-p0" : task.priority === "P1" ? "is-p1" : "is-rest") }, task.priority || "P2"),
2134
2206
  h("span", { className: "sk-sep" }, "·"),
2135
2207
  h("span", { className: "sk-muted" }, subtasks.length ? `${subtaskDone}/${subtasks.length} 子任务` : `进度 ${progress}%`),
2136
2208
  goal && goal.text ? h("span", { className: "sk-goal-text" }, goal.text) : null,
@@ -2147,7 +2219,27 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
2147
2219
  facts.length > 3 ? h("button", { type: "button", className: "sk-text", onClick: () => setFactsExpanded((value) => !value) }, factsExpanded ? "收起" : `展开全部 ${facts.length} 条`) : null,
2148
2220
  ),
2149
2221
  facts.length
2150
- ? h("div", { className: "sk-facts" }, (factsExpanded ? facts : facts.slice(0, 3)).map((fact) => h("div", { key: fact.id, className: "sk-fact" }, h("span", { className: "sk-fact-text" }, fact.text))))
2222
+ ? h(
2223
+ "div",
2224
+ { className: "sk-facts" },
2225
+ (factsExpanded ? facts : facts.slice(0, 3)).map((fact) => {
2226
+ const meta = factMeta(fact.kind);
2227
+ const tone =
2228
+ fact.kind === "progress"
2229
+ ? "var(--sk-brand-accent)"
2230
+ : fact.kind === "issue"
2231
+ ? "var(--dsw-alias-state-warn-primary,#f59e0b)"
2232
+ : fact.kind === "artifact"
2233
+ ? "color-mix(in srgb,var(--dsw-alias-state-success-primary,#22c55e) 80%,transparent)"
2234
+ : "color-mix(in srgb,var(--sk-ink) 45%,transparent)";
2235
+ return h(
2236
+ "div",
2237
+ { key: fact.id, className: "sk-fact" },
2238
+ h("span", { className: "sk-fact-icon", style: { "--fact-tone": tone } }, h(Icon, { name: meta.icon, size: 14 })),
2239
+ h("span", { className: "sk-fact-text" }, fact.text),
2240
+ );
2241
+ }),
2242
+ )
2151
2243
  : h("div", { className: "sk-note-tiny" }, "暂无关键事实"),
2152
2244
  ),
2153
2245
  h(
@@ -2199,14 +2291,32 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
2199
2291
  "div",
2200
2292
  { className: "sk-duo-col" },
2201
2293
  issues.length
2202
- ? issues.slice(0, 3).map((issue, index) => h("div", { key: issue.id || index, className: cx("sk-duo-item", issue.severity === "high" && "is-error"), title: issue.text }, issue.text))
2294
+ ? issues.slice(0, 3).map((issue, index) =>
2295
+ h(
2296
+ "div",
2297
+ {
2298
+ key: issue.id || index,
2299
+ className: cx("sk-duo-item", issue.severity === "high" ? "is-error" : issue.severity === "medium" ? "is-warn" : ""),
2300
+ title: issue.text,
2301
+ },
2302
+ h(Icon, { name: "alert", size: 11 }),
2303
+ h("span", null, issue.text),
2304
+ ),
2305
+ )
2203
2306
  : h("div", { className: "sk-note-tiny" }, "暂无"),
2204
2307
  ),
2205
2308
  h(
2206
2309
  "div",
2207
2310
  { className: "sk-duo-col" },
2208
2311
  nextSteps.length
2209
- ? nextSteps.slice(0, 3).map((step, index) => h("div", { key: index, className: "sk-duo-item", title: step }, step))
2312
+ ? nextSteps.slice(0, 3).map((step, index) =>
2313
+ h(
2314
+ "div",
2315
+ { key: index, className: "sk-duo-item", title: step },
2316
+ h(Icon, { name: "arrow", size: 11 }),
2317
+ h("span", null, step),
2318
+ ),
2319
+ )
2210
2320
  : h("div", { className: "sk-note-tiny" }, "暂无"),
2211
2321
  ),
2212
2322
  ),
@@ -2220,11 +2330,12 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
2220
2330
  { className: "sk-drawer-sec-head" },
2221
2331
  h("span", { className: "sk-drawer-sec-title" }, "会话"),
2222
2332
  h("span", { className: "sk-grow" }),
2223
- h("button", { type: "button", className: "sk-text", onClick: openSession, disabled: !session.sessionId }, "打开"),
2333
+ h("button", { type: "button", className: "sk-text sk-link-text", onClick: openSession, disabled: !session.sessionId }, "打开"),
2224
2334
  ),
2225
2335
  h(
2226
2336
  "div",
2227
2337
  { className: "sk-session-line" },
2338
+ h(Icon, { name: "chat", size: 14, className: "sk-session-icon" }),
2228
2339
  h("span", { className: cx("sk-live-dot", !session.isLive && "off") }),
2229
2340
  h("span", { className: "sk-session-title", title: session.title }, session.title || "未关联会话"),
2230
2341
  ),
@@ -2944,13 +3055,13 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
2944
3055
  const statsLine = h(
2945
3056
  React.Fragment,
2946
3057
  null,
2947
- `${stats.total || tasks.length} 项`,
3058
+ h("span", { className: "sk-stats-group" }, h("span", { className: "sk-dot is-total" }), `${stats.total || tasks.length} 项`),
2948
3059
  h("span", { className: "sk-sep" }, "·"),
2949
- `${(stats.byStatus && stats.byStatus.in_progress) || 0} 进行中`,
3060
+ h("span", { className: "sk-stats-group" }, h("span", { className: "sk-dot is-in_progress" }), `${(stats.byStatus && stats.byStatus.in_progress) || 0} 进行中`),
2950
3061
  h("span", { className: "sk-sep" }, "·"),
2951
- `${(stats.byStatus && stats.byStatus.blocked) || 0} 受阻`,
3062
+ h("span", { className: "sk-stats-group" }, h("span", { className: "sk-dot is-blocked" }), `${(stats.byStatus && stats.byStatus.blocked) || 0} 受阻`),
2952
3063
  h("span", { className: "sk-sep" }, "·"),
2953
- `${(stats.byStatus && stats.byStatus.done) || 0} 完成`,
3064
+ h("span", { className: "sk-stats-group" }, h("span", { className: "sk-dot is-done" }), `${(stats.byStatus && stats.byStatus.done) || 0} 完成`),
2954
3065
  h("span", { className: "sk-sep" }, "·"),
2955
3066
  scanRunning
2956
3067
  ? h(
@@ -2997,19 +3108,7 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
2997
3108
  if (!ok) pushToast("当前环境不支持全屏切换", "error");
2998
3109
  };
2999
3110
 
3000
- const pageMenuItems = [
3001
- { label: "设置", icon: "settings", onClick: () => setSettingsOpen(true) },
3002
- { label: "在右侧栏打开", icon: "panelRight", onClick: openSideFromPage },
3003
- { label: "导出 JSON", icon: "file", onClick: exportJson },
3004
- ];
3005
- const sideMenuItems = [
3006
- { label: "新建任务", icon: "plus", onClick: () => setNewTaskStatus("backlog") },
3007
- { label: "设置", icon: "settings", onClick: () => setSettingsOpen(true) },
3008
- { label: "在全页面板打开", icon: "layout", onClick: openPageFromSide },
3009
- { label: "全屏", icon: "maximize", onClick: goFullscreen },
3010
- "divider",
3011
- { label: "导出 JSON", icon: "file", onClick: exportJson },
3012
- ];
3111
+ const exportMenuItems = [{ label: "导出 JSON", icon: "file", onClick: exportJson }];
3013
3112
 
3014
3113
  const scanButton = h(
3015
3114
  "button",
@@ -3022,16 +3121,28 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
3022
3121
  },
3023
3122
  h(Icon, { name: scanRunning ? "stop" : "refresh", size: 15 }),
3024
3123
  );
3124
+ const actionIcon = (name, title, onClick, extraClass) =>
3125
+ h(
3126
+ "button",
3127
+ { type: "button", className: cx("sk-iconbtn-v3", extraClass), title, "aria-label": title, onClick },
3128
+ h(Icon, { name, size: 15 }),
3129
+ );
3025
3130
  const moreButton = (items) =>
3026
3131
  h(
3027
3132
  DropMenu,
3028
3133
  { open: moreOpen, onClose: () => setMoreOpen(false), items },
3029
3134
  h(
3030
3135
  "button",
3031
- { type: "button", className: "sk-iconbtn-v3", title: "更多", onClick: () => setMoreOpen((value) => !value) },
3136
+ { type: "button", className: "sk-iconbtn-v3", title: "更多(导出)", onClick: () => setMoreOpen((value) => !value) },
3032
3137
  h(Icon, { name: "more", size: 15 }),
3033
3138
  ),
3034
3139
  );
3140
+ const settingsButton = actionIcon("settings", "设置", () => setSettingsOpen(true));
3141
+ const newTextButton = h(
3142
+ "button",
3143
+ { type: "button", className: "sk-btn-primary-v3", onClick: () => setNewTaskStatus("backlog"), title: "新建任务(n)" },
3144
+ "新建",
3145
+ );
3035
3146
 
3036
3147
  const headerNode =
3037
3148
  surface === "side"
@@ -3039,7 +3150,15 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
3039
3150
  "header",
3040
3151
  { className: "sk-header-v3" },
3041
3152
  h("p", { className: "sk-stats-v3" }, statsLine),
3042
- h("div", { className: "sk-header-actions" }, scanButton, moreButton(sideMenuItems)),
3153
+ h(
3154
+ "div",
3155
+ { className: "sk-header-actions" },
3156
+ scanButton,
3157
+ settingsButton,
3158
+ prefs.fullEntry ? actionIcon("layout", "在全页面板打开", openPageFromSide) : null,
3159
+ actionIcon("plus", "新建任务", () => setNewTaskStatus("backlog"), "is-primary"),
3160
+ moreButton(exportMenuItems),
3161
+ ),
3043
3162
  )
3044
3163
  : h(
3045
3164
  "header",
@@ -3065,12 +3184,10 @@ body[data-ds-dark-theme] .sk-btn-primary-v3:hover{background:var(--dsw-alias-but
3065
3184
  }),
3066
3185
  ),
3067
3186
  scanButton,
3068
- moreButton(pageMenuItems),
3069
- h(
3070
- "button",
3071
- { type: "button", className: "sk-btn-primary-v3", onClick: () => setNewTaskStatus("backlog"), title: "新建任务(n)" },
3072
- "新建",
3073
- ),
3187
+ settingsButton,
3188
+ prefs.sideEntry ? actionIcon("panelRight", "在右侧栏打开", openSideFromPage) : null,
3189
+ newTextButton,
3190
+ moreButton(exportMenuItems),
3074
3191
  ),
3075
3192
  );
3076
3193
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-super-kanban",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "AI task kanban for DeepSeek Harness: derives task goals, progress, completion and issues from DSH sessions, with key facts, subtasks, artifacts, session hand-off and a dual full-page/right-sidebar UI.",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",