···6060 // Config
6161 WEB_CORE_ADDRESS,
6262 SETTINGS_ADDRESS,
6363- setPreloadPath,
6463 setProfile,
6564 isTestProfile,
6665 isHeadless,
···104103import { getTileWebPreferencesForWebviewUrl } from './tile-launcher.js';
105104import { getLazyTileManifest } from './tile-lazy.js';
106105import { getTilePreloadPath } from './config.js';
106106+import { createTrustedBuiltinGrant } from './tile-manifest.js';
107107+import { generateToken } from './tile-tokens.js';
107108108109// Early diagnostic logging for URL handling (writes to /tmp/ to avoid userData path issues)
109110try {
···148149 }
149150}
150151151151-// script loaded into every app window
152152-const preloadPath = path.join(ROOT_DIR, 'preload.js');
153153-154152const systemAddress = getSystemAddress();
155153156156-// Initialize backend config with runtime values
157157-setPreloadPath(preloadPath);
158158-159154// Read Proton Pass webauthn.js for main-world injection into web content webviews.
160155// Electron ignores "world": "MAIN" in Chrome extension manifest content_scripts,
161156// so we inject the script ourselves via executeJavaScript() which runs in the main world.
···170165 console.error('[webauthn] Failed to read webauthn.js:', err);
171166}
172167173173-// Inject preload into <webview> elements that load peek:// URLs.
168168+// Inject tile-preload into <webview> elements that load peek:// URLs.
174169// This gives internal widget pages (e.g., HUD widgets, widget-demo cards)
175170// access to window.app API while leaving external web content webviews untouched.
176171//
177177-// Two paths (Phase 2.5 #2 — webview preload migration):
178178-// 1. v2 tiles: if the URL resolves to a registered v2 tile manifest,
179179-// use tile-preload.cjs with capability-token argv (the same shape
180180-// v2 BrowserWindows get via webPreferences.additionalArguments).
181181-// Webview children inherit the parent tile's grant via
182182-// `getTileWebPreferencesForWebviewUrl`.
183183-// 2. v1 fallback: any peek:// URL whose tile-id isn't a v2 tile (e.g.
184184-// `peek://ext/hud/widgets/...` while HUD is still v1) keeps the
185185-// legacy preload.js. This will go away when HUD is migrated.
172172+// Resolution order (Phase 3.11b-hud — v1 preload.js removed):
173173+// 1. HUD widget webviews: peek://ext/hud/widgets/*.html get a dedicated
174174+// trustedBuiltin token (hud-widget). HUD has no v2 manifest but
175175+// widgets use api.window/context/izui/pubsub — all covered by
176176+// trustedBuiltin grant.
177177+// 2. v2 tiles: URL resolves to a registered v2 tile manifest — use
178178+// tile-preload.cjs with manifest-derived capability token.
179179+// 3. Unrecognised peek:// URL: log a warning and skip preload injection
180180+// (no v1 fallback — preload.js has been deleted).
186181//
187182// Also inject Proton Pass webauthn.js into http/https webviews for passkey support.
188183// The script overrides navigator.credentials.create/get in the main world.
···190185 contents.on('will-attach-webview', (_wvEvent, webPreferences, params) => {
191186 if (params.src && params.src.startsWith('peek://')) {
192187 const tilePreloadPath = getTilePreloadPath();
193193- const tileWebPrefs = tilePreloadPath
194194- ? getTileWebPreferencesForWebviewUrl(params.src, tilePreloadPath, getLazyTileManifest)
195195- : null;
188188+ if (!tilePreloadPath) return;
189189+190190+ // Special-case: HUD widget webviews (peek://ext/hud/widgets/*.html).
191191+ // HUD has no v2 manifest; mint a trustedBuiltin token for all widgets
192192+ // under a unified hud-widget tile id so they can use api.window.*,
193193+ // api.context.*, api.izui.*, api.pubsub.*, etc.
194194+ if (params.src.startsWith('peek://ext/hud/widgets/')) {
195195+ const HUD_WIDGET_ID = 'hud-widget';
196196+ const HUD_WIDGET_ENTRY = 'main';
197197+ const hudWidgetGrant = createTrustedBuiltinGrant(HUD_WIDGET_ID);
198198+ const hudWidgetToken = generateToken(HUD_WIDGET_ID, HUD_WIDGET_ENTRY, hudWidgetGrant);
199199+ webPreferences.preload = tilePreloadPath;
200200+ (webPreferences as { additionalArguments?: string[] }).additionalArguments = [
201201+ `--tile-id=${HUD_WIDGET_ID}`,
202202+ `--tile-entry=${HUD_WIDGET_ENTRY}`,
203203+ `--tile-token=${hudWidgetToken}`,
204204+ ];
205205+ DEBUG && console.log(`[webview] Injecting trustedBuiltin tile-preload for HUD widget: ${params.src}`);
206206+ return;
207207+ }
208208+209209+ const tileWebPrefs = getTileWebPreferencesForWebviewUrl(params.src, tilePreloadPath, getLazyTileManifest);
196210 if (tileWebPrefs) {
197211 webPreferences.preload = tileWebPrefs.preload;
198212 // Cast: Electron's WebPreferences type for webviews omits
···204218 `[webview] Injecting v2 tile-preload for ${params.src} (tile=${tileWebPrefs.tileId}, entry=${tileWebPrefs.entryId})`
205219 );
206220 } else {
207207- webPreferences.preload = preloadPath;
208208- DEBUG && console.log(`[webview] Injecting v1 preload for peek:// webview: ${params.src}`);
221221+ // No v1 preload fallback — preload.js has been deleted. Any peek://
222222+ // URL that reaches here is either a developer-extension webview that
223223+ // needs a v2 manifest, or a bug. Log a warning so it surfaces.
224224+ console.warn(`[webview] No tile-preload found for peek:// webview ${params.src} — no preload injected (v1 preload.js removed)`);
209225 }
210226 }
211227 });
···879895// Configure app before ready (registers protocol scheme, sets theme)
880896configure({
881897 rootDir: ROOT_DIR,
882882- preloadPath: preloadPath,
883898 userDataPath: defaultUserDataPath,
884899 profile: PROFILE,
885900 isDev: DEBUG,
+1-3
backend/electron/index.ts
···1212 SETTINGS_ADDRESS,
1313 IPC_CHANNELS,
1414 TOPICS,
1515- setPreloadPath,
1615 setProfile,
1717- getPreloadPath,
1816 getProfile,
1917 isTestProfile,
2018 isDevProfile,
···227225 listBackups,
228226} from './backup.js';
229227230230-// Re-export frontend API types (the contract that preload.js implements)
228228+// Re-export frontend API types (the contract that tile-preload.cjs implements)
231229export type {
232230 IPeekApi,
233231 ApiResult,
+24-3
backend/electron/ipc.ts
···7777 APP_DEF_WIDTH,
7878 APP_DEF_HEIGHT,
7979 WEB_CORE_ADDRESS,
8080- getPreloadPath,
8180 getTilePreloadPath,
8281 IPC_CHANNELS,
8382 isHeadless,
···10721071 DEBUG && console.log('[window-open] settings window: assigned trustedBuiltin tile-preload token');
10731072 }
1074107310741074+ // Special-case: peek://ext/hud/hud.html is the HUD overlay BrowserWindow
10751075+ // opened on-demand by app/hud/background.js via api.window.open(). It
10761076+ // hosts webview widget cards (app/hud/hud.js) and needs tile-preload for
10771077+ // api.settings.get / api.window.resize. No v2 manifest — trustedBuiltin.
10781078+ if (!tileWebPrefs && url === 'peek://ext/hud/hud.html') {
10791079+ const HUD_OVERLAY_ID = 'hud-overlay';
10801080+ const HUD_OVERLAY_ENTRY = 'main';
10811081+ const hudOverlayGrant = createTrustedBuiltinGrant(HUD_OVERLAY_ID);
10821082+ const hudOverlayToken = generateToken(HUD_OVERLAY_ID, HUD_OVERLAY_ENTRY, hudOverlayGrant);
10831083+ tileWebPrefs = {
10841084+ preload: getTilePreloadPath(),
10851085+ additionalArguments: [
10861086+ `--tile-id=${HUD_OVERLAY_ID}`,
10871087+ `--tile-entry=${HUD_OVERLAY_ENTRY}`,
10881088+ `--tile-token=${hudOverlayToken}`,
10891089+ ],
10901090+ tileId: HUD_OVERLAY_ID,
10911091+ entryId: HUD_OVERLAY_ENTRY,
10921092+ };
10931093+ DEBUG && console.log('[window-open] hud-overlay window: assigned trustedBuiltin tile-preload token');
10941094+ }
10951095+10751096 // Special-case: peek://app/datastore/viewer.html uses api.datastore.* to
10761097 // display raw DB tables — not a registered v2 tile but needs tile-preload.
10771098 if (!tileWebPrefs && url === 'peek://app/datastore/viewer.html') {
···11221143 ...options.webPreferences,
11231144 // Canvas page host windows get tile-preload with a trustedBuiltin grant;
11241145 // other v2 tile URLs get their manifest-derived preload; everything else
11251125- // falls back to the legacy preload.js.
11461146+ // gets no preload (v1 preload.js has been deleted — Phase 3.11b-hud).
11261147 preload: useCanvas
11271148 ? getTilePreloadPath()
11281128- : tileWebPrefs ? tileWebPrefs.preload : getPreloadPath(),
11491149+ : tileWebPrefs ? tileWebPrefs.preload : undefined,
11291150 additionalArguments: useCanvas
11301151 ? [
11311152 `--tile-id=${PAGE_HOST_TILE_ID}`,