返回目录
其他 待识别

visualize

Slothtron/visualize

该仓库暂未提供项目说明。

Stars
0
Forks
0
Issues
0
更新
7 天前

PROJECT TOPICS

项目标签

PROJECT README

README

visualize

在 Agent 对话中把模型输出的 HTML / Mermaid 渲染成可交互卡片的产品线。宿主无关:同一套 core 与浏览器 UI 套件,接不同 Agent。

卡片原位替换原来的源码块:客户端观察会话 DOM、按围栏认领 html / mermaid 代码块(v0.6 起免 marker,围栏闭合即出卡),其余内容(散文、推理、图片、其它代码块)仍交回宿主自己的渲染管线。

卡片 chrome(v0.4):唯一一个悬浮工具栏钉在卡片右上角,hover / 键盘聚焦时浮现(源码态常显)。预览态 [↓][⧉][⛶ 放大] │ [‹› 查看源码],源码态 [语言][⧉ 复制] │ [◉ 预览],失败态 [错误码][⧉ 复制][↻ 重试]。源码态直接用宿主代码渲染器(shiki 高亮、<pre> 排版都是宿主的),宿主自带的 banner 折起来、语言名挪进胶囊——所以两种视图都只有一个工具栏。工具栏文案跟随会话语言。

放大查看(v0.7)落在宿主的右栏面板:点 打开右栏的一个标签页(标签名取自围栏语言,如 HTML),面板里是同一张卡片的预览 / 源码 / 复制 / 导出,分栏与浮动由宿主提供。宿主没有右栏面板时这个入口不出现——面板是宿主的面板,插件不自己造(早先塞进宿主表单对话框的做法实测被裁掉且无处可滚,见 CHANGELOG 0.7.0)。

按钮文案永远等于它真实会做的事,没有格式菜单:

卡片状态 下载 复制
可栅格化(export.raster,当前是 mermaid) 下载图片(PNG,尺寸 ≥ 屏幕密度) 复制图片到剪贴板(PNG)
仅矢量(export.vector,当前是 html) 下载 SVG(沙箱 DOM 快照,矢量、可缩放) 复制源码文本
尚无产物(快照未到/超限) 下载 HTML 源文件(自包含沙箱文档) 复制源码文本

导出目标由 action-target.ts 的两个纯函数裁定(primaryDownload / previewCopyStrategy),按钮从中取名,所以不会出现"写着下载图片、其实在下 HTML",也不会出现点了必然失败的图片动作。

分辨率:产物先经 core 的 normalizeSvgSize 把尺寸钉在自己声明的 viewBox 上,再按 max(config.export.scale, devicePixelRatio) 栅格化——响应式 SVG(mermaid 默认 width="100%"、无 height)作为 <img> 只有 300px 固有宽,不这么做导出的图会明显发糊(实测:卡片屏上 1484 设备像素,旧实现导出 600px 宽)。

HTML 卡为什么只有 SVG:Chromium 禁止含 foreignObject 的 SVG 图片读回 canvas(实测见 core/spec/html-artifact.md),所以 HTML 快照既栅格化不成 PNG 也写不进剪贴板。这是浏览器规则,与沙箱无关。

实机截图:预览态(mermaid 卡) · 源码态(HTML 卡) · 放大后的右栏面板 · 面板里的源码态

core/spec(语言中立契约:协议 / schema / 错误码 / golden fixtures)
   ↓
core/ts(纯逻辑:fence 扫描 / gate / 切分计划 / 视图状态机 / 沙箱文档 / 报文校验;零宿主、零 DOM)
   ↓
platforms/web(浏览器 UI 套件:React 组件 + DOM 胶水;宿主差异经 WebPlatformPorts 注入;零宿主 SDK)
   ↓
adapters/dsh(DSH 接线:config wire + 围栏认领通道 + 资产路由 + 端口实现;零业务逻辑)

依赖单向;coreplatforms 都不认识任何宿主。

能力清单

能力 说明
scan 按 fence 协议切分可视围栏(v0.6 起所有配置语言免 marker;render.mermaidRequiresMarker 可单独收紧 mermaid),并返回围栏的行区间
meta meta 键值解析 + HTML 实体反转义(title/preview/height/theme/allow
gate 顺序短路判定:unsupported_language → fence_incomplete → source_too_large → render_disabled → too_many_blocks
index 组合 scan+gate 产出宿主中立的可视化索引(看不到消息正文的宿主形态)
plan-visual 把一条消息的 markdown 切成"文本段 / 卡片段"并随段带上源码,供原位替换(带 UI 的宿主形态)
view-machine 预览/源码双视图纯 reducer,含失败记忆
sandbox-doc 逐字节确定的沙箱文档 + sandbox 属性矩阵 + CSP(安全单源)
capture-validate 捕获报文形状 / 在途 id / dataURL 前缀白名单 / 长度上限校验
svg-size 把 SVG 产物的尺寸钉在其 viewBox 上(导出分辨率不再取决于作者怎么写)
html-artifact 沙箱 DOM 快照 → foreignObject SVG 产物(HTML 卡的导出形态)
naming 导出文件名(slug + 时钟端口时间戳)
visualize_present(可选工具) 把片段渲染成自包含文件并回显路径;默认关闭

indexplan-visual 是同一套 scan+gate 的两种产物;对同一 fence 产出的 IndexItem 逐字节一致。

支持矩阵

宿主

能力 dsh octop pi
原位替换(卡片取代源码块) ✅ DOM 围栏认领(观察 + 旁挂) ⚠ 需宿主有等价的节点渲染器扩展点
已有会话追溯 ✅ 读消息正文即可,无索引/无重放依赖 ⚠ 视宿主能否读到正文
预览 / 源码双视图 ✅(组件经端口注入) ❌ 终端
源码态复用宿主高亮 ✅ 宿主 CodeBlock(shiki) ⚠ 视宿主有无代码渲染器
工具栏位置与文案 ✅ 悬浮于卡片右上角,文案跟随会话语言 ⚠ 视宿主
放大查看 ✅ 宿主右栏标签页(宿主没有右栏 ⇒ 入口不出现) ⚠ 视宿主有无面板
图片导出 ✅ PNG(mermaid,≥ 屏幕密度)/ SVG(html) ⚠ 视 webview 桥能力
配置跟随宿主 config ✅ wire 送归一化 config ⚠ 需等价通道
visualize_present 工具 ✅(可选开关) ⚠ 降级为"写文件 + 路径回显"

第一期只实现 dsh;octop / pi 仅为规划位,未实现。

渲染语言

language marker 要求 导出 沙箱
html 免 marker(v0.6 起全部认领) SVG(沙箱 DOM 快照 → foreignObject;PNG 受 Chromium 限制不可得)+ 自包含 .html 文档 opaque-origin iframe
mermaid 免 marker(render.mermaidRequiresMarker 可改严) PNG + SVG 渲染出的 SVG 同样注入沙箱;引擎为独立懒加载资产(首张卡时经插件自有路由加载)

新增一种渲染语言 = 动 core/spec + core/ts + platforms/web/src/registry.ts 一行,adapters/ 零改动

安装(DSH)

三种方式,按场景选一种即可。推荐方式一:一条命令、零构建。

方式一:从 git 源安装(推荐)

dsh plugin --profile <profile> add \
  "@slothtron/visualize-dsh@git+https://github.com/Slothtron/visualize.git#master&path:adapters/dsh"

dsh --profile <profile> --dump-config | grep -A3 visualize   # 应出现 id: visualize 一行

可复现就把 master 换成 commit(git rev-parse HEAD)或 tag;日常跟分支用 master 最省事。升级=换 ref 重装 (dsh plugin --profile <profile> add "…@git+…#<新 ref>&path:adapters/dsh"),回滚=装回旧 ref。

写法要点(都实测过)

要点 说明
&path:adapters/dsh冒号 写成 &path= 会被当成 ref 的一部分,报 Could not resolve path=adapters/dsh to a commit
整串必须加引号 里面有 #&,不加引号会被 shell 截断或后台化
ref 写实 只写 #path:adapters/dsh 会跟随默认分支 HEAD

前置条件与常见报错

现象 原因 处理
Could not resolve <ref> to a commit of <url> ref 解析不到:写错,或该 commit/tag 还没 push git rev-parse HEAD 取真值并确认已 push
could not read Username for 'https://github.com' 私有库或未登录而走 HTTPS 配 credential helper,或改用 git+ssh://git@github.com/Slothtron/visualize.git#…
安装时提示 git-hosted plugins build on install via their prepare script … allowBuilds 这是 dsh 对所有 git 依赖的通用提示 本包没有 prepare 脚本,可忽略;先解决上面两类真实错误

为什么安装时不需要构建lib/提交入库策略(AGENTS §3 的二选一,见 ./.gitignore 的说明), git 依赖里直接带着构建产物(core 与 platforms/web 已内联进 lib/client.js),所以不跑 prepare、 也不需要 pnpm 的构建授权。代价是产物新鲜度:pnpm run verify 里的 verify:lib 会断言"重新构建后产物 与提交内容一致",改了源码必须把重新构建的 lib/ 一起提交。

方式二:从 tarball 安装

git clone https://github.com/Slothtron/visualize.git && cd visualize
pnpm install && pnpm run verify && pnpm run build
cd adapters/dsh && npm pack --pack-destination /tmp
dsh plugin --profile <profile> add "@slothtron/visualize-dsh@file:/tmp/slothtron-visualize-dsh-<版本>.tgz"

注意:同一路径的 tarball 重装时 pnpm 可能跳过解析(Lockfile is up to date, resolution step is skipped), 导致装的还是旧内容——换一个新文件名再装即可。

方式三:从 registry 安装(发布后)

dsh plugin --profile <profile> add @slothtron/visualize-dsh

配置项与默认值

配置分片由 core 唯一定义(core/ts/src/config.ts);适配器只做映射,不得新增第二份默认值。非法配置在加载期以 invalid_config(附字段路径)失败。

分片 默认值
render enabled true
languages ["html", "mermaid"]
mermaidRequiresMarker false
presentTool false
limits maxSourceBytes 262144(256 KiB)
maxBlocksPerMessage 8
maxIndexedItems 512
maxConcurrentIframes 4
maxCaptureBytes 16777216(16 MiB)
sandbox csp default-src 'none'; style-src 'unsafe-inline'; img-src data:; script-src 'unsafe-inline'
allowAssets false(置真只把 img-src 放宽为 data: https:
export formats ["png", "svg", "html"]
scale 2
maxPixels 4096
timeoutMs 5000
view defaultView "preview"

快速验证

pnpm install
pnpm run verify        # build + typecheck + test + verify-contract + dependency-lint + pack-check + verify:lib

pnpm run verify 是单一入口,等价的 POSIX 包装是 ci/verify.sh。分步:

命令 作用
pnpm run build 三包构建(adapter 内联 core + platform,产物自包含)
pnpm run typecheck tsc --noEmit(core 无 DOM lib,platform 有)
pnpm run test vitest(core 纯 node、platform jsdom、adapter node)
pnpm run verify-contract core/spec/fixtures,含 sandbox-doc 逐字节 golden
pnpm run dependency-lint 强制依赖方向:core 零宿主零 DOM、platforms 零宿主 SDK、adapters 语言无关
pnpm run pack-check npm pack --dry-run 只含白名单(含 lib/client.js
pnpm run verify:lib lib/ 入库策略的护栏:重新构建后产物必须与提交内容一致(见 .gitignore

实机验收(需先起一个装好插件的 dsh 服务;脚本不依赖任何 npm 安装):

node scripts/acceptance-card.mjs "http://127.0.0.1:18765/?token=<token>" /tmp "<会话标题>" [截图前缀]
#   指向含卡片的会话:驱动工具栏、解码导出的图、逐条断言并在失败时以非零码退出

node scripts/probe-export.mjs "http://127.0.0.1:18765/?token=<token>"   # 导出分辨率数值诊断(不出图不断言)
node scripts/probe-html-artifact.mjs "…"                                 # foreignObject 快照为何只能出 SVG

端到端:装插件 → 让模型输出一个 ```html```mermaid 围栏(免 marker)→ 该围栏原位变成卡片(原文不再以代码块出现,但可在卡片的源码视图里带高亮查看/复制/导出)→ 工具栏切换视图 → 导出。

golden fixture 维护

core/spec/fixtures/sandbox-doc/*.expected.html 是安全契约的字节级真相。修改沙箱文档后:

node scripts/verify-contract.mjs --update   # 重新钉 golden

该命令会打印醒目警告;必须逐字节审阅 diff 后再提交,不得用它掩盖非预期的漂移。

CI 接入(托管方无关)

唯一入口是 ci/verify.sh;它不含任何托管方专有内容,换托管方时只需让流水线调用它。

sh ci/verify.sh
托管方 接法
GitHub Actions run: sh ci/verify.sh(workflow 由托管方自行添加)
GitLab CI script: [ "sh ci/verify.sh" ]
Jenkins sh 'sh ci/verify.sh'
内部流水线 调用同一脚本

可选的最新宿主探针(无 matrix 支持时用):

VERIFY_LATEST_HARNESS=1 pnpm run verify:latest-harness

安全模型

  1. 模型输出进入 opaque-origin 沙箱 iframe(sandbox="allow-scripts"禁止 allow-same-origin),绝不进宿主 React 树、绝不用 dangerouslySetInnerHTML
  2. 默认断网:CSP default-src 'none'allow="assets" 放宽 img-src。CSP 串、sandbox 属性矩阵、bootstrap 文档是 core 的 golden fixture,适配器无权放宽
  3. 三重校验:transport 检查 event.source === iframe.contentWindow,core 校验报文形状与在途 id,源取回校验 ref 命中当前索引。
  4. 限额全部走归一化配置,非法即启动失败。
  5. 零凭据、零落盘(visualize_present 例外,且仅在显式开启时写文件);fixtures 全假值。

已知限制与待确认项

M0 运行时疑点已全部实机闭环(dsh 0.1.5-rc.2 / Node 26):

  1. wire 的第三方 projection key 是否真出现在 useProjection props 中 ✅ 成立(无 wire 则恒 undefined)。
  2. assistant/message 事件上的 seq / 载荷字段event.seq + event.data.message.content[]
  3. DSH 如何把插件 config 同时交给 host 与 client 两半 ✅ wire 送归一化 config(key visualize)。
  4. client 半区的 tools 服务注册形态 ✅ 缺失时 visualize_present 显式降级并告警。

剩余边界:

  • 宿主按视口渐进渲染历史消息:不在视口内的围栏尚未进 DOM,滚动到该处时才出卡(真实使用自然发生)。
  • octop / openclaw / hermes 的等价扩展点未评估;若其 UI 读不到消息正文,需退回"索引 + SourcePort"路径(buildIndexSourcePort 已为此保留)。
  • 源码态折起宿主 banner 依赖宿主声明的稳定钩子(data-code-block-banner / data-code-block-content);宿主改名只会让 banner 重新出现,不影响功能。
  • 卡片样式表由适配器注入;若某宿主 CSP 禁掉注入的 <style>,退化为"胶囊常显 + banner 可见 + 源码顶部无内边距",控件仍可用(结构在 inline style 里)。
  • 预览态"复制图片"/"下载图片"仅对可栅格化的渲染器生效(当前是 mermaid);html 卡显示"下载 SVG"/"复制源码"——按钮文案如实反映它真正会做的事,原因见 core/spec/html-artifact.md
  • 本机 WSL2 下 bsk 的 Agent Window 标签页常处于 occluded 状态,Chrome 不跑渲染步(IntersectionObserver 不触发 → 宿主代码块也不高亮)。实机验收需用能真实渲染的环境(见适配器 README 的验收方法说明)。

实测细节、降级清单与 9 条集成缺陷记录见 adapters/dsh/README.md

目录结构

visualize/
├── core/spec/          # 语言中立契约 + golden fixtures
├── core/ts/            # @slothtron/visualize-core
├── platforms/web/      # @slothtron/visualize-web
├── adapters/dsh/       # @slothtron/visualize-dsh
├── scripts/            # verify-contract / verify-lib-fresh / dependency-lint / pack-check
│                       #   / acceptance-card / probe-export / probe-html-artifact
├── ci/verify.sh        # 单一 verify 入口(POSIX)
├── .github/workflows/  # verify 门禁(GitHub 托管时生效;其它托管方调 ci/verify.sh)
├── docs/acceptance/    # 实机验收截图(**不放设计文档**)
└── tests/contract/     # 契约测试说明(实现即 scripts/verify-contract.mjs)

各包 lib/ 属于提交入库的构建产物(策略与理由见 ./.gitignore);sourcemap 不入库。

文档

类型 位置
能力契约与协议(实现与测试的单源) core/spec/(在本仓,随代码演进)
安装 / 配置 / 验证 / 支持矩阵 本 README 与三处包 README(在本仓)
实机验收截图 docs/acceptance/(在本仓,是证据不是设计)
设计稿与开发计划 不在本仓plugins/docs/visualize-design.mdplugins/docs/visualize-dev-plan.mdplugins/docs/visualize-dsh-design.md

设计方案文档刻意与代码仓库分离:仓库里禁止出现设计文档.gitignore 也对这几个路径做了兜底)。要改设计请改仓外那几份,再在 CHANGELOG.md 与本 README 里引用结论。

License

MIT

CLASSIFICATION EVIDENCE

分类依据

项目类型待识别
功能分类其他
规则置信度

系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。