@anionex/dsh-vision-toolkit 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +383 -0
  4. package/README.zh.md +383 -0
  5. package/assets/dsh-conversation-artifact.png +0 -0
  6. package/assets/dsh-conversation-image-qa-top.png +0 -0
  7. package/assets/dsh-conversation-image-qa.png +0 -0
  8. package/assets/dsh-conversation-pixel-diff.png +0 -0
  9. package/assets/dsh-conversation-screenshot-debugging-top.png +0 -0
  10. package/assets/dsh-conversation-screenshot-debugging.png +0 -0
  11. package/assets/dsh-conversation-tool-call.png +0 -0
  12. package/assets/dsh-conversation-vision-trace.png +0 -0
  13. package/assets/hero.png +0 -0
  14. package/assets/social-preview.png +0 -0
  15. package/assets/upstream/README.md +16 -0
  16. package/assets/upstream/image-qa.webp +0 -0
  17. package/assets/upstream/infographic-reference.webp +0 -0
  18. package/assets/upstream/infographic-result.webp +0 -0
  19. package/assets/upstream/screenshot-debugging.webp +0 -0
  20. package/assets/upstream/ui-result.webp +0 -0
  21. package/assets/upstream/ui-sketch.webp +0 -0
  22. package/assets/vision-settings.png +0 -0
  23. package/cordis.patch.yml +6 -0
  24. package/docs/assets/vision-settings.png +0 -0
  25. package/docs/requirements-traceability/README.i18n.yaml +6 -0
  26. package/docs/requirements-traceability/README.md +75 -0
  27. package/docs/requirements-traceability/README.zh.md +75 -0
  28. package/examples/ui-restoration/README.i18n.yaml +6 -0
  29. package/examples/ui-restoration/README.md +70 -0
  30. package/examples/ui-restoration/README.zh.md +70 -0
  31. package/examples/ui-restoration/assets/final-heatmap.png +0 -0
  32. package/examples/ui-restoration/assets/final-report.json +83 -0
  33. package/examples/ui-restoration/assets/implementation.png +0 -0
  34. package/examples/ui-restoration/assets/initial-heatmap.png +0 -0
  35. package/examples/ui-restoration/assets/initial-report.json +83 -0
  36. package/examples/ui-restoration/assets/initial.png +0 -0
  37. package/examples/ui-restoration/assets/metrics.json +12 -0
  38. package/examples/ui-restoration/assets/reference.png +0 -0
  39. package/examples/ui-restoration/implementation.html +94 -0
  40. package/examples/ui-restoration/initial.html +57 -0
  41. package/lib/artifact-access.js +369 -0
  42. package/lib/artifact-access.js.map +1 -0
  43. package/lib/artifacts.js +56 -0
  44. package/lib/artifacts.js.map +1 -0
  45. package/lib/client.js +952 -0
  46. package/lib/client.js.map +1 -0
  47. package/lib/config.js +117 -0
  48. package/lib/config.js.map +1 -0
  49. package/lib/errors.js +56 -0
  50. package/lib/errors.js.map +1 -0
  51. package/lib/exposure.js +213 -0
  52. package/lib/exposure.js.map +1 -0
  53. package/lib/index.js +97 -0
  54. package/lib/index.js.map +1 -0
  55. package/lib/paste-images.js +199 -0
  56. package/lib/paste-images.js.map +1 -0
  57. package/lib/paths.js +325 -0
  58. package/lib/paths.js.map +1 -0
  59. package/lib/runtime-install.js +601 -0
  60. package/lib/runtime-install.js.map +1 -0
  61. package/lib/runtime-manager.js +126 -0
  62. package/lib/runtime-manager.js.map +1 -0
  63. package/lib/runtime.js +1344 -0
  64. package/lib/runtime.js.map +1 -0
  65. package/lib/skill.js +139 -0
  66. package/lib/skill.js.map +1 -0
  67. package/lib/tools.js +528 -0
  68. package/lib/tools.js.map +1 -0
  69. package/lib/types/artifact-access.d.ts +61 -0
  70. package/lib/types/artifact-access.d.ts.map +1 -0
  71. package/lib/types/artifacts.d.ts +42 -0
  72. package/lib/types/artifacts.d.ts.map +1 -0
  73. package/lib/types/client/index.d.ts +179 -0
  74. package/lib/types/client/index.d.ts.map +1 -0
  75. package/lib/types/client/paste-images.d.ts +57 -0
  76. package/lib/types/client/paste-images.d.ts.map +1 -0
  77. package/lib/types/config.d.ts +73 -0
  78. package/lib/types/config.d.ts.map +1 -0
  79. package/lib/types/errors.d.ts +35 -0
  80. package/lib/types/errors.d.ts.map +1 -0
  81. package/lib/types/exposure.d.ts +40 -0
  82. package/lib/types/exposure.d.ts.map +1 -0
  83. package/lib/types/index.d.ts +18 -0
  84. package/lib/types/index.d.ts.map +1 -0
  85. package/lib/types/paste-images.d.ts +21 -0
  86. package/lib/types/paste-images.d.ts.map +1 -0
  87. package/lib/types/paths.d.ts +107 -0
  88. package/lib/types/paths.d.ts.map +1 -0
  89. package/lib/types/runtime-install.d.ts +49 -0
  90. package/lib/types/runtime-install.d.ts.map +1 -0
  91. package/lib/types/runtime-manager.d.ts +60 -0
  92. package/lib/types/runtime-manager.d.ts.map +1 -0
  93. package/lib/types/runtime.d.ts +389 -0
  94. package/lib/types/runtime.d.ts.map +1 -0
  95. package/lib/types/skill.d.ts +15 -0
  96. package/lib/types/skill.d.ts.map +1 -0
  97. package/lib/types/tools.d.ts +22 -0
  98. package/lib/types/tools.d.ts.map +1 -0
  99. package/lib/types/upstream.d.ts +207 -0
  100. package/lib/types/upstream.d.ts.map +1 -0
  101. package/lib/types/version.d.ts +15 -0
  102. package/lib/types/version.d.ts.map +1 -0
  103. package/lib/types/web-request.d.ts +4 -0
  104. package/lib/types/web-request.d.ts.map +1 -0
  105. package/lib/types/web.d.ts +74 -0
  106. package/lib/types/web.d.ts.map +1 -0
  107. package/lib/upstream.js +675 -0
  108. package/lib/upstream.js.map +1 -0
  109. package/lib/version.js +18 -0
  110. package/lib/version.js.map +1 -0
  111. package/lib/web-request.js +20 -0
  112. package/lib/web-request.js.map +1 -0
  113. package/lib/web.js +244 -0
  114. package/lib/web.js.map +1 -0
  115. package/package.json +139 -0
  116. package/runtime/requirements.lock +3 -0
  117. package/src/artifact-access.ts +386 -0
  118. package/src/artifacts.ts +85 -0
  119. package/src/client/index.tsx +866 -0
  120. package/src/client/paste-images.tsx +426 -0
  121. package/src/config.ts +177 -0
  122. package/src/errors.ts +62 -0
  123. package/src/exposure.ts +227 -0
  124. package/src/index.ts +122 -0
  125. package/src/paste-images.ts +234 -0
  126. package/src/paths.ts +348 -0
  127. package/src/runtime-install.ts +723 -0
  128. package/src/runtime-manager.ts +166 -0
  129. package/src/runtime.ts +1783 -0
  130. package/src/skill.ts +143 -0
  131. package/src/tools.ts +668 -0
  132. package/src/upstream.ts +861 -0
  133. package/src/version.ts +37 -0
  134. package/src/web-request.ts +17 -0
  135. package/src/web.ts +329 -0
  136. package/vendor/agent-vision-toolkit/CHANGELOG.md +16 -0
  137. package/vendor/agent-vision-toolkit/LICENSE +21 -0
  138. package/vendor/agent-vision-toolkit/README.md +399 -0
  139. package/vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json +89 -0
  140. package/vendor/agent-vision-toolkit/bin/crop +90 -0
  141. package/vendor/agent-vision-toolkit/bin/detect +13 -0
  142. package/vendor/agent-vision-toolkit/bin/glance +93 -0
  143. package/vendor/agent-vision-toolkit/bin/ground +13 -0
  144. package/vendor/agent-vision-toolkit/bin/trace +129 -0
  145. package/vendor/agent-vision-toolkit/detect.py +56 -0
  146. package/vendor/agent-vision-toolkit/ground.py +216 -0
  147. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/dominant_colors.py +224 -0
  148. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/extract_fg.py +278 -0
  149. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/html_shot.py +108 -0
  150. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/long_screenshot_ocr.py +1245 -0
  151. package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/pixel_diff.py +88 -0
  152. package/vendor/agent-vision-toolkit/vision_client.py +156 -0
@@ -0,0 +1,75 @@
1
+ # Vision Toolkit 需求追踪
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 本参考将 DSH Vision Toolkit 产品需求中承诺的 P0/P1 条目映射到对应实现、自动化覆盖和可运行验收证据。标记为**已交付**的条目属于软件包契约;P2/P3 条目记录有意设置的产品边界,不代表尚未完成的 P0/P1 工作。
6
+
7
+ ## P0 产品需求
8
+
9
+ | 需求 | 状态 | 负责实现 | 验证 |
10
+ |---|---|---|---|
11
+ | P0-1 标准 Profile Bundle | **已交付** | [`package.json`](../../package.json)、[`cordis.patch.yml`](../../cordis.patch.yml)、[`src/index.ts`](../../src/index.ts)、已提交的 `lib/` | [`tests/package-layout.spec.ts`](../../tests/package-layout.spec.ts)、[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts)、干净 `--dump-config`、Web/Headless 启动、禁用和移除检查 |
12
+ | P0-2 上游复用 | **已交付** | [`src/version.ts`](../../src/version.ts)、[`src/runtime-install.ts`](../../src/runtime-install.ts)、[`scripts/upstream-manifest.mjs`](../../scripts/upstream-manifest.mjs)、[`scripts/sync-upstream.mjs`](../../scripts/sync-upstream.mjs)、[`vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json`](../../vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json) | [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts)、[`tests/runtime-install.spec.ts`](../../tests/runtime-install.spec.ts)、构建时 manifest 验证 |
13
+ | P0-3 原生工具 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 中的独立定义、[`src/exposure.ts`](../../src/exposure.ts) 中的 Agent 级发布、[`src/runtime.ts`](../../src/runtime.ts) 中的共享执行、[`src/upstream.ts`](../../src/upstream.ts) 中的上游适配 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts)、[`tests/upstream.spec.ts`](../../tests/upstream.spec.ts)、[`tests/vision-prompt-guard.spec.ts`](../../tests/vision-prompt-guard.spec.ts)、全部五个 P0 工具的真实 Profile 渐进调用 |
14
+ | P0-4 配置与 Credential | **已交付** | [`src/config.ts`](../../src/config.ts)、[`src/runtime.ts`](../../src/runtime.ts) 中的逐操作解析、[`src/upstream.ts`](../../src/upstream.ts) 中的隔离环境构造 | [`tests/config.spec.ts`](../../tests/config.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 和 [`tests/errors.spec.ts`](../../tests/errors.spec.ts) 中的 Credential/错误/脱敏用例 |
15
+ | P0-5 skill 生命周期 | **已交付** | [`src/index.ts`](../../src/index.ts) 中的就绪顺序、生命周期中止和全局 disposer;[`src/exposure.ts`](../../src/exposure.ts) 中的逐 Agent 激活、恢复和释放;[`src/skill.ts`](../../src/skill.ts) 中的打包内容 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的 Agent 隔离、原生/直接/Code Mode 激活、Session 恢复、在途取消和释放用例;[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) 中的渐进暴露、禁用和卸载路径 |
16
+ | P0-6 纯文本模型结果 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 中的 JSON 输出 schema 与纯渲染函数;[`src/artifacts.ts`](../../src/artifacts.ts) 中的规范产物描述;[`src/exposure.ts`](../../src/exposure.ts) 中由持久 Skill 加载证据派生的 schema 可见性 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的工具 schema/展示断言、[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) 中逐请求的 schema 与模型可见 transcript(文本记录)断言 |
17
+ | P0-7 稳定错误 | **已交付** | [`src/errors.ts`](../../src/errors.ts),以及 [`src/paths.ts`](../../src/paths.ts)、[`src/runtime.ts`](../../src/runtime.ts)、[`src/upstream.ts`](../../src/upstream.ts) 中的边界验证 | [`tests/errors.spec.ts`](../../tests/errors.spec.ts)、[`tests/paths.spec.ts`](../../tests/paths.spec.ts),以及运行时/上游测试中的解析、超时、取消、Credential 和容量用例 |
18
+ | P0-8 测试与文档 | **已交付** | 双语 [`README.md`](../../README.md)、软件包测试、managed 运行时、示例和已提交构建产物 | `pnpm run build`、`pnpm test`、`pnpm pack --dry-run`、翻译/Markdown 门禁和无真实 Key 的干净 Profile e2e |
19
+
20
+ ## P1 产品需求
21
+
22
+ | 需求 | 状态 | 负责实现 | 验证 |
23
+ |---|---|---|---|
24
+ | P1-1 产物交付 | **已交付** | [`src/artifacts.ts`](../../src/artifacts.ts) 中的产物描述创建、[`src/paths.ts`](../../src/paths.ts) 中受围栏保护的原子路径、[`src/artifact-access.ts`](../../src/artifact-access.ts) 中的签名能力交付 | [`tests/artifacts.spec.ts`](../../tests/artifacts.spec.ts)、[`tests/artifact-access.spec.ts`](../../tests/artifact-access.spec.ts)、会生成产物的运行时/Profile 测试 |
25
+ | P1-2 扩展工具 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 和 [`src/runtime.ts`](../../src/runtime.ts) 中的 `vision_pixel_diff`、`vision_long_screenshot_ocr`、`vision_extract_foreground`、`vision_dominant_colors`、`vision_html_screenshot` | [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts) 和 [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 中的 P1 解析器/运行时用例;真实 Profile pixel-diff 和长截图 OCR 调用 |
26
+ | P1-3 专用 Web 展示 | **已交付** | [`src/client/index.tsx`](../../src/client/index.tsx) 中的浏览器插件与专用卡片;[`src/artifact-access.ts`](../../src/artifact-access.ts) 中仅供展示的能力元数据 | [`tests/client.spec.ts`](../../tests/client.spec.ts)、[`tests/artifact-access.spec.ts`](../../tests/artifact-access.spec.ts) 中的安全预览测试、Web 视觉/Console QA |
27
+ | P1-4 健康检查 | **已交付** | [`src/runtime.ts`](../../src/runtime.ts) 中的健康检查/版本运行时契约、[`src/web.ts`](../../src/web.ts) 中的同源 Web 操作,以及 [`src/client/index.tsx`](../../src/client/index.tsx) 中的 Settings 界面;这些管理诊断能力有意不进入模型工具注册表 | [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 中的健康检查用例、[`tests/web.spec.ts`](../../tests/web.spec.ts) 中的显式连接行为,以及 [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的模型工具缺席断言 |
28
+ | P1-5 Settings | **已交付** | [`src/config.ts`](../../src/config.ts) 中的 namespace/配置、[`src/runtime-manager.ts`](../../src/runtime-manager.ts) 中的 prepare-before-swap manager、[`src/web.ts`](../../src/web.ts) 中的同源私有路由、[`src/client/index.tsx`](../../src/client/index.tsx) 中的 Settings 分区 | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts)、[`tests/web.spec.ts`](../../tests/web.spec.ts)、[`tests/client.spec.ts`](../../tests/client.spec.ts)、干净 Web Profile 保存/重启 QA |
29
+ | P1-6 安装与升级体验 | **已交付** | [`src/index.ts`](../../src/index.ts) 中的 Bundle 生命周期、[`src/runtime-install.ts`](../../src/runtime-install.ts) 中的内容寻址 managed 运行时、[`src/runtime-manager.ts`](../../src/runtime-manager.ts) 中的 generation manager | 运行时中断/并发测试、软件包布局测试、干净 Profile 生命周期、Settings 持久化和失败候选保留检查 |
30
+
31
+ ## 横切需求
32
+
33
+ | 领域 | 契约与证据 |
34
+ |---|---|
35
+ | 安全 | [`src/paths.ts`](../../src/paths.ts) 通过 realpath 限制读写范围;[`src/runtime.ts`](../../src/runtime.ts) 在上传前解码并限制图片,并在提交前结构化解析 trace SVG;[`src/upstream.ts`](../../src/upstream.ts) 在每个直接和长截图 OCR 视觉模型提示词中把图片文字/指令标记为不可信;[`src/tools.ts`](../../src/tools.ts) 和 [`src/skill.ts`](../../src/skill.ts) 要求文本 agent 只把衍生输出当作证据而非命令;[`src/artifact-access.ts`](../../src/artifact-access.ts) 每次读取都重新验证签名文件并 sandbox SVG;[`src/errors.ts`](../../src/errors.ts) 对密钥脱敏。路径、符号链接、格式、大小、XML/doctype、提示词 guard、伪造 token、文件替换和 CSP 用例均有自动化覆盖。 |
36
+ | 可移植性 | 运行时和浏览器调用使用 argv 向量与 Node 文件系统/进程 API,不拼接 POSIX Shell。Managed 准备同时提供 `uv` 和 `venv`/pip 路径;Python 与 Chrome 探测包含平台候选。软件包测试拒绝机器本地依赖声明,已提交 fixture/Profile 流程无需真实 Key。 |
37
+ | 性能与取消 | [`src/runtime.ts`](../../src/runtime.ts) 应用一个硬截止时间、传递 `AbortSignal`、限制每个会话的并发数、在远程 I/O 前拒绝解码后过大的图片、在一次 glance 操作中对重复输入去重,并为每个活动会话保留一条按内容/配置键控的最近成功相同 glance 缓存。[`src/index.ts`](../../src/index.ts) 会在注销工具前中止插件拥有的调用。超时、调用方/插件取消、缓存命中/未命中、信号量和独立会话行为均有自动化覆盖。 |
38
+ | 可观察性 | [`src/runtime.ts`](../../src/runtime.ts) 记录有界的工具名、结果、总耗时/上游耗时、图片数量/字节/像素、缓存命中、模型和错误类别,同时排除 base64、Credential、鉴权头和无界上游输出。 |
39
+ | 模型上下文经济性 | Profile 级运行时只发布一个很小的引导工具,[`src/exposure.ts`](../../src/exposure.ts) 仅为加载 `vision-tools` 的 Agent 挂载 10 个执行 schema,并在成功后隐藏引导工具。其他 Agent 不受影响。健康检查、连接测试和版本诊断只存在于 Web Settings 边界,永远不会成为模型工具。单元测试覆盖 Agent 隔离与恢复;每条真实 Profile 工具流程都会断言初始和激活后的 schema 集合。 |
40
+ | 可维护性 | 每个工具都调用同一个 [`VisionToolkitRuntime`](../../src/runtime.ts);DSH 专用适配保留在 [`src/tools.ts`](../../src/tools.ts)、[`src/exposure.ts`](../../src/exposure.ts)、[`src/index.ts`](../../src/index.ts)、[`src/web.ts`](../../src/web.ts) 和 [`src/client/index.tsx`](../../src/client/index.tsx),固定上游继续作为算法唯一来源。运行时就绪、skill/引导工具发布和 Agent 级 schema 属于同一个生命周期 generation。 |
41
+ | HTML 截图隔离 | 固定截图 guard 使用一次性 Chrome Profile、`--use-mock-keychain` 和 `--incognito`,每次调用后删除 Profile,并保持网络禁用。[`tests/html-screenshot-guard.spec.ts`](../../tests/html-screenshot-guard.spec.ts) 捕获实际 argv 与清理行为。 |
42
+ | UI 还原验收 | [`scripts/ui-restoration-example.ts`](../../scripts/ui-restoration-example.ts) 通过真实运行时执行参考页面 → HTML 渲染 → 像素差异。[`examples/ui-restoration/README.md`](../../examples/ui-restoration/README.md) 负责可复现流程与已提交证据;[`tests/ui-restoration-example.spec.ts`](../../tests/ui-restoration-example.spec.ts) 强制执行该契约。 |
43
+
44
+ ## 错误与生命周期场景
45
+
46
+ | 场景 | 预期行为 | 证据 |
47
+ |---|---|---|
48
+ | 图片缺失/无效、格式、区域或路径错误 | 在执行上游前以输入、容量或路径安全错误拒绝 | [`tests/paths.spec.ts`](../../tests/paths.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) |
49
+ | Credential 缺失 | 本地工具保持可用;远程工具和显式连接测试返回脱敏且可执行下一步的配置/服务结果 | [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts)、[`tests/web.spec.ts`](../../tests/web.spec.ts) |
50
+ | 401/403、429、超时、畸形输出或取消 | 返回稳定且可执行下一步的类别,保留有界诊断信息,并停止请求/子进程 | [`tests/errors.spec.ts`](../../tests/errors.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts)、[`tests/upstream.spec.ts`](../../tests/upstream.spec.ts) |
51
+ | 初始加载时运行时不可用 | 不注册 skill、激活引导工具或 Agent 级工具;保留 Web Settings 以供修复 | [`src/index.ts`](../../src/index.ts)、[`tests/tools.spec.ts`](../../tests/tools.spec.ts) 与 [`tests/web.spec.ts`](../../tests/web.spec.ts) 中的生命周期测试 |
52
+ | 实时更新时运行时候选失败 | 保留当前服务 generation 和已存储的可用配置 | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts)、[`tests/web.spec.ts`](../../tests/web.spec.ts) |
53
+ | 并发 Settings 候选乱序完成 | 较新的 ticket 获胜;较慢的陈旧候选不能激活 | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts) |
54
+ | 禁用、重新启用或卸载 | 取消插件拥有的活动调用,Agent 级工具、引导工具与 skill 作为一个生命周期单元一起消失和恢复,卸载会移除 Bundle layer | [`tests/tools.spec.ts`](../../tests/tools.spec.ts)、[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) |
55
+
56
+ ## P2 与 P3 边界
57
+
58
+ | 范围 | 状态 | 决策 |
59
+ |---|---|---|
60
+ | P2 稳定 `ctx.visionToolkit` 服务与能力发现 | **按设计推迟** | 产品需求要求至少一个独立插件消费方出现后再稳定该 API。`VisionToolkitRuntime` 保持包内部使用,使 P0/P1 可以继续演进,而不会制造虚假的兼容性承诺。 |
61
+ | P2 提供方生态 | **按设计推迟** | 本包支持固定上游和一个已配置的 OpenAI 兼容视觉端点;不会预先构建无人使用的提供方注册表。 |
62
+ | P3 探索性输入与自动化 | **范围外** | 上传/拖拽、摄像头/视频/音频/文档输入、交互式标注、自动点击、远程集群、模型路由/投票和跨 Session 缓存不属于本版本契约。 |
63
+
64
+ ## 可复现验证
65
+
66
+ 在 `dsh-vision-toolkit/` 中运行软件包检查:
67
+
68
+ ```sh
69
+ pnpm run build
70
+ pnpm test
71
+ pnpm run example:ui-restoration
72
+ pnpm pack --dry-run
73
+ ```
74
+
75
+ 发布验收还会运行仓库插件检查器,把软件包分别安装到干净的 Web 和 Headless `DSH_HOME`,检查 `--dump-config`,执行本地工具和由无真实 Key mock 支持的远程工具调用,验证 Settings 持久化与实时切换,检查专用 Web 卡片且确保浏览器 Console 无错误,禁用/重新启用插件行,最后移除软件包。这些运行时检查补充自动化测试,不替代自动化测试。
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write dsh-vision-toolkit/examples/ui-restoration/README.md
5
+ README.md: 62aca8723a7f92e47d6803a553882e709fc870f5
6
+ README.zh.md: 94b6ca7b43816d533d44a31d1fc853a23f63b3dd
@@ -0,0 +1,70 @@
1
+ # UI restoration example
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ This example is a deterministic, keyless proof that DSH Vision Toolkit can close a local UI-restoration loop through its real runtime. It renders a reference page, an intentionally inaccurate first implementation, and the final implementation with `vision_html_screenshot`; it then evaluates both candidates with `vision_pixel_diff` and enforces numeric acceptance thresholds.
6
+
7
+ ## Flow
8
+
9
+ 1. `tests/fixtures/ui-restoration-reference.html` is copied into a temporary workspace as the reference source.
10
+ 2. `initial.html` and `implementation.html` are copied into the same workspace.
11
+ 3. The runtime renders all three local files at `1200 × 720` and scale `1` through the pinned Chrome-family adapter.
12
+ 4. The runtime compares the initial and final screenshots against the reference with an `8 × 8` grid and six ranked regions.
13
+ 5. Check mode verifies the committed evidence; write mode replaces it only after the initial and final thresholds pass.
14
+
15
+ The runner uses the package's `VisionToolkitRuntime` and `UpstreamAdapter`, not a separate screenshot or image-diff implementation. It requires Python with Pillow and NumPy plus Chrome, Chromium, or Edge; it does not require a vision-service Credential.
16
+
17
+ ## Inputs and evidence
18
+
19
+ | Path | Role |
20
+ |---|---|
21
+ | `initial.html` | Deliberately inaccurate reconstruction used to prove the comparison detects meaningful drift |
22
+ | `implementation.html` | Final reconstruction expected to match the reference |
23
+ | `assets/reference.png` | Browser-rendered reference image |
24
+ | `assets/initial.png` | Browser-rendered initial reconstruction |
25
+ | `assets/initial-heatmap.png` | Pixel-difference heatmap for the initial reconstruction |
26
+ | `assets/initial-report.json` | Portable initial comparison report with relative image paths |
27
+ | `assets/implementation.png` | Browser-rendered final reconstruction |
28
+ | `assets/final-heatmap.png` | Pixel-difference heatmap for the final reconstruction |
29
+ | `assets/final-report.json` | Portable final comparison report with relative image paths |
30
+ | `assets/metrics.json` | Stable viewport and acceptance metrics used by check mode |
31
+
32
+ ![Reference](assets/reference.png)
33
+
34
+ ![Initial reconstruction](assets/initial.png)
35
+
36
+ ![Final reconstruction](assets/implementation.png)
37
+
38
+ ## Run
39
+
40
+ From `dsh-vision-toolkit/`:
41
+
42
+ ```sh
43
+ npm run example:ui-restoration
44
+ ```
45
+
46
+ The command prints a structured result and exits non-zero when the environment, checked-in assets, or thresholds do not match the contract. The committed metrics are:
47
+
48
+ ```json
49
+ {
50
+ "initialDifferencePct": 6.04,
51
+ "finalDifferencePct": 0,
52
+ "initialWorstRegions": 6,
53
+ "finalWorstRegions": 0
54
+ }
55
+ ```
56
+
57
+ The initial comparison must remain at least `1%` different so the example cannot pass with two equivalent fixtures. The final comparison must remain at or below `0.02%`, and the committed evidence currently records an exact `0%` difference.
58
+
59
+ ## Refresh evidence
60
+
61
+ Run write mode only when intentionally changing the reference, reconstruction, viewport, renderer contract, or expected artifacts:
62
+
63
+ ```sh
64
+ npm run example:ui-restoration:write
65
+ npm run example:ui-restoration
66
+ ```
67
+
68
+ Write mode renders into a temporary workspace, validates the thresholds, copies the approved PNGs and reports into `assets/`, rewrites report paths to portable relative paths, and updates `metrics.json`. The following check-mode run is the required confirmation that the committed package can reproduce the refreshed evidence.
69
+
70
+ The automated regression test is `tests/ui-restoration-example.spec.ts`; the macOS Chrome keychain/profile isolation contract is independently covered by `tests/html-screenshot-guard.spec.ts`.
@@ -0,0 +1,70 @@
1
+ # UI 还原示例
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 本示例以确定性、无真实 Key 的方式证明 DSH Vision Toolkit 可以通过真实运行时闭环完成本地 UI 还原。它先用 `vision_html_screenshot` 渲染参考页面、故意不准确的初版实现和最终实现,再用 `vision_pixel_diff` 评估两个候选结果,并执行数值验收阈值。
6
+
7
+ ## 流程
8
+
9
+ 1. 将 `tests/fixtures/ui-restoration-reference.html` 复制到临时工作区,作为参考源文件。
10
+ 2. 将 `initial.html` 和 `implementation.html` 复制到同一工作区。
11
+ 3. 运行时通过固定 Chrome 系适配器,以 `1200 × 720`、缩放 `1` 渲染 3 个本地文件。
12
+ 4. 运行时使用 `8 × 8` 网格和 6 个排序区域,将初版和最终截图分别与参考图比较。
13
+ 5. Check 模式验证已提交证据;write 模式只在初版和最终阈值都通过后替换证据。
14
+
15
+ Runner 使用本包的 `VisionToolkitRuntime` 和 `UpstreamAdapter`,不会另行实现截图或图片差异算法。它需要带 Pillow、NumPy 的 Python,以及 Chrome、Chromium 或 Edge;不需要视觉服务 Credential。
16
+
17
+ ## 输入与证据
18
+
19
+ | 路径 | 作用 |
20
+ |---|---|
21
+ | `initial.html` | 故意不准确的还原结果,用于证明比较流程能检测有意义的偏差 |
22
+ | `implementation.html` | 预期与参考页面匹配的最终还原结果 |
23
+ | `assets/reference.png` | 浏览器渲染的参考图 |
24
+ | `assets/initial.png` | 浏览器渲染的初版结果 |
25
+ | `assets/initial-heatmap.png` | 初版结果的像素差异热力图 |
26
+ | `assets/initial-report.json` | 使用相对图片路径的可移植初版比较报告 |
27
+ | `assets/implementation.png` | 浏览器渲染的最终结果 |
28
+ | `assets/final-heatmap.png` | 最终结果的像素差异热力图 |
29
+ | `assets/final-report.json` | 使用相对图片路径的可移植最终比较报告 |
30
+ | `assets/metrics.json` | Check 模式使用的稳定视口和验收指标 |
31
+
32
+ ![Reference](assets/reference.png)
33
+
34
+ ![Initial reconstruction](assets/initial.png)
35
+
36
+ ![Final reconstruction](assets/implementation.png)
37
+
38
+ ## 运行
39
+
40
+ 在 `dsh-vision-toolkit/` 中执行:
41
+
42
+ ```sh
43
+ npm run example:ui-restoration
44
+ ```
45
+
46
+ 该命令会输出结构化结果;环境、已提交资源或阈值不符合契约时以非零状态退出。已提交指标如下:
47
+
48
+ ```json
49
+ {
50
+ "initialDifferencePct": 6.04,
51
+ "finalDifferencePct": 0,
52
+ "initialWorstRegions": 6,
53
+ "finalWorstRegions": 0
54
+ }
55
+ ```
56
+
57
+ 初版差异必须保持在 `1%` 以上,避免两个等价 fixture 让示例失去检测意义。最终差异必须不高于 `0.02%`;当前已提交证据记录精确的 `0%` 差异。
58
+
59
+ ## 刷新证据
60
+
61
+ 只有有意修改参考页面、还原页面、视口、渲染器契约或预期产物时,才运行 write 模式:
62
+
63
+ ```sh
64
+ npm run example:ui-restoration:write
65
+ npm run example:ui-restoration
66
+ ```
67
+
68
+ Write 模式会在临时工作区中渲染,验证阈值,把通过验收的 PNG 和报告复制到 `assets/`,将报告路径改写为可移植相对路径,并更新 `metrics.json`。随后必须执行 check 模式,确认已提交软件包可以复现刷新后的证据。
69
+
70
+ 自动回归测试位于 `tests/ui-restoration-example.spec.ts`;macOS Chrome 钥匙串/Profile 隔离契约由 `tests/html-screenshot-guard.spec.ts` 独立覆盖。
@@ -0,0 +1,83 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "sourceTool": "vision_pixel_diff",
4
+ "original": {
5
+ "path": "assets/reference.png",
6
+ "bytes": 137315,
7
+ "width": 1200,
8
+ "height": 720,
9
+ "format": "png"
10
+ },
11
+ "rebuilt": {
12
+ "path": "assets/implementation.png",
13
+ "bytes": 137315,
14
+ "width": 1200,
15
+ "height": 720,
16
+ "format": "png"
17
+ },
18
+ "scaled": false,
19
+ "overallDifferencePct": 0,
20
+ "grid": 8,
21
+ "worstRegions": [
22
+ {
23
+ "index": 1,
24
+ "differencePct": 0,
25
+ "box": {
26
+ "x1": 1050,
27
+ "y1": 630,
28
+ "x2": 1200,
29
+ "y2": 720
30
+ }
31
+ },
32
+ {
33
+ "index": 2,
34
+ "differencePct": 0,
35
+ "box": {
36
+ "x1": 1050,
37
+ "y1": 540,
38
+ "x2": 1200,
39
+ "y2": 630
40
+ }
41
+ },
42
+ {
43
+ "index": 3,
44
+ "differencePct": 0,
45
+ "box": {
46
+ "x1": 1050,
47
+ "y1": 450,
48
+ "x2": 1200,
49
+ "y2": 540
50
+ }
51
+ },
52
+ {
53
+ "index": 4,
54
+ "differencePct": 0,
55
+ "box": {
56
+ "x1": 1050,
57
+ "y1": 360,
58
+ "x2": 1200,
59
+ "y2": 450
60
+ }
61
+ },
62
+ {
63
+ "index": 5,
64
+ "differencePct": 0,
65
+ "box": {
66
+ "x1": 1050,
67
+ "y1": 270,
68
+ "x2": 1200,
69
+ "y2": 360
70
+ }
71
+ },
72
+ {
73
+ "index": 6,
74
+ "differencePct": 0,
75
+ "box": {
76
+ "x1": 1050,
77
+ "y1": 180,
78
+ "x2": 1200,
79
+ "y2": 270
80
+ }
81
+ }
82
+ ]
83
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "sourceTool": "vision_pixel_diff",
4
+ "original": {
5
+ "path": "assets/reference.png",
6
+ "bytes": 137315,
7
+ "width": 1200,
8
+ "height": 720,
9
+ "format": "png"
10
+ },
11
+ "rebuilt": {
12
+ "path": "assets/initial.png",
13
+ "bytes": 69296,
14
+ "width": 1200,
15
+ "height": 720,
16
+ "format": "png"
17
+ },
18
+ "scaled": false,
19
+ "overallDifferencePct": 6.04,
20
+ "grid": 8,
21
+ "worstRegions": [
22
+ {
23
+ "index": 1,
24
+ "differencePct": 27.51,
25
+ "box": {
26
+ "x1": 300,
27
+ "y1": 540,
28
+ "x2": 450,
29
+ "y2": 630
30
+ }
31
+ },
32
+ {
33
+ "index": 2,
34
+ "differencePct": 25.99,
35
+ "box": {
36
+ "x1": 750,
37
+ "y1": 360,
38
+ "x2": 900,
39
+ "y2": 450
40
+ }
41
+ },
42
+ {
43
+ "index": 3,
44
+ "differencePct": 24.16,
45
+ "box": {
46
+ "x1": 750,
47
+ "y1": 450,
48
+ "x2": 900,
49
+ "y2": 540
50
+ }
51
+ },
52
+ {
53
+ "index": 4,
54
+ "differencePct": 24.03,
55
+ "box": {
56
+ "x1": 600,
57
+ "y1": 540,
58
+ "x2": 750,
59
+ "y2": 630
60
+ }
61
+ },
62
+ {
63
+ "index": 5,
64
+ "differencePct": 23.88,
65
+ "box": {
66
+ "x1": 450,
67
+ "y1": 540,
68
+ "x2": 600,
69
+ "y2": 630
70
+ }
71
+ },
72
+ {
73
+ "index": 6,
74
+ "differencePct": 22.93,
75
+ "box": {
76
+ "x1": 300,
77
+ "y1": 450,
78
+ "x2": 450,
79
+ "y2": 540
80
+ }
81
+ }
82
+ ]
83
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "viewport": {
4
+ "width": 1200,
5
+ "height": 720,
6
+ "scale": 1
7
+ },
8
+ "initialDifferencePct": 6.04,
9
+ "finalDifferencePct": 0,
10
+ "initialWorstRegions": 6,
11
+ "finalWorstRegions": 0
12
+ }
@@ -0,0 +1,94 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Vision QA Reconstruction</title>
7
+ <style>
8
+ * { box-sizing: border-box; }
9
+ html, body { width: 1200px; height: 720px; margin: 0; overflow: hidden; }
10
+ body { background: #eef1f8; color: #182033; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -webkit-font-smoothing: antialiased; }
11
+ button { font: inherit; }
12
+ .app { position: absolute; left: 48px; top: 36px; width: 1104px; height: 648px; overflow: hidden; border: 1px solid #dfe3ee; border-radius: 28px; background: #fff; box-shadow: 0 24px 60px rgba(45,56,90,.14); }
13
+ .toolbar { position: absolute; inset: 0 0 auto; height: 78px; border-bottom: 1px solid #e8eaf1; background: #fff; }
14
+ .identity { position: absolute; left: 27px; top: 22px; display: flex; align-items: center; gap: 11px; font-size: 17px; font-weight: 760; letter-spacing: -.25px; }
15
+ .identity-icon { position: relative; width: 32px; height: 32px; border-radius: 10px; background: #5a4ff3; box-shadow: inset 0 0 0 1px rgba(255,255,255,.22); }
16
+ .identity-icon::before, .identity-icon::after { content: ""; position: absolute; border-radius: 99px; background: #fff; transform: rotate(-25deg); }
17
+ .identity-icon::before { left: 8px; top: 8px; width: 16px; height: 5px; }
18
+ .identity-icon::after { left: 11px; top: 17px; width: 13px; height: 5px; opacity: .72; }
19
+ .switcher { position: absolute; left: 380px; top: 19px; display: flex; height: 40px; align-items: center; gap: 8px; padding: 4px; border: 1px solid #e8eaf1; border-radius: 13px; background: #f7f8fb; }
20
+ .switcher span { min-width: 82px; padding: 8px 14px; border-radius: 9px; color: #71798e; font-size: 13px; font-weight: 650; text-align: center; }
21
+ .switcher .selected { color: #2b255e; background: #fff; box-shadow: 0 2px 8px rgba(37,43,70,.08); }
22
+ .person { position: absolute; right: 26px; top: 20px; width: 38px; height: 38px; border: 3px solid #fff; border-radius: 50%; background: linear-gradient(135deg,#ffcf88,#f08bb7); box-shadow: 0 0 0 1px #e5e8f0; }
23
+ .ready { position: absolute; right: 79px; top: 28px; display: flex; align-items: center; gap: 7px; color: #697187; font-size: 12px; font-weight: 650; }
24
+ .ready::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: #29b77a; box-shadow: 0 0 0 4px #e7f8f1; }
25
+ .rail { position: absolute; left: 0; top: 78px; bottom: 0; width: 216px; padding: 28px 20px; border-right: 1px solid #e8eaf1; background: #fbfbfd; }
26
+ .rail-title { margin: 0 10px 13px; color: #a0a6b6; font-size: 10px; font-weight: 760; letter-spacing: 1.15px; text-transform: uppercase; }
27
+ .rail-link { position: relative; height: 44px; margin-bottom: 6px; padding: 13px 14px 13px 43px; border-radius: 12px; color: #737b90; font-size: 13px; font-weight: 650; }
28
+ .rail-link.current { color: #3f36cf; background: #eeecff; }
29
+ .rail-link::before { content: ""; position: absolute; left: 15px; top: 14px; width: 14px; height: 14px; border: 2px solid currentColor; border-radius: 5px; opacity: .88; }
30
+ .runtime-card { position: absolute; left: 20px; right: 20px; bottom: 22px; padding: 16px; border: 1px solid #e4e6ef; border-radius: 16px; background: #fff; }
31
+ .runtime-card strong { display: block; margin-bottom: 5px; font-size: 12px; }
32
+ .runtime-card small { display: block; color: #8a91a3; font-size: 10px; line-height: 1.45; }
33
+ .runtime-bar { height: 5px; margin-top: 13px; overflow: hidden; border-radius: 99px; background: #eceef4; }
34
+ .runtime-bar::after { content: ""; display: block; width: 72%; height: 100%; border-radius: inherit; background: #6559f5; }
35
+ .workspace { position: absolute; left: 216px; right: 0; top: 78px; bottom: 0; padding: 30px 34px 28px; background: #f6f7fb; }
36
+ .kicker { margin: 0 0 7px; color: #675bf2; font-size: 10px; font-weight: 800; letter-spacing: 1.25px; text-transform: uppercase; }
37
+ h1 { margin: 0; color: #20263a; font-size: 27px; font-weight: 780; letter-spacing: -.75px; line-height: 1.1; }
38
+ .lede { margin: 7px 0 0; color: #8990a2; font-size: 12px; }
39
+ .run { position: absolute; right: 34px; top: 31px; height: 38px; padding: 0 17px; border: 0; border-radius: 11px; background: #5b50ed; color: #fff; font-size: 12px; font-weight: 720; box-shadow: 0 8px 18px rgba(83,72,225,.24); }
40
+ .summary { display: flex; gap: 13px; margin-top: 23px; }
41
+ .stat { width: 260px; height: 80px; padding: 15px 17px; border: 1px solid #e2e5ef; border-radius: 15px; background: #fff; box-shadow: 0 5px 14px rgba(39,47,76,.035); }
42
+ .stat-label { display: flex; align-items: center; justify-content: space-between; color: #8b92a4; font-size: 10px; font-weight: 700; }
43
+ .stat-label i { width: 8px; height: 8px; border-radius: 50%; }
44
+ .stat-value { margin-top: 7px; font-size: 24px; font-weight: 790; letter-spacing: -.6px; }
45
+ .stat-value em { margin-left: 7px; color: #37a878; font-size: 10px; font-style: normal; font-weight: 760; }
46
+ .comparison { display: grid; grid-template-columns: 500px 286px; gap: 13px; margin-top: 13px; }
47
+ .card { height: 236px; border: 1px solid #e2e5ef; border-radius: 17px; background: #fff; box-shadow: 0 5px 14px rgba(39,47,76,.035); }
48
+ .card-header { display: flex; align-items: center; justify-content: space-between; height: 48px; padding: 0 17px; border-bottom: 1px solid #eceef4; }
49
+ .card-header b { font-size: 12px; font-weight: 760; }
50
+ .badge { padding: 5px 8px; border-radius: 8px; background: #ebf8f2; color: #25825c; font-size: 9px; font-weight: 780; }
51
+ .visual { position: relative; height: 187px; padding: 15px; }
52
+ .visual-stage { position: relative; width: 100%; height: 157px; overflow: hidden; border-radius: 12px; background: linear-gradient(145deg,#11182a,#1b2440); }
53
+ .visual-stage::before { content: ""; position: absolute; inset: 0; background-image: linear-gradient(rgba(255,255,255,.035) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.035) 1px,transparent 1px); background-size: 22px 22px; }
54
+ .shape { position: absolute; left: 51px; top: 33px; width: 112px; height: 112px; border-radius: 35px; background: linear-gradient(135deg,#7d6cff,#41d5ca); transform: rotate(-9deg); box-shadow: 0 22px 42px rgba(73,95,225,.36); }
55
+ .shape::after { content: ""; position: absolute; right: 18px; top: 17px; width: 37px; height: 37px; border-radius: 50%; background: rgba(255,255,255,.52); filter: blur(1px); }
56
+ .copy-lines { position: absolute; left: 199px; top: 42px; width: 229px; }
57
+ .copy-lines i { display: block; height: 10px; margin-bottom: 12px; border-radius: 99px; background: #394563; }
58
+ .copy-lines i:nth-child(1) { width: 86%; background: #eef1ff; }
59
+ .copy-lines i:nth-child(2) { width: 64%; }
60
+ .copy-lines i:nth-child(3) { width: 94%; }
61
+ .copy-lines i:nth-child(4) { width: 74%; }
62
+ .copy-lines i:nth-child(5) { width: 48%; background: #665af4; }
63
+ .target { position: absolute; left: 38px; top: 21px; width: 144px; height: 139px; border: 2px solid #ffcf5a; border-radius: 14px; box-shadow: 0 0 0 1px rgba(255,207,90,.18); }
64
+ .target-label { position: absolute; left: 38px; top: 12px; padding: 4px 7px; border-radius: 6px; background: #ffcf5a; color: #322a19; font-size: 8px; font-weight: 820; }
65
+ .analysis { padding: 14px 17px; }
66
+ .score { display: flex; align-items: baseline; gap: 6px; }
67
+ .score strong { font-size: 32px; font-weight: 800; letter-spacing: -1px; }
68
+ .score span { color: #9298a8; font-size: 10px; font-weight: 650; }
69
+ .score-bar { position: relative; height: 7px; margin: 10px 0 12px; border-radius: 99px; background: #eceef4; }
70
+ .score-bar::after { content: ""; display: block; width: 93%; height: 100%; border-radius: inherit; background: linear-gradient(90deg,#6b5ef5,#34c6ac); }
71
+ .detail { display: flex; align-items: center; justify-content: space-between; height: 28px; border-top: 1px solid #eef0f5; color: #7f8799; font-size: 10px; }
72
+ .detail strong { color: #30374a; font-size: 10px; }
73
+ .artifacts { margin-top: 13px; height: 82px; overflow: hidden; border: 1px solid #e2e5ef; border-radius: 15px; background: #fff; }
74
+ .artifact-head, .artifact-row { display: grid; grid-template-columns: 1.5fr .8fr .8fr .65fr; align-items: center; padding: 0 16px; }
75
+ .artifact-head { height: 30px; border-bottom: 1px solid #eceef4; color: #9aa0b0; font-size: 9px; font-weight: 760; letter-spacing: .4px; text-transform: uppercase; }
76
+ .artifact-row { height: 26px; color: #50586c; font-size: 10px; }
77
+ .artifact-name { display: flex; align-items: center; gap: 8px; font-weight: 700; }
78
+ .artifact-name::before { content: ""; width: 16px; height: 16px; border-radius: 5px; background: #eeecff; box-shadow: inset 0 0 0 1px #d9d5ff; }
79
+ .artifact-state { justify-self: start; padding: 4px 7px; border-radius: 7px; background: #eaf8f2; color: #27815d; font-size: 9px; font-weight: 800; }
80
+ </style>
81
+ </head>
82
+ <body>
83
+ <main class="app">
84
+ <header class="toolbar"><div class="identity"><span class="identity-icon"></span>Vision Forge</div><nav class="switcher"><span class="selected">Workspace</span><span>Runs</span><span>Artifacts</span></nav><div class="ready">Runtime ready</div><span class="person"></span></header>
85
+ <aside class="rail"><p class="rail-title">Vision tools</p><div class="rail-link current">Visual QA</div><div class="rail-link">Grounding</div><div class="rail-link">OCR batches</div><div class="rail-link">Asset trace</div><div class="runtime-card"><strong>Runtime generation 4</strong><small>Pinned upstream and local browser checks passed.</small><div class="runtime-bar"></div></div></aside>
86
+ <section class="workspace">
87
+ <p class="kicker">Latest verification</p><h1>Visual QA run</h1><p class="lede">Reference and rebuilt page compared at 1200 × 720.</p><button class="run">Run comparison</button>
88
+ <section class="summary"><article class="stat"><div class="stat-label">Similarity <i style="background:#5c50ee"></i></div><div class="stat-value">99.86% <em>+3.2%</em></div></article><article class="stat"><div class="stat-label">Largest delta <i style="background:#ffbd4a"></i></div><div class="stat-value">18 × 12 <em>px</em></div></article><article class="stat"><div class="stat-label">Artifacts <i style="background:#35b98a"></i></div><div class="stat-value">4 <em>ready</em></div></article></section>
89
+ <section class="comparison"><article class="card"><header class="card-header"><b>Annotated comparison</b><span class="badge">PASS</span></header><div class="visual"><div class="visual-stage"><span class="shape"></span><div class="copy-lines"><i></i><i></i><i></i><i></i><i></i></div><span class="target"></span><span class="target-label">#1 0.14%</span></div></div></article><article class="card"><header class="card-header"><b>Difference score</b><span class="badge">LOW</span></header><div class="analysis"><div class="score"><strong>0.14%</strong><span>overall</span></div><div class="score-bar"></div><div class="detail"><span>Viewport</span><strong>1200 × 720</strong></div><div class="detail"><span>Worst region</span><strong>Top left</strong></div><div class="detail"><span>Threshold</span><strong>≤ 0.50%</strong></div></div></article></section>
90
+ <section class="artifacts"><div class="artifact-head"><span>Artifact</span><span>Tool</span><span>Size</span><span>Status</span></div><div class="artifact-row"><span class="artifact-name">implementation.png</span><span>html_screenshot</span><span>86 KB</span><span class="artifact-state">READY</span></div><div class="artifact-row"><span class="artifact-name">heatmap.png</span><span>pixel_diff</span><span>41 KB</span><span class="artifact-state">READY</span></div></section>
91
+ </section>
92
+ </main>
93
+ </body>
94
+ </html>
@@ -0,0 +1,57 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Vision QA Initial Reconstruction</title>
7
+ <style>
8
+ * { box-sizing: border-box; }
9
+ html, body { width: 1200px; height: 720px; margin: 0; overflow: hidden; }
10
+ body { background: #e8edf7; color: #20263a; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -webkit-font-smoothing: antialiased; }
11
+ .app { position: absolute; left: 58px; top: 46px; width: 1084px; height: 628px; overflow: hidden; border: 1px solid #d7dce8; border-radius: 20px; background: #fff; box-shadow: 0 18px 48px rgba(45,56,90,.12); }
12
+ .top { height: 72px; border-bottom: 1px solid #e5e8f0; padding: 20px 24px; font-size: 18px; font-weight: 760; }
13
+ .mark { display: inline-block; width: 28px; height: 28px; margin-right: 10px; vertical-align: middle; border-radius: 8px; background: #695cf4; }
14
+ .nav { position: absolute; left: 390px; top: 22px; color: #747c90; font-size: 13px; word-spacing: 32px; }
15
+ .side { position: absolute; left: 0; top: 72px; bottom: 0; width: 198px; padding: 26px 17px; border-right: 1px solid #e5e8f0; background: #fafbfe; }
16
+ .side h2 { margin: 0 10px 15px; color: #a2a8b6; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; }
17
+ .link { margin-bottom: 7px; padding: 13px 14px; border-radius: 10px; color: #71798d; font-size: 13px; font-weight: 650; }
18
+ .link.active { color: #483ed8; background: #e8e6ff; }
19
+ .content { position: absolute; left: 198px; right: 0; top: 72px; bottom: 0; padding: 28px 30px; background: #f7f8fc; }
20
+ .content small { color: #6c60ef; font-size: 10px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase; }
21
+ h1 { margin: 8px 0 6px; font-size: 29px; letter-spacing: -.7px; }
22
+ p { margin: 0; color: #8c93a4; font-size: 12px; }
23
+ button { position: absolute; right: 30px; top: 29px; height: 36px; padding: 0 16px; border: 0; border-radius: 9px; background: #6659f3; color: #fff; font-size: 12px; font-weight: 700; }
24
+ .metrics { display: flex; gap: 12px; margin-top: 22px; }
25
+ .metric { width: 248px; height: 78px; padding: 14px 15px; border: 1px solid #e1e4ed; border-radius: 12px; background: #fff; }
26
+ .metric span { display: block; color: #8c93a4; font-size: 10px; }
27
+ .metric strong { display: block; margin-top: 8px; font-size: 23px; }
28
+ .work { display: grid; grid-template-columns: 470px 280px; gap: 12px; margin-top: 12px; }
29
+ .panel { height: 250px; border: 1px solid #e1e4ed; border-radius: 14px; background: #fff; }
30
+ .panel h3 { height: 46px; margin: 0; padding: 16px; border-bottom: 1px solid #eceef4; font-size: 12px; }
31
+ .preview { position: relative; height: 176px; margin: 14px; overflow: hidden; border-radius: 10px; background: #19223b; }
32
+ .shape { position: absolute; left: 36px; top: 27px; width: 104px; height: 104px; border-radius: 50%; background: linear-gradient(135deg,#7c6df7,#54c9c1); }
33
+ .copy { position: absolute; left: 178px; top: 38px; width: 220px; }
34
+ .copy i { display: block; width: 80%; height: 9px; margin-bottom: 13px; border-radius: 99px; background: #3b4765; }
35
+ .copy i:first-child { background: #eef1ff; }
36
+ .score { padding: 18px; }
37
+ .score strong { font-size: 36px; }
38
+ .bar { height: 8px; margin: 18px 0; border-radius: 99px; background: linear-gradient(90deg,#695df4 76%,#eceef4 76%); }
39
+ .row { display: flex; justify-content: space-between; padding: 11px 0; border-top: 1px solid #eef0f5; color: #81889a; font-size: 10px; }
40
+ .table { height: 86px; margin-top: 12px; border: 1px solid #e1e4ed; border-radius: 12px; background: #fff; }
41
+ .table div { display: grid; grid-template-columns: 1.5fr .8fr .7fr .6fr; padding: 9px 14px; color: #596175; font-size: 10px; }
42
+ .table div:first-child { border-bottom: 1px solid #eceef4; color: #9aa0b0; font-size: 9px; font-weight: 760; text-transform: uppercase; }
43
+ </style>
44
+ </head>
45
+ <body>
46
+ <main class="app">
47
+ <header class="top"><span class="mark"></span>Vision Forge <span class="nav">Workspace Runs Artifacts</span></header>
48
+ <aside class="side"><h2>Vision tools</h2><div class="link active">Visual QA</div><div class="link">Grounding</div><div class="link">OCR batches</div><div class="link">Asset trace</div></aside>
49
+ <section class="content">
50
+ <small>Latest verification</small><h1>Visual QA run</h1><p>Reference and rebuilt page compared at 1200 × 720.</p><button>Run comparison</button>
51
+ <div class="metrics"><article class="metric"><span>Similarity</span><strong>96.66%</strong></article><article class="metric"><span>Largest delta</span><strong>44 × 28</strong></article><article class="metric"><span>Artifacts</span><strong>4 ready</strong></article></div>
52
+ <div class="work"><article class="panel"><h3>Annotated comparison</h3><div class="preview"><span class="shape"></span><div class="copy"><i></i><i></i><i></i><i></i><i></i></div></div></article><article class="panel"><h3>Difference score</h3><div class="score"><strong>3.34%</strong><div class="bar"></div><div class="row"><span>Viewport</span><b>1200 × 720</b></div><div class="row"><span>Worst region</span><b>Top left</b></div><div class="row"><span>Threshold</span><b>≤ 0.50%</b></div></div></article></div>
53
+ <div class="table"><div><span>Artifact</span><span>Tool</span><span>Size</span><span>Status</span></div><div><b>implementation.png</b><span>html_screenshot</span><span>86 KB</span><b>READY</b></div></div>
54
+ </section>
55
+ </main>
56
+ </body>
57
+ </html>