网页与 Tauri
框架只有一份核心代码。网页版提供浏览器能实现的绝大部分功能,Tauri 只补差异——这是不可动摇的定位。核心层不判断运行环境,平台差异只在宿主适配层落地。
相关接口见 Platform 插件参考;资源读取的跨平台行为见资源管线;安装与构建见安装、运行与构建。
六条硬性规则
| 编号 | 规则 |
|---|---|
| R1 | 核心层(Kernel 与全部大插件的逻辑部分)必须能在浏览器完整运行;禁止 import 任何 Tauri 或 Node API |
| R2 | 平台差异只能经 caps:*、host:backend@1 或宿主绑定暴露;能力缺失时必须降级或明确禁用,禁止崩溃 |
| R3 | 任何浏览器可实现的功能,必须先在网页版实现;Tauri 只补差异 |
| R4 | 禁止平台强绑定代码;宿主差异仅限 host/ 与 src-tauri/;核心层禁止编译期条件分支 |
| R5 | Tauri 产物目标为全平台,均由同一份核心代码构建 |
| R6 | 宿主适配层必须通过同一套契约测试,两端各跑一遍 |
核心层零平台分支
| 位置 | 允许做的事 | 禁止做的事 |
|---|---|---|
src/core/、src/shared/ | 只依赖浏览器标准能力 | import Tauri/Node API、判断运行环境、编译期条件分支 |
src/boot/ | 唯一判断运行环境的位置,启动时判断一次 | 把环境判断散布到别处 |
src/host/web/、src/host/tauri/ | 网页与 Tauri 的宿主差异实现 | 被核心层直接 import |
src-tauri/ | 原生路由、文件访问、信任存储等敏感能力 | 把凭据或敏感结果交给网页侧自行裁决 |
启动流程在 src/boot/main.ts 中调用一次 hasTauri():网页路径装载 createWebHost(),Tauri 路径先完成握手,再装载 createTauriHost(tier)。之后的 Kernel、大插件与扩展单元都只看见同一份宿主接口。
契约测试使用同一套用例:项目既有网页回归脚本默认驱动系统 Edge;Tauri 端在 debug 构建的窗口里运行共用契约包并回传结果。浏览器页面实测与兼容性测试使用 Playwright,结果单独记录;两端结果与不同测试工具的结果不能互相代替,也不能用单项通过代表全部验收。
能力矩阵
| 能力域 | 网页(基线) | Tauri(扩展) | 当前状态 |
|---|---|---|---|
| 定位 | 功能基线 | 只补浏览器做不到的差异 | 已完成 |
| 渲染 | Canvas 2D 默认;PixiJS(WebGL)按需载入 | 同网页,运行在系统 WebView 中 | 测试中 |
| 音频 | Web Audio;首次用户操作后解锁 | 同网页 | 测试中 |
| 存档与进度 | IndexedDB | 同网页,仍用 IndexedDB | 已完成 |
| 外部包来源 | 文件选择框选目录或 ZIP;拖入目录或 ZIP;具备 OPFS 时可列出站点私有存储中的产物 | 扫描应用数据目录 packs/;桌面端原生文件选择器;拖入 | 已完成 |
| 翻译产物写出 | 仅在 OPFS 与 Web Locks 都可用时写入站点私有存储;最后写完成标记 | 先写 packs-staging/,再原子改名到 packs/ | 已完成 |
| 界面语言 | 宿主经 platform:host 报告浏览器语言;取不到时为 und | 同一接口,由 WebView 宿主报告 | 已完成 |
| 字体安装 | 宿主具备字体安装能力时提供;没有时跳过章节子集字体 | 同一接口,由 WebView 宿主提供 | 已完成 |
| 包内代码执行 | blob: URL 动态 import() | 一次性自定义协议模块地址,取走即删 | 已完成 |
| 信任存储 | 信任页独立源的 IndexedDB | Rust 侧持有的应用数据文件 | 已完成 |
| 文件所有者 | 不推测,只提供修改时间 | Windows 为 SID 与账户名,unix 为 uid 与用户名 | 测试中 |
| 原生路由 | 不存在 | 静态白名单;来源与按源发放的口令分别核验 | 已完成 |
矩阵中的「测试中」表示功能已落地,但 Phase 2 的真实内容复跑或对应平台验证尚未完成;不以网页端通过代替宿主端结果。
能力与降级
当前两端声明的能力完全一致:
| 能力 | 敏感度 | 可用 | 说明 |
|---|---|---|---|
caps:raw-bytes | T3 | 是 | 原始字节通道,供隔离翻译器使用 |
caps:fs-read | T2 | 是 | 由框架代单元读取用户本次选定的本地输入 |
caps:net-fetch | T2 | 否 | 网络输入随后续阶段接入 |
platform:host.info() 返回独立快照:
{
backend: string,
tier: 'web' | 'tauri-desktop' | 'tauri-mobile',
capabilities: Array<{ name, sensitivity, available }>
}- 修改返回的数组或能力描述不会影响宿主提供的原始能力表。
- 能力不可用是报告事实,不表示宿主为它提供替代实现;调用方必须降级或明确禁用。
select(name)选择已登记的宿主实现,只供框架控制面使用;扩展单元不能代替用户选择。未知名称抛出SlotError,现有选择保留。- 宿主后端的选择顺序目前是:显式选择 → 匹配宿主档的最早登记条目 → 默认条目。该后端的场景声明与选择持久化尚未接入;界面实现的默认声明与持久化见下文。
- 当前两端都不向单元交出能力对象:
createCap对请求明确报错,能力只作为授权记录,由框架代隔离单元读取。 host:backend@1为single,默认builtin,卸载回落默认;Platform 自身不判断 Tauri、操作系统或浏览器类型。
宿主切换时广播 platform:on-capability-change。已装载单元保留原有能力对象,不迁移、不重新创建;后续装载使用新的当前后端。无法取得当前后端时,装载以 GUARD_INDETERMINATE 拒绝。
UI 响应式
桌面与移动同时支持,没有「某端优先」。 设计基准端是横屏:参考内容本身是横屏布局,现代浏览器也支持横屏全屏。
| 场景 | 行为 |
|---|---|
| 桌面横屏 | 完整播放区、顶栏、左列与进度栏 |
| 矮屏横屏(手机横置) | 压缩顶栏、左列与进度栏,保留播放区 |
| 竖屏或窄屏 | 宽度小于 600px 时顶栏折行;设置抽屉从底部升起,翻译等界面内容上下堆叠 |
| 横屏下的设置抽屉 | 自右侧滑出,开合不暂停剧情 |
实现位于 src/shell/shell.css、src/shell/playback.css 与 src/shell/translate.css,使用方向与高度媒体查询,不读取宿主类型。
外壳已接入 ui:screen@1(multi,按界面名分派)、ui:navigator 与 ui:skins,以 core:home 为栈底,设置与剧情分别使用 core:settings、core:story。界面生效顺序为用户选择 → 数据包 ui:screen-defaults 声明 → 内置实现;用户选择可跨卸载保留,所选实现缺席时临时回落。实现详情见 Platform 插件参考。
独立设置提供声音、文字、画面三个可用分组;下载线路与账号显示「暂未提供」。界面语言、界面实现、主题、缩放与皮肤选择保存在外壳本地设置中。进入独立设置时常驻侧栏、任务槽与停靠栏隐藏,顶栏与底栏保留。
Android 物理返回键在宿主层转为 Escape,沿用网页路由:模态层先关闭、剧情隐藏对话框先恢复、正常剧情打开菜单、非剧情界面退栈、栈底静默吸收。信任页显示时按键转交该页,只拒绝裁决或关闭管理页,外壳不响应。既有模拟器记录分别覆盖普通返回键 11 项,以及信任页返回键与配置变化 12 项;完整范围见当前状态与测试,不据此推断所有实体设备。
Android 清单的 configChanges 已覆盖系统字体大小与显示密度变化;已有模拟器记录确认这些变化未重建 Activity,原生桥与返回键继续工作。其他原因导致同一进程内 Activity 重建时,原生桥、返回键与请求池仍不能重新接到新 WebView,需要重启应用;重建恢复尚未交付。
皮肤引擎已应用设计变量、九宫格和控件属性,支持浅深主题;位置与尺寸在界面层直接使用 CSS 像素,外壳整体缩放另行生效。内置默认皮肤由数据包登记、资源包提供,首次运行自动套用;用户明确选择「内置样式(不换皮)」后不再自动回落,指定皮肤的选择可保留。Phase 3 整体仍为实现中,具体玩法与阶段出口尚未完成。
安全关键 UI 不受资源包影响
| 约束 | 实现 |
|---|---|
| 安全关键 UI 只用内置字体与样式 | 信任页、准入裁决、签名提示不经过资源管线取任何资源 |
| 保留命名空间 | assets/sui/ 下的文件不得由任何单元提供;含该类文件的单元在装载期被拒绝,原因码 GUARD_SECURE_UI_OVERRIDE |
| 皮肤开放范围 | 外壳、内置对话框与选项、历史面板、设置等普通界面;信任页、裁决界面与舞台合成器画面不在范围内 |
| 控件属性边界 | 可在白名单内调整位置、尺寸、外观与层级;列表项不接受位置,越界位置夹回容器;不开放行为脚本 |
| 交互下限 | 控件外观保留所选尺寸,以透明补足区保证至少 44×44 CSS 像素可点区域;交互控件不透明度不低于 0.6 |
皮肤 JSON 是由框架解释的表现配置,资源包提供相应文件与图片;所有属性先校验,再由引擎应用。换皮不允许替换安全关键 UI 或执行代码。详见资源管线与信任与签名。
外部包来源与信任存储位置
| 项目 | 网页 | Tauri | 关键约束 |
|---|---|---|---|
| 外部包选择 | 用户手势触发的文件选择框:目录逐文件或整个 ZIP;也支持拖入;具备 OPFS 时列出已完成产物 | 应用数据目录 packs/ 扫描;桌面端原生选择器;拖入 | 网页必须在用户点击事件里同步打开选择框;包内路径取自选择结果;列表与写出能力分别检查 OPFS 与 Web Locks |
| 包读取 | 用户授权的目录或 ZIP,逐文件读取 | 原生按句柄读取,核对真实路径仍在登记时的包根内;大文件每块最多 64 MiB | 不按路径二次打开,防止检查后被换成链接;读取期间大小变化即拒绝 |
| 翻译产物 | OPFS + Web Locks 可用时写入;完成标记最后写 | packs-staging/ → 原子改名到 packs/;文件在已核对的暂存目录句柄下逐级创建 | 网页完成标记 ≤4 KiB、已完成列表 ≤10,000;Tauri 暂存 ≤16 个、合计 4 GiB、空闲 1 小时回收;包列表 ≤10,000、句柄闲置 24 小时失效;产物不自动装载 |
| 信任页 | 独立源;正式部署的独立子域尚未确定 | 独立的自定义协议源 | 外壳与扩展代码在另一个源,读写不到信任存储 |
| 信任存储 | 信任页独立源的 IndexedDB | Rust 侧持有的应用数据文件 | 只有信任页源的路由可读写;不经 Cordis 服务暴露 |
| 原生路由 | 无 | src-tauri/src/routes.rs 的静态表 | 每条路由限定调用方(外壳源/信任页源)与渠道,来源与口令分别核验 |
补充规则:
- 信任存储不受 effect 管理、不参与热重载、永不可被 GC 处置;任何扩展单元都不得读写它。
- Tauri 握手会核对构建 ID 与清单哈希;不符即关闭全部原生路由,不留半启动状态。
- 调试路由只在 debug 构建注册;release 产物不注册 handler、不监听端口。
- Android 桥接的 JSON 请求按
args原文的 UTF-8 字节数核对路由上限;原始二进制请求先筛编码长度,再按解码后的字节数核对。只有模块暂存入口允许 256 MiB,其余原始入口按 1 MiB,JSON 路由不能借用大上限。 - Tauri 请求池把排队与处理中的请求体合计限制为 1 GiB;Android 桥接按消息字符串长度计量同一在途配额。超出只拒绝该次请求;Android 页面销毁时取消排队任务,不把任务继续投递给已销毁的 WebView。
- 启动从读取 RPM 到内置包就绪共享 60 秒期限;到期以
GUARD_INDETERMINATE拒绝,并逆序清理已建立资源。 - 文件所有者信息只在 Tauri 提供:Windows 为 SID 与账户名,unix 为 uid 与用户名;网页端不推测所有者,只提供修改时间。
网页发布构建
网页发布必须显式提供外壳源与信任页源,例如:
npm run build:web -- --shell-origin=https://game.example --trust-origin=https://trust.example两个值必须是不同的、不带路径的 HTTPS 纯源;缺失、格式不合法或相同都会拒绝构建。也可用 ADV_WEB_SHELL_ORIGIN 与 ADV_WEB_TRUST_ORIGIN 环境变量提供它们。输出使用相对部署基址,并生成 web-shell.csp 与 web-trust.csp,部署方须把它们作为响应头策略下发。build:release 面向 Tauri,不包含网页源;把它当作网页部署产物会在启动时被拒绝。
实现位置
| 路径 | 职责 |
|---|---|
src/boot/main.ts | 唯一的运行环境判断;选择宿主、信任页通道与包选择器 |
src/host/web/backend.ts | 网页宿主:能力表、产物写出、blob: 模块执行 |
src/host/web/pack-picker.ts | 网页选包:目录、ZIP、OPFS 产物列表 |
src/host/web/trust-store.ts | 网页信任页的独立源存储 |
src/host/tauri/backend.ts | Tauri 宿主:能力表、产物写出、自定义协议模块执行 |
src/host/tauri/pack-source.ts | Tauri 外部包列举、读取与原生选择器 |
src/host/tauri/trust-store.ts | Tauri 信任存储绑定 |
src-tauri/src/routes.rs | 原生路由静态白名单与调用方校验 |
src-tauri/src/fsguard.rs | 按句柄核对真实路径,防止检查后替换 |
src/core/platform/ | 契约、宿主服务、界面层与皮肤校验;不判断运行环境 |
当前状态
| 项目 | 状态 | 说明 |
|---|---|---|
| 网页与 Tauri 宿主骨架 | 已完成 | Phase 0 交付;两端共用契约测试 |
| 外部包装载与产物写出 | 已完成 | 目录、ZIP、OPFS 与 packs/ 路径均已接入 |
| 信任页与信任存储 | 已完成 | 独立源、Rust 侧存储、防回滚清单 |
| 界面层与皮肤 | 实现中 | 导航、独立设置、界面默认声明与默认皮肤已接入;Phase 3 整体出口未完成 |
| 输入路由、性能分级、无障碍、离线模式 | 未实现 | Phase 4 |
| Windows WebView2 | 测试中 | 既有共用契约 393 项通过、2 项按条件跳过;真实内容出口与新增 Battle 原生复跑未完成 |
| Android | 测试中 | x86_64 WebView 109/128 已有共用契约记录;armeabi-v7a 转译仅有启动与冒烟证据,i686 仅编译通过 |
| Linux 原生程序 | 未实现 | 目前只单独编译过文件访问路径,整套 Tauri 依赖未跑 |
| macOS 与 iOS | 未实现 | 暂无设备验证 |
平台覆盖以实际证据为准,详见当前状态与测试与Phase 4–6。
相关阅读:Platform 插件参考、资源管线、隔离运行、翻译层、装载管线与 guard。