experiments in a post-browser web
10
fork

Configure Feed

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

fix(pagestream): use plain j/k for nav via pubsub, fix page flash

Replace Cmd+J/K global shortcuts (which require modifiers) with pubsub-
based key forwarding. Page host's document keydown listener forwards
j/k/g/G/arrows to pagestream:nav when webview is not focused.

Fix page flash on navigation by keeping webview visible (.ready class)
during subsequent loads instead of resetting opacity each time.

+28 -34
+22 -4
app/page/page.js
··· 858 858 } 859 859 }, api.scopes.GLOBAL); 860 860 861 + // Forward card-navigation keys to pagestream (only fires when webview is NOT focused, 862 + // e.g., after pressing Escape which blurs to the page chrome level) 863 + document.addEventListener('keydown', (e) => { 864 + // Don't intercept if typing in navbar 865 + if (document.activeElement === navbar || navbar.contains(document.activeElement)) return; 866 + 867 + const NAV_MAP = { 868 + 'j': 'down', 'ArrowDown': 'down', 869 + 'k': 'up', 'ArrowUp': 'up', 870 + 'g': 'first', 'Home': 'first', 871 + 'G': 'last', 'End': 'last', 872 + }; 873 + 874 + const action = NAV_MAP[e.key]; 875 + if (action) { 876 + e.preventDefault(); 877 + api.publish('pagestream:nav', { action }, api.scopes.GLOBAL); 878 + } 879 + }); 880 + 861 881 // --- Nav button actions and URL navigation are handled by peek-navbar component --- 862 882 // Event listeners wired up in the "peek-navbar component event wiring" section above. 863 883 // On navigate or dismiss (Escape), also hide the navbar (page-specific behavior). ··· 978 998 webview.classList.add('ready'); 979 999 }); 980 1000 981 - // Reset visibility on new navigations so fade-in works each time 982 - webview.addEventListener('did-start-loading', () => { 983 - webview.classList.remove('ready'); 984 - }); 1001 + // Note: we do NOT remove .ready on did-start-loading — keeping the webview 1002 + // visible during navigation prevents flash when flipping between pages. 985 1003 986 1004 // --- OpenSearch discovery & page:loaded event --- 987 1005
+6 -30
extensions/pagestream/home.js
··· 378 378 }); 379 379 if (result && result.id) { 380 380 state.openWindowId = result.id; 381 - registerGlobalNav(); 382 381 } 383 382 } catch (err) { 384 383 console.error('[pagestream] Failed to open page:', err); ··· 393 392 }; 394 393 395 394 // ===== Global navigation while page host is open ===== 396 - 397 - let globalNavRegistered = false; 398 395 399 396 /** Move card selection and optionally navigate the open page host */ 400 397 const moveSelection = (action) => { ··· 436 433 } 437 434 }; 438 435 439 - const GLOBAL_NAV_SHORTCUTS = [ 440 - ['CommandOrControl+Down', () => moveSelection('down')], 441 - ['CommandOrControl+Up', () => moveSelection('up')], 442 - ['CommandOrControl+J', () => moveSelection('down')], 443 - ['CommandOrControl+K', () => moveSelection('up')], 444 - ['CommandOrControl+Shift+G', () => moveSelection('first')], 445 - ['CommandOrControl+Shift+End', () => moveSelection('last')], 446 - ]; 447 - 448 - const registerGlobalNav = () => { 449 - if (globalNavRegistered) return; 450 - for (const [key, fn] of GLOBAL_NAV_SHORTCUTS) { 451 - api.shortcuts.register(key, fn, { global: true }); 452 - } 453 - globalNavRegistered = true; 454 - debug && console.log('[pagestream] Global nav shortcuts registered'); 455 - }; 456 - 457 - const unregisterGlobalNav = () => { 458 - if (!globalNavRegistered) return; 459 - for (const [key] of GLOBAL_NAV_SHORTCUTS) { 460 - api.shortcuts.unregister(key, { global: true }); 461 - } 462 - globalNavRegistered = false; 463 - debug && console.log('[pagestream] Global nav shortcuts unregistered'); 464 - }; 436 + // Listen for navigation keys forwarded from page host via pubsub 437 + // (subscription lives for the lifetime of pagestream — moveSelection 438 + // is safe to call any time, it just no-ops if nothing is open) 439 + api.subscribe('pagestream:nav', (msg) => { 440 + if (msg.action) moveSelection(msg.action); 441 + }, api.scopes.GLOBAL); 465 442 466 443 const onPageHostClosed = async (closedWindowId) => { 467 444 if (closedWindowId !== state.openWindowId) return; 468 - unregisterGlobalNav(); 469 445 state.openWindowId = null; 470 446 471 447 const cards = getCards();