常见问题
本页按「安装/运行/扩展/测试」分类,只回答当前代码与阶段状态能够支撑的问题。涉及实现边界时,以当前状态与测试为准。
安装
Node.js 必须用 24.x 吗?
开发与测试的环境基线是 Node.js 24.x,仓库在 Windows 与 Linux 上实测 24.21。低于 24 的版本没有按本仓库的依赖与脚本验证,遇到构建或测试异常时,先切到 24.x 再复现。
node --version
npm --version仓库用 package-lock.json 锁定依赖。不要混用 pnpm/yarn,也不要在未确认影响前删除锁文件。完整要求见环境与依赖。
只跑网页也需要 Rust 吗?
需要。网页端的构建会经 build/acgcore-plugin.ts 把 acgcore 编译为 wasm32-unknown-unknown,即使不构建 Tauri 宿主也会触发。缺 Rust 或缺少 wasm32 目标时,npm run dev 与 npm run build:debug 都可能在构建阶段失败。
只需要 Rust 稳定版 1.82 及以上,并安装:
rustup target add wasm32-unknown-unknownnpm install 失败或很慢怎么办?
先判断是哪一类问题:
| 现象 | 先检查 |
|---|---|
| 版本求解失败、依赖树异常 | Node 是否为 24.x;是否混用了其他包管理器 |
| 下载超时或证书错误 | npm registry、网络与代理设置 |
| 安装成功但构建失败 | Rust、wasm32 目标、Cargo 缓存 |
优先用锁文件重装:
npm ci若环境带 http_proxy/https_proxy,确认它们不会拦截 npm registry 与本地回环地址。具体处理见故障排查。
仓库里为什么没有第三方内容?
仓库只放框架自己的代码、原创示例与自造的最小测试夹具。第三方剧本、图像、音频、字体、安装包等都不进入仓库;外部内容由用户在本机选择并装载。这样既避免版权与分发问题,也保证测试不依赖任何特定输入源。
内置示例「一封晚到的信」是原创数据包,覆盖台词、选项、旗标与章节接续,但不含图像与音频。它的包清单在 builtin/framework.sample/pack.json。
运行
http://localhost:5174/ 是什么?为什么打不开应用?
5174 是独立源的信任页,不是第二个应用入口。外壳以 iframe 嵌入该页,并通过专用通道通信;只有需要用户裁决或打开信任管理时才显示。单独打开它只会看到信任页界面(或空白等待连接),不会进入应用。
开发服务器必须同时启动两个源。只开 5173 或把 5174 改成别的端口,会破坏独立源关系。端口说明见安装、运行与构建。
网页端和 Tauri 端有什么差异?
核心行为同构,差异集中在宿主能力:
| 项目 | 网页 | Tauri |
|---|---|---|
| 外部包来源 | 文件选择器选目录或 ZIP;具备 OPFS 时列出已保存的包 | 原生选择器;应用数据目录 packs/ 扫描 |
| 翻译产物落盘 | 只有 OPFS 与 Web Locks 同时可用时写入站点私有存储 | 应用数据目录 packs/ |
| 信任存储 | 信任页独立源的 IndexedDB | Rust 侧持有的应用数据文件 |
| 代码单元执行 | blob: URL 动态 import() | 一次性 advmod 自定义协议地址 |
| 渲染与音频 | Canvas/Pixi 与 Web Audio | 同一套前端,运行在系统 WebView 中 |
插件与 Mod 在两端都与框架同 realm,没有技术隔离。完整对照见网页与 Tauri。
为什么网页端不能写出翻译产物?
网页写出需要 OPFS 与 Web Locks 两项能力同时存在。缺少任一项时,框架会明确报告当前宿主不支持写出产物,不伪造降级存储;读取、装载和其他功能仍可使用。可改用支持这两项能力的浏览器或 Tauri 宿主。
为什么已保存的产物或包句柄会消失?
网页未完成产物只有在超过 24 小时且确认没有写入者时回收;已完成产物列表最多 10,000 个。Tauri 暂存产物空闲 1 小时回收,包列表最多 10,000 个,句柄连续 24 小时未使用会在下一次登记时失效。这些限制用于避免残留目录、列表和句柄无限增长。
网页发布为什么需要两个源参数?
运行时外壳与信任页必须是不同的 HTTPS 纯源。请使用 npm run build:web -- --shell-origin=https://… --trust-origin=https://…,构建会生成相对基址和两份 CSP 文件;build:release 是 Tauri 发布构建,不包含网页源。
为什么播放时没有声音?
浏览器要求先有一次用户操作才允许音频上下文启动。播放界面在尚未解锁时会显示「点击解锁音频」胶囊;点击它,或进行任意一次点击/按键,音频会解锁。
如果解锁后仍无声,依次检查:
- 独立设置或播放设置抽屉中的总音量与对应通道音量是否为 0;
- 当前内容是否真的包含音频资源(内置示例不含音频);
- 系统或浏览器是否静音了该标签页;
- Tauri 端是否运行在缺少音频后端的精简环境中。
音频能力属于 Phase 2,该阶段已完成;真实章节中的非零 PCM 解码与调度已经双端核验,但不代替实际听感检查。
设置在哪里?为什么有两个设置入口?
主页的「设置」打开独立界面,可调整声音、文字、语言、主题、缩放、皮肤与界面实现。播放工具条或菜单的「设置」打开声音与排版抽屉,继续保留剧情与音乐。两处共用声音和阅读设置,修改后立即生效并保存。下载线路与账号分组当前置灰。
默认皮肤和「内置样式(不换皮)」有什么区别?
内置默认皮肤是随包提供、通过普通皮肤管线套用的颜色、九宫格与控件外观。首次启动尚无皮肤选择时自动套用。「内置样式(不换皮)」撤下这些覆盖;一旦主动选中,刷新后也不会再次自动套用默认皮肤。
皮肤所属单元卸载时,选择保留但暂时显示内置样式;重新装载后恢复。皮肤读取失败时也会回落,详情在顶栏日志中。当前已提交版本的画面组「恢复本组默认」会选择不换皮,需要默认皮肤时请直接选中「核心内置默认皮肤」。
装载了新的主页实现,为什么主页没有变?
登记不等于选用。生效顺序为用户选择、数据包指定、内置实现;没有内置实现的新界面才取最早登记项。在独立设置的「画面与皮肤」里选择界面实现;该选项只在有多个候选或已选实现被卸载时显示。所选实现卸载会回落,重新装载后恢复选择。
为什么主页按返回键没有退出?
主页是导航栈底,Esc 与 Android 物理返回键在这里被吸收。设置返回上一层;剧情先处理隐藏对话框、自动/快进或播放菜单,退出剧情用菜单里的「返回主页」。这不使用浏览器/WebView 的历史回退。独立信任页的 Android 返回键转发尚未纳入当前代码基线,裁决时使用页面里的明确按钮。
为什么有些入口是灰的?
置灰表示该能力尚未交付,不是加载失败。当前明确置灰或未实现的包括:
- 顶栏「线路(暂未提供)」与独立设置中的下载线路、账号分组;
- 存读档界面的「自动 · 暂未提供」「快速 · 暂未提供」;
- 云存档、孤儿 GC、输入路由、性能分级(Phase 4);
- 账号、热更新、社交、活动、支付(Phase 5);
- 完整编辑器、本地化工作台、运营后台(Phase 6)。
不要用修改 DOM 或绕过入口的方式「启用」它们;对应服务并不存在。阶段范围见阶段总览。
扩展
插件和 Mod 有什么区别?大插件和小插件又是什么?
先看「是否含代码」与「是否产生写边」:
| 档位 | 含代码 | 是否产生写边 | 典型行为 |
|---|---|---|---|
| 数据包 | 否 | 无 | 写入剧本、规则、数值等 registry 条目 |
| 资源包 | 否 | 无 | 提供 assets/ 下的图像、音频、字体等 |
| 插件 | 是 | 无 | 在实现位登记实现、订阅通知、只读查询 |
| Mod | 是 | 有 | 经 intercept、机制插槽 hook 或 isolate 域内 provide 改写机制 |
大插件是构建期由 RPM 焊死的根插件(Kernel、Platform、Asset 等),第三方不能新增;小插件指实现位条目(Plugin),运行期可装载的是插件、Mod、数据包与资源包,它们是扩展生态的落点。判断档位与写边边界见四档扩展模型与policy 与写边。
为什么装载被拒绝?
常见原因可以归为四类:
| 类别 | 例子 | 排查方向 |
|---|---|---|
| 清单不合法 | 入口缺失、文件表不符、代码单元多文件 | 对照包清单与目录检查 pack.json 与目录 |
| 签名问题 | 签名无效、发布者被撤销 | 检查签名文件、公钥与发布者记录 |
| 准入失败 | 声明的写边落在 closed 目标、代码单元自报为插件却有写边 | 检查 writes 与目标 policy |
| 版本或依赖不满足 | 契约版本不符、同一 ID 已装载 | 检查 ir、依赖声明与当前已装载单元 |
拒绝原因会显示在任务槽与日志中,并带原因码。按原因码查装载原因码,再看装载管线与 guard。
为什么会要求我裁决?是不是有问题?
不是。以下情况按设计必须由用户本人裁决:
- 发布者签名有效,但不在官方签名清单内(首次信任,TOFU);
- 单元未签名;
- 未签名单元的内容在上次批准后发生变化;
- 同一发布者名称出现不同公钥;
- 单元申请敏感能力或要改写
trusted目标。
裁决只在独立源信任页内完成。默认焦点在「拒绝」,Esc 等同拒绝,同意按钮有锁定倒计时;同名异钥覆盖旧记录还须二次确认。信任页的完整说明见首次运行与信任与签名。
批准未签名单元后,它就安全了吗?
不是。批准只表示你见过这份归档并同意执行,同时信任页会记录其归档哈希;内容变化后会再次要求批准。它不构成安全审计,也不改变「插件与 Mod 和框架同 realm、没有技术隔离」这一事实。
因此:
- 只装载你愿意执行其代码的单元;
- 不要因为「已经批准过一次」就忽略后续的内容变化提示;
- 不要把信任记录当成沙箱或权限系统;
- 发现可疑内容时,卸载单元并清除对应发布者记录。
能否热更新大插件?
不能。大插件集合由 RPM 在构建期确定,启动时核对清单哈希与入口哈希;第三方不能新增大插件,也不能在运行期替换根插件。Phase 5 规划的在线能力也不得更新大插件与 RPM。
可以卸载并重新装载的是运行期的数据包、资源包、插件与 Mod;这属于重新装载,不是原地热替换。当前在线热更新本身尚未实现,不要按已有能力设计流程。相关边界见根插件名单(RPM)。
如何制作数据包或资源包?
两者都不含可执行代码,核心是一个 pack.json 与对应目录:
| 档位 | kind | 内容放置 |
|---|---|---|
| 数据包 | datapack | 剧本、规则、数值等 registry 条目 |
| 资源包 | resourcepack | assets/ 下的虚拟路径 |
pack.json 需要声明 ID、版本、名称、发布者、kind、IR 版本与文件表;文件表用 SHA-256 记录每个文件。不要放代码入口,也不要在清单里声明写边。完整字段与目录布局见分发包与包结构与包清单与目录。
制作完成后,用「离线导入」选择目录或 ZIP 装载,先确认它能通过准入,再考虑签名与分发。测试时不要直接改内置示例,保留它作为最小基线。
测试
浏览器页面实测与兼容性测试使用 Playwright。以下命令是项目保留的回归与真实宿主脚本,其结果应单独记录;框架回归不代替文档站页面实测。
为什么测试有「跳过」?
npm test 同时跑两个项目:
| 项目 | 包含 | 跳过来源 |
|---|---|---|
| node | test/unit/、test/contract/ | 浏览器专用用例在 Node 下不运行 |
| web | test/contract/、test/ui/ | 界面测试只在浏览器项目运行 |
因此 Node 项目出现跳过是正常现象,不代表失败。判断结果看退出码与失败列表,不要只看「通过数」;两个项目的通过数也不应直接相加当作覆盖率。测试命令见安装、运行与构建。
如何跑真实宿主测试?
npm run test:tauri它不是独立的浏览器测试:需要先构建并启动调试版宿主,然后由保留的 Playwright 脚本连接已有页面执行契约测试。Windows 的 debug-channel 构建已由宿主代码开放 WebView2 远程调试端口 9437;发布构建不开放此端口。宿主未启动时脚本会报「未找到待测试的 Tauri 外壳」。
准备步骤与限制见 test/host/、src-tauri/README.md 与故障排查。不要把这个端口暴露到非回环地址。