Files
next-tjwater-frontend/docs/agent-persona-resource.md
jiang 2d8506fea0
Generic Container CI/CD / test-build-publish (push) Successful in 2m9s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m39s
feat: add project-aware agent workspace
2026-10-22 18:52:18 +08:00

11 KiB
Raw Permalink Blame History

Agent 动画资源与离线水纹

Agent 保留原版 Obsidian 图形与运动结构,采用经实际动画对比选定的 02「冰水蓝」配色,并在 Rive 文件内部增加“静水待机”离线设计。在线保留外围水纹光圈;离线关闭外围三层光圈,仅保留球体内部的缓慢流动。在线、离线和交互使用同一个 default 状态机。

当前视觉规格、尺寸、标题区与交互约定统一见 Persona 设计。本文维护资源结构、生成过程与缓存实现。

文件与再生成

文件 用途
运行时源数据 从原版解析的 775 个对象,包含图形、属性、关键帧、状态机和绑定数据
冰水蓝材质参数 已确认的水体、暗部、白色高光和水纹颜色,以及球面底色遮蔽度
离线设计参数 可编辑的播放速度、振幅、混合时长和三层柔光/反射强度
生成脚本 验证原版重编码、修改离线动画,生成本地资源
应用资源 前端加载的 9,879 字节资源
缓存版本清单 生成脚本同步输出的路径、SHA-256 和字节数,用于客户端缓存更新
静态帧生成脚本 从实际资源渲染在线、离线 PNG,供首次加载、减少动态效果和失败兜底使用
资源对照测试 真实 WebGL2 运行时定时推进,比较原版与修改版画面

在前端仓库运行:

node scripts/build-agent-persona.mjs
node scripts/render-agent-persona-stills.mjs
node scripts/build-agent-persona.mjs --check
# 将原版还原到一个尚不存在的文件,应用资源不会被替换
node scripts/build-agent-persona.mjs --restore-original /tmp/obsidian-original.riv
pnpm test:browser tests/browser/agent-persona-resource.e2e.ts tests/browser/agent-orb.e2e.ts --workers=1

JSON 保存原始运行数据;脚本负责可审查的定制。--check 验证原始数据能逐字节恢复,并检查生成结果与应用资源一致。基线 SHA-256 为 c6ae7d08f5787a560e38d3453ba155c28250acf2db75e6acf2b0d1a0cd6017db。替换上游基线时需重新解析和验证,不应只修改散列值跳过检查。

配色编辑 obsidian-material.json,其中 bodyColor 只控制球面底色,bodyOpacity 控制它在待机/活动时的遮蔽度,其他蓝色反射沿用 colors 映射;离线动画编辑 obsidian-offline.json;重新生成 .riv 和静态帧后刷新前端。静态帧脚本使用已安装的 Playwright、esbuild 与 Rive WASM,无需访问外部动画服务。fps 控制离线时间轴速度,amplitude 控制位移与缩放的变化幅度,transitionMs 控制在线/离线的混合时长。各层 opacity 中的 rest 和 crest 是相对原始透明度的比例,分别对应循环开始/结束与中点。数值需满足 0 ≤ rest ≤ crest ≤ 1;保留循环端点一致,避免接缝。外围三个光圈均固定为 0。

--restore-original 使用只创建模式,目标文件已存在时会报错,避免覆盖已有编辑。原版还原文件与基线逐字节相同,包含原版离线消失的行为;前端继续加载带离线设计的应用资源。

冰水蓝与离线设计

材质采用经原生 Rive 预览选定的 02 方案:水体 #299FDF、同色相暗部 #1482BC、白色高光 #FFFFFF、外围水纹 #4C9ECC。外围水纹从初选时的 #B0E3FF 加深,以提高浅色地图上的辨识度;光圈粗细、渐变 alpha 和运动保持原定义。球面底色独立设置为 #DCEFFA,待机/活动时的不透明度为 94%/98%,同步调整对应的活动状态关键帧。浅色且高遮蔽度的底层减少地图道路穿过球体的现象,蓝色反射仍由原来的运动图层呈现;暗部沿用蓝色调,避免叠加灰黑阴影。渐变位置、羽化、裁剪、运动曲线及其他节点的原有 alpha 保留,不添加 CSS 滤镜或 View Model 绑定。

此前 A「水务蓝」使用灰蓝主体、浅蓝高光与 78%/90% 的底色不透明度,在白底实际动画中显得发灰、浑浊,已被本方案替换。后续调整以同一背景下的真实动画及 40/64px 入口效果为准,不以色卡单独判断材质。

离线以“水面逐渐平静”表达连接不可用:外围三层光圈在 600ms 混合期间消退至完全不可见,内部柔光降低并保持缓慢流动。球体整体透明度和缩放保持完整;悬停不会重新打开离线光圈。恢复在线时重新显现光圈与在线节奏。业务状态仍由入口按钮的可访问名称和提示提供。

层 柔光强度 反射强度 循环周期
内层与下侧柔光 56%–68% 0,关闭 15 秒
中层与内部柔光 48%–62% 0,关闭 19.25 秒
外层与上侧柔光 60%–72% 0,关闭 31.375 秒

上表是设计参数当前值的解释。内部柔光以原版贝塞尔缓动从 rest 升到 crest 再回落,周期错开,避免同步闪烁;光圈透明度保持为零。40/64px 入口共用同一资源,状态切换不改变布局尺寸。

参数中的图层名称是根据运行时结构补充的语义别名,不是恢复出的作者原始命名。objectId 是画板组件编号,不能用 JSON 对象数组的索引替代:绑定记录不占组件编号。

解析结果与修改

原版有 1 个 Obsidian 画板、20 段动画、13 个状态机层和 5 个布尔输入。asleep 动画在第 45 帧将节点 4 的透明度从 1 降至 0,并将节点 3、190、199、208 的缩放降至 0.6–0.8,因此离线时球体消失。

修改版保留原有图形、渐变结构、羽化、裁剪和在线运动,改变以下部分:

  • 根据已选定的配色映射 RGB,并独立设置底色填充节点 189 的颜色、节点 186 的静止及活动透明度;原始 JSON 不改动,仍可完整还原银灰原版 .riv。
  • asleep / wake 的透明度与缩放关键帧保持 1,避免隐藏或缩小球体。
  • 在原有 20 段动画后追加 offline_loop_1、offline_loop_2、offline_loop_3,保持原有动画编号。
  • 离线循环复用原版三层水纹,时间轴速率从 60 改为 24,约为原速的 40%;这是时间轴播放速度,不是将屏幕渲染限制为 24fps。
  • 位移和呼吸振幅缩至原来的 25%;完整旋转保留首尾角度,避免循环接缝。
  • 离线循环增加六个柔光/光圈节点的透明度轨道,其中节点 190、199、208 的光圈透明度固定为零;在线循环对应增加原始透明度常量轨道,确保恢复连接后完整恢复。
  • 三个循环层根据既有 asleep 输入切换,使用 600ms 状态混合。其他输入、状态层和交互保持原定义。

生成结果包含 957 个对象、23 段动画。调整设计参数后需再生成并运行对照测试;无需修改前端组件或安装额外依赖。

前端接入与生命周期

悬停沿用原版节点 110 的反射尺寸,以及节点 129 对指针目标 1 的跟随约束。Persona 保留 Rive 监听器,通过外层 Pointer Events 向画布转交鼠标的进入、移动和离开坐标,Rive 自行完成画布与画板坐标转换。画布内部不直接命中鼠标或触摸,只有就绪的外层接收并筛选主指针;不转交触摸和按压,因此手机点击不会触发白色高光。仅手动设置 hover 而禁用原生监听,会使目标停在中心,造成中央大片变白。这个问题由事件通路修复,不通过缩小光圈或改变材质补偿。

persona-resource.ts 加载同源的 /agent/obsidian-water-v1.riv?v=<SHA-256>。同一页面共用下载 Promise,跨刷新、再次打开页面使用浏览器 Cache Storage 的 tjwater-agent-persona-v1 缓存;只共享不可变字节,不共享 Rive 实例或 View Model。

读取顺序为页面内存、当前版本的持久缓存、网络。下载及缓存命中均检查字节数和 RIVE 文件头;支持 Web Crypto 时进一步验证 SHA-256。损坏缓存重新下载,错误页或不匹配的资源不写入缓存。新版本成功保存后,只删除本缓存中该资源路径的旧版本,不清理其他应用数据。重新生成动画时同步更新版本清单,--check 同时验证资源与清单。

持久缓存不可用、被禁用或空间不足时仍可下载并在当前页面复用。Cache Storage 需要安全上下文,生产环境使用 HTTPS,本机 localhost 开发可用;浏览器清理站点数据也可能移除缓存,见 Cache API 说明。缓存测试覆盖真实页面刷新,并阻断资源网络路径验证动画仍能播放。

Agent 服务不可用不妨碍前端读取该资源。这项缓存仅覆盖 .riv,Rive WASM、应用代码与其他资源沿用各自的加载策略;不构成整站离线支持。浏览器完全断网且应用或运行时尚未加载时,仍可能无法播放动画。

在线与离线切换不卸载可见的 Rive 实例,由状态机处理过渡。静态帧仅用于首次加载、资源失败、减少动态效果及未挂载动画的情况:在线使用 obsidian-water-idle.png,离线使用无外围光圈的 obsidian-water-offline.png。两张 192px PNG 均由当前 Rive 材质直接渲染,URL 携带资源版本;首次加载维持 180ms 淡入,不再出现银灰占位图。保留可见性卸载、Strict Mode 延迟初始化和原版悬停;没有重新加入 Motion 按压效果或 View Model 绑定。历史约束见 Rive 内存记录。

验证边界与来源

运行时 JSON 能恢复当前 .riv,但不等于作者的 .rev 编辑器工程;导出时省略的编辑信息没有凭空恢复。原作者提供可编辑 .rev 的说明见 资源定制文档,格式差异见 Rive 备份说明。

原始图形来源为 Surge Obsidian 动画。解析时参考了 rive-rs-cli 的格式读取实现及其从 Rive 定义生成的类型/属性表;应用不依赖或包含该工具。原图形的作者归属不因解析和局部修改而改变。

对照测试在同一 WebGL2 运行时以固定时间步推进。就绪、思考、待回复、输出和悬停状态要求与“仅应用相同冰水蓝材质的原版”采样画面逐像素一致,并验证主色由灰变蓝、合成到白底后仍保有蓝色,球体中心采样区域的不透明度不低于 90%。悬停另外比较左右指针位置对应的高光重心,要求局部反射随指针移动且移出后完全恢复。工作台测试验证实际坐标到达原生监听器。离线检查完整球体、持续变化、初始离线和反复恢复;通过外围像素计数验证光圈完全消失,悬停不会重新出现,恢复在线后再次可见。另用相同运动轨迹、取消离线光照变化的对照资源,验证快速切换后在线明暗完整恢复。该检查覆盖固定采样时刻,不是所有设备、运行时间和状态序列的穷举验证。