experiments in a post-browser web
10
fork

Configure Feed

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

refactor(extensions): create sync extension with sync command

Move sync command from cmd/commands/sync.js to its own extension at
extensions/sync/. This follows the same pattern as the groups extension,
using the cmd:ready subscription pattern to register commands.

+182 -7
+5 -7
extensions/cmd/commands/index.js
··· 1 1 /** 2 2 * Commands module - exports all available commands 3 3 * Note: groups commands are now provided by the groups extension 4 + * Note: sync command is now provided by the sync extension 4 5 */ 5 6 import openCommand from './open.js'; 6 7 import debugCommand from './debug.js'; ··· 10 11 import urlModule from './url.js'; 11 12 import historyModule from './history.js'; 12 13 import tagModule from './tag.js'; 13 - import syncCommand from './sync.js'; 14 14 15 15 // Chaining commands - for command composition pipelines 16 16 import listsCommand from './lists.js'; 17 - import csvCommand from './csv.js'; 18 - import saveCommand from './save.js'; 17 + // csv and save commands moved to files extension 19 18 20 19 console.log('[cmd:commands/index] tagModule.commands:', tagModule.commands?.map(c => c.name)); 21 20 ··· 33 32 34 33 // Active commands - only these will be loaded 35 34 // Note: groups commands are dynamically registered by the groups extension 35 + // Note: sync command is dynamically registered by the sync extension 36 36 const activeCommands = [ 37 37 openCommand, 38 38 debugCommand, 39 39 modalCommand, 40 - syncCommand, 41 40 ...noteModule.commands, 42 41 ...tagsetModule.commands, 43 42 ...urlModule.commands, ··· 45 44 ...tagModule.commands, 46 45 47 46 // Chaining commands 48 - listsCommand, 49 - csvCommand, 50 - saveCommand 47 + listsCommand 48 + // csv and save commands moved to files extension 51 49 ]; 52 50 53 51 console.log('[cmd:commands/index] activeCommands:', activeCommands.map(c => c.name));
+50
extensions/sync/background.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <meta charset="utf-8"> 5 + <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"> 6 + <title>Sync Extension</title> 7 + </head> 8 + <body> 9 + <script type="module"> 10 + import extension from './background.js'; 11 + 12 + const api = window.app; 13 + const extId = extension.id; 14 + 15 + console.log(`[ext:${extId}] background.html loaded`); 16 + 17 + // Signal ready to main process 18 + api.publish('ext:ready', { 19 + id: extId, 20 + manifest: { 21 + id: extension.id, 22 + labels: extension.labels, 23 + version: '1.0.0' 24 + } 25 + }, api.scopes.SYSTEM); 26 + 27 + // Initialize extension 28 + if (extension.init) { 29 + console.log(`[ext:${extId}] calling init()`); 30 + extension.init(); 31 + } 32 + 33 + // Handle shutdown request from main process 34 + api.subscribe('app:shutdown', () => { 35 + console.log(`[ext:${extId}] received shutdown`); 36 + if (extension.uninit) { 37 + extension.uninit(); 38 + } 39 + }, api.scopes.SYSTEM); 40 + 41 + // Handle extension-specific shutdown 42 + api.subscribe(`ext:${extId}:shutdown`, () => { 43 + console.log(`[ext:${extId}] received extension shutdown`); 44 + if (extension.uninit) { 45 + extension.uninit(); 46 + } 47 + }, api.scopes.SYSTEM); 48 + </script> 49 + </body> 50 + </html>
+119
extensions/sync/background.js
··· 1 + /** 2 + * Sync Extension Background Script 3 + * 4 + * Provides sync commands and functionality 5 + * 6 + * Runs in isolated extension process (peek://ext/sync/background.html) 7 + */ 8 + 9 + const api = window.app; 10 + 11 + const id = 'sync'; 12 + const labels = { 13 + name: 'Sync', 14 + description: 'Sync commands and functionality' 15 + }; 16 + 17 + console.log('[ext:sync] background', labels.name); 18 + 19 + // ===== Command definitions ===== 20 + 21 + const commandDefinitions = [ 22 + { 23 + name: 'Sync now', 24 + description: 'Trigger manual sync with server', 25 + execute: async () => { 26 + console.log('[sync] Triggering manual sync...'); 27 + 28 + // Check if sync API is available 29 + if (!api?.sync?.syncAll) { 30 + console.error('[sync] Sync API not available'); 31 + return { 32 + success: false, 33 + error: 'Sync API not available' 34 + }; 35 + } 36 + 37 + try { 38 + const result = await api.sync.syncAll(); 39 + 40 + if (result.success) { 41 + const { pulled, pushed, conflicts } = result.data || {}; 42 + console.log('[sync] Sync completed:', result.data); 43 + 44 + let message = 'Sync completed'; 45 + const details = []; 46 + if (pulled) details.push(`${pulled} pulled`); 47 + if (pushed) details.push(`${pushed} pushed`); 48 + if (conflicts) details.push(`${conflicts} conflicts`); 49 + if (details.length) message += `: ${details.join(', ')}`; 50 + 51 + return { 52 + success: true, 53 + command: 'sync', 54 + message, 55 + data: result.data 56 + }; 57 + } else { 58 + console.error('[sync] Sync failed:', result.error); 59 + return { 60 + success: false, 61 + command: 'sync', 62 + error: result.error || 'Sync failed' 63 + }; 64 + } 65 + } catch (err) { 66 + console.error('[sync] Error during sync:', err); 67 + return { 68 + success: false, 69 + command: 'sync', 70 + error: err.message 71 + }; 72 + } 73 + } 74 + } 75 + ]; 76 + 77 + // ===== Registration ===== 78 + 79 + let registeredCommands = []; 80 + 81 + const initCommands = () => { 82 + commandDefinitions.forEach(cmd => { 83 + api.commands.register(cmd); 84 + registeredCommands.push(cmd.name); 85 + }); 86 + console.log('[ext:sync] Registered commands:', registeredCommands); 87 + }; 88 + 89 + const uninitCommands = () => { 90 + registeredCommands.forEach(name => { 91 + api.commands.unregister(name); 92 + }); 93 + registeredCommands = []; 94 + console.log('[ext:sync] Unregistered commands'); 95 + }; 96 + 97 + const init = () => { 98 + console.log('[ext:sync] init'); 99 + 100 + // Wait for cmd:ready before registering commands 101 + api.subscribe('cmd:ready', () => { 102 + initCommands(); 103 + }, api.scopes.GLOBAL); 104 + 105 + // Query in case cmd is already ready (it usually is since cmd loads first) 106 + api.publish('cmd:query', {}, api.scopes.GLOBAL); 107 + }; 108 + 109 + const uninit = () => { 110 + console.log('[ext:sync] uninit'); 111 + uninitCommands(); 112 + }; 113 + 114 + export default { 115 + id, 116 + init, 117 + uninit, 118 + labels 119 + };
+8
extensions/sync/manifest.json
··· 1 + { 2 + "id": "sync", 3 + "name": "Sync", 4 + "version": "1.0.0", 5 + "description": "Sync commands and functionality", 6 + "background": "background.html", 7 + "builtin": true 8 + }