dsh-ppt-canvas
元素级 PPT 画布,就地改写已打开的 .pptx:点选、框选或整页选中后用自然语言描述改动,AI 以字节保真的 OOXML 补丁写入,支持撤销与重做。
安装
dsh plugin --profile web add dsh-ppt-canvas
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
dsh-ppt-canvas
DeepSeek Harness 插件:在 PPT 画布上点选 / 框选组件(文字、色块、图片、线条、表格),
输入一句自然语言修改要求,由 AI 生成具体的修改方案并实时落到 .pptx 文件与画布。
选择状态、AI 结果、PPT 文档三方同步,带撤销/重做与完整的错误处理。
灵感来自腾讯 WorkBuddy 的「人机双写」(V5.3.5,2026 07 30 发布)中的 PPT 能力:点选或框选元素 → 下达指令 → 只改选区。
本插件把它做成 DSH 的原生插件:改的是磁盘上的真实 .pptx,不是渲染出来的中间产物。
上图:左侧页面缩略图、中间画布(LibreOffice 精确渲染)、右侧属性面板,最右是折叠起来的对话转播栏;
底部是 AI 修改指令框。画布上选中的文本框(textbox 34)坐标/字号/填充都可在右侧直接改。
✨ 功能
能力 · 说明
可视化选择 · 画布按 .pptx 的真实几何/样式渲染每页元素:文本框、色块(含圆角/描边/渐变)、图片(含裁剪)、线条、表格、组合
单击选中 · 点击任一元素即选中,2px 主色描边 + 四角把手实时高亮;悬停显示虚线预览
框选(marquee) · 空白处拖拽出选框,与选框相交的元素全部选中
多选 / 批量 · Shift/Ctrl + 点击 增删;属性面板出现「批量对齐」(左右/居中/顶底/水平等距)
选整页 · 点击页面缩略图左上角「整页」,一次选中该页全部元素(对应 WorkBuddy 的「选页做 AI 编辑」)
拖拽打开 · 把 .pptx 直接拖到面板上即可打开;拖拽时整块界面给出投放提示
可拖动浮窗 · 面板标题栏可拖动、右下角可缩放,位置与尺寸记忆在 localStorage。启动时一律最小化(只留右下角「PPT 画布」入口),打开文稿也不会自动弹出来挡界面
对话转播 · 右侧把当前会话的对话内容按普通对话形式转播(你 / AI 两条角色),推理过程弱化为附注;不再拆成「思考过程 / 回复」两栏
PPT 意图提示 · 会话里提到做/改 PPT(PPT、演示文稿、幻灯片…)且画布没打开时,右下角弹出「看起来你在做 PPT · 打开画布 / 不用了」;插件自己发的指令不会触发
自然语言指令 · 指令框输入要求,Enter 直接发送(Shift+Enter 换行);「加入待发」可把多条攒起来一起发
选区上下文 · 选中元素的位置、尺寸、样式、层级 z、文本内容随指令一起交给 AI(不需要截图或复述内容)
AI 修改方案 · AI 通过 ppt_canvas_context 取回选区,产出 ops 数组,再用 ppt_canvas_apply 落盘;复杂方案可先 dryRun 试算
实时同步 · 落盘后画布自动刷新到新 rev;AI 刚改动的元素淡黄色呼吸高亮 3 秒(面板每 4 秒轮询 rev)
撤销 / 重做 · Host 侧字节级快照(每次写盘前留档),Ctrl+Z / Ctrl+Shift+Z,最多 50 步
错误处理 · 逐条校验错误、版本冲突(staleRev)、文件占用、AI 未产出方案 —— 都有明确提示与「重试」
🚀 安装
方式一:安装到当前 profile(推荐)
node tools/install-profile.mjs profile desktop
脚本做两件幂等的事:在 ~/.dsh/profiles/<profile>/node_modules 下建一个指向本仓库的
junction,并把包名加进 profile package.json 的 dsh.profile.bundles。
(dsh plugin add 也可以,但本机 profile 的 node_modules 是用 pnpm 从
~/.pnpm/store/v11 link 出来的,pnpm 11 默认找 %LOCALAPPDATA%\pnpm\store,
会以 ERR_PNPM_UNEXPECTED_STORE 失败——本插件零依赖,所以直接建 junction 更稳。)
卸载:node tools/install-profile.mjs profile desktop uninstall
安装后重启 DSH(新增 loader 行需要重新 boot)。之后:
侧边栏底部出现 ▦ PPT 画布 按钮;
对话里 AI 会多出 4 个工具:ppt_canvas_open / ppt_canvas_context / ppt_canvas_apply / ppt_canvas_history。
验证装好了:
# Host 路由应在(未重启时会 404)
curl.exe -s http://127.0.0.1:19387/dsh ppt/api/selftest
# 客户端 bundle 应在
curl.exe -s -o NUL -w "%{http_code}" http://127.0.0.1:19387/plugins/dsh-ppt-canvas/client.js
# 或者直接跑加载测试
node tests/host-load.test.mjs
方式二:改客户端即时生效(开发用)
plugin/client.js 改动不需要重启:DSH 的 client modules 每次请求都重读文件,HMR 会按内容 hash 热替换。
只有 package.json 的 dsh.client 声明、或 cordis 加载行变化才需要重启。
# 改完 client/ 下的源码后同步到 plugin/(内联 CSS + 复制引擎) node tools/sync-plugin.mjs
🖱 用法
1. 打开面板:点侧边栏底部 ▦ PPT 画布(或面板最小化后右下角的「PPT 画布」启动器)。
2. 打开文件,三种方式任选:
拖拽:把 .pptx 直接拖到面板上(拖进去时整块界面会变蓝并提示);
打开:点「打开」走系统文件选择(DSH 的目录选择服务,返回绝对路径);
演示文稿:打开内置示例(会复制到 ~/.dsh/ppt-canvas/)。
插件直接读写这个文件,每次修改前自动留档(可撤销)。建议先备份。
3. 选择元素:点选 / Shift+点击 多选 / 空白处拖拽框选 / 缩略图「整页」。
4. 输入指令,回车发送(Shift+Enter 换行)。例:
把标题改成红色并右对齐
这三个色块统一成品牌蓝 #1A73E8,圆角 8
把这行字缩小到 14px 并居中
把这张图片右移 40px,并置顶
把选中的两个文本框左对齐、水平等距
5. AI 改完后画布自动刷新,改动元素高亮;不满意就 Ctrl+Z。
也可以在对话里直接说「打开 D:\work\deck.pptx」,AI 会调用 ppt_canvas_open。
独立桌面窗口(已移除)
这一版不包含「独立桌面窗口」功能。它曾经实现过(Electron 起一个可自由拖动、
位置记忆的窗口),但在这台机器上始终无法通过宿主侧的验证,已按要求整体撤掉。
源文件与恢复步骤都保存在 archive/standalone-window/(含一份 README.md 说明
当时踩过的坑:ELECTRON_RUN_AS_NODE 必须以删除键的方式清理、被 vendor 的裸
electron.exe 不提供 require('electron')、plugin/index.js 会遮蔽入口等)。
tests/no-window.test.mjs 会守住这个边界:面板里不再有入口按钮、没有窗口路由、
ppt_canvas_window 工具已注销、归档文件仍然可恢复。
🧠 它是怎么工作的
Client half (plugin/client.js) Host half (host/*.js)
┌──────────────────────────┐ ┌─────────────────────────────┐
│ 画布:按 Deck 模型渲染 │ POST /dsh ppt/api │ api.js HTTP 路由 │
│ 选择:点选/框选/多选 │ ──────────────────► │ deck store.js 唯一权威 │
│ 指令框 + 撤销/重做 UI │ ◄────────────────── │ pptx read.js OOXML → Deck │
└──────────────────────────┘ Deck / ApplyResult │ pptx write.js Patch → OOXML│
│ │ zip.js / xml.js 自研底层 │
│ session.prompt(...) │ history.js 字节级快照 │
▼ │ tools.js 4 个模型工具 │
┌───────────┐ ppt_canvas_context/apply │ standalone server.js │
│ DeepSeek │ ──────────────────────────────│ 独立 loopback :19488 │
└───────────┘ └─────────────────────────────┘
两条 HTTP 通道,客户端自动择一。 插件优先访问自起的独立 loopback 服务
(127.0.0.1:19488,端口被占用则顺延),失败才回退到宿主路由 /dsh ppt/api。
这样即使宿主的路由表没有注册(受限宿主下很常见),画布也能连上 Host。
界面层与宿主解耦。 client/app.js 里没有任何 DSH 专有 API:React / ReactDOM
由 plugin/client.js 从 DSH 的模块表取,prompt 走 session.prompt,apiBase 指向 Host
通道。client/app.js 与 client/ppt-style.js 在构建时被内联进 plugin/client.js。
关键设计:
Host 是唯一权威。磁盘上的 .pptx 是事实,rev 每次成功写盘 +1;客户端永远以 Host 返回的 Deck 覆盖本地模型,所以不存在「三方各说各话」。
选区先暂存,再交给 AI。客户端发指令前把选区 POST 到 /dsh ppt/api/context 拿到 requestId,AI 只能通过 ppt_canvas_context(requestId) 取回——这样用户消息里不需要塞大段 JSON,也便于审计「AI 是否真的读了选区」。
校验先于写入。applyPatch 先在内存里跑完整校验与试算,任何一条 op 不合法就整包拒绝并返回逐条原因,文件一个字节都不动。
编辑作用在解析出来的原树上。不是正则替换也不是重建 XML,所以不认识的属性、扩展、关系、母版引用都不会丢;未触碰的页字节级不变(有测试守着)。
撤销存字节不存 diff。OOXML 补丁的逆运算极易失真,回放写盘前的完整字节是唯一无损的做法。
为什么不用 npm 依赖
插件以 link 方式安装在 profile 的 node_modules 下,解析不到宿主包,也不方便带依赖。
所以 ZIP 读写(node:zlib + 自研中央目录解析)、XML 保前缀解析、PPTX 读写全部自研,只依赖 node: 内建模块。
📋 Patch 能力(AI 可用的修改指令)
op · 作用
setText · 替换整段文本(保留原 run 样式,自动处理 endParaRPr)
setTextStyle · 颜色 / 字号 / 粗体 / 斜体 / 下划线 / 对齐 / 字体(同时写 `a:
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:2,最后更新 2026-10-17