dsh-infinite-canvas
v0.2.2
Published
Infinite dialogue canvas: non-linear AI conversations on a DSH Web GUI canvas (fork and multi-select context by zero-copy reference)
Readme
dsh-infinite-canvas · 无限对话画布
DeepSeek Harness(DSH)的 Cordis 插件:在 DSH Web GUI 内提供一块无限画布, 以非线性方式组织 AI 对话。画布上每个「对话框」是一条真实的 DSH Session 线程; 画布只存引用与布局,会话本体仍由 DSH 会话日志管理。
AI 编码代理请先阅读 AGENTS.md:目录结构、构建命令、技术约束、发版流程与验收标准。
安装
dsh plugin --profile web add dsh-infinite-canvas要求 DSH ≥ 0.1.2-alpha.4(需要 sessionController 服务)。开发迭代请用 link: 指向本仓库(见「开发环境」),勿装 npm 版。
核心特性
- 独立对话:「+ 新建对话」创建一个框 = 一条全新 Session,可选 agent preset (标准 / PTC 等)。
- 多选上下文:标题栏点选(Ctrl 多选)→「用所选 N 个对话新建」,新框挂上 N 个 session 引用。
- 消息级 fork(引用式):任意消息 hover「⑂ 分叉」→ 新框只记录「源框 + 分叉点 seq」,不复制历史数据;组装时实时读取源会话到该点为止的前缀。
- 产物引用:write/edit 等工具产出的文件按轮折叠——产物面板跟随产生它的 那一轮对话(与主界面轮尾产物一致,进行中轮次挂在框内末尾),不再在框底 汇总整场会话;「引用」把产物挂到新对话——只传元数据,模型用文件工具按需 读取,内容零拷贝。
- @ 引用:输入
@弹出文件 / 对话候选浮层,选中写入 mention 文本, 与主界面同一协议,由宿主统一展开为上下文。 - / 指令:行首
/弹出命令菜单——「命令」(remote.commands,60s 惰性刷新) 与「技能」(remote.skills,user-invocable 技能目录)两组;命中命令目录经remote.commands.execute执行,技能与未命中行按普通消息发送,宿主 pre-step 统一识别注入。 - 框内会话设置:「模型 ▾」(模型 + 思考强度)与「权限 ▾」(preset 切换) 两枚芯片,按 sessionId 持久化、下一轮生效,不要求该会话是主界面当前选中。
- 图片支持:输入区 📎 或直接粘贴发图(png/jpg/webp/gif,最多 6 张/条), 与主界面同一契约上行,经 admission 校验、内容寻址存储。⚠️ 当前部署的 DeepSeek 文本模型不支持图片输入,发图会被模型闸门拒绝,换用声明 image 输入的模型后即可用。
- 原生内容渲染:AI 回复用 DSH 自带
MarkdownText(GFM、表格、公式、 代码高亮),样式只消费ui-theme语义 token,深浅色与主界面自动一致。 - 思考 / 提问 / 权限:reasoning 折叠为「思考过程 · N 字」徽标;
ask_user_question与权限审批未决时在框内出卡片,直接作答并回灌主界面 waterfall。 - 系统注入折叠:系统 context、会话引用快照、画布注入前缀均折叠为字数徽标, 消息正文只显示用户自己的话。
- 引用仅随首条消息发送:发送成功后标记 consumed,后续消息不再重复注入; 分叉与产物引用走服务端组装,失败不消费、下一条消息自动重试。
- 主界面桥接:框标题栏「⤢」经
sessions.open()在主界面以原生对话视图 打开该会话。 - 按工作区的画布:每个 workspace 一块独立画布,工具栏下拉切换;新建对话 自动带对应 cwd / workspaceId。
- 删除与失效:移除被引用的框弹确认;引用来源不可用时框上出现「来源失效」 徽标。不级联删除。
- 持久化:画布布局(框位置 / 尺寸 / 标题 / 引用 + 派生连线)经 DSH storage
(
infinite_canvasKV 域)自动保存 / 恢复。
画布操作
参照 tldraw / Excalidraw / Figma / React Flow 的通行约定,核心是 滚轮分流(框内滚动永不缩放画布)与空格 / 中键平移:
| 操作 | 行为 |
| ----------------------------------------- | ----------------- |
| 滚轮悬停在框内 | 归框内内容滚动,永不缩放画布 |
| 滚轮悬停在空白画布 | 以光标为锚点缩放(0.2×–4×) |
| Ctrl / Cmd + 滚轮(含触控板捏合) | 始终缩放画布 |
| 空格 / 鼠标中键 + 拖拽 | 平移画布 |
| 框标题栏拖拽 / 点按 | 移动框 / 选中(Ctrl 多选) |
| 框右下角斜纹手柄 | 调整框大小(最小 260×200) |
| 点击空白画布 | 取消选中 |
| Delete / Backspace | 移除选中框(被引用时弹确认) |
| Escape | 清除选中;输入框内退出输入 |
| Ctrl / Cmd + A | 全选 |
| + / - / 0(Home / Shift+1 同 0) | 缩放 / 回到中心 |
| 「? 指南」按钮 | 画布内查看完整操作指南 |
输入框内的按键全部归输入框,不触发画布快捷键。框内新消息只在阅读位置贴近 底部时自动跟随滚动,回看历史时不被拽走。
客户端 ⇄ 服务端通道
第三方插件无法加入构建期固定的 Typert remote facade,插件自有 API 走文档化的
ctx.webServer.register({ kind: 'prefix' }) 前缀路由(JSON):
| 端点 | 用途 |
| --------------------------------------------------- | --------------------- |
| GET /infinite-canvas/api/health | 存活检查 |
| GET / PUT /infinite-canvas/api/docs/default | 读 / 保存画布文档 |
| GET /infinite-canvas/api/sessions/:id/transcript | 会话转写 + 产物 |
| POST /infinite-canvas/api/assemble | 活引用上下文组装(预算截断 + 失效注明) |
发消息 / 停止走客户端 ctx.sessions 的 Session face(beginSubmission +
prompt);框内转写读宿主 durable 日志(1.2s 轮询)。
框内不嵌 DSH 原生 ConversationRoot / ChatView:它们不对外导出,且事件窗口
同一时刻只服务一个 current 会话。因此复用边界是——渲染层用平台开放的
ui-primitives,数据层走自有 HTTP 通道 + 轮询,完整体验经 sessions.open()
桥接回主界面。
界面设计系统
全部自定义样式只消费 ui-theme 注入在 body 上的语义 token(不定义主题、
不写死色值),深浅色跟随 body[data-ds-dark-theme] 自动切换,字号读正文阶梯;
颜色、几何(elevated 表面 / 发丝边 / 圆角)、字体、提问卡(对齐
QuestionComposer)、权限卡(对齐 ApprovalPanel)、折叠徽标(对齐 Pill)、
图标钮等均与 DSH 原生组件同源。
开发环境(Windows)
# 1) 依赖(宿主树)
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness && pnpm install && pnpm run build
# 2) 插件依赖解析(junction 到宿主树;本仓库已配置)
# 3) 构建
../deepseek-harness/node_modules/.bin/tsdown
# 4) 安装进 profile(link: 活源迭代)并启动
set DSH_HOME=D:\work\dsh_cordis\.dsh-home
node apps/cli/lib/bin.js plugin --profile web add "link:D:/work/dsh_cordis/infinite_dialogue_canvas"
node apps/cli/lib/bin.js web
# 打开控制台输出的 http://127.0.0.1:3080/?token=...,侧边栏底部「画布」类型检查:tsc -p tsconfig.host.json --noEmit 与 tsc -p tsconfig.client.json --noEmit。
