experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(electron): sweep stale v1/preload.js archaeology from comments

Comment-only cleanup pass:
- Drop "v1 preload.js was here" archaeology comments — git history is
authoritative.
- Replace descriptive comments mentioning preload.js with what the code
actually does today.
- Strip Phase 3.x.y tags from section headers and explanatory comments.
- Update [webview] warn message to drop the v1-removal context.
- Rename '/fake/preload.js' fixtures to '/fake/tile-preload.cjs' for
accuracy.

Code deletions (all empty no-op functions whose comments were the only
content):
- registerDatastoreHandlers, registerExtensionHandlers,
registerProfileHandlers, registerModesHandlers,
registerWebExtensionHandlers, registerMiscHandlers,
registerDarkModeSettingHandlers — and their callers in
registerAllHandlers + index.ts re-exports.

Build clean.

+150 -361
+3 -5
app/cmd/background.js
··· 156 156 /** 157 157 * Get current app and extension versions. 158 158 * 159 - * Under the v2 resident-renderer flow, `api.app` / `api.extensions` 160 - * (v1 preload) are no longer present. Use `api.features.list()` — its 161 - * `{ entries }` response carries per-feature manifest versions. The app 162 - * version is resolved via the injected build-number constant so we 163 - * don't need a separate app-info IPC. 159 + * Uses `api.features.list()` — its `{ entries }` response carries 160 + * per-feature manifest versions. The app version is resolved via the 161 + * injected build-number constant so we don't need a separate app-info IPC. 164 162 * 165 163 * @returns {Promise<{appVersion: string, extensionVersions: Object}>} 166 164 */
+3 -3
app/drag.js
··· 1 1 // app/drag.js 2 - // Window dragging is now handled by the unified drag system in preload.js. 2 + // Window dragging is handled by the unified drag system in tile-preload.cts. 3 3 // This module is kept as a no-op stub so existing imports (e.g., settings.html) 4 4 // don't break. The preload drag runs automatically in every window. 5 5 6 6 /** 7 - * No-op: drag is handled by preload.js 7 + * No-op: drag is handled by tile-preload. 8 8 */ 9 9 export function initWindowDrag() { 10 - // Preload handles window dragging for all windows. 10 + // tile-preload handles window dragging for all windows. 11 11 }
+2 -2
backend/electron/cmd-logic.test.ts
··· 4 4 * Tests extracted pure functions from: 5 5 * - extensions/cmd/panel.js: ESC behavior, shift-tab cycling, frecency sorting 6 6 * - extensions/cmd/commands/note.js: parseTagset, detectType 7 - * - preload.js: shouldBlockDrag 7 + * - tile-preload.cts drag IIFE: shouldBlockDrag 8 8 * 9 9 * These are logic-only tests -- no DOM, no Electron, no extensions runtime. 10 10 */ ··· 50 50 } 51 51 52 52 // ============================================================================ 53 - // shouldBlockDrag (from preload.js) 53 + // shouldBlockDrag (from tile-preload.cts drag IIFE) 54 54 // ============================================================================ 55 55 56 56 interface MockElement {
+1 -3
backend/electron/core-glue.ts
··· 15 15 * features via `core:feature:toggle` pubsub. 16 16 * - Settings shortcut registration (Cmd+,). 17 17 * 18 - * Pre-Phase-2.5, this window was created by `createBackgroundWindow()` 19 - * with the v1 `preload.js` and node integration. This module replaces 20 - * that path with the same mechanics used by cmd/hud/page/test fixture: 18 + * Uses the same mechanics as cmd/hud/page/test fixture: 21 19 * 22 20 * - `tile-preload.cjs` as preload, reading `--tile-id=core`, 23 21 * `--tile-entry=background`, and `--tile-token=<...>` from
+8 -9
backend/electron/entry.ts
··· 169 169 // This gives internal widget pages (e.g., HUD widgets, widget-demo cards) 170 170 // access to window.app API while leaving external web content webviews untouched. 171 171 // 172 - // Resolution order (Phase 3.11b-hud — v1 preload.js removed): 172 + // Resolution order: 173 173 // 1. HUD widget webviews: peek://hud/widgets/*.html get a dedicated 174 - // trustedBuiltin token (hud-widget). HUD has no v2 manifest but 174 + // trustedBuiltin token (hud-widget). HUD has no tile manifest but 175 175 // widgets use api.window/context/izui/pubsub — all covered by 176 176 // trustedBuiltin grant. 177 - // 2. v2 tiles: URL resolves to a registered v2 tile manifest — use 177 + // 2. Tile-backed URLs: URL resolves to a registered tile manifest — use 178 178 // tile-preload.cjs with manifest-derived capability token. 179 - // 3. Unrecognised peek:// URL: log a warning and skip preload injection 180 - // (no v1 fallback — preload.js has been deleted). 179 + // 3. Unrecognised peek:// URL: log a warning and skip preload injection. 181 180 // 182 181 // Also inject Proton Pass webauthn.js into http/https webviews for passkey support. 183 182 // The script overrides navigator.credentials.create/get in the main world. ··· 232 231 `[webview] Injecting v2 tile-preload for ${params.src} (tile=${tileWebPrefs.tileId}, entry=${tileWebPrefs.entryId})` 233 232 ); 234 233 } else { 235 - // No v1 preload fallback — preload.js has been deleted. Any peek:// 236 - // URL that reaches here is either a developer-extension webview that 237 - // needs a v2 manifest, or a bug. Log a warning so it surfaces. 238 - console.warn(`[webview] No tile-preload found for peek:// webview ${params.src} — no preload injected (v1 preload.js removed)`); 234 + // Any peek:// URL that reaches here is either a developer-extension 235 + // webview that needs a tile manifest, or a bug. Log a warning so it 236 + // surfaces. 237 + console.warn(`[webview] No tile-preload found for peek:// webview ${params.src} — no preload injected`); 239 238 } 240 239 } 241 240 });
-3
backend/electron/index.ts
··· 183 183 184 184 // IPC handlers 185 185 export { 186 - registerDatastoreHandlers, 187 - registerExtensionHandlers, 188 186 registerDarkModeHandlers, 189 187 registerWindowHandlers, 190 - registerMiscHandlers, 191 188 registerAllHandlers, 192 189 restoreSavedTheme, 193 190 getDarkModeSetting,
+14 -178
backend/electron/ipc.ts
··· 265 265 } 266 266 267 267 268 - /** 269 - * Register datastore IPC handlers 270 - */ 271 - export function registerDatastoreHandlers(): void { 272 - // Legacy `extract-page-content` removed (v1 removal Phase 3.7c). Tile- 273 - // preload's api.datastore.extractPageContent uses 274 - // `tile:datastore:extract-page-content` (capability-gated). Was the 275 - // preload.js v1 surface only. 276 - } 277 - 278 - /** 279 - * Register extension IPC handlers 280 - * 281 - * Note (Phase 3.6c): The 10 legacy extension-* handlers 282 - * (extension-pick-folder, extension-validate-folder, extension-add, 283 - * extension-remove, extension-update, extension-get-all, extension-get, 284 - * extension-window-list, extension-list-all-registered, 285 - * extension-window-devtools, extension-reload) have been deleted. 286 - * tile-preload now routes all api.extensions.* calls through the strict 287 - * tile:extensions:* channels registered in tile-ipc.ts. 288 - */ 289 - export function registerExtensionHandlers(): void { 290 - // Legacy `feature-settings-{get,set,get-key,set-key}` handlers removed 291 - // (v1 removal Phase 3.7d). Tile-preload's api.settings.{get,set,getKey, 292 - // setKey} routes through `tile:settings:*` (capability-gated). Was the 293 - // preload.js v1 surface only. 294 - } 295 - 296 268 297 269 /** 298 270 * Theme settings storage keys ··· 376 348 } 377 349 }); 378 350 379 - // ===== Theme management (add/remove/reload) — Phase 3.7f ===== 351 + // ===== Theme management (add/remove/reload) ===== 380 352 // 381 - // Strict `tile:theme:pickFolder` / `tile:theme:validateFolder` / 353 + // The `tile:theme:pickFolder` / `tile:theme:validateFolder` / 382 354 // `tile:theme:add` / `tile:theme:remove` / `tile:theme:reload` handlers 383 355 // live in tile-ipc.ts. Settings UI consumes them via api.theme.{pickFolder, 384 356 // validateFolder, add, remove, reload}. ··· 387 359 // Keep old function name for compatibility but call new one 388 360 export function registerDarkModeHandlers(): void { 389 361 registerThemeHandlers(); 390 - registerDarkModeSettingHandlers(); 391 362 } 392 363 393 364 /** ··· 419 390 nativeTheme.themeSource = (mode === 'force') ? 'dark' : 'system'; 420 391 } 421 392 422 - /** 423 - * Register dark mode setting IPC handlers 424 - * 425 - * Strict `tile:darkMode:*` handlers live in tile-ipc.ts (Phase 3.7f). 426 - * Legacy `darkMode:*` ipc handlers were removed there too. 427 - */ 428 - function registerDarkModeSettingHandlers(): void { 429 - // No-op — tile:darkMode:get / tile:darkMode:set live in tile-ipc.ts. 430 - } 431 393 432 394 /** 433 395 * Reopen the most recently closed window. ··· 658 620 // Use profile-specific session for isolation 659 621 const profileSession = getProfileSession(); 660 622 661 - // If this URL points to a registered v2 tile, use the tile preload 662 - // (tile-preload.js) with a capability token so the feature's home.html 663 - // gets `api.initialize()`, `api.pubsub.subscribe/publish`, strict 664 - // `api.datastore.*` routing, and every other v2 surface. 623 + // If this URL points to a registered tile, use tile-preload.cjs with 624 + // a capability token so the feature's home.html gets `api.initialize()`, 625 + // `api.pubsub.subscribe/publish`, strict `api.datastore.*` routing, and 626 + // every other api.* surface. 665 627 // 666 - // Non-tile URLs (web pages, legacy extension content, new-tab, etc.) 667 - // fall back to the legacy preload.js. 628 + // Non-tile URLs (web pages, new-tab, etc.) get no preload. 668 629 let tileWebPrefs = getTileWebPreferencesForUrl( 669 630 url, 670 631 getTilePreloadPath(), ··· 720 681 webPreferences: { 721 682 ...options.webPreferences, 722 683 // Canvas page host windows get tile-preload with a trustedBuiltin grant; 723 - // other v2 tile URLs get their manifest-derived preload; everything else 724 - // gets no preload (v1 preload.js has been deleted — Phase 3.11b-hud). 684 + // other tile URLs get their manifest-derived preload; everything else 685 + // gets no preload. 725 686 preload: useCanvas 726 687 ? getTilePreloadPath() 727 688 : tileWebPrefs ? tileWebPrefs.preload : undefined, ··· 2061 2022 return { success: false, error: message }; 2062 2023 } 2063 2024 }; 2064 - // Legacy `window-open` ipcMain handler removed (v1 removal Phase 3.7h). 2065 2025 // The strict `tile:window:open` handler in tile-ipc.ts delegates to 2066 - // `invokeWindowOpen` (the same implementation) — it's the single 2067 - // source of truth for window creation. 2026 + // `windowOpenHandler` (this implementation) — it's the single source of 2027 + // truth for window creation. 2068 2028 invokeWindowOpen = windowOpenHandler; 2069 - 2070 - // Legacy `window-*` handlers removed (v1 removal Phase 3.7h). All 2071 - // tile-preload methods route through strict `tile:window:*` channels 2072 - // gated on the tile's `window` capability shape (or trustedBuiltin). 2073 - // Removed: window-open, window-close, window-hide, window-show, 2074 - // window-focus, window-exists, window-list, window-center, 2075 - // window-center-all, window-maximize, window-fullscreen, 2076 - // window-set-ignore-mouse-events, window-set-overlay-focus-target. 2077 - // Earlier removals (Phase 3.7b/g): window-blur, window-resize, 2078 - // window-move, window-get-bounds, window-set-bounds, window-get-position, 2079 - // get-display-info, window-devtools, get-focused-visible-window-id. 2080 - 2081 - // pubsub-stats removed (v1 removal Phase 3.7g). Strict 2082 - // tile:pubsub:stats handler lives in tile-ipc.ts (trustedBuiltin only). 2083 - } 2084 - 2085 - /** 2086 - * Register miscellaneous IPC handlers (shortcuts, pubsub, commands, etc.) 2087 - */ 2088 - export function registerMiscHandlers(onQuit: () => void): void { 2089 - // Legacy `window-is-transient` removed (v1 removal Phase 3.7b). Tile- 2090 - // preload's api.izui.isTransient uses tile:izui:is-transient. Remaining 2091 - // callers were all in deleted preload.js. 2092 - 2093 - // ============================================================================ 2094 - // IZUI State Coordinator IPC Handlers 2095 - // ============================================================================ 2096 - // Centralized IZUI state management for consistent transient detection, 2097 - // mode display, and ESC behavior across all windows. 2098 - 2099 - // Check if IZUI session is in transient mode (app wasn't focused when invoked) 2100 - // Uses the session's entry mode which was set at window show time (before focus 2101 - // was stolen). Don't re-evaluate here - by this point the requesting window 2102 - // (e.g., cmd panel) has already stolen focus from other windows, making 2103 - // isFocused() checks unreliable. 2104 - // Legacy `izui-*` handlers (is-transient, get-effective-mode, get-state, 2105 - // get-pre-overlay-focus-target, close-self) removed (v1 removal Phase 3.7d). 2106 - // Tile-preload routes everything through `tile:izui:*` (capability-gated). 2107 - // The only callers were in deleted preload.js. 2108 - 2109 - // Legacy `window-get-position` removed (v1 removal Phase 3.7b). Tile- 2110 - // preload's drag uses `tile:window:get-bounds` (returns x/y/w/h for 2111 - // the sender's own window). 2112 - 2113 - // Legacy `window-set-scroll-position` removed (v1 removal Phase 3.7b). 2114 - // Was called from preload.js (deleted). page.js' undo-close-window scroll 2115 - // restoration would need a strict tile:* equivalent if reintroduced. 2116 - 2117 - // Legacy `get-focused-visible-window-id` removed (v1 removal Phase 3.7g). 2118 - // Tile-preload routes through strict `tile:window:get-focused-visible-id` 2119 - // which uses `getLastFocusedVisibleWindowId()` from this file. 2120 - 2121 - // Register shortcut 2122 - // 2123 - // The callback closure captures the IpcMainEvent so ev.reply() can route 2124 - // the response back to the correct sender frame at callback time. 2125 - // 2126 - // ev.reply() internally uses sender.sendToFrame(frameId, ...) which correctly 2127 - // handles both: 2128 - // - Normal BrowserWindow senders (main frame) 2129 - // - Sub-frame senders (for forward compatibility) 2130 - // 2131 - // We guard against destroyed senders (extension reload) 2132 - // to prevent uncaught exceptions. The previous approaches that failed: 2133 - // - BrowserWindow.fromId(ev.sender.id): wrong ID space (WebContents vs Window) 2134 - // - BrowserWindow.fromWebContents(ev.sender): returns null for iframe senders 2135 - // - webContents.fromId(id): does not find sub-frame WebContents 2136 - // - sender.send(): sends to main frame only, not the specific iframe 2137 - // IPC_CHANNELS.* handlers (REGISTER_SHORTCUT, UNREGISTER_SHORTCUT, 2138 - // CLOSE_WINDOW, PUBLISH, SUBSCRIBE, MODIFY_WINDOW) were removed as 2139 - // part of the v1 removal (Phase 3.7e). Their only caller was the 2140 - // deleted `preload.js`. Tile-preload uses strict `tile:*` IPC paths 2141 - // instead (tile:shortcuts:register, tile:pubsub:publish, etc.). 2142 - 2143 - // Legacy `file-save-dialog` removed (v1 removal Phase 3.7d). api.dialogs.save 2144 - // uses tile:dialogs:save (capability-gated). 2145 - 2146 - // Legacy `file-open-dialog`, `file-read-from-path`, `file-write-to-path`, 2147 - // `app-info`, `net-fetch` removed (v1 removal Phase 3.7d). Their only 2148 - // callers were in deleted preload.js. Tile-preload uses tile:dialogs:open, 2149 - // tile:filesystem:read/write, tile:network:fetch, etc. 2150 - 2151 2029 } 2152 2030 2153 2031 /** ··· 2182 2060 } 2183 2061 }); 2184 2062 2185 - // sync-get-config / sync-set-config / sync-pull / sync-push / sync-status 2186 - // were here. Strict counterparts live in tile-ipc.ts (Phase 3.7f) as 2187 - // tile:sync:get-config / set-config / pull / push / status. 2063 + // The tile:sync:get-config / set-config / pull / push / status 2064 + // handlers live in tile-ipc.ts. 2188 2065 } 2189 2066 2190 2067 /** 2191 - * Register profile-related IPC handlers 2192 - * 2193 - * Strict tile:profiles:* counterparts live in tile-ipc.ts (Phase 3.7f). 2194 - * Legacy `profiles:*` ipcMain handlers were removed there too. Settings 2195 - * UI consumes these via api.profiles.*. 2196 - */ 2197 - export function registerProfileHandlers(): void { 2198 - // No-op — see tile-ipc.ts for the strict tile:profiles:* handlers. 2199 - } 2200 - 2201 - /** 2202 - * Register modes-related IPC handlers 2203 - * 2204 - * These handlers provide backwards compatibility with the old modes API. 2205 - * New code should use the context API (api.context) instead. 2206 - */ 2207 - export function registerModesHandlers(): void { 2208 - // Legacy `modes:*` handlers (getWindowMode, setMajorMode, listModes, 2209 - // getCommandContext) removed (v1 removal Phase 3.7d). Tile-preload's 2210 - // api.modes.* and api.context.* route through `tile:context:*` 2211 - // (capability-gated). Only callers were in deleted preload.js. 2212 - } 2213 - 2214 - 2215 - /** 2216 2068 * Persist adBlockerEnabled pref in the datastore. 2217 2069 * Reads current core prefs, updates adBlockerEnabled, writes back. 2218 2070 */ ··· 2240 2092 } 2241 2093 2242 2094 /** 2243 - * Register IPC handlers for bundled web extensions (adblocker + chrome extensions) 2244 - * 2245 - * Strict tile:adblocker:* counterparts live in tile-ipc.ts (Phase 3.7f). 2246 - * Settings UI + page widget consume them via api.adblocker.*. 2247 - */ 2248 - export function registerWebExtensionHandlers(): void { 2249 - // No-op — see tile-ipc.ts for the strict tile:adblocker:* handlers. 2250 - } 2251 - 2252 - /** 2253 2095 * Register all IPC handlers 2254 2096 */ 2255 - export function registerAllHandlers(onQuit: () => void): void { 2097 + export function registerAllHandlers(_onQuit: () => void): void { 2256 2098 registerDarkModeHandlers(); 2257 - registerDatastoreHandlers(); 2258 - registerExtensionHandlers(); 2259 2099 registerWindowHandlers(); 2260 2100 registerSyncHandlers(); 2261 - registerProfileHandlers(); 2262 - registerModesHandlers(); 2263 - registerWebExtensionHandlers(); 2264 - registerMiscHandlers(onQuit); 2265 2101 }
+7 -11
backend/electron/main.ts
··· 780 780 781 781 /** 782 782 * Load a lazy extension on demand. 783 - * Phase 3.10: extensionHostWindow deleted — all consolidated extensions are 784 - * now v2 tiles launched eagerly. The lazy-load path is a no-op; it resolves 785 - * immediately so callers don't hang. If a true lazy-load mechanism is needed 786 - * in future it should target the v2 tile launcher, not the v1 iframe host. 783 + * All consolidated extensions are now tiles launched eagerly, so this path 784 + * is a no-op that resolves immediately. If a true lazy-load mechanism is 785 + * needed in future it should target the tile launcher. 787 786 */ 788 787 function loadLazyExtension(extId: string): Promise<void> { 789 788 if (lazyExtensionLoaded.has(extId)) { 790 789 return Promise.resolve(); 791 790 } 792 - // No extension host window — resolve immediately (v2 tiles load eagerly). 793 - console.warn(`[ext:lazy] loadLazyExtension('${extId}') called but extensionHostWindow has been removed. If this extension needs lazy loading, migrate it to a v2 tile.`); 791 + console.warn(`[ext:lazy] loadLazyExtension('${extId}') called but no lazy-load mechanism exists. If this extension needs lazy loading, migrate it to a tile.`); 794 792 lazyExtensionLoaded.add(extId); 795 793 return Promise.resolve(); 796 794 } ··· 1143 1141 // a live registry. 1144 1142 1145 1143 // Launch the privileged test-fixture renderer when running under 1146 - // Playwright (E2E_TEST=true). This is the v2 replacement for the v1 1147 - // `bgWindow` iframe inside `extensionHostWindow`; tests access it via 1148 - // `getBackgroundWindow()` in `tests/fixtures/desktop-app.ts`. A no-op 1149 - // in production. See docs/v1-removal-plan.md — Phase 2. 1144 + // Playwright (E2E_TEST=true). Tests access it via `getBackgroundWindow()` 1145 + // in `tests/fixtures/desktop-app.ts`. A no-op in production. 1150 1146 if (process.env.E2E_TEST === 'true') { 1151 1147 try { 1152 1148 await initTestFixture({ tilePreloadPath }); ··· 1717 1713 */ 1718 1714 function publishExternalUrl(url: string, sourceId: string): void { 1719 1715 const processUrl = () => { 1720 - // Note: Using trackingSource/trackingSourceId because preload.js overwrites msg.source 1716 + // Note: Using trackingSource/trackingSourceId because tile-preload overwrites msg.source 1721 1717 publish(getSystemAddress(), 'external:open-url', { 1722 1718 url, 1723 1719 trackingSource: 'external',
+1 -6
backend/electron/test-fixture-glue.ts
··· 1 1 /** 2 2 * Electron-specific plumbing for the Playwright test fixture renderer. 3 3 * 4 - * This is the v2 replacement for the v1 `bgWindow` (an iframe inside 5 - * `extensionHostWindow` loading `peek://app/background.html` with 6 - * `preload.js`). Launched ONLY when `E2E_TEST=true`; a no-op in normal 7 - * application startup. 4 + * Launched ONLY when `E2E_TEST=true`; a no-op in normal application startup. 8 5 * 9 6 * The test renderer lives at `app/_test/index.html` and uses the same 10 7 * tile mechanics as cmd/hud/page core renderers: ··· 19 16 * After the window finishes loading, the renderer sets `window.__testReady` 20 17 * to true. `initTestFixture()` blocks on that flag so test code never 21 18 * observes a partially-constructed `window.app` surface. 22 - * 23 - * See docs/v1-removal-plan.md — Phase 2. 24 19 */ 25 20 26 21 import { BrowserWindow } from 'electron';
+4 -4
backend/electron/tile-command-registration.test.ts
··· 117 117 { name: 'bar', description: 'Bar command', action: { type: 'execute' } }, 118 118 ]); 119 119 120 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 120 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 121 121 122 122 // Phase E emits one batch per tile, not per command. 123 123 assert.strictEqual(registered.length, 1, 'expected a single batch publish per tile'); ··· 142 142 params: [{ name: 'query', required: true }], 143 143 } as TileCommand, 144 144 ]); 145 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 145 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 146 146 147 147 assert.strictEqual(received.length, 1); 148 148 const batch = received[0] as { commands: Array<{ name: string; params?: unknown[] }> }; ··· 157 157 const manifest = makeManifestWithCommands('cmd-test-4', [ 158 158 { name: 'doit', action: { type: 'execute' } }, 159 159 ]); 160 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 160 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 161 161 162 162 const realHandlerReceived: unknown[] = []; 163 163 launcherState.onLaunch = () => { ··· 198 198 action: { type: 'window', url: 'peek://cmd-test-5/home.html' }, 199 199 } as TileCommand, 200 200 ]); 201 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 201 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 202 202 203 203 assert.strictEqual(received.length, 1); 204 204 const batch = received[0] as { commands: Array<{ name: string; action?: { type: string } }> };
+6 -9
backend/electron/tile-escape-enforcement.ts
··· 2 2 * Strict Escape Forwarding — Pure module (no Electron deps) 3 3 * 4 4 * Escape is a UX primitive: every window gets it, regardless of declared 5 - * capabilities. The v1 preload exposes `api.escape.onEscape(handler)` by 6 - * listening to the `escape-pressed` IPC that the main-process window 7 - * escape handler dispatches (see `windows.ts::askRendererToHandleEscape`). 5 + * capabilities. tile-preload exposes `api.escape.onEscape(handler)` which 6 + * registers via `tile:escape:on-escape` and receives `tile:escape:pressed` 7 + * dispatches from the main-process window escape handler (see 8 + * `windows.ts::askRendererToHandleEscape`). 8 9 * 9 - * The strict tile path mirrors this without a capability check: a tile 10 - * that declares nothing still needs ESC to close dialogs or navigate 11 - * back. We add `tile:escape:on-escape` as a registration channel — the 12 - * tile sends once to opt-in, then receives `tile:escape:pressed` 13 - * dispatches with a reply channel identical to the v1 `escape-pressed` 14 - * protocol. 10 + * No capability check: a tile that declares nothing still needs ESC to 11 + * close dialogs or navigate back. 15 12 * 16 13 * This module is intentionally tiny — the check always returns `ok`. 17 14 * It exists to mirror the pure-module pattern used by the other
+31 -39
backend/electron/tile-ipc.ts
··· 551 551 unsubscribe(args.source, args.topic); 552 552 }); 553 553 554 - // ── tile:pubsub:stats (Phase 3.7g) ──────────────────────────────── 554 + // ── tile:pubsub:stats ──────────────────────────────────────────── 555 555 // 556 556 // Pubsub telemetry for the HUD stats widget + developer diagnostics. 557 557 // trustedBuiltin only — exposes raw subscription counts which a ··· 3041 3041 } 3042 3042 }); 3043 3043 3044 - // ── tile:window:devtools (Phase 3.7g) ───────────────────────────── 3044 + // ── tile:window:devtools ───────────────────────────────────────── 3045 3045 // 3046 3046 // Open devtools (detached) on the last-focused content window 3047 3047 // (http/https). With an explicit id, opens devtools on that window. ··· 3930 3930 } 3931 3931 }); 3932 3932 3933 - // ── tile:theme:pickFolder / validateFolder / add / remove / reload (Phase 3.7f) ─ 3933 + // ── tile:theme:pickFolder / validateFolder / add / remove / reload ── 3934 3934 // 3935 - // Strict counterparts of the legacy `theme:pickFolder`/`theme:validateFolder`/ 3936 - // `theme:add`/`theme:remove`/`theme:reload` channels in ipc.ts (deleted in 3937 - // Phase 3.7f). Settings UI uses these to install user-supplied themes from 3938 - // disk. trustedBuiltin only — touches filesystem and theme registration. 3935 + // Settings UI uses these to install user-supplied themes from disk. 3936 + // trustedBuiltin only — touches filesystem and theme registration. 3939 3937 3940 3938 registerTileIpc('tile:theme:pickFolder', { mode: 'handle' }, async (event, args: { 3941 3939 token: string; ··· 4354 4352 } 4355 4353 }); 4356 4354 4357 - // ── tile:sync:get-config / set-config / status / pull / push (Phase 3.7f) ── 4355 + // ── tile:sync:get-config / set-config / status / pull / push ──────── 4358 4356 // 4359 - // Strict counterparts of the legacy `sync-get-config`, `sync-set-config`, 4360 - // `sync-status`, `sync-pull`, `sync-push` channels in ipc.ts (deleted in 4361 - // Phase 3.7f). Settings UI consumes these via api.sync.{getConfig,setConfig, 4362 - // getStatus,pull,push}. trustedBuiltin only — sync touches network, server 4363 - // creds, and profile state. 4357 + // Settings UI consumes these via api.sync.{getConfig,setConfig, 4358 + // getStatus,pull,push}. trustedBuiltin only — sync touches network, 4359 + // server creds, and profile state. 4364 4360 4365 4361 registerTileIpc('tile:sync:get-config', { mode: 'handle' }, (event, args: { 4366 4362 token: string; ··· 4484 4480 } 4485 4481 }); 4486 4482 4487 - // ── tile:profiles:* (Phase 3.7f) ────────────────────────────────── 4483 + // ── tile:profiles:* ────────────────────────────────────────────── 4488 4484 // 4489 - // Strict counterparts of the legacy `profiles:*` channels in ipc.ts 4490 - // (deleted in Phase 3.7f). Settings UI consumes via api.profiles. 4491 - // trustedBuiltin only — admin surface (touches profiles.db, can 4492 - // relaunch app, holds sync creds). 4485 + // Settings UI consumes via api.profiles. trustedBuiltin only — 4486 + // admin surface (touches profiles.db, can relaunch app, holds sync 4487 + // creds). 4493 4488 4494 4489 registerTileIpc('tile:profiles:list', { mode: 'handle' }, (event, args: { 4495 4490 token: string; ··· 4679 4674 } 4680 4675 }); 4681 4676 4682 - // ── tile:adblocker:* (Phase 3.7f) ────────────────────────────────── 4677 + // ── tile:adblocker:* ──────────────────────────────────────────── 4683 4678 // 4684 - // Strict counterparts of the legacy `adblocker:*` channels in ipc.ts 4685 - // (deleted in Phase 3.7f). Settings + page widget consume via 4686 - // api.adblocker.*. trustedBuiltin only — global blocker state + 4687 - // per-site allowlist persisted to feature_settings. 4679 + // Settings + page widget consume via api.adblocker.*. trustedBuiltin 4680 + // only — global blocker state + per-site allowlist persisted to 4681 + // feature_settings. 4688 4682 // 4689 4683 // Per-site allowlist storage helpers — keep DRY across the four 4690 4684 // handlers that touch it. ··· 6892 6886 } 6893 6887 }); 6894 6888 6895 - // ── tile:extensions:* strict shims (Phase 3.5c) ──────────────────── 6889 + // ── tile:extensions:* strict shims ──────────────────────────────── 6896 6890 // 6897 6891 // Strict counterparts of the legacy `extension-*` channels in ipc.ts. 6898 6892 // All handlers require trustedBuiltin — feature tiles must not be able ··· 7145 7139 handleViolation(grant, 'extensions', 'tile:extensions:windowDevtools', 'trustedBuiltin required', args.token); 7146 7140 return { success: false, error: 'trustedBuiltin required for tile:extensions:windowDevtools' }; 7147 7141 } 7148 - // extensionWindows map was removed in Phase 3.2; v2 tiles have their own devtools path. 7149 - return { success: false, error: `Extension ${args.id} is not running as a legacy window` }; 7142 + // Tiles have their own devtools path; this handler exists only for 7143 + // backward shape compatibility and always returns failure. 7144 + return { success: false, error: `Extension ${args.id} is not running as a tile window` }; 7150 7145 }); 7151 7146 7152 7147 registerTileIpc('tile:extensions:reload', { mode: 'handle' }, async (event, args: { ··· 7174 7169 } 7175 7170 }); 7176 7171 7177 - // ── tile:chrome-extensions:* strict shims (Phase 3.5d) ────────────── 7172 + // ── tile:chrome-extensions:* strict shims ──────────────────────── 7178 7173 // 7179 7174 // Strict counterparts of the legacy `chrome-ext:*` channels in ipc.ts. 7180 7175 // All handlers require trustedBuiltin — feature tiles must not be able ··· 7353 7348 app.quit(); 7354 7349 }); 7355 7350 7356 - // ── tile:backup:* strict shims (Phase 3.11b) ───────────────────────── 7351 + // ── tile:backup:* strict shims ─────────────────────────────────────── 7357 7352 // 7358 - // Strict counterparts of the legacy `backup-*` channels in ipc.ts. 7359 - // All handlers require trustedBuiltin — settings/diagnostic/page-host 7360 - // tiles will consume these once they migrate off preload.js. 7353 + // Backup create/list/restore handlers. All require trustedBuiltin — 7354 + // settings/diagnostic/page-host tiles consume these. 7361 7355 7362 7356 registerTileIpc('tile:backup:create', { mode: 'handle' }, async (event, args: { 7363 7357 token: string; ··· 7417 7411 } 7418 7412 }); 7419 7413 7420 - // ── tile:shell:open-path strict shim (Phase 3.11b) ─────────────────── 7414 + // ── tile:shell:open-path strict shim ──────────────────────────────── 7421 7415 // 7422 7416 // Strict counterpart of the legacy `shell-open-path` channel. 7423 7417 // Requires trustedBuiltin — opens a path in the OS file manager. ··· 7505 7499 return getPrefs(); 7506 7500 }); 7507 7501 7508 - // ── tile:darkMode:* (Phase 3.7f) ───────────────────────────────────── 7502 + // ── tile:darkMode:* ────────────────────────────────────────────── 7509 7503 // 7510 - // Strict counterparts of the legacy `darkMode:get` / `darkMode:set` 7511 - // channels in ipc.ts (deleted in Phase 3.7f). Settings UI consumes 7512 - // these via api.darkMode.{get,set} in tile-preload. trustedBuiltin 7513 - // only — dark mode is global app state. 7504 + // Settings UI consumes these via api.darkMode.{get,set} in 7505 + // tile-preload. trustedBuiltin only — dark mode is global app state. 7514 7506 7515 7507 registerTileIpc('tile:darkMode:get', { mode: 'handle' }, (event, args: { 7516 7508 token: string; ··· 7562 7554 return { success: true, data: { mode, restartRequired: tier2Changed } }; 7563 7555 }); 7564 7556 7565 - // ── tile:nav:* strict shims (Phase 3.11b-nav-session) ──────────────── 7557 + // ── tile:nav:* strict shims ────────────────────────────────────── 7566 7558 // 7567 7559 // Strict counterparts of the legacy `web-nav-*` channels in ipc.ts. 7568 7560 // All require trustedBuiltin — only the core background renderer uses these. ··· 7639 7631 }; 7640 7632 }); 7641 7633 7642 - // ── tile:session:* strict shims (Phase 3.11b-nav-session) ───────────── 7634 + // ── tile:session:* strict shims ────────────────────────────────── 7643 7635 // 7644 7636 // Strict counterparts of the legacy `session-save`, 7645 7637 // `session-restore-interactive`, and `window-reopen-last-closed` channels.
+8 -10
backend/electron/tile-launcher.ts
··· 583 583 } 584 584 585 585 /** 586 - * Resolve a peek:// URL to v2 tile web preferences (preload + token). 586 + * Resolve a peek:// URL to tile web preferences (preload + token). 587 587 * 588 - * Used by the `window-open` IPC handler so that windows opened via 589 - * `api.window.open('peek://<tile>/<entry.url>')` get the strict tile 590 - * preload with a capability token, instead of the legacy `preload.js` 591 - * (which has the old v1 API surface and no `api.initialize()`). 588 + * Used by the window-open path so that windows opened via 589 + * `api.window.open('peek://<tile>/<entry.url>')` get tile-preload with 590 + * a capability token. 592 591 * 593 592 * Returns null when: 594 593 * - the URL isn't a `peek://` tile URL 595 - * - the feature isn't a registered v2 tile 594 + * - the feature isn't a registered tile 596 595 * - no tile entry matches the URL's path 597 - * In those cases the caller should fall back to the legacy preload. 596 + * In those cases the caller should open the window with no preload. 598 597 * 599 598 * The returned `additionalArguments` are formatted as Electron web-pref 600 599 * args: `--tile-id=X --tile-entry=Y --tile-token=Z`. tile-preload.ts ··· 672 671 * - Otherwise, fall back to the manifest's first tile entry — webview 673 672 * children inherit the parent tile's full capability grant 674 673 * 675 - * Returns null when the URL isn't peek:// or the tile-id isn't a v2 676 - * tile. Callers should fall back to the legacy preload in that case 677 - * (e.g. for v1 extensions like HUD). 674 + * Returns null when the URL isn't peek:// or the tile-id isn't a 675 + * registered tile. Callers should inject no preload in that case. 678 676 */ 679 677 export function getTileWebPreferencesForWebviewUrl( 680 678 url: string,
+7 -7
backend/electron/tile-lazy-events.test.ts
··· 121 121 122 122 it('registers lazy tile', () => { 123 123 const manifest = makeManifest('test-tile-1', ['test1:open']); 124 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 124 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 125 125 assert.ok(isLazyTileRegistered('test-tile-1')); 126 126 }); 127 127 128 128 it('loads tile when a declared lazyEvent topic is published', async () => { 129 129 const manifest = makeManifest('test-tile-2', ['test2:open']); 130 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 130 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 131 131 132 132 // On launch, simulate the tile registering its real handler and signaling ready. 133 133 launcherState.onLaunch = () => { ··· 152 152 153 153 it('the real handler receives the deferred message after load', async () => { 154 154 const manifest = makeManifest('test-tile-3', ['test3:open']); 155 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 155 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 156 156 157 157 const received: unknown[] = []; 158 158 launcherState.onLaunch = () => { ··· 174 174 175 175 it('subsequent events after load do not re-launch the tile', async () => { 176 176 const manifest = makeManifest('test-tile-4', ['test4:open']); 177 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 177 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 178 178 179 179 const received: unknown[] = []; 180 180 launcherState.onLaunch = () => { ··· 204 204 205 205 it('multiple lazyEvents on one tile share a single launch', async () => { 206 206 const manifest = makeManifest('test-tile-5', ['test5:open', 'test5:add']); 207 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 207 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 208 208 209 209 const received: Array<{ topic: string; msg: unknown }> = []; 210 210 launcherState.onLaunch = () => { ··· 239 239 240 240 it('concurrent fires during load: one launch, every message is delivered', async () => { 241 241 const manifest = makeManifest('test-tile-6', ['test6:open']); 242 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 242 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 243 243 244 244 // Defer ready so we can fire multiple publishes while still loading. 245 245 let readyTrigger: (() => void) | null = null; ··· 276 276 // Register a tile with a specific lazyEvent topic — an unrelated 277 277 // publish must not go through the dispatch hook (no launch, no defer). 278 278 const manifest = makeManifest('test-tile-7', ['test7:only-this-topic']); 279 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 279 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 280 280 281 281 const received: unknown[] = []; 282 282 subscribe('peek://unrelated', 'unrelated:topic', (msg) => {
+2 -2
backend/electron/tile-lazy-timeout.test.ts
··· 136 136 137 137 it('publishes a structured notification and resolves the resultTopic with error on load timeout', async () => { 138 138 const manifest = makeCommandManifest('tile-timeout-1', 'slow-cmd'); 139 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 139 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 140 140 141 141 // Collect whatever comes back on the resultTopic so we can assert 142 142 // the proxy's promise settles (rather than hanging 30s). ··· 201 201 202 202 it('rejects pending dispatches immediately when the tile transitions loading → crashed', async () => { 203 203 const manifest = makeCommandManifest('tile-crash-2', 'crashy-cmd'); 204 - registerLazyTile(manifest, '/fake/path', '/fake/preload.js'); 204 + registerLazyTile(manifest, '/fake/path', '/fake/tile-preload.cjs'); 205 205 206 206 const resultTopic = 'cmd:execute:crashy-cmd:result:unit-test-2'; 207 207 let resultPayload: unknown = null;
+46 -59
backend/electron/tile-preload.cts
··· 6 6 * token with the main process, and builds an API object with ONLY the 7 7 * granted capabilities. Injected via contextBridge.exposeInMainWorld('app', api). 8 8 * 9 - * NOTE: This file is written as a CommonJS module (like preload.js) because 10 - * Electron preload scripts must use require(), not import. 9 + * NOTE: This file is written as a CommonJS module because Electron preload 10 + * scripts must use require(), not import. 11 11 */ 12 12 13 13 const { contextBridge, ipcRenderer } = require('electron'); ··· 177 177 // ── Profiles (admin surface) ── 178 178 // 179 179 // Strict tile:profiles:* counterparts of the legacy `profiles:*` 180 - // handlers live in tile-ipc.ts (Phase 3.7f). trustedBuiltin only 180 + // handlers live in tile-ipc.ts. trustedBuiltin only 181 181 // — touches profiles.db, can relaunch the app, holds sync creds. 182 182 // Settings UI is the only real consumer. 183 183 api.profiles = { ··· 272 272 273 273 // ── Commands (if granted) ── 274 274 // 275 - // Supports two registration shapes to match existing feature code: 275 + // Supports two registration shapes: 276 276 // 277 277 // api.commands.register(name, handler) 278 - // Minimal v2 signature — just attaches a handler for cmd:execute:{name}. 278 + // Minimal signature — attaches a handler for cmd:execute:{name}. 279 279 // 280 280 // api.commands.register({name, description, execute, scope, modes, params, ...}) 281 - // v1-compatible signature. Re-publishes cmd:register so the cmd panel 282 - // picks it up (matches preload.js behaviour) and wires the execute 283 - // handler identically. 281 + // Descriptor signature. Re-publishes cmd:register so the cmd panel 282 + // picks it up and wires the execute handler identically. 284 283 // 285 284 // Both flow through the shared pubsub-based cmd:execute:{name} / :result 286 285 // protocol, so lazy-tile replay and the cmd panel proxy work unchanged. ··· 427 426 * Dual-path: 428 427 * - STRICT: when the tile declared the `commands` capability, 429 428 * route through the gated `tile:commands:unregister` channel. 430 - * - V1-COMPAT: fall back to a direct `publish(cmd:unregister)` 431 - * on GLOBAL scope — matches the v1 preload.js behaviour. 429 + * - FALLBACK: publish `cmd:unregister` on GLOBAL scope so tiles 430 + * without the capability can still unhook from the registry. 432 431 */ 433 432 unregister: (name: unknown) => { 434 433 if (typeof name !== 'string' || name.length === 0) { ··· 445 444 }); 446 445 return; 447 446 } 448 - // V1-compat path: publish cmd:unregister on GLOBAL scope. 447 + // Fallback path: publish cmd:unregister on GLOBAL scope. 449 448 if (tokenValid) { 450 449 ipcRenderer.send('tile:pubsub:publish', { 451 450 token: tileToken, ··· 457 456 }, 458 457 459 458 /** 460 - * List all registered commands. Mirrors the v1 preload.js behaviour 461 - * — publishes `cmd:query-commands` on GLOBAL scope, listens for the 462 - * `cmd:query-commands-response` reply. The cmd resident renderer 463 - * answers with the full registry. 459 + * List all registered commands. Publishes `cmd:query-commands` on 460 + * GLOBAL scope, listens for the `cmd:query-commands-response` reply. 461 + * The cmd resident renderer answers with the full registry. 464 462 * 465 463 * Resolves with `{ success: true, data: Array<{name, description, 466 464 * source, scope?, modes?}> }` on success, or ··· 1188 1186 1189 1187 // Theme directory management — Settings UI uses these to install 1190 1188 // user-supplied themes from disk. Strict tile:theme:* counterparts 1191 - // live in tile-ipc.ts (Phase 3.7f). trustedBuiltin only. 1189 + // live in tile-ipc.ts. trustedBuiltin only. 1192 1190 pickFolder: () => ipcRenderer.invoke('tile:theme:pickFolder', { token: tileToken }), 1193 1191 validateFolder: (folderPath: string) => ipcRenderer.invoke('tile:theme:validateFolder', { token: tileToken, folderPath }), 1194 1192 add: (folderPath: string) => ipcRenderer.invoke('tile:theme:add', { token: tileToken, folderPath }), ··· 1476 1474 1477 1475 // ── Convenience aliases ────────────────────────────────────────── 1478 1476 // These wrap the generic get/set/subscribe surface for the common 1479 - // 'mode' key, mirroring v1 preload.js api.context.setMode / getMode 1480 - // / watchMode / getWindowsInSpace. 1477 + // 'mode' key — api.context.setMode / getMode / watchMode / 1478 + // getWindowsInSpace. 1481 1479 1482 1480 setMode: (mode: unknown, options?: { metadata?: Record<string, unknown>; windowId?: number | null }) => { 1483 1481 const metadata = options?.metadata; ··· 1775 1773 return ipcRenderer.invoke('tile:sync:sync-all', { token: tileToken }); 1776 1774 }, 1777 1775 // Strict tile:sync:* counterparts of the legacy `sync-*` handlers 1778 - // live in tile-ipc.ts (Phase 3.7f). trustedBuiltin only — sync 1776 + // live in tile-ipc.ts. trustedBuiltin only — sync 1779 1777 // touches network, server creds, and profile state. 1780 1778 getConfig: () => ipcRenderer.invoke('tile:sync:get-config', { token: tileToken }), 1781 1779 setConfig: (config: unknown) => ipcRenderer.invoke('tile:sync:set-config', { token: tileToken, config }), ··· 1787 1785 // ── Ad blocker ──────────────────────────────────────────────────── 1788 1786 // 1789 1787 // Strict tile:adblocker:* counterparts of the legacy `adblocker:*` 1790 - // handlers live in tile-ipc.ts (Phase 3.7f). trustedBuiltin only — 1788 + // handlers live in tile-ipc.ts. trustedBuiltin only — 1791 1789 // global blocker state + per-site allowlist persisted to feature_settings. 1792 1790 api.adblocker = { 1793 1791 getStatus: () => ipcRenderer.invoke('tile:adblocker:getStatus', { token: tileToken }), ··· 1858 1856 // to tell the backend "I handled the ESC, don't close the window", 1859 1857 // or `{ handled: false }` (or undefined) to let the backend decide. 1860 1858 // 1861 - // Implementation: the existing `escape-pressed` IPC is shared with 1862 - // the v1 preload (windows.ts dispatches the same channel per-window 1863 - // regardless of preload variant). We listen to it here and invoke 1864 - // the tile's callback, replying on the provided responseChannel. 1859 + // Implementation: windows.ts dispatches `escape-pressed` per-window. 1860 + // We listen to it here and invoke the tile's callback, replying on 1861 + // the provided responseChannel. 1865 1862 let tileEscapeCallback: (() => unknown) | null = null; 1866 1863 let tileEscapeInstalled = false; 1867 1864 api.escape = { ··· 1933 1930 // renderers: cmd, hud, page, _test). Feature tiles never see this 1934 1931 // method — their api.invoke is a no-op that returns an error object. 1935 1932 // 1936 - // The v1 preload.js exposed `api.invoke` gated on `isCore` (i.e. 1937 - // `sourceAddress.startsWith('peek://app/')`). The test fixture and 1938 - // core diagnostics rely on it to talk to main-process handlers that 1939 - // don't (yet) have a tile-surfaced wrapper (backup-*, get-display- 1940 - // info, extension-*). Under v2 tile mechanics, trustedBuiltin is the 1941 - // renderer-level equivalent of v1's isCore. 1933 + // The test fixture and core diagnostics rely on it to talk to 1934 + // main-process handlers that don't (yet) have a tile-surfaced wrapper. 1942 1935 // 1943 1936 // Two behaviours are possible: 1944 1937 // - trustedBuiltin grant present → forward raw 1945 1938 // - no trustedBuiltin → return failure 1946 1939 // The boolean is populated by the `tile:validate-token` IPC response. 1947 - // Validation is now kicked off at module load time; callers no longer 1948 - // need to call api.initialize() before using api.invoke. 1940 + // Validation is kicked off at module load time; callers don't need to 1941 + // call api.initialize() before using api.invoke. 1949 1942 api.invoke = async (channel: unknown, data?: unknown) => { 1950 1943 const _valid = await validationPromise; 1951 1944 if (!_valid) { ··· 1969 1962 return ipcRenderer.invoke(channel, data); 1970 1963 }; 1971 1964 1972 - // ── Extensions management (Phase 3.6c) ──────────────────────────────── 1965 + // ── Extensions management ──────────────────────────────────────────── 1973 1966 // 1974 1967 // All methods now route through tile:extensions:* strict channels in 1975 1968 // tile-ipc.ts (trustedBuiltin enforcement + capability-token validation). ··· 2054 2047 2055 2048 // ── App control (trustedBuiltin only) ──────────────────────────────── 2056 2049 // 2057 - // v1 preload exposed `api.quit` / `api.restart` (fire-and-forget IPC 2058 - // sends). The core background renderer (app/index.js) registers 2059 - // `quit` / `restart` cmd palette commands that invoke these. The 2060 - // channels are `ipcMain.on` (not .handle), so `api.invoke` cannot 2061 - // replace them — a dedicated `.send` wrapper is required. 2050 + // `api.quit` / `api.restart` are fire-and-forget IPC sends. The core 2051 + // background renderer (app/index.js) registers `quit` / `restart` cmd 2052 + // palette commands that invoke these. The channels are `ipcMain.on` 2053 + // (not .handle), so `api.invoke` cannot replace them — a dedicated 2054 + // `.send` wrapper is required. 2062 2055 api.quit = () => { 2063 2056 if (!trustedBuiltin) { 2064 2057 console.warn('[tile-preload] api.quit requires trustedBuiltin — ignored'); ··· 2132 2125 2133 2126 // ── Chrome Extensions (trustedBuiltin only) ────────────────────────── 2134 2127 // 2135 - // v1 preload exposed `api.chromeExtensions.*` for the core background 2136 - // renderer to enumerate bundled chrome extensions + their UI entry 2137 - // points (popup, options, new-tab, etc.) and register cmd palette 2138 - // commands for each openable entry. Gated on trustedBuiltin — feature 2139 - // tiles must not enumerate or launch chrome extensions. 2128 + // `api.chromeExtensions.*` lets the core background renderer enumerate 2129 + // bundled chrome extensions + their UI entry points (popup, options, 2130 + // new-tab, etc.) and register cmd palette commands for each openable 2131 + // entry. Gated on trustedBuiltin — feature tiles must not enumerate or 2132 + // launch chrome extensions. 2140 2133 // 2141 - // Phase 3.5d: strict shims added as tile:chrome-extensions:* channels in 2142 - // tile-ipc.ts (trustedBuiltin enforcement + capability-token validation). 2143 - // The legacy chrome-ext:* invocations below are preserved; Wave 3.6d 2144 - // will flip these to tile:chrome-extensions:* and delete the legacy handlers. 2134 + // Routes through tile:chrome-extensions:* channels in tile-ipc.ts 2135 + // (trustedBuiltin enforcement + capability-token validation). 2145 2136 api.chromeExtensions = { 2146 2137 list: () => { 2147 2138 if (!trustedBuiltin) { ··· 2183 2174 2184 2175 // ── Backup (trustedBuiltin only) ───────────────────────────────────── 2185 2176 // 2186 - // Strict counterparts of the legacy `backup-*` channels. Consumed by the 2187 - // settings tile once it migrates off preload.js (Phase 3.11b). 2177 + // Backup create/list/restore for the settings tile. 2188 2178 api.backup = { 2189 2179 create: () => { 2190 2180 if (!trustedBuiltin) { ··· 2246 2236 2247 2237 // ── Nav (trustedBuiltin only) ───────────────────────────────────────── 2248 2238 // 2249 - // Strict counterparts of the legacy `web-nav-*` channels. Consumed by the 2250 - // core background renderer (app/index.js) for the back/forward/reload/copy-url 2251 - // cmd palette commands (Phase 3.11b-nav-session). 2239 + // Web nav handlers consumed by the core background renderer 2240 + // (app/index.js) for the back/forward/reload/copy-url cmd palette 2241 + // commands. 2252 2242 api.nav = { 2253 2243 back: (windowId?: number) => { 2254 2244 if (!trustedBuiltin) { ··· 2278 2268 2279 2269 // ── Session (trustedBuiltin only) ───────────────────────────────────── 2280 2270 // 2281 - // Strict counterparts of the legacy `session-save`, 2282 - // `session-restore-interactive`, and `window-reopen-last-closed` channels. 2283 - // Consumed by the core background renderer (app/index.js) 2284 - // (Phase 3.11b-nav-session). 2271 + // Session save / restore-interactive / reopen-last-closed handlers, 2272 + // consumed by the core background renderer (app/index.js). 2285 2273 api.session = { 2286 2274 save: () => { 2287 2275 if (!trustedBuiltin) { ··· 2321 2309 // hold-drag system that pipes through the webview console-message 2322 2310 // bridge). 2323 2311 // 2324 - // Ported from the deleted v1 preload.js (Phase 3.11b-hud removal lost 2325 - // it). Settings, cmd panel, hud overlay, and other pseudo-tile windows 2326 - // all relied on this for window movement; without it, drag was dead. 2312 + // Settings, cmd panel, hud overlay, and other pseudo-tile windows all 2313 + // rely on this for window movement. 2327 2314 { 2328 2315 const MOVE_THRESHOLD = 3; // px of movement after hold to engage drag 2329 2316 const JITTER_TOLERANCE = 3; // px allowed during hold without cancelling
+5 -9
backend/electron/tile-sync-enforcement.ts
··· 9 9 * these checks can be unit-tested without pulling in `tile-ipc`'s 10 10 * Electron dependencies. 11 11 * 12 - * Background: the existing `sync-full` / `sync-push` / `sync-pull` IPC 13 - * channels are only reachable from the v1 preload (the main desktop 14 - * `preload.js`), not from the tile preload. The sync feature currently 15 - * calls `api.sync.syncAll()` — provided for v1 but missing from the 16 - * tile-preload surface. The strict handler provides the same syncAll 17 - * entry point gated on a declared `sync` capability. Sync is already 18 - * tagged as a `BUILTIN_ONLY_CAPABILITIES` member in 19 - * `tile-manifest.ts`, so `resolveCapabilities` will deny it for 20 - * external tiles automatically. 12 + * The sync feature calls `api.sync.syncAll()` from tile-preload; the 13 + * strict handler routes that to the syncAll implementation gated on a 14 + * declared `sync` capability. Sync is tagged as a 15 + * `BUILTIN_ONLY_CAPABILITIES` member in `tile-manifest.ts`, so 16 + * `resolveCapabilities` denies it for external tiles automatically. 21 17 */ 22 18 23 19 import type { CapabilityGrant } from './tile-manifest.js';
+1 -1
features/hello-world/home.html
··· 167 167 log('initialize THREW: ' + (err.message || err), 'fail'); 168 168 } 169 169 } else { 170 - log('skipping — no api.initialize (legacy preload)', 'warn'); 170 + log('skipping — no api.initialize', 'warn'); 171 171 } 172 172 173 173 // ── Register command ──
+1 -1
features/theme-shichijuni/variables.css
··· 4 4 * Colors change every ~5 days following the traditional Japanese calendar, 5 5 * using colors from the 伝統色 (dentōshoku) palette. 6 6 * 7 - * The preload.js sets data-microseason="0"–"71" on <html> based on the current date. 7 + * The host sets data-microseason="0"–"71" on <html> based on the current date. 8 8 * Each micro-season has a unique primary accent; supporting accents shift 9 9 * gradually through 6 seasonal arcs. 10 10 *