experiments in a post-browser web
10
fork

Configure Feed

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

notes

+109
+49
notes/datastore-architecture.md
··· 565 565 // IPC handler updated, but api.datastore.addAddress() unchanged 566 566 ``` 567 567 568 + ## URL Normalization 569 + 570 + URLs are normalized before storage to prevent duplicate entries for equivalent URLs. 571 + 572 + ### Normalization Rules 573 + 574 + 1. **Trailing slash on root paths**: URLs without a path get a trailing slash 575 + - `https://example.com` → `https://example.com/` 576 + - `http://localhost` → `http://localhost/` 577 + 578 + 2. **Paths with content are unchanged**: 579 + - `https://example.com/page` stays as-is 580 + - `https://example.com/page/` stays as-is 581 + 582 + ### Implementation 583 + 584 + Normalization is applied in two places: 585 + 586 + 1. **Main process** (`index.js`): `normalizeUrl()` in `datastore-add-address` handler 587 + 2. **Renderer** (`app/datastore/history.js`): `normalizeUrl()` in `trackNavigation()` 588 + 589 + ```javascript 590 + const normalizeUrl = (uri) => { 591 + try { 592 + const url = new URL(uri); 593 + if (!url.pathname || url.pathname === '') { 594 + url.pathname = '/'; 595 + } 596 + return url.toString(); 597 + } catch (error) { 598 + return uri; 599 + } 600 + }; 601 + ``` 602 + 603 + ### Migration 604 + 605 + Existing databases may have duplicate entries due to pre-normalization data. Run the migration script to merge duplicates: 606 + 607 + ```bash 608 + node scripts/migrate-normalize-urls.mjs 609 + ``` 610 + 611 + The migration: 612 + - Finds addresses with the same normalized URL 613 + - Keeps the one with tags (if any), otherwise the normalized one, otherwise most visits 614 + - Merges visit counts and updates all visit references 615 + - Removes duplicates 616 + 568 617 ## Benefits Realized 569 618 570 619 1. **Clean Separation**: Storage logic completely isolated from UI code
+60
notes/extensibility.md
··· 34 34 - Hotkey registration 35 35 - Pubsub messaging 36 36 37 + ## Command API 38 + 39 + Extensions can register commands that appear in the cmd palette. 40 + 41 + ### Registration 42 + 43 + ```javascript 44 + // Register a command 45 + api.commands.register({ 46 + name: 'my command', // Searchable name (required) 47 + description: 'Description', // Shown in palette (optional) 48 + execute: async (ctx) => { // Handler function (required) 49 + // ctx contains: typed, name, params, search 50 + console.log('Command executed:', ctx); 51 + } 52 + }); 53 + 54 + // Unregister when extension unloads 55 + api.commands.unregister('my command'); 56 + ``` 57 + 58 + ### Context Object 59 + 60 + The `execute` function receives a context object: 61 + 62 + ```javascript 63 + { 64 + typed: 'my command foo bar', // Full typed string 65 + name: 'my command', // Matched command name 66 + params: ['foo', 'bar'], // Parameters after command 67 + search: 'foo bar' // Text after command (for search-style commands) 68 + } 69 + ``` 70 + 71 + ### Implementation Details 72 + 73 + - Commands are registered via pubsub with GLOBAL scope (cross-window) 74 + - Execute handlers are stored locally (functions can't cross IPC) 75 + - The cmd background process maintains a registry of registered commands 76 + - The cmd panel queries the registry when opened 77 + - Execution requests are published back to the registering extension 78 + 79 + ### Example: Groups Extension 80 + 81 + ```javascript 82 + const init = () => { 83 + api.commands.register({ 84 + name: 'groups', 85 + description: 'Open the groups manager', 86 + execute: async (ctx) => { 87 + api.window.open('peek://ext/groups/home.html', { ... }); 88 + } 89 + }); 90 + }; 91 + 92 + const uninit = () => { 93 + api.commands.unregister('groups'); 94 + }; 95 + ``` 96 + 37 97 Dev workflow: 38 98 39 99 - User can open/close devtools for a given extension (via a cmd)