experiments in a post-browser web
10
fork

Configure Feed

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

fix settings display of extensions, add example extension

+153 -5
+7 -4
app/settings/settings.js
··· 641 641 // Initial load (may be incomplete if extensions still loading) 642 642 await refreshExtensionsList(); 643 643 644 - // Reactively update when extensions finish loading 645 - api.subscribe('ext:all-loaded', () => { 646 - refreshExtensionsList(); 647 - }, api.scopes.GLOBAL); 644 + // Store refresh function for external access (ext:all-loaded is subscribed once in main init) 645 + window._refreshExtensionsList = refreshExtensionsList; 648 646 649 647 return container; 650 648 }; ··· 1072 1070 await loadExtensionSettings(); 1073 1071 1074 1072 // Listen for all extensions loaded event to catch any we missed 1073 + // NOTE: Only ONE ext:all-loaded subscription per source - pubsub overwrites duplicates 1075 1074 api.subscribe('ext:all-loaded', () => { 1076 1075 loadExtensionSettings(); 1076 + // Also refresh the Extensions list if it's been rendered 1077 + if (window._refreshExtensionsList) { 1078 + window._refreshExtensionsList(); 1079 + } 1077 1080 }, api.scopes.GLOBAL); 1078 1081 1079 1082 // Add Datastore link
+49
extensions/example/background.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>Example Extension</title> 6 + </head> 7 + <body> 8 + <script type="module"> 9 + import extension from './background.js'; 10 + 11 + const api = window.app; 12 + const extId = extension.id; 13 + 14 + console.log(`[ext:${extId}] background.html loaded`); 15 + 16 + // Signal ready to main process 17 + api.publish('ext:ready', { 18 + id: extId, 19 + manifest: { 20 + id: extension.id, 21 + labels: extension.labels, 22 + version: '1.0.0' 23 + } 24 + }, api.scopes.SYSTEM); 25 + 26 + // Initialize extension 27 + if (extension.init) { 28 + console.log(`[ext:${extId}] calling init()`); 29 + extension.init(); 30 + } 31 + 32 + // Handle shutdown request from main process 33 + api.subscribe('app:shutdown', () => { 34 + console.log(`[ext:${extId}] received shutdown`); 35 + if (extension.uninit) { 36 + extension.uninit(); 37 + } 38 + }, api.scopes.SYSTEM); 39 + 40 + // Handle extension-specific shutdown 41 + api.subscribe(`ext:${extId}:shutdown`, () => { 42 + console.log(`[ext:${extId}] received extension shutdown`); 43 + if (extension.uninit) { 44 + extension.uninit(); 45 + } 46 + }, api.scopes.SYSTEM); 47 + </script> 48 + </body> 49 + </html>
+35
extensions/example/background.js
··· 1 + /** 2 + * Example Extension - Hello World 3 + */ 4 + 5 + const api = window.app; 6 + 7 + const extension = { 8 + id: 'example', 9 + labels: { 10 + name: 'Example' 11 + }, 12 + 13 + init() { 14 + console.log('[example] init'); 15 + 16 + // Register a command 17 + api.commands.register({ 18 + name: 'hello', 19 + description: 'Say hello', 20 + execute: () => { 21 + console.log('[example] Hello from command!'); 22 + alert('Hello World!'); 23 + } 24 + }); 25 + 26 + console.log('[example] Extension loaded'); 27 + }, 28 + 29 + uninit() { 30 + console.log('[example] Goodbye!'); 31 + api.commands.unregister('hello'); 32 + } 33 + }; 34 + 35 + export default extension;
+9
extensions/example/manifest.json
··· 1 + { 2 + "id": "example", 3 + "shortname": "example", 4 + "name": "Example", 5 + "description": "A hello world example extension", 6 + "version": "1.0.0", 7 + "background": "background.html", 8 + "settingsSchema": "./settings-schema.json" 9 + }
+20
extensions/example/settings-schema.json
··· 1 + { 2 + "prefs": { 3 + "type": "object", 4 + "properties": { 5 + "greeting": { 6 + "type": "string", 7 + "description": "Custom greeting message", 8 + "default": "Hello World" 9 + } 10 + } 11 + }, 12 + "storageKeys": { 13 + "PREFS": "prefs" 14 + }, 15 + "defaults": { 16 + "prefs": { 17 + "greeting": "Hello World" 18 + } 19 + } 20 + }
+33 -1
index.js
··· 700 700 } 701 701 } 702 702 703 + // Load external extensions from datastore 704 + if (datastoreStore) { 705 + const extensionsTable = datastoreStore.getTable('extensions') || {}; 706 + for (const [extId, extData] of Object.entries(extensionsTable)) { 707 + // Skip if already loaded (shouldn't happen but be safe) 708 + if (extensionWindows.has(extId)) continue; 709 + 710 + // Skip if not enabled 711 + if (extData.enabled !== 1) { 712 + console.log(`[ext:win] Skipping disabled external extension: ${extId}`); 713 + continue; 714 + } 715 + 716 + // Need a path to load from 717 + if (!extData.path) { 718 + console.log(`[ext:win] Skipping external extension without path: ${extId}`); 719 + continue; 720 + } 721 + 722 + console.log(`[ext:win] Loading enabled external extension: ${extId}`); 723 + await createExtensionWindow(extId); 724 + } 725 + } 726 + 703 727 console.log(`[ext:win] Loaded ${extensionWindows.size} extensions`); 704 728 705 729 // Signal that all extensions are loaded (GLOBAL so Settings can receive it) ··· 822 846 registerExtensionPath('groups', path.join(__dirname, 'extensions', 'groups')); 823 847 registerExtensionPath('peeks', path.join(__dirname, 'extensions', 'peeks')); 824 848 registerExtensionPath('slides', path.join(__dirname, 'extensions', 'slides')); 849 + registerExtensionPath('example', path.join(__dirname, 'extensions', 'example')); 825 850 826 851 // Register as default handler for http/https URLs (if not already and user hasn't declined) 827 852 const defaultBrowserPrefFile = path.join(profileDataPath, 'default-browser-pref.json'); ··· 1274 1299 1275 1300 // Create new window 1276 1301 const win = new BrowserWindow(winOptions); 1302 + 1303 + // Forward console logs from window to main process stdout (for debugging) 1304 + win.webContents.on('console-message', (event, level, message, line, sourceId) => { 1305 + // Only forward for peek:// URLs to avoid noise 1306 + if (url.startsWith('peek://')) { 1307 + console.log(`[${url.replace('peek://', '')}] ${message}`); 1308 + } 1309 + }); 1277 1310 1278 1311 try { 1279 1312 await win.loadURL(url); ··· 2399 2432 ipcMain.handle('extension-window-list', async (ev) => { 2400 2433 try { 2401 2434 const running = getRunningExtensions(); 2402 - console.log('extension-window-list:', running.map(e => e.id)); 2403 2435 return { success: true, data: running }; 2404 2436 } catch (error) { 2405 2437 console.error('extension-window-list error:', error);