experiments in a post-browser web
10
fork

Configure Feed

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

fix tag command, frecency scoring

+68 -34
+3 -1
app/cmd/commands.js
··· 19 19 */ 20 20 function onCommandsUpdated() { 21 21 window.dispatchEvent(new CustomEvent('cmd-update-commands', { detail: commands })); 22 - console.log('main sending updated commands out', Object.keys(commands)); 22 + console.log('cmd-update-commands dispatched, commands:', Object.keys(commands)); 23 23 } 24 24 25 25 /** ··· 39 39 40 40 // Load commands from the commands module 41 41 const moduleCommands = commandsModule.commands; 42 + console.log('moduleCommands:', moduleCommands.map(c => c.name)); 42 43 moduleCommands.forEach(command => { 44 + console.log('adding command:', command.name); 43 45 addCommand(command); 44 46 }); 45 47
+4
app/cmd/commands/index.js
··· 9 9 import historyModule from './history.js'; 10 10 import tagModule from './tag.js'; 11 11 12 + console.log('tagModule.commands:', tagModule.commands?.map(c => c.name)); 13 + 12 14 // Source commands (commented out as they need browser extension APIs) 13 15 // These modules contain command sources that dynamically generate commands 14 16 import bookmarkletsSource from './bookmarklets.js'; ··· 31 33 ...historyModule.commands, 32 34 ...tagModule.commands 33 35 ]; 36 + 37 + console.log('activeCommands:', activeCommands.map(c => c.name)); 34 38 35 39 // Inactive commands - these require browser extension APIs and are not loaded 36 40 const inactiveCommands = [
+6 -2
app/cmd/commands/tag.js
··· 109 109 description: 'Add tags to the active window URL', 110 110 async execute(ctx) { 111 111 // Get active window 112 + api.log('tag command execute, ctx:', ctx); 112 113 const activeWindow = await getActiveWindow(); 114 + api.log('tag command: activeWindow =', activeWindow); 113 115 if (!activeWindow) { 114 - console.log('No active window found'); 116 + api.log('No active window found'); 115 117 return { success: false, error: 'No active window' }; 116 118 } 117 119 118 120 const url = activeWindow.url; 119 - console.log('Tagging URL:', url); 121 + api.log('Tagging URL:', url); 120 122 121 123 // Find address in datastore 122 124 let address = await findAddressByUri(url); ··· 164 166 } 165 167 return { success: true, removed }; 166 168 } else { 169 + api.log('Adding tags to address:', address.id, 'tags:', tagsToProcess); 167 170 const results = await addTagsToAddress(address.id, tagsToProcess); 171 + api.log('addTagsToAddress results:', results); 168 172 const added = results.filter(r => !r.alreadyExists).map(r => r.tag.name); 169 173 const existing = results.filter(r => r.alreadyExists).map(r => r.tag.name); 170 174 if (added.length > 0) {
+19 -22
app/cmd/panel.js
··· 9 9 const clear = false; 10 10 11 11 const store = openStore(id, defaults, clear /* clear storage */); 12 + 12 13 const api = window.app; 13 14 14 15 // Storage keys for persistent adaptive matching ··· 60 61 commandInput.addEventListener('input', () => { 61 62 state.typed = commandInput.value; 62 63 if (state.typed) { 63 - // Special case: if input contains a space, display matches but highlight the prefix 64 - const spaceIndex = state.typed.indexOf(' '); 65 - if (spaceIndex !== -1) { 66 - const prefix = state.typed.substring(0, spaceIndex); 67 - const temp = findMatchingCommands(prefix); 68 - if (temp.length > 0) { 69 - state.matches = temp; 70 - state.matchIndex = 0; 71 - } else { 72 - state.matches = findMatchingCommands(state.typed); 73 - state.matchIndex = 0; 74 - } 75 - } else { 76 - // Regular case: update matches based on typed text 77 - state.matches = findMatchingCommands(state.typed); 78 - state.matchIndex = 0; 79 - } 64 + // Always pass full text to findMatchingCommands so it can detect parameters 65 + state.matches = findMatchingCommands(state.typed); 66 + state.matchIndex = 0; 80 67 } else { 81 68 state.matches = []; 82 69 state.matchIndex = 0; ··· 238 225 * Executes a command 239 226 */ 240 227 async function execute(name, typed) { 228 + api.log('execute() called with:', name, typed); 241 229 if (state.commands[name]) { 242 - debug && console.log('executing cmd', name, typed); 230 + api.log('executing cmd', name, typed); 243 231 const context = buildExecutionContext(name, typed); 244 232 debug && console.log('execution context', context); 245 233 state.commands[name].execute(context); ··· 289 277 } 290 278 } 291 279 292 - // Sort by adaptive score first, then by match count (frecency) 293 - // Adaptive score takes priority - commands you've selected for this input pattern 294 - // will float to the top based on reinforcement learning 280 + // Sort by: 281 + // 1. Exact match with parameters (highest priority) 282 + // 2. Adaptive score 283 + // 3. Match count (frecency) 295 284 matches.sort(function(a, b) { 296 - // First compare adaptive scores for this typed string 285 + // If we have parameters, prioritize exact command match 286 + if (hasParameters) { 287 + const aExact = a.toLowerCase() === commandPart.toLowerCase(); 288 + const bExact = b.toLowerCase() === commandPart.toLowerCase(); 289 + if (aExact && !bExact) return -1; 290 + if (bExact && !aExact) return 1; 291 + } 292 + 293 + // Then compare adaptive scores for this typed string 297 294 const aAdaptive = getAdaptiveScore(commandPart, a); 298 295 const bAdaptive = getAdaptiveScore(commandPart, b); 299 296
+29 -8
index.js
··· 784 784 785 785 // ***** API ***** 786 786 787 + // Renderer log forwarding - prints renderer console.log to terminal 788 + ipcMain.on('renderer-log', (ev, msg) => { 789 + const shortSource = msg.source.replace('peek://app/', ''); 790 + console.log(`[${shortSource}]`, ...msg.args); 791 + }); 792 + 787 793 ipcMain.on(strings.msgs.registerShortcut, (ev, msg) => { 788 794 console.log('ipc register shortcut', msg); 789 795 ··· 1491 1497 ipcMain.handle('datastore-get-or-create-tag', async (ev, data) => { 1492 1498 try { 1493 1499 const { name } = data; 1500 + console.log('datastore-get-or-create-tag:', name); 1494 1501 const slug = name.toLowerCase().trim().replace(/\s+/g, '-'); 1495 1502 const timestamp = now(); 1496 1503 ··· 1508 1515 } 1509 1516 1510 1517 if (existingTag) { 1518 + console.log(' -> found existing tag:', existingTagId); 1511 1519 return { success: true, data: { id: existingTagId, ...existingTag }, created: false }; 1512 1520 } 1513 1521 ··· 1528 1536 }; 1529 1537 1530 1538 datastoreStore.setRow('tags', tagId, newTag); 1539 + console.log(' -> created new tag:', tagId); 1531 1540 return { success: true, data: { id: tagId, ...newTag }, created: true }; 1532 1541 } catch (error) { 1533 1542 console.error('datastore-get-or-create-tag error:', error); ··· 1539 1548 ipcMain.handle('datastore-tag-address', async (ev, data) => { 1540 1549 try { 1541 1550 const { addressId, tagId } = data; 1551 + console.log('datastore-tag-address:', { addressId, tagId }); 1542 1552 const timestamp = now(); 1543 1553 1544 1554 // Check if link already exists ··· 1605 1615 try { 1606 1616 const { domain } = data || {}; 1607 1617 const tagsTable = datastoreStore.getTable('tags'); 1618 + console.log('datastore-get-tags-by-frecency: tagsTable has', Object.keys(tagsTable).length, 'tags'); 1608 1619 let tags = Object.entries(tagsTable).map(([id, tag]) => ({ id, ...tag })); 1609 1620 1610 1621 // Recalculate frecency scores (they decay over time) ··· 1800 1811 const winDevtoolsConfig = bw => { 1801 1812 const windowData = windowManager.getWindow(bw.id); 1802 1813 const params = windowData ? windowData.params : {}; 1803 - 1814 + 1815 + console.log('winDevtoolsConfig:', bw.id, 'openDevTools:', params.openDevTools, 'address:', params.address); 1816 + 1804 1817 // Check if devTools should be opened 1805 1818 if (params.openDevTools === true) { 1806 1819 const isDetached = params.detachedDevTools === true; ··· 1812 1825 }; 1813 1826 1814 1827 console.log(`Opening DevTools for window ${bw.id} with options:`, devToolsOptions); 1815 - bw.webContents.openDevTools(devToolsOptions); 1816 1828 1817 - // when devtools completely open, ensure content window has focus 1818 - bw.webContents.once('devtools-opened', () => { 1819 - if (bw.isVisible()) { 1820 - bw.focus(); 1821 - } 1822 - }); 1829 + // Open DevTools after a slight delay to let the main window settle 1830 + setTimeout(() => { 1831 + bw.webContents.openDevTools(devToolsOptions); 1832 + 1833 + // when devtools completely open, ensure content window has focus 1834 + bw.webContents.once('devtools-opened', () => { 1835 + // Re-focus the content window after devtools opens 1836 + setTimeout(() => { 1837 + if (bw.isVisible() && !bw.isDestroyed()) { 1838 + bw.focus(); 1839 + bw.webContents.focus(); 1840 + } 1841 + }, 100); 1842 + }); 1843 + }, 50); 1823 1844 } 1824 1845 }; 1825 1846
+7 -1
preload.js
··· 6 6 const src = 'preload'; 7 7 console.log(src, 'init', window); 8 8 9 - const DEBUG = process.env.DEBUG || false; 9 + const DEBUG = !!process.env.DEBUG; 10 + console.log('preload DEBUG:', process.env.DEBUG, '->', DEBUG); 10 11 const DEBUG_LEVELS = { 11 12 BASIC: 1, 12 13 FIRST_RUN: 2 ··· 23 24 const rndm = () => Math.random().toString(16).slice(2); 24 25 25 26 let api = {}; 27 + 28 + // Log to main process (shows in terminal) 29 + api.log = (...args) => { 30 + ipcRenderer.send('renderer-log', { source: sourceAddress, args }); 31 + }; 26 32 27 33 api.debug = DEBUG; 28 34 api.debugLevels = DEBUG_LEVELS;