experiments in a post-browser web
10
fork

Configure Feed

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

lots of cmd fixes

+280 -69
+3 -1
README.md
··· 452 452 preview gif 453 453 - empty background 454 454 - slide: open right slide, start music on hypem.com 455 - - cmd: open a search w/ cmd, click on recipe link 455 + 456 + - cmd: open a search for 'xiao mian' w/ cmd, click on recipe link 457 + - add a 'search' command 456 458 - copy chinese text 457 459 - peek #1 at translate to get english 458 460 - copy image of noodle soup
+78 -7
app/cmd/commands.js
··· 1 1 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 2 - import { openStore, flattenObj } from '../utils.js'; 2 + import { openStore } from '../utils.js'; 3 + import windows from '../windows.js'; 3 4 4 5 console.log('commands'); 5 6 ··· 50 51 } 51 52 52 53 const sourceOpenURL = () => { 53 - const cmdName = 'open'; 54 + // Add a regular open command 54 55 addCommand({ 55 - name: cmdName, 56 + name: 'open', 56 57 execute: msg => { 58 + console.log('open command', msg); 59 + 60 + const parts = msg.typed.split(' '); 61 + parts.shift(); 62 + 63 + const address = parts.shift(); 57 64 58 - console.log(cmdName, 'msg', msg); 65 + if (!address) { 66 + return; 67 + } 68 + 69 + // Use the new windows API 70 + windows.createWindow(address, { 71 + width: 800, 72 + height: 600, 73 + openDevTools: debug // Only open DevTools in debug mode 74 + }).then(windowController => { 75 + console.log('Window opened with ID:', windowController.id); 76 + }).catch(error => { 77 + console.error('Failed to open window:', error); 78 + }); 79 + 80 + return { 81 + command: 'open', 82 + address 83 + }; 84 + } 85 + }); 86 + 87 + // Add a debug command that opens windows with DevTools 88 + addCommand({ 89 + name: 'debug', 90 + execute: msg => { 91 + console.log('debug command', msg); 59 92 60 93 const parts = msg.typed.split(' '); 61 94 parts.shift(); ··· 66 99 return; 67 100 } 68 101 69 - const params = { 102 + // Use the new windows API with DevTools enabled 103 + windows.createWindow(address, { 104 + width: 900, 105 + height: 700, 106 + openDevTools: true, 107 + detachedDevTools: true 108 + }).then(windowController => { 109 + console.log('Debug window opened with ID:', windowController.id); 110 + }).catch(error => { 111 + console.error('Failed to open debug window:', error); 112 + }); 113 + 114 + return { 115 + command: 'debug', 70 116 address 71 117 }; 118 + } 119 + }); 120 + 121 + // Add a modal window command 122 + addCommand({ 123 + name: 'modal', 124 + execute: msg => { 125 + console.log('modal command', msg); 72 126 73 - window.open(address, '_blank', flattenObj(params)); 127 + const parts = msg.typed.split(' '); 128 + parts.shift(); 129 + 130 + const address = parts.shift(); 131 + 132 + if (!address) { 133 + return; 134 + } 135 + 136 + // Use the modal window API 137 + windows.openModalWindow(address, { 138 + width: 700, 139 + height: 500 140 + }).then(result => { 141 + console.log('Modal window opened:', result); 142 + }).catch(error => { 143 + console.error('Failed to open modal window:', error); 144 + }); 74 145 75 146 return { 76 - command: 'open', 147 + command: 'modal', 77 148 address 78 149 }; 79 150 }
+19 -6
app/cmd/index.js
··· 22 22 height, 23 23 width, 24 24 // Using modal parameter so it hides on escape/blur 25 - modal: true, 26 - 27 - // Remove titlebar and make window frameless 25 + //modal: true, 26 + 27 + // Keep resident in the background 28 + keepLive: true, 29 + 30 + // Completely remove window frame and decorations 28 31 frame: false, 29 - titleBarStyle: 'hidden', 30 32 transparent: true, 31 33 32 34 // Make sure the window stays on top ··· 37 39 38 40 // Set a reasonable minimum size 39 41 minWidth: 400, 40 - minHeight: 30 42 + minHeight: 50, 43 + 44 + // Make sure shadows are shown for visual appearance 45 + hasShadow: true, 46 + 47 + // Additional window behavior options 48 + skipTaskbar: true, 49 + resizable: false, 50 + fullscreenable: false, 51 + 52 + openDevTools: true, 53 + detachedDevTools: true, 41 54 }; 42 55 43 56 // Use the modal window API to open the window 44 - windows.openModalWindow(address, params) 57 + windows.createWindow(address, params) 45 58 .then(result => { 46 59 console.log('Command window opened:', result); 47 60 })
+48 -19
app/cmd/panel.css
··· 1 + /* Reset styles */ 2 + html, body { 3 + margin: 0; 4 + padding: 0; 5 + width: 100%; 6 + height: 100%; 7 + box-sizing: border-box; 8 + overflow: hidden; 9 + } 1 10 2 - /* Styling for the frameless command window */ 11 + /* Basic body styling */ 3 12 body { 4 - margin: 0; 5 - padding: 10px; 6 - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif; 7 13 background-color: rgba(40, 44, 52, 0.9); 14 + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif; 8 15 color: white; 9 16 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; 17 + -webkit-app-region: drag; /* Allow dragging by entire window */ 18 + user-select: none; /* Prevent text selection when dragging */ 19 + 20 + /* Table display for perfect vertical centering */ 21 + display: table; 15 22 } 16 23 17 - /* Allow interaction with input fields */ 18 - input, button, select, textarea, [contenteditable] { 19 - -webkit-app-region: no-drag; 24 + /* Command container - this is the table cell that centers content */ 25 + .command-container { 26 + /* Table-cell vertical centering technique */ 27 + display: table-cell; 28 + vertical-align: middle; 29 + /* Other styles */ 30 + padding: 0 10px; 20 31 } 21 32 22 - /* Style for the command input */ 33 + /* Input styling - keep this minimal */ 23 34 #command-input { 24 35 width: 100%; 25 36 background: transparent; 26 37 color: white; 27 38 border: none; 28 39 outline: none; 29 - font-size: 16px; 30 - padding: 5px; 40 + font-size: 20px; 41 + font-weight: 500; 42 + height: 40px; 43 + margin: 0; 44 + padding: 0; 45 + -webkit-app-region: no-drag; /* Allow interaction */ 31 46 } 32 47 33 - /* Style for the command results */ 48 + /* Results container */ 34 49 #results { 35 - margin-top: 10px; 36 - max-height: 400px; 50 + display: none; /* Hidden by default */ 51 + margin-top: 5px; 52 + max-height: 150px; 37 53 overflow-y: auto; 54 + -webkit-app-region: no-drag; /* Allow interaction */ 38 55 } 39 56 40 - /* Style for any command items */ 57 + /* Show results only when they have content */ 58 + #results:not(:empty) { 59 + display: block; 60 + } 61 + 62 + /* Command items in results list */ 41 63 .command-item { 42 64 padding: 5px; 43 65 cursor: pointer; ··· 51 73 .command-item.selected { 52 74 background-color: rgba(255, 255, 255, 0.2); 53 75 } 76 + 77 + /* Hide all browser chrome */ 78 + .window-controls, .titlebar, .title-bar, ::-webkit-scrollbar-button { 79 + display: none !important; 80 + opacity: 0 !important; 81 + visibility: hidden !important; 82 + }
+69 -6
app/cmd/panel.html
··· 3 3 <head> 4 4 <title>peek:cmd:panel</title> 5 5 <meta charset="utf-8"> 6 - <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> 7 6 <meta http-equiv="Content-Security-Policy" content="script-src 'self';"> 8 - <meta name="description" content=""> 9 7 <meta name="viewport" content="width=device-width, initial-scale=1"> 10 - <link rel="stylesheet" href="panel.css"> 8 + <meta name="apple-mobile-web-app-capable" content="yes"> 9 + <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> 10 + <style> 11 + html, body { 12 + margin: 0; 13 + padding: 0; 14 + height: 100%; 15 + width: 100%; 16 + background-color: rgba(40, 44, 52, 0.9); 17 + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif; 18 + color: white; 19 + overflow: hidden; 20 + -webkit-app-region: drag; 21 + } 22 + 23 + .center-wrapper { 24 + width: 100%; 25 + height: 100%; 26 + display: flex; 27 + align-items: center; 28 + padding: 0 10px; 29 + box-sizing: border-box; 30 + } 31 + 32 + #command-input { 33 + width: 100%; 34 + height: 40px; 35 + border: none; 36 + outline: none; 37 + background: transparent; 38 + color: white; 39 + font-size: 20px; 40 + font-weight: 500; 41 + padding: 0; 42 + -webkit-app-region: no-drag; 43 + } 44 + 45 + #results { 46 + display: none; 47 + position: absolute; 48 + top: 60%; 49 + left: 10px; 50 + right: 10px; 51 + max-height: 200px; 52 + overflow-y: auto; 53 + -webkit-app-region: no-drag; 54 + } 55 + 56 + #results:not(:empty) { 57 + display: block; 58 + } 59 + 60 + .command-item { 61 + padding: 5px; 62 + cursor: pointer; 63 + border-radius: 4px; 64 + } 65 + 66 + .command-item:hover { 67 + background-color: rgba(255, 255, 255, 0.1); 68 + } 69 + 70 + .command-item.selected { 71 + background-color: rgba(255, 255, 255, 0.2); 72 + } 73 + </style> 11 74 </head> 12 75 <body> 13 - <div class="command-container"> 76 + <div class="center-wrapper"> 14 77 <input id="command-input" type="text" autofocus placeholder="Type a command..." /> 15 - <div id="results"></div> 16 78 </div> 79 + <div id="results"></div> 17 80 18 81 <script type=module src="../utils.js"></script> 19 82 <script type=module src="./config.js"></script> 20 83 <script type=module src="./panel.js"></script> 21 84 <script type=module src="./commands.js"></script> 22 85 </body> 23 - </html> 86 + </html>
+11 -9
app/cmd/panel.js
··· 63 63 }; 64 64 65 65 window.addEventListener('cmd-update-commands', function(e) { 66 - //console.log('ui received updated commands'); 66 + console.log('ui received updated commands'); 67 67 state.commands = e.detail; 68 68 }); 69 69 ··· 95 95 commandInput.focus(); 96 96 }); 97 97 98 - // Automatically focus the input when the window loads 98 + // Automatically focus the input when the window loads and position cursor at end 99 99 setTimeout(() => { 100 100 commandInput.focus(); 101 - }, 100); 101 + // Place cursor at the end of the input 102 + const length = commandInput.value.length; 103 + commandInput.setSelectionRange(length, length); 104 + }, 50); 102 105 } 103 106 104 107 render(); ··· 123 126 } 124 127 125 128 function findMatchingCommands(text) { 126 - const r = true; 127 - r || console.log('findMatchingCommands', text, state.commands.length); 129 + console.log('findMatchingCommands', text, state.commands.length); 128 130 129 131 let count = state.commands.length, 130 132 matches = []; ··· 137 139 // 1. typed string is anywhere in a command name 138 140 // 2. command name is at beginning of typed string 139 141 // (eg: for command input - "weather san diego") 140 - r || console.log('testing option...', name); 142 + console.log('testing option...', name); 141 143 if (name.toLowerCase().indexOf(state.typed.toLowerCase()) != -1 || 142 144 state.typed.toLowerCase().indexOf(name.toLowerCase()) === 0) { 143 145 matches.push(name); ··· 189 191 } 190 192 191 193 async function onKeyup(e) { 192 - // flag for logging 193 - const r = true; 194 - 195 194 // Get the command input element and results container 196 195 const commandInput = document.getElementById('command-input'); 197 196 const resultsContainer = document.getElementById('results'); 198 197 199 198 // Use the input value as the typed text 200 199 state.typed = commandInput.value; 200 + 201 + console.log('onKeyup', e.key, state.typed); 201 202 202 203 if (isModifier(e)) { 203 204 return; ··· 211 212 212 213 // if user pressed return, attempt to execute command 213 214 if (e.key == 'Enter' && !hasModifier(e)) { 215 + console.log('enter pressed', state.typed); 214 216 let name = state.matches[state.matchIndex]; 215 217 if (name && Object.keys(state.commands).indexOf(name) > -1) { 216 218 execute(name, state.typed);
-1
app/utils.js
··· 51 51 return store; 52 52 }; 53 53 54 - // Removed openWindow - use windows.js instead 55 54 // The flattenObj helper is now private - it's only needed for window.open 56 55 57 56 export {
+4
app/windows.js
··· 12 12 * Opens a modal window with the provided address and parameters 13 13 * @param {string} address - URL to open in the window 14 14 * @param {Object} params - Window parameters 15 + * @param {boolean} [params.openDevTools=false] - Whether to open DevTools for this window 16 + * @param {boolean} [params.detachedDevTools=true] - Whether DevTools should be detached 15 17 * @returns {Promise<Object>} - Promise resolving to the window API result 16 18 */ 17 19 const openModalWindow = (address, params = {}) => { ··· 33 35 * Creates a window and handles its lifecycle 34 36 * @param {string} address - URL to open in the window 35 37 * @param {Object} params - Window parameters 38 + * @param {boolean} [params.openDevTools=false] - Whether to open DevTools for this window 39 + * @param {boolean} [params.detachedDevTools=true] - Whether DevTools should be detached 36 40 * @returns {Promise<Object>} - Promise resolving to an object with methods to interact with the window 37 41 */ 38 42 const createWindow = async (address, params = {}) => {
+48 -20
index.js
··· 429 429 const win = new BrowserWindow(winPrefs); 430 430 win.loadURL(webCoreAddress); 431 431 432 - // Setup devtools 433 - winDevtoolsConfig(win); 432 + // Setup devtools for the background window (always open in debug mode) 433 + if (DEBUG) { 434 + win.webContents.openDevTools({ mode: 'detach' }); 435 + 436 + win.webContents.on('devtools-opened', () => { 437 + if (win.isVisible()) { 438 + win.webContents.focus(); 439 + } else { 440 + app.focus(); 441 + } 442 + }); 443 + } 434 444 435 445 // Add to window manager 436 446 windowManager.addWindow(win.id, { ··· 520 530 521 531 // Add escape key handler 522 532 addEscHandler(newWin); 533 + 534 + // Set up DevTools if requested 535 + winDevtoolsConfig(newWin); 523 536 524 537 // Set up modal behavior 525 538 if (featuresMap.modal === true) { ··· 657 670 if (options.y !== undefined) { 658 671 winOptions.y = parseInt(options.y); 659 672 } 673 + 674 + if (options.modal === true) { 675 + winOptions.frame = false; 676 + } 660 677 661 678 console.log('Creating window with options:', winOptions); 662 679 ··· 678 695 679 696 // Add escape key handler to all windows 680 697 addEscHandler(win); 698 + 699 + // Set up DevTools if requested 700 + winDevtoolsConfig(win); 681 701 682 702 // Set up modal behavior if requested 683 703 if (options.modal === true) { ··· 976 996 977 997 // show/configure devtools when/after a window is opened 978 998 const winDevtoolsConfig = bw => { 979 - // TODO: make detach mode configurable 980 - // really want to get so individual app windows can easily control this 981 - // for themselves 982 - bw.webContents.openDevTools({ mode: 'detach' }); 983 - //win.webContents.openDevTools(); 984 - 985 - // when devtools completely open 986 - bw.webContents.on('devtools-opened', () => { 987 - // if window is visible, focus content window 988 - if (bw.isVisible()) { 989 - bw.webContents.focus(); 990 - } 991 - // otherwise force devtools focus 992 - // (for some reason doesn't focus when no visible window...) 993 - else { 994 - app.focus(); 995 - } 996 - }); 999 + const windowData = windowManager.getWindow(bw.id); 1000 + const params = windowData ? windowData.params : {}; 1001 + 1002 + // Check if devTools should be opened 1003 + if (params.openDevTools === true) { 1004 + // Determine if detached mode should be used 1005 + const devToolsOptions = { 1006 + mode: params.detachedDevTools === true ? 'detach' : 'right' 1007 + }; 1008 + 1009 + console.log(`Opening DevTools for window ${bw.id} with options:`, devToolsOptions); 1010 + bw.webContents.openDevTools(devToolsOptions); 1011 + 1012 + // when devtools completely open 1013 + bw.webContents.on('devtools-opened', () => { 1014 + // if window is visible, focus content window 1015 + if (bw.isVisible()) { 1016 + bw.webContents.focus(); 1017 + } 1018 + // otherwise force devtools focus 1019 + // (for some reason doesn't focus when no visible window...) 1020 + else { 1021 + app.focus(); 1022 + } 1023 + }); 1024 + } 997 1025 }; 998 1026 999 1027 // window closer