experiments in a post-browser web
10
fork

Configure Feed

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

Merge pull request #24 from autonome/switch

Switch

authored by

Dietrich Ayala and committed by
GitHub
af2b3444 7ebe4d98

+229 -120
+63 -31
README.md
··· 156 156 - [x] move all possible code from the electron file to the web app 157 157 - [x] move to web implemented globalShortcut 158 158 - [x] move to web implemented openWindow 159 - - [ ] move settings re-use code to utils lib 160 - - [ ] ability to add clickable links in settings panes 161 - - [ ] add links to Settings app 162 - - [ ] per-feature settings ui 163 - - [ ] feature metadata in manifest 159 + - [x] move settings re-use code to utils lib 160 + - [x] ability to add clickable links in settings panes 161 + - [x] add links to Settings app 162 + - [x] per-feature settings ui 164 163 165 164 Core+settings 166 165 - [x] move feature list and enablement to storage ··· 170 169 - [x] wire up tray icon to pref 171 170 - [x] tray click opens default app 172 171 172 + Core/Basic 173 + - [x] basic command bar to open pages 174 + - [x] fix setting layout wrapping issue 175 + 176 + Core blockers 177 + - [ ] built-in feature loading from origin not file 178 + - [ ] combine settings and background in built-in features? 179 + 173 180 Commands/messaging 174 181 - [x] implement pubsub api 175 182 - [x] way to tell feature to open default ui (if there is one) 176 - - [ ] way to tell feature to open settings ui (if there is one) 177 - - [ ] figure out re-init/reload story when pref/feature changes 178 - - [ ] figure out feature unload/reload (unreg shortcuts, close windows, etc) 183 + - [x] way tell feature to open its settings ui (if there is one) 184 + 185 + Feature un/install and reloads 186 + - [ ] feature unload/reload - init/uninit whole feature and window 187 + - [ ] unreg shortcuts 188 + - [ ] close other windows, not just background (track all feature wins? hierarchy? window manager?) 189 + - [ ] figure out re-init/reload story when pref/feature change is saved 190 + - can leave to the apps? eg document.reload()? likely not for OS level stuff 191 + - could do a storage change listener, but all kinds of reasons why you *wouldn't* do full reload... 192 + - preload could register window + thing (eg kb listener) and listen for feature-disable events 193 + - ok so basically do at api level 194 + - [ ] language: call them feature or apps? other? 195 + - [ ] core settings re-render on feature toggle 179 196 180 - Misc 197 + Daily driver blockers 198 + - [x] debug vs profile(s) for app dir 199 + - [ ] actually load/unload peeks when enabled/disabled 200 + - [ ] actually load/unload slides when enabled/disabled 201 + - [ ] actually load/unload scripts when enabled/disabled 202 + - [ ] fix ESC not working right 181 203 - [ ] fix ESC not working in web content 204 + - [ ] make it so start feature can be unset 182 205 183 - Navigation 184 - - [ ] esc stack: from feature settings back to core settings 185 - - [ ] add to izui stack (and ix w/ history?) 186 - 187 - Core/Basic 188 - - [x] basic command bar to open pages 189 - - [x] fix setting layout wrapping issue 190 - - [ ] re-enable label previews, eg "Peek {key} - {address}" 191 - - [ ] add support for per-feature hidden prefs (should be per-schema) 206 + Focus vs not focused app mode 207 + - [ ] openWindow option to not close on escape (perma windows w/ controls) 208 + - [ ] app focus detection in shortcuts 209 + - [ ] separate global shortcuts from app shortcuts (eg quit) 210 + - [ ] all-window show/hide when doing global shortcuts while app unfocused 192 211 193 212 Features cleanup 194 - - [ ] enable/disable individual slides, peeks 195 - - [ ] enable/disable individual scripts 196 - - [ ] visible-but-not-changeable settings should be per-schema 213 + - [x] enable/disable individual slides, peeks 214 + - [x] enable/disable individual scripts 215 + 216 + Internal cleanup 217 + - [ ] fix label names, match to pwa manifest 218 + - [ ] put in log labels 219 + 220 + Dev niceties 221 + - [ ] figure out single devtools window if possible 222 + 223 + Window controls/persistence/etc (after perma window) 224 + - [ ] window position persistence where it makes sense (settings, groups, cmd) and make configurable? 225 + - [ ] window size persistence where it makes sense (slides, peeks) and make configurable? 226 + - [ ] window controls 227 + - [ ] window resizers 228 + 229 + Window animations 197 230 - [ ] add window open animation (to/from coords, time) to openWindow 198 231 - [ ] update slides impl to use animation again 199 - - [ ] add window position persistence where it makes sense (settings, groups, cmd) and make configurable? 200 - - [ ] add window size persistence where it makes sense (slides, peeks) and make configurable? 201 - - [ ] global shortcuts vs app shortcuts 202 - - [ ] openWindow option to not close on escape 203 - 204 - Core cleanup 205 - - [ ] move feature bg pages to iframes in core bg page? 206 232 207 233 Deployment 208 234 - [ ] app updates 209 235 - [ ] icons 210 236 - [ ] about page 211 237 212 - Daily driver blockers 213 - - [x] debug vs profile(s) for app dir 238 + ### v0.2 - extensibility / remember shit 214 239 215 - ### v0.2 - extensibility / remember shit 240 + Navigation 241 + - [ ] make izui stack manager (part of window mgr?) 242 + - [ ] esc stack: from feature settings back to core settings 243 + - [ ] add to izui stack (and ix w/ history?) 216 244 217 245 Install/load/address features 246 + - [ ] pull from manifest (load/install via manifest with special key?) 218 247 - [ ] manifests for feature metadata 219 248 - [ ] feature urls? eg peek://settings(/index.html) 220 249 - [ ] maybe fine to file urls for now, would have to migrate later 250 + - [ ] feature metadata in manifest 251 + - [ ] app protocol? webextension? pwa? wtf? 252 + - [ ] move feature bg pages to iframes in core bg page? 221 253 222 254 History 223 255 - [ ] push navigations out through pubsub?
+2 -2
features/cmd/background.js
··· 19 19 20 20 const params = { 21 21 debug, 22 - feature: labels.featureType, 22 + feature: labels.name, 23 23 file: 'features/cmd/panel.html', 24 24 height, 25 25 width ··· 34 34 35 35 const params = { 36 36 debug, 37 - feature: labels.featureType, 37 + feature: labels.name, 38 38 file: 'features/core/settings.html', 39 39 height, 40 40 width
+1 -1
features/cmd/commands.js
··· 71 71 const width = 800; 72 72 73 73 const params = { 74 - feature: labels.featureType, 74 + feature: labels.name, 75 75 address, 76 76 height, 77 77 width
+2 -5
features/cmd/config.js
··· 1 - const id = 'features/cmd'; 2 - const guid = 'cee1225d-40ac-41e5-a34c-e2edba69d599'; 1 + const id = 'cee1225d-40ac-41e5-a34c-e2edba69d599'; 3 2 4 3 const labels = { 5 - featureType: 'cmd', 6 - featureDisplay: 'Cmd', 4 + name: 'Cmd', 7 5 prefs: { 8 6 shortcutKey: 'Cmd shortcut', 9 7 } ··· 41 39 42 40 export { 43 41 id, 44 - guid, 45 42 labels, 46 43 schemas, 47 44 storageKeys,
+1 -1
features/cmd/settings.js
··· 33 33 const gui = new GUI({ 34 34 touchStyles: false, 35 35 container: container, 36 - title: labels.featureDisplay 36 + title: labels.name 37 37 }); 38 38 39 39 // anytime anything changes, persist to storage
+50 -11
features/core/background.js
··· 8 8 const debug = window.app.debug; 9 9 const clear = false; 10 10 11 - const _store = openStore(id, defaults, clear /* clear storage */); 12 - const _api = window.app; 11 + const store = openStore(id, defaults, clear /* clear storage */); 12 + const api = window.app; 13 + 14 + const winKeyCache = new Map(); 13 15 14 16 const openSettingsWindow = (prefs) => { 15 17 const height = prefs.height || 600; ··· 17 19 18 20 const params = { 19 21 debug, 20 - feature: labels.featureType, 22 + feature: labels.name, 21 23 file: 'features/core/settings.html', 22 24 height, 23 25 width 24 26 }; 25 27 26 - _api.openWindow(params); 28 + api.openWindow(params); 27 29 }; 28 30 29 31 const initShortcut = (prefs) => { 30 - _api.shortcuts.register(prefs.shortcutKey, () => { 32 + api.shortcuts.register(prefs.shortcutKey, () => { 31 33 openSettingsWindow(prefs); 32 34 }); 33 35 }; ··· 37 39 return; 38 40 } 39 41 40 - log('initializing feature ' + f); 42 + console.log('initializing feature ', f); 41 43 42 44 const params = { 43 45 feature: f.name, ··· 47 49 show: debug 48 50 }; 49 51 50 - window.app.openWindow(params); 51 - //window.app.openWindow(params, () => window.app.log('win opened')); 52 + window.app.openWindow(params, r => { 53 + console.log(`initFeature(): win opened for ${f.name}`, r) 54 + winKeyCache.set(f.id, r.key); 55 + }); 56 + 57 + console.log('window opened'); 58 + }; 59 + 60 + const uninitFeature = f => { 61 + 62 + const key = winKeyCache.get(f.id); 63 + if (key) { 64 + console.log('closing window for', f.name); 65 + window.app.closeWindow(key, r => { 66 + console.log(`uninitFeature(): win closed for ${f.name}`, r) 67 + winKeyCache.delete(f.id); 68 + }); 69 + } 52 70 }; 53 71 54 72 // unused, worth testing more tho ··· 66 84 }); 67 85 }; 68 86 69 - const prefs = () => _store.get(storageKeys.PREFS); 70 - const features = () => _store.get(storageKeys.FEATURES); 87 + const prefs = () => store.get(storageKeys.PREFS); 88 + const features = () => store.get(storageKeys.FEATURES); 71 89 72 90 const init = () => { 73 91 log('init'); ··· 95 113 } 96 114 }); 97 115 98 - // main process uses these for initi 116 + // main process uses these for initialization 99 117 window.app.publish('prefs', { 100 118 feature: id, 101 119 prefs: p 120 + }); 121 + 122 + // feature enable/disable 123 + window.app.subscribe('core:feature:toggle', msg => { 124 + console.log('feature toggle', msg) 125 + 126 + const f = features().find(f => f.id = msg.featureId); 127 + if (f) { 128 + console.log('feature toggle', f); 129 + if (msg.enabled == false) { 130 + console.log('disabling', f.name); 131 + uninitFeature(f); 132 + } 133 + else if (msg.enabled == true) { 134 + console.log('enabling', f.name); 135 + initFeature(f); 136 + } 137 + } 138 + else { 139 + console.log('feature toggle - no feature found for', f.name); 140 + } 102 141 }); 103 142 }; 104 143
+2 -7
features/core/config.js
··· 1 - const id = 'features/core'; 2 - const guid = '8aadaae5-2594-4968-aba0-707f0d371cfb'; 1 + const id = '8aadaae5-2594-4968-aba0-707f0d371cfb'; 3 2 4 3 const labels = { 5 - featureType: 'settings', 6 - featureDisplay: 'Settings', 7 - itemType: 'feature', 8 - itemDisplay: 'Feature', 4 + name: 'Settings', 9 5 prefs: { 10 6 shortcutKey: 'Settings shortcut', 11 7 } ··· 158 154 159 155 export { 160 156 id, 161 - guid, 162 157 labels, 163 158 schemas, 164 159 storageKeys,
+5 -7
features/core/settings.js
··· 19 19 20 20 const init = () => { 21 21 22 - /* 23 - pubsub.publish('open', { 24 - feature: msg.prefs.startupFeature 25 - }); 26 - */ 27 - 28 22 // Initialize settings UI 29 23 const container = document.querySelector('.houseofpane'); 30 24 31 25 const gui = new GUI({ 32 26 touchStyles: false, 33 27 container: container, 34 - title: labels.featureDisplay 28 + title: labels.name 35 29 }); 36 30 37 31 // anytime anything changes, persist to storage ··· 71 65 addToGUI(folder, 'Enabled', feature.enabled).onChange(e => { 72 66 // TODO: validate new value against schema 73 67 features[i].enabled = e; 68 + window.app.publish('core:feature:toggle', { 69 + featureId: feature.id, 70 + enabled: e 71 + }); 74 72 }); 75 73 addToGUI(folder, 'Settings', () => { 76 74 const title = `${feature.name} - Settings`;
+1 -1
features/groups/background.js
··· 18 18 const width = 800; 19 19 20 20 const params = { 21 - feature: labels.featureType, 21 + feature: labels.name, 22 22 file: 'features/groups/home.html', 23 23 height, 24 24 width
+2 -5
features/groups/config.js
··· 1 - const id = 'features/groups'; 2 - const guid = '82de735f-a4b7-4fe6-a458-ec29939ae00d'; 1 + const id = '82de735f-a4b7-4fe6-a458-ec29939ae00d'; 3 2 4 3 const labels = { 5 - featureType: 'groups', 6 - featureDisplay: 'Groups', 4 + name: 'Groups', 7 5 prefs: { 8 6 shortcutKey: 'Groups shortcut', 9 7 } ··· 41 39 42 40 export { 43 41 id, 44 - guid, 45 42 labels, 46 43 schemas, 47 44 storageKeys,
+1 -1
features/groups/settings.js
··· 35 35 const gui = new GUI({ 36 36 touchStyles: false, 37 37 container: container, 38 - title: labels.featureDisplay 38 + title: labels.name 39 39 }); 40 40 41 41 // anytime anything changes, persist to storage
+2 -2
features/peeks/background.js
··· 24 24 width, 25 25 26 26 // peek 27 - feature: labels.featureType, 28 - windowKey: `${labels.featureType}:${item.keyNum}`, 27 + feature: labels.name, 28 + windowKey: `${labels.name}:${item.keyNum}`, 29 29 keepLive: item.keepLive || false, 30 30 persistData: item.persistData || false 31 31 };
+2 -7
features/peeks/config.js
··· 1 - const id = 'features/peeks'; 2 - const guid = 'ef3bd271-d408-421f-9338-47b615571e43'; 1 + const id = 'ef3bd271-d408-421f-9338-47b615571e43'; 3 2 4 3 const labels = { 5 - featureType: 'peeks', 6 - featureDisplay: 'Peeks', 7 - itemType: 'peek', 8 - itemDisplay: 'Peek', 4 + name: 'Peeks', 9 5 prefs: { 10 6 keyPrefix: 'Peek shortcut prefix', 11 7 } ··· 128 124 129 125 export { 130 126 id, 131 - guid, 132 127 labels, 133 128 schemas, 134 129 storageKeys,
+1 -1
features/peeks/settings.js
··· 35 35 const gui = new GUI({ 36 36 touchStyles: false, 37 37 container: container, 38 - title: labels.featureDisplay 38 + title: labels.name 39 39 }); 40 40 41 41 // anytime anything changes, persist to storage
+1 -1
features/scripts/background.js
··· 25 25 `; 26 26 27 27 const params = { 28 - feature: labels.featureType, 28 + feature: labels.name, 29 29 address: script.address, 30 30 show: false, 31 31 script: {
+2 -7
features/scripts/config.js
··· 1 - const id = 'features/scripts'; 2 - const guid = '30c25027-d367-4595-b37f-9db3de853c37'; 1 + const id = '30c25027-d367-4595-b37f-9db3de853c37'; 3 2 4 3 const labels = { 5 - featureType: 'scripts', 6 - featureDisplay: 'Scripts', 7 - itemType: 'script', 8 - itemDisplay: 'Script', 4 + name: 'Scripts', 9 5 prefs: { 10 6 } 11 7 }; ··· 126 122 127 123 export { 128 124 id, 129 - guid, 130 125 labels, 131 126 schemas, 132 127 storageKeys,
+1 -1
features/scripts/settings.js
··· 35 35 const gui = new GUI({ 36 36 touchStyles: false, 37 37 container: container, 38 - title: labels.featureDisplay 38 + title: labels.name 39 39 }); 40 40 41 41 // anytime anything changes, persist to storage
+2 -2
features/slides/background.js
··· 92 92 width, 93 93 94 94 // peek 95 - feature: labels.featureType, 96 - windowKey: `${labels.featureType}:${item.screenEdge}`, 95 + feature: labels.name, 96 + windowKey: `${labels.name}:${item.screenEdge}`, 97 97 keepLive: item.keepLive || false, 98 98 persistData: item.persistData || false, 99 99
+2 -7
features/slides/config.js
··· 1 - const id = 'features/slides'; 2 - const guid = '434108f3-18a6-437a-b507-2f998f693bb2'; 1 + const id = '434108f3-18a6-437a-b507-2f998f693bb2'; 3 2 4 3 const labels = { 5 - featureType: 'slides', 6 - featureDisplay: 'Slides', 7 - itemType: 'slide', 8 - itemDisplay: 'Slide', 4 + name: 'Slides', 9 5 prefs: { 10 6 keyPrefix: 'Slide shortcut prefix', 11 7 } ··· 160 156 161 157 export { 162 158 id, 163 - guid, 164 159 labels, 165 160 schemas, 166 161 storageKeys,
+1 -1
features/slides/settings.js
··· 35 35 const gui = new GUI({ 36 36 touchStyles: false, 37 37 container: container, 38 - title: labels.featureDisplay 38 + title: labels.name 39 39 }); 40 40 41 41 // anytime anything changes, persist to storage
+59 -11
index.js
··· 61 61 const profileDataPath = path.join(defaultUserDataPath, PROFILE); 62 62 const sessionDataPath = path.join(profileDataPath, 'chromium'); 63 63 64 - console.log('udp', defaultUserDataPath); 65 - console.log('pdp', profileDataPath); 66 - console.log('sdp', sessionDataPath); 64 + //console.log('udp', defaultUserDataPath); 65 + //console.log('pdp', profileDataPath); 66 + //console.log('sdp', sessionDataPath); 67 67 68 68 // create filesystem 69 69 if (!fs.existsSync(sessionDataPath)){ ··· 85 85 }); 86 86 /* 87 87 try { 88 - require('electron-reloader')(module); 88 + require('electron-reloader')(module); 89 89 } catch {} 90 90 */ 91 91 } ··· 154 154 155 155 return { 156 156 publish: (topic, msg) => { 157 + console.log('ps.pub', topic, msg); 157 158 if (topics.has(topic)) { 158 159 topics.get(topic).forEach(subscriber => { 159 160 subscriber(msg); ··· 161 162 } 162 163 }, 163 164 subscribe: (topic, cb) => { 165 + console.log('ps.sub', topic); 164 166 if (!topics.has(topic)) { 165 167 topics.set(topic, [cb]); 166 168 } ··· 262 264 263 265 ipcMain.on('openwindow', (ev, msg) => { 264 266 openWindow(msg.params, output => { 265 - ev.reply(msg.replyTopic, output); 267 + if (msg && msg.replyTopic) { 268 + ev.reply(msg.replyTopic, output); 269 + } 270 + }); 271 + }); 272 + 273 + ipcMain.on('closewindow', (ev, msg) => { 274 + closeWindow(msg.params, output => { 275 + if (msg && msg.replyTopic) { 276 + ev.reply(msg.replyTopic, output); 277 + } 266 278 }); 267 279 }); 268 280 269 281 // generic dispatch - messages only from trusted code (💀) 270 282 ipcMain.on('publish', (ev, msg) => { 271 - //console.log('publish', msg); 283 + console.log('ipc:publish', msg); 272 284 273 285 pubsub.publish(msg.topic, msg.data); 274 286 }); 275 287 276 288 ipcMain.on('subscribe', (ev, msg) => { 277 - //console.log('subscribe', msg); 289 + console.log('ipc:subscribe', msg); 278 290 279 291 pubsub.subscribe(msg.topic, data => { 292 + console.log('ipc:subscribe:notification', msg); 280 293 ev.reply(msg.replyTopic, data); 281 294 }); 282 295 }); ··· 329 342 330 343 // window opener 331 344 const openWindow = (params, callback) => { 332 - console.log('openWindow', params); 345 + console.log('openWindow', params, callback != null); 333 346 334 347 // if no source identifier, barf 335 348 if (!params.hasOwnProperty('feature') || params.feature == undefined) { ··· 367 380 if (show) { 368 381 win.show(); 369 382 } 383 + else { 384 + // asking to open an already cached window 385 + // eg background app processes that weren't cleaned up maybe? 386 + } 387 + 388 + if (callback != null) { 389 + callback({ 390 + cache: true, 391 + key: key 392 + }); 393 + } 394 + 370 395 return; 371 396 } 372 397 } ··· 480 505 //win.webContents.send('window', { type: labels.featureType, id: win.id}); 481 506 //broadcastToWindows('window', { type: labels.featureType, id: win.id}); 482 507 483 - // TODO: fix func-level callback handling and resp obj 508 + // TODO: fix func-level callback handling and resp obj 484 509 485 510 if (params.script) { 486 511 const script = params.script; ··· 491 516 const r = await win.webContents.executeJavaScript(script.script); 492 517 if (callback) { 493 518 callback({ 494 - scriptOutput: r 495 - }); 519 + key: key, 520 + scriptOutput: r 521 + }); 496 522 } 497 523 } catch(ex) { 498 524 console.error('cs exec error', ex); ··· 502 528 } 503 529 }); 504 530 } 531 + else if (callback != null) { 532 + callback({ 533 + key: key 534 + }); 535 + } 505 536 506 537 return win; 538 + }; 539 + 540 + // window closer 541 + const closeWindow = (params, callback) => { 542 + console.log('closeWindow', params, callback != null); 543 + 544 + if (windowCache.hasKey(params.key)) { 545 + const winData = windowCache.byKey(params.key); 546 + BrowserWindow.fromId(winData.id).close(); 547 + } 548 + else { 549 + // wtf 550 + } 551 + 552 + if (callback != null) { 553 + callback(); 554 + } 507 555 }; 508 556 509 557 /*
+26 -8
preload.js
··· 65 65 }; 66 66 67 67 api.openWindow = (params, callback) => { 68 - log(src, ['openwindow', JSON.stringify(params), 'for', window.location].join(', ')); 68 + log(src, ['api.openwindow', JSON.stringify(params), 'for', window.location].join(', ')); 69 + 69 70 // TODO: won't work for features that open multiple windows 70 71 const replyTopic = `${params.feature}${params.address}`; 71 72 ··· 74 75 replyTopic 75 76 }); 76 77 77 - if (callback) { 78 - ipcRenderer.once(replyTopic, (ev, msg) => { 79 - log(src, 'resp from main'); 80 - log(src, msg); 78 + ipcRenderer.once(replyTopic, (ev, msg) => { 79 + log(src, 'api.openwindow', 'resp from main', msg); 80 + if (callback) { 81 81 callback(msg); 82 - }); 83 - } 82 + } 83 + }); 84 + }; 85 + 86 + api.closeWindow = (key, callback) => { 87 + log(src, ['api.closewindow', key, 'for', window.location].join(', ')); 88 + 89 + const replyTopic = `${key}${Math.random().toString(16).slice(2)}`; 90 + 91 + ipcRenderer.send('closewindow', { 92 + params: { key }, 93 + replyTopic 94 + }); 95 + 96 + ipcRenderer.once(replyTopic, (ev, msg) => { 97 + log(src, 'api.closewindow', 'resp from main', msg); 98 + if (callback) { 99 + callback(msg); 100 + } 101 + }); 84 102 }; 85 103 86 104 api.log = log; ··· 116 134 return; 117 135 } 118 136 119 - const replyTopic = `${Date.now()}`; 137 + const replyTopic = `${topic}${Math.random().toString(16).slice(2)}`; 120 138 121 139 ipcRenderer.send('subscribe', { 122 140 topic,