experiments in a post-browser web
10
fork

Configure Feed

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

fixed esc, updated window apis, and more. generally working

+284 -288
+28 -4
app/cmd/index.js
··· 1 1 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 2 - import { openStore, openWindow } from "../utils.js"; 2 + import { openStore } from "../utils.js"; 3 + import windows from "../windows.js"; 3 4 import api from '../api.js'; 4 5 5 6 console.log('index', labels.name); ··· 17 18 18 19 const params = { 19 20 debug, 20 - address, 21 21 key: address, 22 22 height, 23 - width 23 + width, 24 + // Using modal parameter so it hides on escape/blur 25 + modal: true, 26 + 27 + // Remove titlebar and make window frameless 28 + frame: false, 29 + titleBarStyle: 'hidden', 30 + transparent: true, 31 + 32 + // Make sure the window stays on top 33 + alwaysOnTop: true, 34 + 35 + // Center the window 36 + center: true, 37 + 38 + // Set a reasonable minimum size 39 + minWidth: 400, 40 + minHeight: 30 24 41 }; 25 42 26 - openWindow(address, params); 43 + // Use the modal window API to open the window 44 + windows.openModalWindow(address, params) 45 + .then(result => { 46 + console.log('Command window opened:', result); 47 + }) 48 + .catch(error => { 49 + console.error('Failed to open command window:', error); 50 + }); 27 51 }; 28 52 29 53 const initShortcut = (prefs) => {
+49 -1
app/cmd/panel.css
··· 1 1 2 + /* Styling for the frameless command window */ 2 3 body { 3 4 margin: 0; 4 - padding: 0; 5 + padding: 10px; 6 + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif; 7 + background-color: rgba(40, 44, 52, 0.9); 8 + color: white; 9 + border-radius: 8px; 10 + overflow: hidden; 11 + /* Allow dragging by entire window */ 12 + -webkit-app-region: drag; 13 + /* Prevent text selection when dragging */ 14 + user-select: none; 15 + } 16 + 17 + /* Allow interaction with input fields */ 18 + input, button, select, textarea, [contenteditable] { 19 + -webkit-app-region: no-drag; 20 + } 21 + 22 + /* Style for the command input */ 23 + #command-input { 24 + width: 100%; 25 + background: transparent; 26 + color: white; 27 + border: none; 28 + outline: none; 29 + font-size: 16px; 30 + padding: 5px; 31 + } 32 + 33 + /* Style for the command results */ 34 + #results { 35 + margin-top: 10px; 36 + max-height: 400px; 37 + overflow-y: auto; 38 + } 39 + 40 + /* Style for any command items */ 41 + .command-item { 42 + padding: 5px; 43 + cursor: pointer; 44 + border-radius: 4px; 45 + } 46 + 47 + .command-item:hover { 48 + background-color: rgba(255, 255, 255, 0.1); 49 + } 50 + 51 + .command-item.selected { 52 + background-color: rgba(255, 255, 255, 0.2); 5 53 }
+6 -3
app/cmd/panel.html
··· 7 7 <meta http-equiv="Content-Security-Policy" content="script-src 'self';"> 8 8 <meta name="description" content=""> 9 9 <meta name="viewport" content="width=device-width, initial-scale=1"> 10 - <style> 11 - </style> 12 10 <link rel="stylesheet" href="panel.css"> 13 11 </head> 14 12 <body> 13 + <div class="command-container"> 14 + <input id="command-input" type="text" autofocus placeholder="Type a command..." /> 15 + <div id="results"></div> 16 + </div> 17 + 15 18 <script type=module src="../utils.js"></script> 16 19 <script type=module src="./config.js"></script> 17 20 <script type=module src="./panel.js"></script> 18 21 <script type=module src="./commands.js"></script> 19 22 </body> 20 - </html> 23 + </html>
+96 -190
app/cmd/panel.js
··· 68 68 }); 69 69 70 70 async function render() { 71 - // Outer container 72 - let panel = document.createElement('div'); 73 - panel.id = 'cmdPanel'; 74 - panel.classList.add('cmdPanel'); 75 - 76 - await css(panel, { 77 - //border: '1px solid black', 78 - display: 'flex', 79 - flexDirection: 'column', 80 - justifyContent: 'center', 81 - margin: '0', 82 - padding: '0', 83 - height: '3rem', 84 - width: '20rem', 85 - paddingLeft: '1rem', 86 - paddingRight: '1rem', 71 + // Get the command input element and results container 72 + const commandInput = document.getElementById('command-input'); 73 + const resultsContainer = document.getElementById('results'); 74 + 75 + // Set placeholder and focus the input 76 + commandInput.placeholder = 'Start typing...'; 77 + commandInput.focus(); 78 + 79 + // Add event listeners to the input 80 + commandInput.addEventListener('keyup', onKeyup); 81 + commandInput.addEventListener('keydown', (e) => { 82 + // Allow arrows, tab, escape 83 + if (!['ArrowUp', 'ArrowDown', 'Tab', 'Escape', 'Enter'].includes(e.key)) { 84 + return; // Don't prevent default for normal typing 85 + } 86 + e.preventDefault(); // Prevent default for special keys 87 87 }); 88 - 89 - // Where text is shown 90 - let input = document.createElement('div'); 91 - input.id = 'cmdInput'; 92 - 93 - await css(input, { 94 - //border: '1px solid black', 95 - //overflow: 'hidden', 96 - whiteSpace: 'nowrap', 97 - fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif', 98 - fontSize: 'large' 88 + 89 + // Make sure the input stays focused 90 + window.addEventListener('blur', () => { 91 + setTimeout(() => commandInput.focus(), 10); 99 92 }); 100 - 101 - panel.appendChild(input); 102 - 103 - document.body.appendChild(panel); 104 - 105 - updateInputUI('Start typing...') 106 - 107 - // add event listeners 108 - document.addEventListener('keyup', onKeyup, true); 109 - document.addEventListener('keypress', onKeyDummyStop, true); 110 - document.addEventListener('keydown', onKeyDummyStop, true); 111 - document.addEventListener('input', onKeyDummyStop, true); 93 + 94 + window.addEventListener('focus', () => { 95 + commandInput.focus(); 96 + }); 97 + 98 + // Automatically focus the input when the window loads 99 + setTimeout(() => { 100 + commandInput.focus(); 101 + }, 100); 112 102 } 113 103 114 104 render(); ··· 201 191 async function onKeyup(e) { 202 192 // flag for logging 203 193 const r = true; 204 - 205 - e.preventDefault(); 194 + 195 + // Get the command input element and results container 196 + const commandInput = document.getElementById('command-input'); 197 + const resultsContainer = document.getElementById('results'); 198 + 199 + // Use the input value as the typed text 200 + state.typed = commandInput.value; 206 201 207 202 if (isModifier(e)) { 208 203 return; 209 204 } 210 205 211 - r || console.log('onKeyup', e.key, e.which) 212 - r || console.log('hasModifier', hasModifier(e), 'isModifier', isModifier(e), 'isIgnorable', isIgnorable(e)); 213 - 214 - e.preventDefault(); 215 - 216 206 // if user pressed escape, go away 217 207 if (e.key == 'Escape' && !hasModifier(e)) { 218 - r || console.log('onKeyUp: escape!'); 219 208 await shutdown(); 209 + return; 220 210 } 221 211 222 212 // if user pressed return, attempt to execute command 223 - else if (e.key == 'Enter' && !hasModifier(e)) { 224 - r || console.log('onKeyUp: enter!', state.typed); 213 + if (e.key == 'Enter' && !hasModifier(e)) { 225 214 let name = state.matches[state.matchIndex]; 226 - if (Object.keys(state.commands).indexOf(name) > -1) { 227 - //await shutdown(); 215 + if (name && Object.keys(state.commands).indexOf(name) > -1) { 228 216 execute(name, state.typed); 229 217 state.lastExecuted = name; 230 218 updateMatchCount(name); 231 219 updateMatchFeedback(state.typed, name); 220 + commandInput.value = ''; 232 221 state.typed = ''; 222 + resultsContainer.innerHTML = ''; 233 223 } 224 + return; 234 225 } 235 226 236 - // attempt to complete typed characters to a command 237 - // or do other modifications based on user typed keys 238 - else if (!hasModifier(e) && !isModifier(e) && !isIgnorable(e)) { 239 - r || console.log('LEGIT... no modifier, is not a modifier and not ignorable') 240 - 241 - // correct on backspace 242 - if (e.key == 'Backspace') { 243 - r || console.log('back', state.typed); 244 - if (state.typed.length > 0) { 245 - r || console.log('back, no typed tho'); 246 - state.typed = state.typed.substring(0, state.typed.length - 1); 247 - } 248 - } 249 - // otherwise add typed character to buffer 250 - else { 251 - r || console.log('updating', e.key); 252 - state.typed += e.key 253 - } 254 - 255 - // search, and update UI 256 - state.matches = findMatchingCommands(state.typed); 257 - if (state.matches.length) { 258 - r || console.log('matches!', state.matches); 259 - updateInputUI(state.typed, state.matches[0]); 260 - state.matchIndex = 0; 261 - } 262 - else { 263 - r || console.log('no matches for ', state.typed); 264 - updateInputUI(state.typed); 265 - } 227 + // Handle up/down arrows for navigation 228 + if (e.key == 'ArrowUp' && state.matchIndex > 0) { 229 + state.matchIndex--; 230 + updateResultsUI(); 231 + return; 266 232 } 267 233 268 - // if up arrow and currently visible command is not first, select one previous 269 - else if (e.key == 'ArrowUp' && state.matchIndex) { 270 - r || console.log('onKeyUp: arrow up!'); 271 - updateInputUI(state.typed, state.matches[--state.matchIndex]); 234 + if (e.key == 'ArrowDown' && state.matchIndex + 1 < state.matches.length) { 235 + state.matchIndex++; 236 + updateResultsUI(); 237 + return; 272 238 } 273 239 274 - // if down arrow and there are more matches, select the next one 275 - else if (e.key == 'ArrowDown' && state.matchIndex + 1 < state.matches.length) { 276 - r || console.log('onKeyUp: arrow down!'); 277 - updateInputUI(state.typed, state.matches[++state.matchIndex]); 240 + // Handle tab for autocompletion 241 + if (e.key == 'Tab' && state.matches && state.matches.length > 0) { 242 + commandInput.value = state.matches[state.matchIndex]; 243 + state.typed = state.matches[state.matchIndex]; 244 + return; 278 245 } 279 246 280 - // Old behavior on tab: 281 - // tab -> shift to next result 282 - // shift + tab -> shift to previous result 283 - // New behavior on tab: 284 - // autocomplete to the matched command 285 - // which allows easy adding onto a command name 286 - // without having to type all the same visible text 287 - else if (e.key == 'Tab' && state.matches) { 288 - r || console.log('onKeyUp: tab!'); 289 - state.typed = state.matches[state.matchIndex] 290 - updateInputUI(state.typed, state.matches[state.matchIndex]); 291 - //if (e.shiftKey && matchIndex) 292 - // updateInputUI(state.typed, state.matches[--state.matchIndex]); 293 - //else if (state.matchIndex + 1 < state.matches.length) 294 - // updateInputUI(state.typed, state.matches[++state.matchIndex]); 247 + // Update matches based on typed text 248 + state.matches = findMatchingCommands(state.typed); 249 + state.matchIndex = 0; 250 + 251 + // Update the results UI 252 + updateResultsUI(); 253 + } 254 + 255 + function updateResultsUI() { 256 + const resultsContainer = document.getElementById('results'); 257 + resultsContainer.innerHTML = ''; 258 + 259 + if (state.matches.length === 0) { 260 + return; 295 261 } 262 + 263 + // Create and append result items 264 + state.matches.forEach((match, index) => { 265 + const item = document.createElement('div'); 266 + item.className = 'command-item'; 267 + if (index === state.matchIndex) { 268 + item.classList.add('selected'); 269 + } 270 + item.textContent = match; 271 + 272 + // Add click handler 273 + item.addEventListener('click', () => { 274 + state.matchIndex = index; 275 + execute(match, state.typed); 276 + state.lastExecuted = match; 277 + updateMatchCount(match); 278 + updateMatchFeedback(state.typed, match); 279 + document.getElementById('command-input').value = ''; 280 + state.typed = ''; 281 + resultsContainer.innerHTML = ''; 282 + }); 283 + 284 + resultsContainer.appendChild(item); 285 + }); 296 286 } 297 287 298 288 function hasModifier(e) { ··· 333 323 } 334 324 } 335 325 336 - function updateInputUI(typed, completed) { 337 - const r = true; 338 - r || console.log('updateInputUI', typed, completed); 339 - let str = '' 340 - if (completed) { 341 - str = generateUnderlined(typed, completed); 342 - } 343 - // no match 344 - else if (typed) { 345 - str = typed; 346 - } 347 - 348 - let parser = new DOMParser(); 349 - let doc = parser.parseFromString(str, 'text/html'); 350 - 351 - let input = document.querySelector('#cmdInput'); 352 - if (input && input.firstElementChild) 353 - input.removeChild(input.firstElementChild); 354 - input.appendChild(doc.firstElementChild); 355 - 356 - /* 357 - let parent = input.parentNode; 358 - state.matches.forEach(match => { 359 - let node = document.createElement('div') 360 - node.innerText = match 361 - parent.appendChild(node) 362 - }); 363 - */ 364 - } 365 - 366 - // typed text, inline matching suggestion 367 - function generateUnderlined(typed, match) { 368 - const r = 1; 369 - r || console.log('generateUnderlined', typed, match); 370 - // user already matched a commmand and added params 371 - /* 372 - if (match.length > typed.length && 373 - match.indexOf(typed) === 0) { 374 - return match; 375 - } 376 - */ 377 - 378 - if (typed.length == 0 || match.length == 0) 379 - return typed; 380 - 381 - // look for typed within match 382 - var startIndex = match.toLowerCase().indexOf(typed.toLowerCase()); 383 - if (startIndex == -1) { 384 - // otherwise look for match in typed 385 - // (why would this happen?!) 386 - startIndex = match.toLowerCase().indexOf(typed.toLowerCase()); 387 - if (startIndex == -1) { 388 - return typed; 389 - } 390 - } 391 - 392 - var endIndex = startIndex + match.length; 393 - r || console.log('startIndex', startIndex, 'endIndex', endIndex); 394 - var str = '' 395 - 396 - // substring is empty 397 - if (!match) { 398 - r || console.log('no suggestion, so no underline') 399 - str = '<span>' + typed + '</span>' 400 - } 401 - // occurs at beginning 402 - else if (startIndex === 0) { 403 - r || console.log('start'); 404 - str = '<span style="text-decoration: underline;">' + typed + '</span>' + 405 - '<span style="color: #6E6E6E;">' + match.substring(typed.length) + '</span>'; 406 - } 407 - // occurs in middle 408 - else if (startIndex > 0) { 409 - r || console.log('middle'); 410 - str = "<span style='color: #6E6E6E;'>" + match.substring(0, startIndex) + "</span>" + 411 - "<span style='text-decoration: underline;'>" + match.substring(startIndex, startIndex + typed.length) + "</span>" + 412 - "<span style='color: #6E6E6E;'>" + match.substring(startIndex + typed.length) + "</span>"; 413 - } 414 - // occurs at the end 415 - else { 416 - r || console.log('end'); 417 - str = "<span class='completed'>" + typed.substring(0, startIndex) + "</span>" + 418 - "<span class='typed'>" + match + "</span>"; 419 - } 420 - return str; 421 - } 326 + // These functions are replaced by the new updateResultsUI function that 327 + // works with the actual HTML input field instead of custom rendering
+13 -4
app/groups/index.js
··· 1 1 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 2 - import { openStore, openWindow } from "../utils.js"; 2 + import { openStore } from "../utils.js"; 3 + import windows from "../windows.js"; 3 4 import api from '../api.js'; 4 5 5 6 console.log('background', labels.name); ··· 16 17 const width = 800; 17 18 18 19 const params = { 19 - address, 20 20 key: address, 21 21 height, 22 - width 22 + width, 23 + // Not using modal so window stays open when clicking elsewhere 24 + modal: false 23 25 }; 24 26 25 - openWindow(address, params); 27 + // Use the window creation API 28 + windows.createWindow(address, params) 29 + .then(window => { 30 + console.log('Groups window opened:', window); 31 + }) 32 + .catch(error => { 33 + console.error('Failed to open groups window:', error); 34 + }); 26 35 }; 27 36 28 37 const initShortcut = shortcut => {
+43 -28
app/index.js
··· 1 1 import appConfig from './config.js'; 2 - import { openStore, openWindow } from "./utils.js"; 2 + import { openStore } from "./utils.js"; 3 + import windowManager from "./windows.js"; // Renamed to avoid naming collision 3 4 import api from './api.js'; 4 5 import fc from './features.js'; 5 6 ··· 21 22 22 23 let _settingsWin = null; 23 24 24 - const openSettingsWindow = (prefs) => { 25 + const openSettingsWindow = async (prefs) => { 25 26 console.log('openSettingsWindow()'); 26 27 27 - /* 28 - // TODO: fuck, have to call main process to do this 29 - if (_settingsWin) { 30 - console.log('win exists, focusing'); 31 - _settingsWin.focus(); 32 - console.log('focused'); 33 - return; 34 - } 35 - */ 36 - 37 28 // Get screen dimensions from window object 38 29 const screenWidth = window.screen.availWidth; 39 30 const screenHeight = window.screen.availHeight; ··· 46 37 47 38 const params = { 48 39 debug, 49 - address: settingsAddress, 50 40 key: settingsAddress, 51 41 transparent: true, 52 42 height, 53 - width 43 + width, 44 + // Settings window should stay open when clicking elsewhere, so not modal 45 + modal: false 54 46 }; 55 47 56 - console.log('opening settings window', params); 57 - _settingsWin = openWindow(settingsAddress, params); 58 - console.log('opened settings window', _settingsWin); 48 + console.log('Opening settings window with params:', params); 49 + 50 + try { 51 + // Use the window creation API from windows.js 52 + const windowController = await windowManager.createWindow(settingsAddress, params); 53 + 54 + console.log('Settings window opened successfully with controller:', windowController); 55 + _settingsWin = windowController; 56 + 57 + // Focus the window to bring it to front 58 + await windowController.focus(); 59 + } catch (error) { 60 + console.error('Failed to open settings window:', error); 61 + } 59 62 }; 60 63 61 64 const initSettingsShortcut = (prefs) => { ··· 110 113 const prefs = () => store.get(storageKeys.PREFS); 111 114 const features = () => store.get(storageKeys.ITEMS); 112 115 113 - const init = () => { 116 + const init = async () => { 114 117 console.log('init'); 115 118 116 119 const p = prefs(); ··· 127 130 api.subscribe('open', msg => { 128 131 // eg from the tray icon. 129 132 if (msg.address && msg.address == settingsAddress) { 130 - openSettingsWindow(p); 133 + openSettingsWindow(p).catch(err => { 134 + console.error('Error opening settings window from open event:', err); 135 + }); 131 136 } 132 137 }); 133 138 139 + // Open settings window on startup if configured 134 140 if (p.startupFeature == settingsAddress) { 135 - openSettingsWindow(p); 141 + try { 142 + await openSettingsWindow(p); 143 + } catch (error) { 144 + console.error('Error opening startup settings window:', error); 145 + } 136 146 } 137 147 138 148 // feature enable/disable ··· 163 173 //features.forEach(initIframeFeature); 164 174 165 175 /* 176 + // Example of using the new windows.js API: 166 177 const addy = 'http://localhost'; 167 178 const params = { 168 179 debug, 169 - address: addy, 170 180 key: addy, 171 181 height: 300, 172 182 width: 300 173 183 }; 174 184 175 - const w = openWindow(addy, params); 176 - 177 - api.subscribe('onWindowOpened', msg => { 178 - api.modifyWindow(params.key, { 179 - hide: true 185 + windowManager.createWindow(addy, params) 186 + .then(windowController => { 187 + // Can use windowController to interact with the window 188 + windowController.hide(); 189 + }) 190 + .catch(error => { 191 + console.error('Error opening example window:', error); 180 192 }); 181 - }); 182 193 */ 183 194 }; 184 195 185 - window.addEventListener('load', init); 196 + window.addEventListener('load', () => { 197 + init().catch(error => { 198 + console.error('Error during application initialization:', error); 199 + }); 200 + }); 186 201 187 202 /* 188 203 const odiff = (a, b) => Object.entries(b).reduce((c, [k, v]) => Object.assign(c, a[k] ? {} : { [k]: v }), {});
+18 -5
app/peeks/index.js
··· 1 1 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 2 - import { openStore, openWindow } from "../utils.js"; 2 + import { openStore } from "../utils.js"; 3 + import windows from "../windows.js"; 3 4 import api from '../api.js'; 4 5 5 6 console.log('background', labels.name); ··· 10 11 const store = openStore(id, defaults, clear /* clear storage */); 11 12 12 13 const executeItem = (item) => { 13 - console.log('executeItem:slide', item); 14 + console.log('executeItem:peek', item); 14 15 const height = item.height || 600; 15 16 const width = item.width || 800; 16 17 17 18 const params = { 18 19 // browserwindow 19 - address: item.address, 20 20 height, 21 21 width, 22 22 23 23 // peek 24 24 feature: labels.name, 25 25 keepLive: item.keepLive || false, 26 - persistState: item.persistState || false 26 + persistState: item.persistState || false, 27 + 28 + // Create a unique key for this peek using its address 29 + key: `peek:${item.address}`, 30 + 31 + // Use modal behavior (closes on escape/blur) 32 + modal: true 27 33 }; 28 34 29 - openWindow(item.address, params); 35 + // Use the modal window API for peeks 36 + windows.openModalWindow(item.address, params) 37 + .then(result => { 38 + console.log('Peek window opened:', result); 39 + }) 40 + .catch(error => { 41 + console.error('Failed to open peek window:', error); 42 + }); 30 43 }; 31 44 32 45 const initItems = (prefs, items) => {
+20 -4
app/scripts/index.js
··· 1 1 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 2 - import { openStore, openWindow } from "../utils.js"; 2 + import { openStore } from "../utils.js"; 3 + import windows from "../windows.js"; 3 4 import api from '../api.js'; 4 5 5 6 console.log('background', labels.name); ··· 21 22 `; 22 23 23 24 const params = { 24 - address: script.address, 25 25 show: false, 26 26 script: { 27 27 script: str, 28 28 domEvent: 'dom-ready', 29 29 closeOnCompletion: true, 30 - } 30 + }, 31 + // Make script windows hidden and auto-close 32 + modal: false 31 33 }; 32 34 33 - openWindow(script.address, params); 35 + // For script windows, we use createWindow for more control 36 + windows.createWindow(script.address, params) 37 + .then(window => { 38 + console.log('Script window opened and running'); 39 + 40 + // Auto-close after execution 41 + setTimeout(() => { 42 + window.close().catch(err => { 43 + console.error('Error closing script window:', err); 44 + }); 45 + }, 5000); // Give it 5 seconds to execute 46 + }) 47 + .catch(error => { 48 + console.error('Failed to open script window:', error); 49 + }); 34 50 }; 35 51 36 52 const initItems = (prefs, items) => {
+1 -1
app/slides/index.js
··· 1 1 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 2 - import { openStore, openWindow } from "../utils.js"; 2 + import { openStore } from "../utils.js"; 3 3 import windows from "../windows.js"; 4 4 import api from '../api.js'; 5 5
+3 -30
app/utils.js
··· 51 51 return store; 52 52 }; 53 53 54 - const flattenObj = o => Object.keys(o).map(k => { 55 - // Make sure boolean values are properly converted to strings 56 - if (typeof o[k] === 'boolean') { 57 - return `${k}=${o[k]}`; 58 - } 59 - // For numbers and strings, just convert directly 60 - else { 61 - return `${k}=${o[k]}`; 62 - } 63 - }).join(','); 64 - 65 - const openWindow = (address, params) => { 66 - const target = params.hasOwnProperty('key') ? params.key : '_blank'; 67 - 68 - // Log parameters to help with debugging 69 - console.log('openWindow called with params:', params); 70 - 71 - if (window.app && window.app.window) { 72 - // Use the IPC window API if available (this goes through main process) 73 - console.log('Using window.app.window.open API'); 74 - return window.app.window.open(address, params); 75 - } else { 76 - // Fall back to regular window.open if API not available 77 - console.log('Using regular window.open', flattenObj(params)); 78 - return window.open(address, target, flattenObj(params)); 79 - } 80 - }; 54 + // Removed openWindow - use windows.js instead 55 + // The flattenObj helper is now private - it's only needed for window.open 81 56 82 57 export { 83 - flattenObj, 84 - openStore, 85 - openWindow 58 + openStore 86 59 };
+7 -18
app/windows.js
··· 1 - import { openStore, openWindow } from "./utils.js"; 1 + import { openStore } from "./utils.js"; 2 2 import api from './api.js'; 3 3 import fc from './features.js'; 4 4 ··· 20 20 21 21 console.log('Opening modal window with params:', params); 22 22 23 - // Prefer using the IPC API directly 23 + // Always use the IPC API 24 24 if (api.window && api.window.open) { 25 25 return api.window.open(address, params); 26 26 } else { 27 - return openWindow(address, params); 27 + console.error('API window.open not available'); 28 + throw new Error('API window.open not available. Cannot open window.'); 28 29 } 29 30 }; 30 31 ··· 39 40 40 41 let windowId; 41 42 42 - // Prefer using the IPC API directly 43 + // Always use the IPC API 43 44 if (api.window && api.window.open) { 44 45 const result = await api.window.open(address, params); 45 46 if (result.success) { ··· 49 50 throw new Error(`Failed to open window: ${result.error}`); 50 51 } 51 52 } else { 52 - // Fallback to regular window.open 53 - const win = openWindow(address, params); 54 - return { 55 - window: win, 56 - close: () => { 57 - if (win) win.close(); 58 - }, 59 - hide: () => { 60 - if (win) win.close(); 61 - }, 62 - show: () => { 63 - // Can't re-open with this method 64 - } 65 - }; 53 + console.error('API window.open not available'); 54 + throw new Error('API window.open not available. Cannot open window.'); 66 55 } 67 56 68 57 // Return an API for the window