# 任务看板（dsh-task-board）

[English](README.en.md) | 中文

[![npm](https://img.shields.io/npm/v/@firetruck666/dsh-task-board)](https://www.npmjs.com/package/@firetruck666/dsh-task-board)
[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
[![Node](https://img.shields.io/badge/Node-%5E22.19.0%20%7C%7C%20%3E%3D24.0.0-339933)](README.md#环境要求)

DeepSeek Harness 的任务看板插件。它在 Web 界面的侧边栏加一个「任务看板」入口，用五列看板管理任务；任务交给 DSH 自己的会话真实执行，状态自动回写到卡片上。

插件不修改 DSH 源码，卸载后界面恢复原状。看板数据保存在 DSH 主进程（host）一侧，电脑和手机打开同一个部署看到的是同一块板，改动经 SSE 实时同步；窄屏自动进入紧凑布局。

## 目录

<!-- toc:start -->

- [环境要求](#环境要求)
- [安装](#安装)
- [更新](#更新)
- [主要能力](#主要能力)
- [数据保存在哪里](#数据保存在哪里)
- [从源码构建](#从源码构建)
- [贡献指南](#贡献指南)
- [常见问题](#常见问题)
- [命名约定](#命名约定)
- [许可证](#许可证)

<!-- toc:end -->

## 环境要求

- DeepSeek Harness `0.2.2-alpha.2` 或更高。这是**最低支持版本**（本插件就是按这一版构建和测试的），不是「当前版本」：低于它的没验证过，**上限不设**——插件跟着 DSH 更新，升级 DSH 之后装最新版即可。加载失败时先按「常见问题」里的说明处理。
- Web 端（命令行起的 `dsh web`）另需 Node.js `^22.19.0` 或 `>= 24.0.0`，以及 pnpm 10 或更高。
- **桌面应用不需要你另装 Node 和 pnpm**：它自带这两样，包操作走它自己的运行时。

## 安装

先认一件事，它决定了后面所有命令：**桌面端和 Web 端是两份独立的安装。**

两边各有自己的 profile（`~/.dsh/profiles/<名字>`）。插件装在哪个 profile 里，就**只有那个端**
能看见它：在 Web 端装过，桌面端不会跟着有，反过来也一样。两个端的界面长得一样、安装动作也
长得一样，但**各装各的**。

| 你在用什么 | 它用哪个 profile | 谁管它 |
| --- | --- | --- |
| 桌面应用（DSH Desktop） | `desktop` | 应用自己（自带运行时） |
| `dsh web`（命令行起的 Web GUI） | `web` | 你，或用它的那个 profile 名 |

装法只有一条规则：**给正在用的那个端的 profile 装**。下面的命令里把 `<profile>` 换成上表里
对应那个词就行。

### 桌面应用

**用应用自己的插件页**——不用碰终端。

侧边栏「插件」→「添加插件」→ 填包名 `@firetruck666/dsh-task-board` → 选安装源 → 安装。

终端也能装，但**前提是用对那个 `dsh`**：桌面应用那份 profile 只认它自己的命令行。用 npm 装出来的
`dsh` 会被直接拒掉：

```
error: profile "desktop" is managed exclusively by the Electron application
```

要用终端的话，先让应用把自带的 `dsh` 装到系统里：菜单里的**「管理 dsh 命令…」**（就在「检查更新…」
下面）点一次即可，Windows 加到当前用户的 PATH，macOS 装到 `/usr/local/bin/dsh`。然后用它：

```sh
dsh plugin --profile desktop add @firetruck666/dsh-task-board
```

两个前提，否则命令会失败：应用**至少打开过一次**（profile 要由它自己创建），并且**已经完全退出**
（它没退出时文件被占用）。

### Web 端

```sh
dsh plugin --profile web add @firetruck666/dsh-task-board
```

### 三种来源

上面两条命令都只是「往哪个 profile 装」，装什么由包名决定。包名有三种写法，选一种。

#### 从 npm 安装

```sh
dsh plugin --profile <profile> add @firetruck666/dsh-task-board
```

适合日常使用。拿到的是最近一次正式发布的版本。

#### 从 GitHub 安装

```sh
dsh plugin --profile <profile> add github:FiretrUCK666/dsh-task-board
```

拿到的是仓库 `main` 分支的最新代码。它和 npm 装出来的一样是可直接使用的安装包，不是开发环境（没有测试、没有构建工具，改不了代码）。适合想第一时间用上还没发版改动的人；稳定性取决于仓库当时的状态。

#### 本地开发安装

把仓库下载到本地后，在这个仓库目录里执行：

```sh
pnpm install
pnpm build
dsh plugin --profile <profile> add .
```

这条路径用于改代码。详见下面的「从源码构建」。

本地安装是指向工作目录的链接（不是复制），所以挂载一次即可：改完代码只需 `pnpm build` 再重启 `dsh web`，不必重跑上面的安装命令。改 client 半区刷新页面即可，改 host 半区才需要重启——见「常见问题」里的「改了代码没生效」。

#### 三种来源的区别

前两种是安装：拿到的都是可以直接使用的包，只包含发布清单里的文件（运行代码、源码、挂载声明、说明文档），差别只在版本新旧。第三种是开发：拿到的是完整仓库，能改代码、能运行测试。

| | 从 npm | 从 GitHub | 本地 |
| --- | --- | --- | --- |
| 用途 | 安装使用 | 安装使用 | 开发 |
| 拿到什么 | 最近一次发布版 | `main` 最新代码 | 你本地改动 |
| 版本特点 | 稳定 | 最新，可能不稳定 | 你自己决定 |
| 更新依据 | npm 上的版本号 | 仓库默认分支 | 安装的即本地目录，无需更新 |

### 安装之后

**把那个端重启一次**：Web 端停掉正在运行的 `dsh web` 再启动它；桌面应用完全退出再打开。
只刷新页面不够——插件的 host 半区在服务端进程里加载，不重启不会加载。重启后侧边栏就会出现
「任务看板」入口。

入口和 DSH 自带的「插件」面板并排：点它，看板占用中间主区域；再点一次同一个入口、或点侧边栏里的任意会话，即可回到对话。

这个插件的开关在两处，各管各的：侧边栏「插件」→ 往下拉到「已安装」，那一行右边的开关是**总开关**；
或点开「任务看板」进它自己的页面，右上角那个开关做的是同一件事。**关掉 = 整个插件不加载**
（侧边栏入口、看板、总览、清单、后台接口一起停）。页面里「包含的组件」下面还有四行，
每行一个开关，管各自那一半（看板 / 总览 / 清单 / AI 接口）——详见「常见问题」。

### 先试着走一遍

装完重启之后，花两分钟就能看明白它在干什么：

1. 点侧边栏的「任务看板」→ 按 **＋新建一条**，随手记一件真要做的事（只填标题也行）。
2. 在左栏点开这一条，右边的「运行配置」里选好工作区与 Agent，按 **问 AI**——它就地变成一张卡，
   会话开始干活。
3. 回看板：那张卡在「进行中」；跑完它自己落到「待审核」，板顶那行数字开始报数。
4. 点开卡片读完结果，按「通过」。

### 不需要手动配置任何东西

装完就能用，不用改配置文件、不用手写任何声明。而且**不要去改 `cordis.patch.yml`**——插件那一行住在插件包里，安装命令只负责把这个包登记进 profile，两者一配合就装配好了；手工再补一行会造成同一个插件出现两条。

| 环节 | 谁做的 |
| --- | --- |
| 把插件登记进本机 profile | 安装命令（往 `dsh.profile.bundles` 加一个包名） |
| 插件那一行（id、包名） | 插件包里的 `cordis.patch.yml`，由 `package.json` 的 `dsh.bundle.patch` 指向，装配时自动生效 |
| 界面入口、看板舞台 | 插件自己注册到 DSH 的官方扩展点，启动时自动完成 |
| 数据存哪 | 自动使用 `~/.dsh/storages/dsh_task_board/` 目录，第一次运行时创建 |
| 插件显示的名字、说明、图标 | 包里的 `locale/` 与 `icon.svg`，DSH 直接读取 |
| 那个开关的位置 | 插件管理页的开关写 profile 里这一行的 `disabled`（关掉时才会写上） |

唯一必须你做的一步是**重启那个端**（host 半区在服务端进程里，不重启不会加载）。三种安装方式（npm、GitHub、本地）在这件事上完全一样，两个端也一样。

这个插件没有设置项，也不需要设置面板：看板的行为（任务、定时、巡航、规则）全在看板界面上直接编辑，而开与关就是上面那个开关。

卸载：

```sh
dsh plugin --profile <profile> remove @firetruck666/dsh-task-board
```

同样需要重启那个端。卸载不会删除你的任务数据。

## 更新

看板 Header 右侧有常驻的「检查更新」按钮。点一下，它回答三件事：**你正在运行的是哪一份**
（哪个 profile、装在哪、怎么装的）、**有没有新版**、**该怎么更新**。

它读的是**正在运行的那一份自己的位置与清单**，不是某个写死的名字。所以同一台机器上 Web 端
装的是本地检出、桌面端装的是 npm 时，两边各说各的，不会互相认错；最新版也是**由 host 读**
的——它知道这份 profile 用的哪个源（官方源、镜像源还是内网源），不会因为浏览器连不上
`registry.npmjs.org` 就告诉你「检查失败」。读不到时它直说读不到，**不会说成「已是最新」**。

| 安装方式 | 「一键更新」做什么 |
| --- | --- |
| 从 npm 安装 | 先卸载，再装 npm 上的最新版 |
| 从 GitHub 安装 | 先卸载，再按仓库默认分支的当前代码重装 |
| 本地开发安装 | 在仓库目录里 `git pull --ff-only`，再按仓库自己声明的包管理器重建 `lib/` |

「先卸载再安装」是 DSH 自己的更新方式（HMR 开着时不允许直接覆盖已安装的包），所以按下它，
插件会短暂消失几秒。装完可能需要重启那个端；**若安装失败，插件会保持未安装**，按「安装」一节
里对应你那个端的做法装回来即可。**任务数据在任何一步都不受影响**：它住在
`~/.dsh/storages/dsh_task_board/`，不在插件包里。

跑完发现**版本没变**时，对话框直说「没有变化」，不写成「已更新到」——那是这一次没装上新版，
不是失败。常见原因两个：**安装器自己的供应链保护期**（刚发布的版本会被包管理器暂时挡在解析
之外，实测发布后十几分钟内去装，拿到的还是上一版，等它过了保护期再按一次即可）；以及**这份
profile 里记下的版本范围**把它钉住了——对话框里「安装声明」那一行就是它，卸载后重新安装会重新
解析。想知道一台机器上到底装的是哪一份、哪一版，看这三行就够了：运行中的这一份、位置、安装声明。

对话框里同时给出等价的终端命令（桌面端除外，原因见下），命令里的 profile 名就是你正在运行的那一份：

```sh
dsh plugin --profile <profile> add @firetruck666/dsh-task-board@latest
```

```sh
dsh plugin --profile <profile> add github:FiretrUCK666/dsh-task-board
```

本地开发安装则是：

```sh
git pull --ff-only && pnpm run build
```

**桌面端**：那份 profile 由应用托管，上面这条命令**在它身上不成立**——npm 装出来的 `dsh` 会拒绝
`desktop` 这个 profile 名，所以桌面端的对话框不给终端命令，只指向应用自己的插件页。在「插件」页里
卸载后重新添加即可更新（用应用菜单装的那个 `dsh` 也可以，见「安装」一节）。

装了插件市场的话，也可以在它的「已安装」标签页里点「更新」。

## 主要能力

### 三页一个工作台

侧栏有**总览**、**任务看板**、**任务清单**三个入口，它们共用顶上一行导航，所以你在哪一页、
另外两页各有多少东西在等你，一眼就看得见。

- **总览**：等你处理、跑完没看、今天到期、已逾期四个数字，一条流水线（五栏各几张、最旧几天
  没动），最近七天的产出，以及最近发生了什么。点数字就跳到那一页。
- **可以互通，也可以互不相干**：看板卡片上「挂 N 条清单」跳到清单并只看这张卡的条目；清单一行
  的卡片芯片跳回看板并打开那张卡。不点它们的时候，两页各干各的，互不影响。
- **每一页都能单独关掉**：插件详情页里一页一个开关，关掉的那页不会出现在导航里。

### 任务看板

五列，从左到右就是一件事的一生：待规划 → 待办 → 进行中 → 待审核 → 已完成。卡片拖着换列，
同一列里也能上下拖，指定先后。

- **执行是真的。** 详情里点「执行」，DSH 会话真的开始干活；跑完自动落到「待审核」。跑过几次、
  上次成没成，卡片上写着；正在跑的卡有黄边。
- **等你的东西会自己冒出来。** 板顶一行数字：几个会话挂起等你回答、几张卡等你裁决。点开是
  通知中心，动态页按日分组，说清最近发生了什么。
- **AI 在评论区里问你，你就在卡片上答。** 计划待审也一样——「同意执行」直接作答，「要求修改」
  把它退回输入框，不必开一趟会话。每张卡的评论区就是它的 DSH 会话：排队发、插话发都行，
  图片和文件直接拖进去。
- **引用一句话再接着问。** 在会话内容里选中一段文字，旁边会浮出「引用文本」；按下它就把它
  以引用行的形式放进输入框，接着写你要说的话。引用进去的是普通文字，发出去在对话里就是一段
  引用块——和 DSH 会话里引用的一句话是同一种东西。
- **会话区按「一轮」折叠：你一句话，它一段话。** 中间的过程（调了哪些工具、想了一下、
  注入了哪些上下文）收进一个可展开的行里，跑完只剩最后那段话。想看细节就点开那一行。
  折多少、什么时候折、折起来还留哪几条回复，都跟着「设置 · 通用设置」里那三项走——不在
  这里再开一套开关。
- **侧栏里的会话或工作区，拖到某一列就成一张卡。**
- 复核页是一次执行的完整记录，直接「通过 / 打回」。
- 多选之后改色、执行、删除一次做完。详情能存成模板，运行配置能存预设、设默认。

### 任务清单

有些事还没到能开工的程度，先写下来。

- 「＋新建一条」：**标题在最上面**，下面依次是正文、步骤、上下文备注。左列写内容，右列定属性
  ——优先级、状态、标签、三个日期、挂到哪张卡。
- **挂不挂卡由你**：不挂（先放着，以后再说）、挂到已有的卡（那张卡里的内容一个字都不动）、
  或新建一张卡——选新建，你会先看到**这张卡要用什么跑**（工作区 / Agent / 模型 / 思考程度 /
  权限），它带着你看到的那一份出生，之后在卡上或这一行的详情里改。
- 一行自己的状态是**五档**（待规划 / 待办 / 进行中 / 待审核 / 已完成），词与色都用看板那一套。
  没挂卡时五档随你选；挂着卡时跟着那张卡走，但**你自己按下的「已完成」压过卡片**——一件做完
  的事，不会因为卡还在跑就变回没做完。按那一枚状态芯片会跳到看板并打开它挂着的卡。
- **「问 AI」是这一页唯一交给 AI 的一按，没挂卡的条目也能按**：那张卡已经有对话就**接着说进
  那条对话**，还没有就**开一轮**——**由卡自己决定**，你不用挑。送过去的是这一条自己的内容
  （标题、标签、正文、步骤、备注，以及正文带的图片与文件），不是卡上的执行 Prompt，所以卡
  自己的 Prompt 是空的也照样问得出去。
- 筛选全在左栏：日历、按日期（日期已过 / 日期未到 / 没定日期）、按多久没动、优先级、状态、
  日程、已删除。每一项都带数字，0 也写。
- 删除有撤销；删掉的都在左栏底部「已删除」，30 天内能找回，也能彻底删除。
- 三个日期各自独立：开始、计划、截止。只有**错过截止**才变红，那一行写「**超期 N 天**」；
  计划滑了是黄的，写「落后 N 天」——承诺失守和计划推迟是两回事。
- 三件容易漏掉的设计：**日期框认人话**——`@明天`、`10月15日`、`2026-10-15`、`+3`、`周三`
  都行，读不懂时它教你怎么写，而你写下的日期一个字都不动；**正文与备注的框高度是你自己拉的**，
  抓住整条下沿往上往下拖，拉完记住；**窄屏上五列变成横向滑动**，功能一个不少。

### 自动化、多端与 AI 入口

- **定时跑**与**跑完接着跑**：按时间表启动任务，或一张卡跑完自动接下一轮。
- **给会话发指令**：按时间表把一句话发进某个会话，指令可以带图片和文件，规则那一行会写明
  带了几个。
- **自动巡航**：板头一颗开关，设好同时最多几件、哪些时段能跑，剩下的它自己排。
- 数据在 host 上，**电脑和手机看到的是同一块板**，改动实时同步；断网也能用，连上后自动对齐。
- `/task` 把你写的话交给当前会话的模型；`/task-continue` 让它接着上次往下做。
- 键盘：`A` 新建 · `J`/`K` 移动 · `X` 多选 · `1`–`4` 优先级 · `E` 改标题 · `⌘Z` 撤销 ·
  `Esc` 一层一层收。


## 数据保存在哪里

- 看板真相：`~/.dsh/storages/dsh_task_board/` 目录。`documents/` 下**每种数据一个文件**：`board.json` 是任务台账、巡航、定时预设、运行配置预设与删除墓碑；`items.json` 是任务清单。人可读、原子写入，可以直接整目录备份；删除这个目录等于清空看板与清单。
- 浏览器 localStorage 保存离线镜像和本地状态。两份**承载数据**的镜像是 `dsh.taskBoard.v1`（看板）与
  `dsh.taskBoard.items.v1`（任务清单）—— **两份都要备，只备一份等于清单那份丢了**。其余的键
  （`cruise` / `presets` / `runPresets` / `templates` / `drafts` / `preSync`）是这个浏览器自己的状态或
  一次性备份，不是另一份真相。**键名以 `src/client/` 里实际写下的为准**；这里列的是**哪些值得你
  操心**，不是全部键的清单。
- 第一次连接时如果本地数据与 host 有分歧，本地副本备份到 `dsh.taskBoard.preSync.v1`，host 为准。
- host 没有挂载存储后端时，看板自动退回纯 localStorage 模式。

## 从源码构建

前置：Node.js 22 或 24、pnpm、能访问官方 npm 源（类型和运行时 API 全部来自 `@deepseek-ai/*`，不需要 DSH 源码 checkout）。

```sh
git clone https://github.com/FiretrUCK666/dsh-task-board.git
cd dsh-task-board
pnpm install
pnpm build       # 产出 lib/index.js 和 lib/client.js
pnpm typecheck   # 类型检查
pnpm test        # 单元与契约测试
pnpm verify      # 独立插件静态门禁
```

`lib/` 是构建产物，但它随仓库一起提交。原因是从 GitHub 或 npm 安装时只会复制文件，不会执行构建，仓库里没有 `lib/` 的话装完就启动不了。改完源码要重新构建，并把 `lib/` 和源码放在同一次提交里（CI 会检查两者是否一致）。

仓库根目录的 `AGENTS.md` 记录了项目的约定、架构索引与发布流程，是 AI 助手在这个仓库里工作时用的依据。

### 设计系统预览页

改界面时有一个现成的页面可以对照：设 `DSH_TASK_BOARD_DESIGN_PREVIEW=1` 再启动，侧边栏会多出一行
「设计系统」，里面把设计变量、字阶、全部按钮与 chip 档位、五个状态、全部图标、数据调色板、反馈
文法、断点表一次摆出来，并且每个标本都是**真组件本身**——面板上长什么样，这一页就是什么样。

```sh
DSH_TASK_BOARD_DESIGN_PREVIEW=1 pnpm dsh web   # 或在你自己的启动方式上加这个环境变量
```

它只在设了这个变量时注册，**不设就完全不注册**——不出现在侧边栏、不出现在插件页的开关里，
普通用户碰不到它。（它的代码在插件产物里，因为它与两个面板共用同一份浏览器 bundle；
让它在界面上消失靠的是两道闸：宿主只在设了这个变量时才宣告这一面，浏览器读不到宣告时不注册。）

## 贡献指南

欢迎提 Issue 与 Pull Request。动手前先读 [CONTRIBUTING.md](CONTRIBUTING.md)：开发环境、提交 PR 前要跑的检查、硬性规范都在里面。报告加载失败类问题时，附上三个信息：DeepSeek Harness 版本、本插件版本、报错原文。

## 常见问题

**插件页面上的五个开关是什么。**
侧边栏「插件」→ 已安装 → 点「任务看板」→「包含的组件」列出五行，每行一个开关：

| 那一行 | 关掉之后 |
| --- | --- |
| 任务看板 | 侧栏面板列表里没有「任务看板」，看板面板不再出现 |
| 总览 | 侧栏面板列表里没有「总览」，工作台的第一页不再出现（看板与清单照常） |
| 任务清单 | 侧栏面板列表里没有「任务清单」，清单面板不再出现 |
| **AI 接口** | **所有会话的 AI 都看不到本插件的工具、斜杠命令与系统提示**——它们从未被注册，不是被拒绝 |
| 任务看板（插件本体） | 整个插件不加载，上面四行随之无意义 |

「AI 接口」这一行是**装配层的开关**：关掉它，插件的那几个模块根本不加载，所以没有任何一次运行
时判断可以漏掉。三个界面各自要开关，是因为它们是三个面板；而一个 npm 包只有一份浏览器产物，
所以这些行由宿主侧的模块宣告，浏览器开机时读一次。

**看板上的状态词都什么意思。**
它们读的是**同一份推导**，所以同一张卡在任何一处都是同一个答案：

| 你看到的词 | 它在说什么 | 什么时候消失 |
| --- | --- | --- |
| **待你决断**（失败时写「失败待决断」） | 卡在「待审核」列，有跑完但那个**会话**你还没看过的结果 | 打开那个会话的评论区，或点「通过 / 打回」 |
| **等你处理 N 项** | N 个会话正挂起等你回答（审批 / 计划确认 / 提问） | **就在板内那张卡上回答**——同意执行、要求修改、或者选中它给的选项；不去会话也可以 |
| **待审核 M 张** | M 张卡在「待审核」列且那个会话你还没看过 | 同「待你决断」 |
| **新 N / 新留言** | 这张卡有还没看过的**新内容** | 点开卡片即消失 |
| **待处理 N** | N 个会话在等你（N > 1 时） | 逐个回答完 |
| **N 次执行** | 这张卡跑过几轮（历史，不是催你） | 不会自己消失 |
| **失败 · 已暂停** | 自动化因上次失败而暂停 | 在详情里重新启用 |

**「待你决断」和「新 N」是两件事**：「新」是「这张卡你没打开过」，「待你决断」是「那个会话你没
看过、也还没裁决」。**取消不算「待你决断」**——取消是中止，它只出现在评论线程和动态里。

**装完刷新页面看不到入口。**
host 半区在服务端进程里加载，必须重启 `dsh web`，只刷新页面不够。

**入口在哪、开关在哪。**
看板与清单是侧边栏里的两个面板图标，与 DSH 自带的「插件」面板并排。
这个插件的开关就在插件自己的页面上：侧边栏「插件」→ 已安装 → 点「任务看板」，
五行分别是看板、总览、清单、AI 接口与宿主半区，每行一个开关，关掉哪行哪一半就不注册。
如果你看不到入口，说明这一端还在跑旧的前端资源——刷新页面即可。

**升级 DeepSeek Harness 之后插件加载失败（页面提示 Failed to load plugins）。**
DeepSeek Harness 的内部接口会随版本变化，本插件需要跟着改。先做这两步：

1. 把本插件更新到最新版，然后重启 `dsh web`：

   ```sh
   dsh plugin --profile web add @firetruck666/dsh-task-board@latest
   ```

2. 仍然失败，说明本插件还没跟上你用的那个 DSH 版本。请到 [Issues](https://github.com/FiretrUCK666/dsh-task-board/issues) 提交，附上三个信息：你的 DeepSeek Harness 版本、本插件版本（**在插件页的「已安装」列表里看——不是设置页，本插件没有设置项**）、页面上那段报错原文。有这三样就能直接定位。

**看板能打开，但多设备不同步、任务清单也一直是空的。**
这是 host 半区没加载成功的表现，也是升级 DSH 之后最容易被忽略的一种：看板本身仍在页面上正常工作，但数据只留在这一个浏览器里，另一台设备看到的不是同一块板，定时与巡航也不会真正触发。任务清单在这种情况下会明说「暂时读不到 host 上的清单，正在用本机的副本」，不会假装自己一条都没有。处理方式与上一条相同——更新插件、重启 `dsh web`。

**打开右侧栏之后看板变了样子。**
这是正常的响应式行为，不是故障。DSH 的右侧栏默认占视口约 45%、最少 300px，中栏硬保 400px，所以打开它之后**中间那块板本身变窄了**：看板的响应式看的是**自己盒子的宽度**（不是窗口宽度），板盒一旦窄过它的紧凑档阈值，五列就会变成横向滑动的列轨、并出现五等分的列导航标签。把右侧栏收回去就恢复。这条规则本意就是「板盒多宽就摆多宽」，侧栏开合、分屏、手机上都成立。

**改了代码没生效。**
改 host 半区（`src/index.ts`、`src/host/`）需要重启 `dsh web`；改 client 半区刷新页面即可。两种情况都要先 `pnpm build`。

**手机上打开位置不对，或反复开合侧栏会跳位。**
这是修复过的问题。如果仍然出现，先确认服务端进程是重启过的新版本：插件在板头会显示「服务端未重启 · 点此了解」，点开可以看到当前连接的服务端进程的启动时间。如果时间明显是旧的，说明这个地址连到了另一个没重启的实例。

**两端同时开着会不会重复执行定时任务。**
不会。定时、巡航、接续这类行为由 host 租约仲裁，同一时刻只有一个界面在执行，另一台设备切到前台后会接管并补上期间漏掉的状态。

**任务数据在哪，换电脑会丢吗。**
在 `~/.dsh/storages/dsh_task_board/` 目录。换电脑时把整个目录拷过去即可；浏览器里的 localStorage 只是离线镜像。

**找不到任务清单的入口。**
清单是**侧栏面板列表里紧跟「任务看板」的第二行**——把左栏展开就能看到两行图标，点第二行打开清单，
再点一下回到会话。侧栏收成一条窄图标带时，两行挨着排在一起。

如果那一行**根本没有**：多半是这一端还在跑旧的前端资源，**刷新页面**即可；还不行就去看插件页上
「任务清单」那个开关是不是被关了（见上面「五个开关」）。清单关掉不影响看板。

**切到看板面板时清单不见了。**
那不是坏了，是**主舞台一次只显示一个面板**——它本来就是这么设计的，不是两个能同屏的东西。
想回清单，点侧栏里清单那一行；想回对话，点「返回对话」或再点一次当前那一行。

**`dsh plugin` 报错找不到 pnpm。**
先安装 pnpm（`npm install -g pnpm`），再重新执行安装命令。

## 命名约定

| 用途 | 值 |
| --- | --- |
| 插件 id | `dsh-task-board` |
| npm 包名 | `@firetruck666/dsh-task-board` |
| 权限预设路由 | `/api/dsh-task-board/permissions` |
| 看板数据路由 | `/api/dsh-task-board/board`（含 `/items`、`/surfaces`、`/lease`、`/command`、`/events`） |
| 其余 host 路由 | `/api/dsh-task-board/session-state`、`/update`、`/client-report`、`/attachments`（收字节与按 ref 读回） |
| host 存储单元 | `dsh_task_board`（`documents/` 下每种数据一个文件） |
| 看板舞台 slot | `main`（`key: dsh-task-board`） |
| 任务清单舞台 slot | `main`（`key: dsh-task-board-items`，与看板同一个座位、不同键） |
| 总览舞台 slot | `main`（`key: dsh-task-board-home`） |
| 设计预览舞台 slot | `main`（`key: dsh-task-board-design`，只在开发环境注册） |
| 侧栏入口 slot | `sidebar.panellist`（`id` 与各自的 `main` 键一致） |
| localStorage 键 | `dsh.taskBoard.v1` 等 |

插件 id 和包名是两件事：id 决定加载器行、浏览器资源路径、路由、存储单元和上面那几个扩展点；包名只是 pnpm 安装时的标识。包名带作用域不会改变 id。

本插件没有设置项，所以也没有设置路由和设置面板：插件市场里那条开关写的是 profile 行的 `disabled`（关掉时才写上），插件本身不声明任何设置字段。

## 许可证

MIT，见 LICENSE 文件。
