v0.81.0
Added
-
宿主动作桥新增
compose():把内容放进输入框草稿(宿主 API v1 → v2)——startChat()是「新建对话并把一段话直接发出去」(脚本化,prompt立刻发),但「元素拾取」这类场景需要的是人在环中:内容先落进输入框,用户补一句「这三处间距不一致」再自己发。现在window.__piWebUiHost.compose({ text?, attachments? })干这件事,与startChat的差别是不要求连接就绪(草稿是本地状态,断线也能先攒着)且输入框没挂载时明确拒收(返回 false,不静默丢)。合并语义复用「撤回消息放回输入框」的同一个纯函数(空则填入、非空追加、绝不覆盖用户正在打的内容);附件按 path+mode+行区间去重,与手动 attach 的口径一致。定义见web/src/plugin-host.ts+web/src/composer-bridge.ts(草稿在 ChatInput、附件在 App,两处各自注册自己那一半)。 -
浏览器扩展「网页元素拾取」(
plugins/page-picker):在开发中的网页上点选元素,整理成 AI 能直接动手的上下文,一键注入 pi-web-ui 输入框(Alt+Shift+P/ 扩展图标 → hover 高亮 → 点击拾取,Shift+点击多选,Esc退出,Ctrl+Enter直接发送)。采集的不是截图而是能让 AI 一次改对的东西:React fiber 里的组件源码位置(Card.tsx:18:5+ 调用链)、Vue SFC 文件、命中的 CSS 规则源文件与行号(Vite dev 的<style data-vite-dev-id>的 textContent 与源文件逐字对应,行号可精确反推)、计算样式里只保留与默认值/继承值不同的项(现场造同 tag 空元素当探针比对,一个真实卡片通常只剩 3~5 行而不是 300 个属性)、短且唯一的定位串(#card>section.card> 兜底全:nth-of-type,兄弟冲突会在父级内补:nth-of-type收窄)、HTML 骨架、可选元素截图(走对话附件,不是把 base64 塞进正文)。详细度三档(精简/标准/完整)在采集层就生效。失败一律有兜底:没开 pi-web-ui 页面 / 版本过旧 / 输入框未就绪 / 截屏失败,都会把 Markdown 复制到剪贴板并说明原因,绝不出现「点了添加什么都没发生」。- 装法:下载
page-picker-extension.zip(打 tag 由.github/workflows/extension-release.yml自动出包,含 CRC 自校验;zip 打包器是自写的零依赖实现,Windows 上也能出同样的包)→ 解压 →chrome://extensions开发者模式「加载已解压的扩展程序」。也可以从源码npm run build:extension后加载plugins/page-picker/extension/。远程/局域网部署只需在选项页多点一下「授权该地址」。
- 装法:下载
-
pi-web-ui命令行/插件市场不适用于浏览器扩展:那条通道装的是服务端插件(<dataDir>/plugins/<id>/),装不了浏览器扩展 —— 这一点在根 README 与插件 README 里都写明了,免得有人对着pi-web-ui install找半天。 -
legado-web 插件:阅读页章末导航(读到底就能翻章)——阅读页原来只有顶部工具栏有「上一章 / 下一章」,正文读到页面底部什么也没有,这一章看完想接着读必须滚回顶部。现在正文末尾多一条「← 上一章 / 目录 / 下一章 →」(跟在正文下面,带《书名》· 第 n/总 章),换章后自动回到页面顶部;第一章「上一章」、最后一章「下一章」置灰并写明「已是最后一章」(顶栏同名按钮同规则,不再点了没反应),章末「目录」展开目录并回到顶部。回归:
tests/unit/legado-chapnav.test.ts(禁用态与文案边界:首章/中间章/末章/单章/空目录)+tests/legado-web-reader-test.mjs(真浏览器 + 3 章假书源,钉住导航条长在正文末尾、换章回顶、末章置灰、目录展开)。
Fixed
-
输入框里自动折行的长草稿,按
↑会误触历史回溯、打断正在进行的编辑(issue #127)——历史回溯的边界判定原先只看逻辑行(value 里有没有\n),可输入框是按宽度自动折行的:一段没有换行符的长草稿在界面上明明是多行,却被当成「只有一行」,光标停在第三行按↑也直接切到上一条历史(↓能切回来、草稿没丢,但编辑被打断,想改上一行只能动鼠标)。现在改按视觉行判定:新增web/src/caret-visual-line.ts,把与折行相关的样式(字体 / 行高 / 字距 /white-space/overflow-wrap)拷到一个隐藏镜像 div 上,塞入「光标前的文本 + 一个零宽标记」,量标记的offsetTop—— 与 textarea 自身的折行规则一致(pre-wrap+break-word),于是「光标上方 / 下方还有没有可见行」直接比像素:首视觉行 ⇔ 标记贴顶,末视觉行 ⇔ 与文末标记同高。拿不到布局的宿主(SSR / jsdom / 未挂载 /display:none)回落到旧的逻辑行判定,宁可少一次精确判定也不误判成「可以翻历史」;有选区、输入法组合中一律不碰历史。功能本身没退化:光标真的走到首 / 末视觉行后照旧翻历史,Esc/↓仍能回到草稿。回归:tests/unit/caret-visual-line.test.ts(像素折算 + 无布局回落 + 选区 / 空输入框边界)+tests/composer-history-test.mjs(真浏览器:折成 4 行的无换行草稿要按满 4 次↑才切历史、前 3 次逐行上移且内容不变、↓切回草稿、换行草稿与单行草稿的老边界行为不变、测量节点不残留草稿正文)。 -
MCP 桥把非文本内容块静默丢掉:截图 / 图像生成 / 图表类工具一律返回空串——
server/mcp-bridge.ts的McpClient.call()以前只拼type === "text"的块,image与resource块被直接丢弃,模型既不报错也拿不到任何东西,工具形同虚设(同一browser_screenshot调用:桥内得到"",桥外直连 stdio 是 22840 字符的image/png)。现在按块类型保序映射:image原样透传成 SDK 的ImageContent({type,data,mimeType},进会话后由 SDK 的normalizeToolResultImages统一缩放,超大图不会再让 provider 整段报错);文本型resource(resource.text)当文本透传——MCP 的EmbeddedResource分 TextResourceContents 与 BlobResourceContents 两种,前者是真实正文(filesystem 类 MCP 的 read_text_file 就走这条),退化成「已跳过」等于把文件内容吞掉;PDF 这类 blob 与 audio 退化为「mimeType + 约 N 字节,无法内联」的提示(SDK 内容联合只有 text/image/thinking/toolCall,没有 blob 载体);纯文本结果仍返回拼接字符串(老形状不变,不破坏既有调用方)。回归:tests/unit/mcp-bridge.test.ts(image 逐字保真 / 文本资源不丢正文 / blob 退化提示 / 混合保序)+tests/mcp-bridge-test.mjs(e2e 握手 8 tools)。限定:Web UI 的工具卡按既有行为只渲染文本(工具结果里的图片在序列化时是[image result]),图片会进模型上下文但不在 tool 卡里显示。 -
命令行
pi-web-ui install <插件> --force之后插件一直「不存在」:CLI 装插件是先整目录删掉再拷新的(install --force的 rm→cp 窗口),撞上这个窗口期的一次插件扫描会把插件当成「已卸载」反激活;而反激活时没把插件从attempted集合里摘掉,目录回来后永远不会再激活——插件的 HTTP 路由(如 legado-web 的/plugins-api/legado-web/proxy)与 AI 工具在本进程内彻底消失,前端只报「代理请求失败 404 」,CLI 承诺的「服务运行中刷新浏览器即可加载」失效,必须重启服务才恢复。现在反激活会摘掉attempted并推进 epoch(重新import拿到磁盘上的新代码、浏览器也重拉插件 client bundle),刷新浏览器即自愈。回归:tests/unit/plugin-manager.test.ts(目录消失→回来必须重新激活且用新代码)+tests/plugin-test.mjs(真实 HTTP 路由的 rm→cp 窗口自愈)。
i18n
- 本版无文案增量(相对 v0.80.2,已核查)。