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 #25 from autonome/switch

well fuck. not pretty, but it's a start.

authored by

Dietrich Ayala and committed by
GitHub
cb72fcc2 af2b3444

+731 -118
+24 -13
README.md
··· 125 125 - collect microformats, metadata, events 126 126 - web page w/ some locations as an input to a map (creates overlay) "map this page" 127 127 - be able to see where a book/etc recommendation came from 128 + - save a tweet, with URL / image / relevant text, but not whole page webrecorder style 128 129 129 130 ## Roadmap 130 131 ··· 182 183 - [x] way to tell feature to open default ui (if there is one) 183 184 - [x] way tell feature to open its settings ui (if there is one) 184 185 186 + Install/load/address features 187 + - [ ] app protocol? webextension? pwa? wtf? 188 + - [ ] pull from manifest (load/install via manifest with special key?) 189 + - [ ] manifests for feature metadata 190 + - [ ] feature urls? eg peek://settings(/index.html) 191 + - [ ] feature metadata in manifest 192 + - [ ] move feature bg pages to iframes in core bg page? 193 + 185 194 Feature un/install and reloads 186 - - [ ] feature unload/reload - init/uninit whole feature and window 187 - - [ ] unreg shortcuts 195 + - [x] feature unload/reload - init/uninit whole feature and window 196 + - [ ] all api calls have feature id accessible by preload (via manifest?) 197 + - [ ] unreg shortcuts on unload 198 + - confirm sucessful reg 199 + - send pubsub msgs on shortcut reg/unreg with feature id 200 + - in core/bg, listen for regs and map to feature 201 + - then on feature uninstall, unreg 188 202 - [ ] close other windows, not just background (track all feature wins? hierarchy? window manager?) 189 203 - [ ] figure out re-init/reload story when pref/feature change is saved 190 204 - can leave to the apps? eg document.reload()? likely not for OS level stuff ··· 214 228 - [x] enable/disable individual scripts 215 229 216 230 Internal cleanup 217 - - [ ] fix label names, match to pwa manifest 218 - - [ ] put in log labels 231 + - [x] s/guid/id/ 232 + - [x] fix label names, match to pwa manifest 233 + - [x] put readable log labels back in 219 234 220 235 Dev niceties 221 236 - [ ] figure out single devtools window if possible ··· 229 244 Window animations 230 245 - [ ] add window open animation (to/from coords, time) to openWindow 231 246 - [ ] update slides impl to use animation again 247 + 248 + Window transparency 249 + - [ ] add support to api 250 + - [ ] update core settings to use it 251 + - [ ] update app settings to use it 232 252 233 253 Deployment 234 254 - [ ] app updates ··· 241 261 - [ ] make izui stack manager (part of window mgr?) 242 262 - [ ] esc stack: from feature settings back to core settings 243 263 - [ ] add to izui stack (and ix w/ history?) 244 - 245 - Install/load/address features 246 - - [ ] pull from manifest (load/install via manifest with special key?) 247 - - [ ] manifests for feature metadata 248 - - [ ] feature urls? eg peek://settings(/index.html) 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? 253 264 254 265 History 255 266 - [ ] push navigations out through pubsub?
+2 -2
features/cmd/background.js
··· 3 3 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 4 4 import { log as l, openStore } from "../utils.js"; 5 5 6 - const log = function(...args) { l(id, args); }; 6 + const log = function(...args) { l(labels.name, args); }; 7 7 8 - log('background', id); 8 + log('background', labels.name); 9 9 10 10 const debug = window.app.debug; 11 11 const clear = false;
+1 -1
features/cmd/commands.js
··· 1 1 import { id, labels, schemas, ui, defaults } from './config.js'; 2 2 import { log as l, openStore } from "../utils.js"; 3 3 4 - const log = function(...args) { l(id, args); }; 4 + const log = function(...args) { l(labels.name, args); }; 5 5 6 6 log('background'); 7 7
+1 -1
features/cmd/panel.js
··· 41 41 import { id, labels, schemas, ui, defaults } from './config.js'; 42 42 import { log as l, openStore } from "../utils.js"; 43 43 44 - const log = function(...args) { l(id, args); }; 44 + const log = function(...args) { l(labels.name, args); }; 45 45 46 46 log('background'); 47 47
+2 -3
features/cmd/settings.html
··· 8 8 <meta name="description" content=""> 9 9 <meta name="viewport" content="width=device-width, initial-scale=1"> 10 10 <link rel="stylesheet" href="settings.css"> 11 - <link rel="stylesheet" href="settings.css"> 12 - <link rel="stylesheet" href="../../node_modules/lil-gui/dist/lil-gui.min.css"> 11 + <link rel="stylesheet" href="node_modules/lil-gui/dist/lil-gui.min.css"> 13 12 </head> 14 13 <body> 15 14 <div> ··· 25 24 Electron <span id="electron-version"></span><br> 26 25 </div> 27 26 28 - <script type=module src="../utils.js"></script> 27 + <script type=module src="./utils.js"></script> 29 28 <script type=module src="./config.js"></script> 30 29 <script type=module src="./settings.js"></script> 31 30
+2 -9
features/cmd/settings.js
··· 2 2 import { log as l, openStore, addToGUI } from "../utils.js"; 3 3 import GUI from './../../node_modules/lil-gui/dist/lil-gui.esm.min.js'; 4 4 5 - const log = function(...args) { l(id, args); }; 5 + const log = function(...args) { l(labels.name, args); }; 6 6 7 - log('background', id); 7 + log('loading', labels.name, 'settings'); 8 8 9 9 const debug = window.app.debug; 10 10 const clear = false; ··· 20 20 }; 21 21 22 22 const init = () => { 23 - 24 - /* 25 - pubsub.publish('open', { 26 - feature: msg.prefs.startupFeature 27 - }); 28 - */ 29 - 30 23 // Initialize settings UI 31 24 const container = document.querySelector('.houseofpane'); 32 25
+95
features/cmd/utils.js
··· 1 + const id = 'features/utils'; 2 + 3 + const log = (...args) => { 4 + if (!window.app.debug) { 5 + return; 6 + } 7 + 8 + const aargs = [...args]; 9 + const source = aargs.shift(); 10 + const str = aargs.map(JSON.stringify).join(', '); 11 + //const str = aargs.join(', '); 12 + console.log(str); 13 + window.app.log(source, str); 14 + }; 15 + 16 + const openStore = (prefix, defaults, clear = false) => { 17 + 18 + //log(id, 'openStore', prefix, (defaults ? Object.keys(defaults) : '')); 19 + 20 + // multiple contexts 21 + const keyify = k => `${prefix}+${k}`; 22 + 23 + // Simple localStorage abstraction/wrapper 24 + const store = { 25 + set: (k, v) => { 26 + const key = keyify(k); 27 + const value = JSON.stringify(v); 28 + //log(id, 'store.set', key) 29 + localStorage.setItem(key, value); 30 + }, 31 + get: (k) => { 32 + const key = keyify(k); 33 + //log(id, 'store.get', key) 34 + const r = localStorage.getItem(key); 35 + return r ? JSON.parse(r) : null; 36 + }, 37 + clear: () => localStorage.clear() 38 + }; 39 + 40 + if (window.app.debug 41 + && window.app.debugLevel == window.app.debugLevels.FIRST_RUN) { 42 + log(id, 'openStore(): clearing storage') 43 + store.clear(); 44 + } 45 + 46 + if (clear) { 47 + store.clear(); 48 + } 49 + 50 + const initStore = (store, data) => { 51 + Object.keys(data).forEach(k => { 52 + const v = store.get(k); 53 + if (!v) { 54 + //log(id, 'openStore(): init is setting', k, data[k]); 55 + store.set(k, data[k]); 56 + } 57 + }); 58 + }; 59 + 60 + if (defaults != null) { 61 + //log('UTILS/openStore()', 'initing'); 62 + initStore(store, defaults); 63 + } 64 + 65 + return store; 66 + }; 67 + 68 + const addToGUI = (gui, label, value, disabled = false, step = null, max = null) => { 69 + const params = {}; 70 + params[label] = value; 71 + 72 + const ctr = gui.add(params, label); 73 + 74 + /* 75 + if (disabled == true) { 76 + ctr.disable(); 77 + } 78 + 79 + if (max != null) { 80 + ctr.max(max); 81 + } 82 + 83 + if (step != null) { 84 + ctr.step(step); 85 + } 86 + */ 87 + 88 + return ctr; 89 + } 90 + 91 + export { 92 + log, 93 + openStore, 94 + addToGUI 95 + };
+4 -4
features/core/background.js
··· 1 1 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 2 2 import { log as l, openStore } from "../utils.js"; 3 3 4 - const log = function(...args) { l(id, args); }; 4 + const log = function(...args) { l(labels.name, args); }; 5 5 6 - log('background', id); 6 + log('background', labels.name); 7 7 8 8 const debug = window.app.debug; 9 9 const clear = false; ··· 20 20 const params = { 21 21 debug, 22 22 feature: labels.name, 23 - file: 'features/core/settings.html', 23 + address: 'peek://core/settings.html', 24 24 height, 25 25 width 26 26 }; ··· 44 44 const params = { 45 45 feature: f.name, 46 46 debug, 47 - file: f.start_url, 47 + address: f.start_url, 48 48 keepLive: true, 49 49 show: debug 50 50 };
+11 -11
features/core/config.js
··· 109 109 shortcutKey: 'Option+,', 110 110 height: 850, 111 111 width: 800, 112 - startupFeature: 'feature/core/settings', 112 + startupFeature: 'peek://core/settings', 113 113 showTrayIcon: true, 114 114 showInTrayAndSwitcher: true 115 115 }, ··· 117 117 { id: 'cee1225d-40ac-41e5-a34c-e2edba69d599', 118 118 name: 'Cmd', 119 119 description: 'Command bar', 120 - start_url: 'features/cmd/background.html', 120 + start_url: 'peek://cmd/background.html', 121 121 enabled: false, 122 - settings_url: 'features/cmd/settings.html', 122 + settings_url: 'peek://cmd/settings.html', 123 123 }, 124 124 { id: '82de735f-a4b7-4fe6-a458-ec29939ae00d', 125 125 name: 'Groups', 126 126 description: 'View your web in groups', 127 - start_url: 'features/groups/background.html', 127 + start_url: 'peek://groups/background.html', 128 128 enabled: false, 129 - settings_url: 'features/groups/settings.html', 129 + settings_url: 'peek://groups/settings.html', 130 130 }, 131 131 { id: 'ef3bd271-d408-421f-9338-47b615571e43', 132 132 name: 'Peeks', 133 133 description: 'Peek at pages in a transient popup using keyboard shortcuts', 134 - start_url: 'features/peeks/background.html', 134 + start_url: 'peek://peeks/background.html', 135 135 enabled: false, 136 - settings_url: 'features/peeks/settings.html', 136 + settings_url: 'peek://peeks/settings.html', 137 137 }, 138 138 { id: '30c25027-d367-4595-b37f-9db3de853c37', 139 139 name: 'Scripts', 140 140 description: 'Create, manage and run content scripts', 141 - start_url: 'features/scripts/background.html', 141 + start_url: 'peek://scripts/background.html', 142 142 enabled: false, 143 - settings_url: 'features/scripts/settings.html', 143 + settings_url: 'peek://scripts/settings.html', 144 144 }, 145 145 { id: '434108f3-18a6-437a-b507-2f998f693bb2', 146 146 name: 'Slides', 147 147 description: 'Open specific pages as side/top/bottom bars', 148 - start_url: 'features/slides/background.html', 148 + start_url: 'peek://slides/background.html', 149 149 enabled: false, 150 - settings_url: 'features/slides/settings.html', 150 + settings_url: 'peek://slides/settings.html', 151 151 } 152 152 ] 153 153 };
+4 -3
features/core/settings.html
··· 8 8 <meta name="description" content=""> 9 9 <meta name="viewport" content="width=device-width, initial-scale=1"> 10 10 <link rel="stylesheet" href="settings.css"> 11 - <link rel="stylesheet" href="settings.css"> 12 - <link rel="stylesheet" href="../../node_modules/lil-gui/dist/lil-gui.min.css"> 11 + <link rel="stylesheet" href="node_modules/lil-gui/dist/lil-gui.min.css"> 13 12 </head> 14 13 <body> 15 14 <div> ··· 19 18 <div class="houseofpane"> 20 19 </div> 21 20 21 + <!-- 22 22 <div> 23 23 Node.js <span id="node-version"></span><br> 24 24 Chromium <span id="chrome-version"></span><br> 25 25 Electron <span id="electron-version"></span><br> 26 26 </div> 27 + --> 27 28 28 - <script type=module src="../utils.js"></script> 29 + <script type=module src="./utils.js"></script> 29 30 <script type=module src="./config.js"></script> 30 31 <script type=module src="./settings.js"></script> 31 32
+3 -3
features/core/settings.js
··· 2 2 import { log as l, openStore, addToGUI } from "../utils.js"; 3 3 import GUI from './../../node_modules/lil-gui/dist/lil-gui.esm.min.js'; 4 4 5 - const log = function(...args) { l(id, args); }; 5 + const log = function(...args) { l(labels.name, args); }; 6 6 const DEBUG = window.app.debug; 7 7 8 - log('loading', id); 8 + log('loading', labels.name, 'settings'); 9 9 10 10 const store = openStore(id); 11 11 const container = document.querySelector('.houseofpane'); ··· 80 80 const openSettingsAddress = (title, address) => { 81 81 const params = { 82 82 feature: title, 83 - file: address, 83 + address, 84 84 }; 85 85 86 86 window.app.openWindow(params, () => window.app.log(title, 'settings win opened', address));
+95
features/core/utils.js
··· 1 + const id = 'features/utils'; 2 + 3 + const log = (...args) => { 4 + if (!window.app.debug) { 5 + return; 6 + } 7 + 8 + const aargs = [...args]; 9 + const source = aargs.shift(); 10 + const str = aargs.map(JSON.stringify).join(', '); 11 + //const str = aargs.join(', '); 12 + console.log(str); 13 + window.app.log(source, str); 14 + }; 15 + 16 + const openStore = (prefix, defaults, clear = false) => { 17 + 18 + //log(id, 'openStore', prefix, (defaults ? Object.keys(defaults) : '')); 19 + 20 + // multiple contexts 21 + const keyify = k => `${prefix}+${k}`; 22 + 23 + // Simple localStorage abstraction/wrapper 24 + const store = { 25 + set: (k, v) => { 26 + const key = keyify(k); 27 + const value = JSON.stringify(v); 28 + //log(id, 'store.set', key) 29 + localStorage.setItem(key, value); 30 + }, 31 + get: (k) => { 32 + const key = keyify(k); 33 + //log(id, 'store.get', key) 34 + const r = localStorage.getItem(key); 35 + return r ? JSON.parse(r) : null; 36 + }, 37 + clear: () => localStorage.clear() 38 + }; 39 + 40 + if (window.app.debug 41 + && window.app.debugLevel == window.app.debugLevels.FIRST_RUN) { 42 + log(id, 'openStore(): clearing storage') 43 + store.clear(); 44 + } 45 + 46 + if (clear) { 47 + store.clear(); 48 + } 49 + 50 + const initStore = (store, data) => { 51 + Object.keys(data).forEach(k => { 52 + const v = store.get(k); 53 + if (!v) { 54 + //log(id, 'openStore(): init is setting', k, data[k]); 55 + store.set(k, data[k]); 56 + } 57 + }); 58 + }; 59 + 60 + if (defaults != null) { 61 + //log('UTILS/openStore()', 'initing'); 62 + initStore(store, defaults); 63 + } 64 + 65 + return store; 66 + }; 67 + 68 + const addToGUI = (gui, label, value, disabled = false, step = null, max = null) => { 69 + const params = {}; 70 + params[label] = value; 71 + 72 + const ctr = gui.add(params, label); 73 + 74 + /* 75 + if (disabled == true) { 76 + ctr.disable(); 77 + } 78 + 79 + if (max != null) { 80 + ctr.max(max); 81 + } 82 + 83 + if (step != null) { 84 + ctr.step(step); 85 + } 86 + */ 87 + 88 + return ctr; 89 + } 90 + 91 + export { 92 + log, 93 + openStore, 94 + addToGUI 95 + };
+2 -2
features/groups/background.js
··· 3 3 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 4 4 import { log as l, openStore } from "../utils.js"; 5 5 6 - const log = function(...args) { l(id, args); }; 6 + const log = function(...args) { l(labels.name, args); }; 7 7 8 - log('background', id); 8 + log('background', labels.name); 9 9 10 10 const debug = window.app.debug; 11 11 const clear = false;
+2 -3
features/groups/settings.html
··· 8 8 <meta name="description" content=""> 9 9 <meta name="viewport" content="width=device-width, initial-scale=1"> 10 10 <link rel="stylesheet" href="settings.css"> 11 - <link rel="stylesheet" href="settings.css"> 12 - <link rel="stylesheet" href="../../node_modules/lil-gui/dist/lil-gui.min.css"> 11 + <link rel="stylesheet" href="node_modules/lil-gui/dist/lil-gui.min.css"> 13 12 </head> 14 13 <body> 15 14 <div> ··· 25 24 Electron <span id="electron-version"></span><br> 26 25 </div> 27 26 28 - <script type=module src="../utils.js"></script> 27 + <script type=module src="./utils.js"></script> 29 28 <script type=module src="./config.js"></script> 30 29 <script type=module src="./settings.js"></script> 31 30
+2 -9
features/groups/settings.js
··· 2 2 import { log as l, openStore, addToGUI } from "../utils.js"; 3 3 import GUI from './../../node_modules/lil-gui/dist/lil-gui.esm.min.js'; 4 4 5 - const log = function(...args) { l(id, args); }; 5 + const log = function(...args) { l(labels.name, args); }; 6 6 7 - log('background', id); 7 + log('loading', labels.name, 'settings'); 8 8 9 9 const debug = window.app.debug; 10 10 const clear = false; ··· 22 22 }; 23 23 24 24 const init = () => { 25 - 26 - /* 27 - pubsub.publish('open', { 28 - feature: msg.prefs.startupFeature 29 - }); 30 - */ 31 - 32 25 // Initialize settings UI 33 26 const container = document.querySelector('.houseofpane'); 34 27
+95
features/groups/utils.js
··· 1 + const id = 'features/utils'; 2 + 3 + const log = (...args) => { 4 + if (!window.app.debug) { 5 + return; 6 + } 7 + 8 + const aargs = [...args]; 9 + const source = aargs.shift(); 10 + const str = aargs.map(JSON.stringify).join(', '); 11 + //const str = aargs.join(', '); 12 + console.log(str); 13 + window.app.log(source, str); 14 + }; 15 + 16 + const openStore = (prefix, defaults, clear = false) => { 17 + 18 + //log(id, 'openStore', prefix, (defaults ? Object.keys(defaults) : '')); 19 + 20 + // multiple contexts 21 + const keyify = k => `${prefix}+${k}`; 22 + 23 + // Simple localStorage abstraction/wrapper 24 + const store = { 25 + set: (k, v) => { 26 + const key = keyify(k); 27 + const value = JSON.stringify(v); 28 + //log(id, 'store.set', key) 29 + localStorage.setItem(key, value); 30 + }, 31 + get: (k) => { 32 + const key = keyify(k); 33 + //log(id, 'store.get', key) 34 + const r = localStorage.getItem(key); 35 + return r ? JSON.parse(r) : null; 36 + }, 37 + clear: () => localStorage.clear() 38 + }; 39 + 40 + if (window.app.debug 41 + && window.app.debugLevel == window.app.debugLevels.FIRST_RUN) { 42 + log(id, 'openStore(): clearing storage') 43 + store.clear(); 44 + } 45 + 46 + if (clear) { 47 + store.clear(); 48 + } 49 + 50 + const initStore = (store, data) => { 51 + Object.keys(data).forEach(k => { 52 + const v = store.get(k); 53 + if (!v) { 54 + //log(id, 'openStore(): init is setting', k, data[k]); 55 + store.set(k, data[k]); 56 + } 57 + }); 58 + }; 59 + 60 + if (defaults != null) { 61 + //log('UTILS/openStore()', 'initing'); 62 + initStore(store, defaults); 63 + } 64 + 65 + return store; 66 + }; 67 + 68 + const addToGUI = (gui, label, value, disabled = false, step = null, max = null) => { 69 + const params = {}; 70 + params[label] = value; 71 + 72 + const ctr = gui.add(params, label); 73 + 74 + /* 75 + if (disabled == true) { 76 + ctr.disable(); 77 + } 78 + 79 + if (max != null) { 80 + ctr.max(max); 81 + } 82 + 83 + if (step != null) { 84 + ctr.step(step); 85 + } 86 + */ 87 + 88 + return ctr; 89 + } 90 + 91 + export { 92 + log, 93 + openStore, 94 + addToGUI 95 + };
+2 -2
features/peeks/background.js
··· 3 3 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 4 4 import { log as l, openStore } from "../utils.js"; 5 5 6 - const log = function(...args) { l(id, args); }; 6 + const log = function(...args) { l(labels.name, args); }; 7 7 8 - log('background', id); 8 + log('background', labels.name); 9 9 10 10 const debug = window.app.debug; 11 11 const clear = false;
+2 -3
features/peeks/settings.html
··· 8 8 <meta name="description" content=""> 9 9 <meta name="viewport" content="width=device-width, initial-scale=1"> 10 10 <link rel="stylesheet" href="settings.css"> 11 - <link rel="stylesheet" href="settings.css"> 12 - <link rel="stylesheet" href="../../node_modules/lil-gui/dist/lil-gui.min.css"> 11 + <link rel="stylesheet" href="node_modules/lil-gui/dist/lil-gui.min.css"> 13 12 </head> 14 13 <body> 15 14 <div> ··· 25 24 Electron <span id="electron-version"></span><br> 26 25 </div> 27 26 28 - <script type=module src="../utils.js"></script> 27 + <script type=module src="./utils.js"></script> 29 28 <script type=module src="./config.js"></script> 30 29 <script type=module src="./settings.js"></script> 31 30
+2 -9
features/peeks/settings.js
··· 2 2 import { log as l, openStore, addToGUI } from "../utils.js"; 3 3 import GUI from './../../node_modules/lil-gui/dist/lil-gui.esm.min.js'; 4 4 5 - const log = function(...args) { l(id, args); }; 5 + const log = function(...args) { l(labels.name, args); }; 6 6 7 - log('background', id); 7 + log('loading', labels.name, 'settings'); 8 8 9 9 const debug = window.app.debug; 10 10 const clear = false; ··· 22 22 }; 23 23 24 24 const init = () => { 25 - 26 - /* 27 - pubsub.publish('open', { 28 - feature: msg.prefs.startupFeature 29 - }); 30 - */ 31 - 32 25 // Initialize settings UI 33 26 const container = document.querySelector('.houseofpane'); 34 27
+95
features/peeks/utils.js
··· 1 + const id = 'features/utils'; 2 + 3 + const log = (...args) => { 4 + if (!window.app.debug) { 5 + return; 6 + } 7 + 8 + const aargs = [...args]; 9 + const source = aargs.shift(); 10 + const str = aargs.map(JSON.stringify).join(', '); 11 + //const str = aargs.join(', '); 12 + console.log(str); 13 + window.app.log(source, str); 14 + }; 15 + 16 + const openStore = (prefix, defaults, clear = false) => { 17 + 18 + //log(id, 'openStore', prefix, (defaults ? Object.keys(defaults) : '')); 19 + 20 + // multiple contexts 21 + const keyify = k => `${prefix}+${k}`; 22 + 23 + // Simple localStorage abstraction/wrapper 24 + const store = { 25 + set: (k, v) => { 26 + const key = keyify(k); 27 + const value = JSON.stringify(v); 28 + //log(id, 'store.set', key) 29 + localStorage.setItem(key, value); 30 + }, 31 + get: (k) => { 32 + const key = keyify(k); 33 + //log(id, 'store.get', key) 34 + const r = localStorage.getItem(key); 35 + return r ? JSON.parse(r) : null; 36 + }, 37 + clear: () => localStorage.clear() 38 + }; 39 + 40 + if (window.app.debug 41 + && window.app.debugLevel == window.app.debugLevels.FIRST_RUN) { 42 + log(id, 'openStore(): clearing storage') 43 + store.clear(); 44 + } 45 + 46 + if (clear) { 47 + store.clear(); 48 + } 49 + 50 + const initStore = (store, data) => { 51 + Object.keys(data).forEach(k => { 52 + const v = store.get(k); 53 + if (!v) { 54 + //log(id, 'openStore(): init is setting', k, data[k]); 55 + store.set(k, data[k]); 56 + } 57 + }); 58 + }; 59 + 60 + if (defaults != null) { 61 + //log('UTILS/openStore()', 'initing'); 62 + initStore(store, defaults); 63 + } 64 + 65 + return store; 66 + }; 67 + 68 + const addToGUI = (gui, label, value, disabled = false, step = null, max = null) => { 69 + const params = {}; 70 + params[label] = value; 71 + 72 + const ctr = gui.add(params, label); 73 + 74 + /* 75 + if (disabled == true) { 76 + ctr.disable(); 77 + } 78 + 79 + if (max != null) { 80 + ctr.max(max); 81 + } 82 + 83 + if (step != null) { 84 + ctr.step(step); 85 + } 86 + */ 87 + 88 + return ctr; 89 + } 90 + 91 + export { 92 + log, 93 + openStore, 94 + addToGUI 95 + };
+2 -2
features/scripts/background.js
··· 3 3 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 4 4 import { log as l, openStore } from "../utils.js"; 5 5 6 - const log = function(...args) { l(id, args); }; 6 + const log = function(...args) { l(labels.name, args); }; 7 7 8 - log('background', id); 8 + log('background', labels.name); 9 9 10 10 const debug = window.app.debug; 11 11 const clear = false;
+2 -3
features/scripts/settings.html
··· 8 8 <meta name="description" content=""> 9 9 <meta name="viewport" content="width=device-width, initial-scale=1"> 10 10 <link rel="stylesheet" href="settings.css"> 11 - <link rel="stylesheet" href="settings.css"> 12 - <link rel="stylesheet" href="../../node_modules/lil-gui/dist/lil-gui.min.css"> 11 + <link rel="stylesheet" href="node_modules/lil-gui/dist/lil-gui.min.css"> 13 12 </head> 14 13 <body> 15 14 <div> ··· 25 24 Electron <span id="electron-version"></span><br> 26 25 </div> 27 26 28 - <script type=module src="../utils.js"></script> 27 + <script type=module src="./utils.js"></script> 29 28 <script type=module src="./config.js"></script> 30 29 <script type=module src="./settings.js"></script> 31 30
+2 -9
features/scripts/settings.js
··· 2 2 import { log as l, openStore, addToGUI } from "../utils.js"; 3 3 import GUI from './../../node_modules/lil-gui/dist/lil-gui.esm.min.js'; 4 4 5 - const log = function(...args) { l(id, args); }; 5 + const log = function(...args) { l(labels.name, args); }; 6 6 7 - log('background', id); 7 + log('loading', labels.name, 'settings'); 8 8 9 9 const debug = window.app.debug; 10 10 const clear = false; ··· 22 22 }; 23 23 24 24 const init = () => { 25 - 26 - /* 27 - pubsub.publish('open', { 28 - feature: msg.prefs.startupFeature 29 - }); 30 - */ 31 - 32 25 // Initialize settings UI 33 26 const container = document.querySelector('.houseofpane'); 34 27
+95
features/scripts/utils.js
··· 1 + const id = 'features/utils'; 2 + 3 + const log = (...args) => { 4 + if (!window.app.debug) { 5 + return; 6 + } 7 + 8 + const aargs = [...args]; 9 + const source = aargs.shift(); 10 + const str = aargs.map(JSON.stringify).join(', '); 11 + //const str = aargs.join(', '); 12 + console.log(str); 13 + window.app.log(source, str); 14 + }; 15 + 16 + const openStore = (prefix, defaults, clear = false) => { 17 + 18 + //log(id, 'openStore', prefix, (defaults ? Object.keys(defaults) : '')); 19 + 20 + // multiple contexts 21 + const keyify = k => `${prefix}+${k}`; 22 + 23 + // Simple localStorage abstraction/wrapper 24 + const store = { 25 + set: (k, v) => { 26 + const key = keyify(k); 27 + const value = JSON.stringify(v); 28 + //log(id, 'store.set', key) 29 + localStorage.setItem(key, value); 30 + }, 31 + get: (k) => { 32 + const key = keyify(k); 33 + //log(id, 'store.get', key) 34 + const r = localStorage.getItem(key); 35 + return r ? JSON.parse(r) : null; 36 + }, 37 + clear: () => localStorage.clear() 38 + }; 39 + 40 + if (window.app.debug 41 + && window.app.debugLevel == window.app.debugLevels.FIRST_RUN) { 42 + log(id, 'openStore(): clearing storage') 43 + store.clear(); 44 + } 45 + 46 + if (clear) { 47 + store.clear(); 48 + } 49 + 50 + const initStore = (store, data) => { 51 + Object.keys(data).forEach(k => { 52 + const v = store.get(k); 53 + if (!v) { 54 + //log(id, 'openStore(): init is setting', k, data[k]); 55 + store.set(k, data[k]); 56 + } 57 + }); 58 + }; 59 + 60 + if (defaults != null) { 61 + //log('UTILS/openStore()', 'initing'); 62 + initStore(store, defaults); 63 + } 64 + 65 + return store; 66 + }; 67 + 68 + const addToGUI = (gui, label, value, disabled = false, step = null, max = null) => { 69 + const params = {}; 70 + params[label] = value; 71 + 72 + const ctr = gui.add(params, label); 73 + 74 + /* 75 + if (disabled == true) { 76 + ctr.disable(); 77 + } 78 + 79 + if (max != null) { 80 + ctr.max(max); 81 + } 82 + 83 + if (step != null) { 84 + ctr.step(step); 85 + } 86 + */ 87 + 88 + return ctr; 89 + } 90 + 91 + export { 92 + log, 93 + openStore, 94 + addToGUI 95 + };
+2 -2
features/slides/background.js
··· 3 3 import { id, labels, schemas, storageKeys, defaults } from './config.js'; 4 4 import { log as l, openStore } from "../utils.js"; 5 5 6 - const log = function(...args) { l(id, args); }; 6 + const log = function(...args) { l(labels.name, args); }; 7 7 8 - log('background', id); 8 + log('background', labels.name); 9 9 10 10 const debug = window.app.debug; 11 11 const clear = false;
+2 -3
features/slides/settings.html
··· 8 8 <meta name="description" content=""> 9 9 <meta name="viewport" content="width=device-width, initial-scale=1"> 10 10 <link rel="stylesheet" href="settings.css"> 11 - <link rel="stylesheet" href="settings.css"> 12 - <link rel="stylesheet" href="../../node_modules/lil-gui/dist/lil-gui.min.css"> 11 + <link rel="stylesheet" href="node_modules/lil-gui/dist/lil-gui.min.css"> 13 12 </head> 14 13 <body> 15 14 <div> ··· 25 24 Electron <span id="electron-version"></span><br> 26 25 </div> 27 26 28 - <script type=module src="../utils.js"></script> 27 + <script type=module src="./utils.js"></script> 29 28 <script type=module src="./config.js"></script> 30 29 <script type=module src="./settings.js"></script> 31 30
+2 -9
features/slides/settings.js
··· 2 2 import { log as l, openStore, addToGUI } from "../utils.js"; 3 3 import GUI from './../../node_modules/lil-gui/dist/lil-gui.esm.min.js'; 4 4 5 - const log = function(...args) { l(id, args); }; 5 + const log = function(...args) { l(labels.name, args); }; 6 6 7 - log('background', id); 7 + log('loading', labels.name, 'settings'); 8 8 9 9 const debug = window.app.debug; 10 10 const clear = false; ··· 22 22 }; 23 23 24 24 const init = () => { 25 - 26 - /* 27 - pubsub.publish('open', { 28 - feature: msg.prefs.startupFeature 29 - }); 30 - */ 31 - 32 25 // Initialize settings UI 33 26 const container = document.querySelector('.houseofpane'); 34 27
+95
features/slides/utils.js
··· 1 + const id = 'features/utils'; 2 + 3 + const log = (...args) => { 4 + if (!window.app.debug) { 5 + return; 6 + } 7 + 8 + const aargs = [...args]; 9 + const source = aargs.shift(); 10 + const str = aargs.map(JSON.stringify).join(', '); 11 + //const str = aargs.join(', '); 12 + console.log(str); 13 + window.app.log(source, str); 14 + }; 15 + 16 + const openStore = (prefix, defaults, clear = false) => { 17 + 18 + //log(id, 'openStore', prefix, (defaults ? Object.keys(defaults) : '')); 19 + 20 + // multiple contexts 21 + const keyify = k => `${prefix}+${k}`; 22 + 23 + // Simple localStorage abstraction/wrapper 24 + const store = { 25 + set: (k, v) => { 26 + const key = keyify(k); 27 + const value = JSON.stringify(v); 28 + //log(id, 'store.set', key) 29 + localStorage.setItem(key, value); 30 + }, 31 + get: (k) => { 32 + const key = keyify(k); 33 + //log(id, 'store.get', key) 34 + const r = localStorage.getItem(key); 35 + return r ? JSON.parse(r) : null; 36 + }, 37 + clear: () => localStorage.clear() 38 + }; 39 + 40 + if (window.app.debug 41 + && window.app.debugLevel == window.app.debugLevels.FIRST_RUN) { 42 + log(id, 'openStore(): clearing storage') 43 + store.clear(); 44 + } 45 + 46 + if (clear) { 47 + store.clear(); 48 + } 49 + 50 + const initStore = (store, data) => { 51 + Object.keys(data).forEach(k => { 52 + const v = store.get(k); 53 + if (!v) { 54 + //log(id, 'openStore(): init is setting', k, data[k]); 55 + store.set(k, data[k]); 56 + } 57 + }); 58 + }; 59 + 60 + if (defaults != null) { 61 + //log('UTILS/openStore()', 'initing'); 62 + initStore(store, defaults); 63 + } 64 + 65 + return store; 66 + }; 67 + 68 + const addToGUI = (gui, label, value, disabled = false, step = null, max = null) => { 69 + const params = {}; 70 + params[label] = value; 71 + 72 + const ctr = gui.add(params, label); 73 + 74 + /* 75 + if (disabled == true) { 76 + ctr.disable(); 77 + } 78 + 79 + if (max != null) { 80 + ctr.max(max); 81 + } 82 + 83 + if (step != null) { 84 + ctr.step(step); 85 + } 86 + */ 87 + 88 + return ctr; 89 + } 90 + 91 + export { 92 + log, 93 + openStore, 94 + addToGUI 95 + };
-2
features/utils.js
··· 1 - import {Pane} from './../node_modules/tweakpane/dist/tweakpane.js'; 2 - 3 1 const id = 'features/utils'; 4 2 5 3 const log = (...args) => {
+78 -9
index.js
··· 20 20 ipcMain, 21 21 Menu, 22 22 nativeTheme, 23 + net, 24 + protocol, 23 25 Tray 24 26 } = require('electron'); 25 27 26 - const fs = require('fs'); 27 - const path = require('path'); 28 + const fs = require('node:fs'); 29 + const path = require('node:path'); 30 + const { pathToFileURL } = require('url'); 28 31 29 32 // script loaded into every app window 30 33 const preloadPath = path.join(__dirname, 'preload.js'); 31 34 35 + const APP_SCHEME = 'peek'; 36 + const APP_CORE_PATH = 'features'; 37 + 32 38 // app hidden window to load 33 39 // core application logic is here 34 - const webCoreAddress = 'features/core/background.html'; 40 + const webCoreAddress = 'peek://core/background.html'; 35 41 36 42 const p = process.env.PROFILE; 37 43 console.log('env prof?', p, p != undefined, typeof p) ··· 196 202 return _tray; 197 203 }; 198 204 205 + // ***** protocol handling 206 + 207 + protocol.registerSchemesAsPrivileged([{ 208 + scheme: APP_SCHEME, 209 + privileges: { 210 + standard: true, 211 + secure: true, 212 + supportFetchAPI: true, 213 + bypassCSP: true, 214 + corsEnabled: true, 215 + allowServiceWorkers: false 216 + } 217 + }]); 218 + 219 + const initAppProtocol = () => { 220 + protocol.handle(APP_SCHEME, (req) => { 221 + const { host, pathname } = new URL(req.url); 222 + 223 + // TODO: nope 224 + if (pathname === '/') { 225 + pathname = 'background.html'; 226 + } 227 + 228 + // TODO: unhack all this 229 + const isNode = pathname.indexOf('node_modules') > -1; 230 + 231 + const hackedPath = isNode ? pathname.replace(/^\//, '') 232 + : path.join(APP_CORE_PATH, host, pathname.replace(/^\//,'')); 233 + 234 + const pathToServe = path.resolve(__dirname, hackedPath); 235 + 236 + const relativePath = path.relative(__dirname, pathToServe); 237 + 238 + // NB, this checks for paths that escape the bundle, e.g. 239 + // app://bundle/../../secret_file.txt 240 + const isSafe = relativePath && !relativePath.startsWith('..') 241 + && !path.isAbsolute(relativePath); 242 + 243 + // ugh 244 + if (!isNode && !isSafe) { 245 + console.log('NOTSAFE'); 246 + return new Response('bad', { 247 + status: 400, 248 + headers: { 'content-type': 'text/html' } 249 + }) 250 + } 251 + 252 + const finalPath = pathToFileURL(pathToServe).toString(); 253 + 254 + return net.fetch(finalPath); 255 + }); 256 + } 257 + 199 258 // ***** init ***** 200 259 201 260 // Electron app load ··· 210 269 return; 211 270 } 212 271 272 + // handle peek:// 273 + initAppProtocol(); 274 + 213 275 // init web core 214 276 const rootWin = openWindow({ 215 277 feature: 'Core', 216 - file: webCoreAddress, 278 + address: webCoreAddress, 217 279 show: true, 218 280 keepLive: true, 219 281 keepVisible: true, ··· 345 407 console.log('openWindow', params, callback != null); 346 408 347 409 // if no source identifier, barf 410 + // TODO: test the protocol 348 411 if (!params.hasOwnProperty('feature') || params.feature == undefined) { 349 412 throw new Error('openWindow: no identifying source for openWindow request!'); 350 413 } ··· 355 418 // keep visible 356 419 const keepVisible = params.hasOwnProperty('keepVisible') ? params.keepVisible : false; 357 420 358 - if (!params.address && !params.file) { 359 - console.error('openWindow: neither address nor file!'); 421 + // validate address 422 + if (!params.hasOwnProperty('address') || params.address.length <= 0) { 423 + console.error('openWindow: no address or is empty!'); 360 424 return; 361 425 } 362 426 ··· 407 471 408 472 let webPreferences = {}; 409 473 410 - if (params.file) { 411 - console.log('FILE', params.file); 412 - params.address = `file://${path.join(__dirname)}/${params.file}`; 474 + const url = new URL(params.address); 475 + 476 + if (url.protocol == APP_SCHEME + ':') { 477 + console.log('APP ADDRESS', params.address); 478 + 479 + //params.address = `file://${path.join(__dirname)}/${params.file}`; 480 + 481 + // add preload 413 482 webPreferences.preload = preloadPath; 414 483 } 415 484
+5 -1
package.json
··· 5 5 "main": "index.js", 6 6 "author": "dietrich ayala", 7 7 "license": "MIT", 8 + "repository": { 9 + "type": "git", 10 + "url": "https://github.com/autonome/peek" 11 + }, 8 12 "devDependencies": { 9 13 "@electron-forge/cli": "7.2.0", 10 14 "@electron-forge/maker-deb": "7.2.0", ··· 22 26 "electron-store": "8.1.0", 23 27 "electron-unhandled": "4.0.1", 24 28 "lil-gui": "^0.19.2", 25 - "tweakpane": "4.0.1" 29 + "update-electron-app": "^3.0.0" 26 30 }, 27 31 "scripts": { 28 32 "start": "electron-forge start",