experiments in a post-browser web
at main 213 lines 5.2 kB view raw
1/** 2 * Help Docs Extension Background Script 3 * 4 * Spawns a fullscreen transparent overlay window with animated 5 * documentation hints that appear from screen edges. 6 * 7 * Runs in isolated extension process (peek://ext/helpdocs/background.html) 8 */ 9 10const api = window.app; 11const debug = api.debug; 12 13console.log('[ext:helpdocs] background init'); 14 15const OVERLAY_ADDRESS = 'peek://ext/helpdocs/overlay.html'; 16const STORAGE_KEY = 'helpdocs_enabled'; 17 18let enabled = false; 19let overlayWindowId = null; 20let appFocused = true; 21 22/** 23 * Load enabled state 24 */ 25const loadState = async () => { 26 try { 27 const stored = localStorage.getItem(STORAGE_KEY); 28 enabled = stored === 'true'; // Default disabled 29 console.log('[ext:helpdocs] Loaded state from localStorage - enabled:', enabled); 30 } catch (err) { 31 console.log('[ext:helpdocs] Failed to load state from localStorage:', err); 32 } 33 return enabled; 34}; 35 36/** 37 * Save enabled state 38 */ 39const saveState = (value) => { 40 enabled = value; 41 try { 42 localStorage.setItem(STORAGE_KEY, String(value)); 43 console.log('[ext:helpdocs] Saved state to localStorage - enabled:', value); 44 } catch (err) { 45 console.error('[ext:helpdocs] Failed to save state to localStorage:', err); 46 } 47}; 48 49/** 50 * Open the overlay window (fullscreen, transparent, click-through) 51 */ 52const openOverlay = async () => { 53 if (overlayWindowId) { 54 const exists = await api.window.exists(overlayWindowId); 55 if (exists.success && exists.data) { 56 await api.window.show(overlayWindowId); 57 return; 58 } 59 overlayWindowId = null; 60 } 61 62 const screenW = window.screen.width; 63 const screenH = window.screen.height; 64 65 const params = { 66 key: OVERLAY_ADDRESS, 67 width: screenW, 68 height: screenH, 69 x: 0, 70 y: 0, 71 transparent: true, 72 alwaysOnTop: true, 73 skipTaskbar: true, 74 focusable: false, 75 resizable: false, 76 frame: false, 77 hasShadow: false, 78 escapeMode: 'ignore', 79 title: 'Help Docs', 80 centerOnParent: false, 81 show: true 82 }; 83 84 try { 85 const result = await api.window.open(OVERLAY_ADDRESS, params); 86 if (result.success) { 87 overlayWindowId = result.id; 88 console.log('[ext:helpdocs] Overlay opened:', overlayWindowId); 89 90 // Make the window click-through but still receive mouse move events 91 // forward: true means mouse moves are still forwarded to the renderer 92 setTimeout(async () => { 93 try { 94 await api.window.setIgnoreMouseEvents(overlayWindowId, true, true); 95 console.log('[ext:helpdocs] Set click-through with forward'); 96 } catch (err) { 97 console.error('[ext:helpdocs] Failed to set click-through:', err); 98 } 99 }, 500); 100 } else { 101 console.error('[ext:helpdocs] Failed to open overlay:', result.error); 102 } 103 } catch (error) { 104 console.error('[ext:helpdocs] Error opening overlay:', error); 105 } 106}; 107 108const showOverlay = async () => { 109 if (overlayWindowId) { 110 const exists = await api.window.exists(overlayWindowId); 111 if (exists.success && exists.data) { 112 await api.window.show(overlayWindowId); 113 } 114 } 115}; 116 117const hideOverlay = async () => { 118 if (overlayWindowId) { 119 const exists = await api.window.exists(overlayWindowId); 120 if (exists.success && exists.data) { 121 await api.window.hide(overlayWindowId); 122 } 123 } 124}; 125 126const closeOverlay = async () => { 127 if (overlayWindowId) { 128 await api.window.close(overlayWindowId); 129 overlayWindowId = null; 130 console.log('[ext:helpdocs] Overlay closed'); 131 } 132}; 133 134const toggle = async () => { 135 const newState = !enabled; 136 saveState(newState); 137 if (newState) { 138 await openOverlay(); 139 return { output: 'Help docs enabled', mimeType: 'text/plain' }; 140 } else { 141 await closeOverlay(); 142 return { output: 'Help docs disabled', mimeType: 'text/plain' }; 143 } 144}; 145 146// Commands 147const commandDefinitions = [ 148 { 149 name: 'help docs', 150 description: 'Toggle help documentation overlay', 151 execute: async () => { 152 console.log('[ext:helpdocs] Toggle command executed'); 153 return await toggle(); 154 } 155 } 156]; 157 158let registeredCommands = []; 159 160const initCommands = () => { 161 commandDefinitions.forEach(cmd => { 162 api.commands.register(cmd); 163 registeredCommands.push(cmd.name); 164 }); 165 console.log('[ext:helpdocs] Registered commands:', registeredCommands); 166}; 167 168const uninitCommands = () => { 169 registeredCommands.forEach(name => { 170 api.commands.unregister(name); 171 }); 172 registeredCommands = []; 173}; 174 175const init = async () => { 176 console.log('[ext:helpdocs] init'); 177 178 await loadState(); 179 initCommands(); 180 181 // Track app focus to show/hide overlay 182 api.subscribe('app:focus-changed', async (msg) => { 183 appFocused = !!msg.focused; 184 if (enabled && overlayWindowId) { 185 if (appFocused) { 186 await showOverlay(); 187 } else { 188 await hideOverlay(); 189 } 190 } 191 }, api.scopes.GLOBAL); 192 193 if (enabled) { 194 await openOverlay(); 195 } 196 197 console.log('[ext:helpdocs] Initialized, enabled:', enabled); 198}; 199 200const uninit = () => { 201 console.log('[ext:helpdocs] uninit'); 202 uninitCommands(); 203 closeOverlay(); 204}; 205 206export default { 207 id: 'helpdocs', 208 labels: { 209 name: 'Help docs' 210 }, 211 init, 212 uninit 213};