8.5 KiB
在 SillyTavern(ST)的圈子里,“前端卡”(Frontend Card / HTML Character Card)是一个进阶概念,它打破了传统角色卡“纯文字描述”的限制。
作为开发者,在重构“云酒馆”时,理解前端卡至关重要,因为它是 ST 社区目前最活跃、最“花哨”的部分。
1. 什么是前端卡?
传统角色卡只包含姓名、描述、问候语等文本数据。而前端卡本质上是一个以角色卡为载体的“微型网页应用”。
它利用 ST 的扩展系统(Extensions)和 HTML 渲染能力,在聊天界面注入自定义的 HTML、CSS 和 JavaScript。常见的表现形式形式包括:
- 状态栏:显示角色的好感度、心情、体力值。
- 互动 UI:点击按钮触发特定的指令或对话。
- 视觉小说(VN)模式:将背景、立绘和对话框完全重新设计。
- RPG 系统:自带背包、商店、任务系统。
2. 实现原理(ST 源码逻辑)
前端卡的实现主要依赖以下几个技术点:
A. HTML 注入
ST 允许在 World Info(世界书)或者角色卡描述中使用特定的标签。最常用的是配合 Quick Reply(快速回复) 扩展或特定的 Extension 钩子。
- 逻辑:后端读取到包含 HTML 字符串的数据,前端通过
$.append()或innerHTML将其塞进 DOM。
B. JavaScript 脚本执行
这是前端卡的“灵魂”。制作者通常会将加密或压缩后的 JS 代码嵌入在角色卡的“深度提示词”(Depth Prompt)或世界书条目中。
- 执行机制:ST 并没有为这些脚本提供沙箱环境。脚本运行在全局作用域下,可以直接通过
window.SillyTavern对象访问 ST 的内部函数(如printMessage(),setVar(),token等)。
C. 通信与状态同步
前端卡通过以下方式与 ST 核心交互:
- Macros(宏):利用
{{getvar::hp}}渲染变量。 - Slash Commands:通过 JS 调用
/setvar key=value来更新后端数据。 - Event Listeners:监听
character_selected或message_received事件来刷新 UI。
3. 重构时的“重难点”与注意事项
如果你希望“云酒馆”完全兼容这些前端卡,你在使用 React + Go 重构时会面临巨大的架构挑战:
1. 安全性(最致命的问题)
- ST 原版:因为是单机运行,JS 随便跑,顶多是用户自己害自己。
- 你的公共平台:这是 跨站脚本攻击(XSS) 的重灾区。如果用户 A 上传了一张“前端卡”,用户 B 导入后,这张卡里的 JS 脚本可以直接偷走用户 B 的浏览器
LocalStorage(包含你的平台 Token)或者劫持 API Key。 - 解决方案:
- 必须隔离:绝对不能直接在 React 主页面插入角色卡的 HTML/JS。
- Iframe 沙箱:将前端卡的渲染逻辑放在一个独立的、跨域的
<iframe>中,限制sandbox属性(禁止访问父窗口)。 - 消息传递:React 宿主环境与 Iframe 之间通过
postMessage进行受限的通信。
2. API 兼容层
前端卡通常会调用 SillyTavern.XXX 这种全局对象。
- 重构方案:你需要在 Iframe 沙箱中预先注入一个“模拟对象”,模拟出 ST 的全局 API 环境,将这些调用映射到你自己的后端 API 或 Zustand Store。
3. 数据持久化
前端卡依赖大量的自定义变量(Variables)。
- 重构方案:确保你的 PostgreSQL 数据库中
user_variables表能够支持高频读写,因为一些前端卡会通过 JS 脚本每秒多次更新变量(例如计时器或动态动画)。
4. 样式的冲突
前端卡自带的 CSS 经常会全局污染样式。
- 重构方案:使用 Shadow DOM 或 CSS Modules。当然,如果用了 Iframe,天然就解决了这个问题。
4. 架构要点(结合你们现有系统)
安全与隔离
- 使用 iframe 沙箱模式来渲染前端卡内容,避免直接在宿主页面全局作用域执行脚本。
- 通过 postMessage 实现前端卡与宿主系统的受控通信,限定可访问的接口集合。
- 设定严格的内容策略(Content Security Policy),对内联脚本、外部资源、以及跨域请求进行管理。
数据与 API 设计
- 后端存储前端卡的 HTML/CSS/JS 片段、扩展数据、以及变量定义。后端仅做存储与校验,不执行脚本。
- 前端卡通过受控接口访问后端数据(如变量、角色信息、事件触发等)。
- 提供可版本化的 API 接口,用于加载、更新、禁用前端卡,以及回滚到稳定版本。
渲染与交互
- 前端卡在 iframe 内部渲染,用 Shadow DOM/CSS Modules/或独立的样式域来避免全局样式冲突。
- 提供一个明确的 UI 生命周期:初始化、加载资源、渲染、更新、销毁。
- 交互事件通过 postMessage 传递,宿主统一进行权限校验与路由。
兼容性与迁移
- 对现有角色卡数据结构进行向后兼容处理,Gradual Migration 路线图:先支持最小化前端卡,再逐步引入扩展能力。
- 做好版本回滚与降级策略,确保单卡或单次迁移失败时系统可继续工作。
性能与监控
- 对前端卡资源进行容量与时间上的监控(加载时间、渲染时间、内存占用)。
- 对复杂卡片设定资源上限,避免页面整体降级。
5. 实施工具与注意事项
安全策略
- 明确规定 iframe sandbox 属性,例如:sandbox="allow-scripts"(可选限制更多能力),禁用同源策略外的访问,严格限制跨域能力。
- 使用 CSP、Content Security Policy 报告与阻断机制,避免内联脚本被滥用。
数据边界
- 设置前端卡大小的上限(HTML/JS 总长度、资源大小),避免 UI 注入导致页面阻塞或崩溃。
日志与审计
- 记录哪些前端卡被加载、哪些代码被执行、以及潜在的安全事件。
流程与回滚
- 卡片的版本化、灰度发布、快速回滚机制,确保单卡异常不会影响全局。
测试要点
- 安全性测试(XSS/恶意脚本注入)、性能压测、跨域通信正确性测试、回滚演练。
6. 开发路线与任务清单(建议)
- 确定沙箱实现方案(iframe + postMessage)与接口白名单。
- 设计并实现一个前端卡沙箱适配层:在宿主页面暴露受控 API,映射到后端或前端状态商店。
- 定义前端卡数据模型:HTML/CSS/JS、Extensions、World Info 引用、变量定义等的数据库字段与版本控制。
- 引入内容审核/过滤机制,对上传的前端卡进行静态/动态分析,防止危害。
- 提供一个最小可用版本(MVP):只支持简单的 UI 注入与变量调用,逐步扩展互动组件、VN 模式、背包/商店等系统。
- 编写迁移指南与回滚策略,保障升级过程的可控性。
- 与前端团队协同,建立 UI/UX 一致性规范(渐进式增强、UI 风格、交互动画等)。
7. 风险与注意事项
- 安全性是第一位,绝不能让前端卡访问宿主的敏感 API、Token、Key 等。
- 资源占用管理,避免长时间执行脚本导致内存/CPU 过高。
- 兼容性测试要覆盖历史卡、扩展卡、以及新卡的回滚路径。
- 版本与数据一致性,确保升级/回滚时的卡状态不会导致数据错乱。
8. 参考与链接
- ST 官方文档(前端卡相关章节)
- 安全最佳实践(Iframe 沙箱、postMessage、Content Security Policy)
- 你们的现有实现片段:后端 API、WorldbookEngine、Extensions、Macros、Variables
9. 结论
前端卡 = 角色卡数据 + 动态 UI 脚本。
在重构时,我建议你将其视为**“第三方不可信插件”**来处理。
- 后端(Go):负责存储和校验 HTML/JS 字符串,但不执行。
- 前端(React):负责开辟一个“隔离区”(沙箱),把脚本关进去运行。
- 兼容性:你需要实现一个适配器,让那些习惯于 ST 语境的脚本以为自己还在 ST 里,但实际上是在受你监控的“云酒馆”容器中。
如果你能完美解决“安全隔离”的同时保留“交互能力”,你的云酒馆将直接降维打击目前市面上大部分只能聊天的网页端酒馆。
10. 案例分析:Clannad_v3.1.png 落地要点
请参阅独立文档 docs/Clannad_v3.1_analysis.md 以获取完整的落地要点、字段设计、实现步骤与验收标准。