The workbench sandbox inlines only the entry script, so code-split
chunks had no runtime address and dynamic imports 404'd. Serve each
installed plugin's ui.root through dbx-plugin://localhost/<id>/<path>
(WebView2 maps the scheme to http://dbx-plugin.localhost), inject the
platform-correct form as the sandbox document <base>, and allow it in
the resource CSP. Reads reuse the registry's ui-asset validation
(traversal confinement, 32 MiB cap, sha256 etag) and answer CORS with
ACAO:* for the opaque-origin iframe; responses are no-store so plugin
updates land immediately.
Existing plugins are unaffected. Builds that emit relative asset URLs
(vite base "./" / webpack publicPath "./") additionally regain lazy
loading and font loading; documented for plugin authors in both
languages.
Plugin workbenches run in a sandbox="allow-scripts" iframe with an opaque
origin, so every scripted clipboard path (async Clipboard API, offscreen
textarea + execCommand) is denied there — plugins could not copy share
links to the system clipboard. The bridge now exposes host.copy: the
workbench host reuses copyToClipboard (Tauri clipboard-manager on desktop,
Web Clipboard with a legacy fallback on web hosts), and the sandbox iframe
additionally gets allow="clipboard-write" so engines that honor the
delegation can write directly.
The dev host matches production: its debug page answers host.copy in the
top-level document and its plugin iframes carry the same allow attribute.
No new plugin permission is required, so old hosts keep installing new
plugins and old plugins simply never call the method.
Plugin iframes cannot trigger downloads: WKWebView cancels
shouldPerformDownload navigations when no host download handler is
registered, so blob-anchor downloads were silent no-ops on desktop.
The bridge now accepts a transferred ArrayBuffer (512 MiB cap, base64
fallback) and persists it through the native save dialog
(plugin-dialog + plugin-fs on Tauri, top-level anchor on web hosts),
resolving null when the user cancels.