跳转到主要内容

L4 远程 WebView

L4 是插件渲染的最高隔离层级:插件页面运行在独立的 Tauri WebviewWindow(独立 renderer 进程)中,与主界面进程级隔离。即使插件陷入 busy-loop 也只冻结自己的窗口,不影响主 UI(主界面仍可响应、渲染、关闭)。 适合重 UI、不受控的插件(如复杂编辑器、游戏 UI 增强、长驻后台页面)——这类插件在 iframe(L2)里仍与主界面共享同一 WebView,一个死循环就能卡死整个启动器。

manifest 声明

满足以下任一条件即启用 L4 渲染:
  • "render": "webview",或
  • layers 数组包含 "l4"
判定优先级(plugin-loader.tsx useWebview):render === 'webview' || layers.includes('l4')。声明 l4 的插件仍须提供 entry.frontend(否则不会被激活渲染)。

跨窗口桥原理

l4 窗口不是 window.open 出来的普通子窗口,而是经 Tauri IPC WebviewWindow 创建的独立 WebView 窗口(opener 为 null),因此不能依赖 window.opener.postMessage。跨窗口通信走 Tauri 事件系统(emitTo / listen,core:event 默认允许):
  • 激活:插件启用时 createRemoteWebview 打开窗口 plugin-webview-{id}(默认 1200×800),地址为插件页 URL 并带 pluginWebview=1 标记。
  • 轻量启动:App.tsx 检测到该标记后不加载主 Layout / 路由 / 插件自动激活,只渲染 PluginWebviewPage。
  • 渲染:PluginWebviewPage 拉取插件前端资源,经 buildPluginDoc 注入主题变量与桥脚本,渲染进 sandbox="allow-scripts" 的 iframe。
  • API 转发:iframe 内 __PLUGIN_API__ 调用发出 __plugin_api_call 消息 → 页面转发为 Tauri 事件 emitTo('main', 'plugin-l4-api-call', {pluginId, id, method, args})。
  • 主窗口执行:主窗口 initL4Bridge 收到事件 → 调用与 iframe 相同的 handleApiCall(METHOD_PERMISSIONS 权限校验 + executePluginMethod)→ 响应经 emitTo(l4label, 'plugin-l4-api-response') 回发 → PluginWebviewPage listen 后 postMessage 原样回传 iframe。
  • 流式中止:__plugin_api_abort → plugin-l4-api-abort 事件 → 主窗口 abortStream。
消息体复用 sandbox.ts 的 postMessage 协议({type,id,method,args} / {type,id,result,error}),仅传输层换成跨窗口事件,插件侧无需感知差异。

非 Tauri 降级

l4 依赖 Tauri WebViewWindow,在非 Tauri 环境自动降级为 iframe(l2):
  • 纯浏览器 / Vite dev(无 window.__TAURI_INTERNALS__):createRemoteWebview 直接走 createSandbox(iframe 沙箱)。
  • 窗口创建失败(tauri://error):日志提示「降级为 iframe」,并回退到 createSandbox。
窗口按 plugin-webview-{id} label 去重(已存在则聚焦不重建);用户手动关闭窗口时清理实例表,避免去重误判。

与 iframe(l2) / WASM(l3) 的取舍

  • 常规 UI 插件 → 默认 iframe(l2),不要随意声明 l4。
  • 插件有重计算或可能卡 UI 的渲染 → 优先把重计算挪到 WASM(l3);只有”整个页面都必须隔离在独立进程”时才用 l4。
  • 需要访问主界面 DOM → iframe 无法满足,用 "render": "inline"(与主界面同 window,隔离最弱,慎用)。

已知边界(以代码为准)

  • registerMethod / callPlugin 的插件间调用:l4 窗口经主窗口 plugin-registry.ts 中转;l4 侧直接使用 registry 的路径标注为 TODO(见 ADR-054),插件间互调建议以主窗口 registry 为准。
  • 主题推送:l4 窗口无完整主题管线,iframe onload 时一次性推送当前主题变量(__qomicex_theme);主题切换后新值不自动同步(以代码为准)。