Commit 930e266beb35562806c1081330831a203ab75c66
Merge pull request #180 from ssbc/tabs
add reconbot shortcuts!mix irving authored on 2/17/2018, 6:46:39 AM
GitHub committed on 2/17/2018, 6:46:39 AM
Parent: 4d3d556ad7d85bcca74606933b74757af9fc4454
Parent: a20d5059cc43f723aefd7fe6e3a79aa751ded3a9
Files changed
README.md | changed |
app/html/app.js | changed |
app/html/tabs.js | changed |
app/sync/initialise/electronState.js | added |
app/sync/initialise/errorCatcher.js | added |
app/sync/initialise/styles.js | added |
app/sync/initialise/userActionListeners.js | added |
index.js | changed |
README.md | ||
---|---|---|
@@ -7,9 +7,8 @@ | ||
7 | 7 … | Patchbay is built using [patchcore](https://github.com/ssbc/patchcore) + [depject](https://github.com/dominictarr/depject). The goal is to make it easier to develop new features, and enable or disable features. This has so far been quite successful! |
8 | 8 … | |
9 | 9 … | This makes in very easy to create say, a renderer for a new message type, or switch to a different method for choosing user names. |
10 | 10 … | |
11 | - | |
12 | 11 … | ## Setup |
13 | 12 … | |
14 | 13 … | Libsodium has some build dependencies. On ubuntu systems the following might help: |
15 | 14 … | |
@@ -99,8 +98,16 @@ | ||
99 | 98 … | # from the patchbay repo folder |
100 | 99 … | npm run dev |
101 | 100 … | ``` |
102 | 101 … | |
102 … | +## Keyboard shortcuts | |
103 … | +`CmdOrCtrl` is the `command` key on Apple keyboards or the `ctrl` key on PC keyboards. | |
104 … | + | |
105 … | +### Tabs and Window | |
106 … | +- `CmdOrCtrl+Shift+]` and `CmdOrCtrl+Shift+[` will cycle the tabs left and right | |
107 … | +- `CmdOrCtrl+w` will close the current tab | |
108 … | +- `CmdOrCtrl+Shift+w` will close the current window | |
109 … | + | |
103 | 110 … | ## How to add a feature |
104 | 111 … | |
105 | 112 … | To add a new message type, add add a js to `./modules/` that exports a function named `message_content` (it should return an HTML element). To add a new tab, export a function named `screen_view` (returns an html element). |
106 | 113 … |
app/html/app.js | ||
---|---|---|
@@ -1,23 +1,18 @@ | ||
1 | 1 … | const nest = require('depnest') |
2 | 2 … | const { h } = require('mutant') |
3 | -const insertCss = require('insert-css') | |
4 | -const electron = require('electron') | |
5 | 3 … | |
6 | 4 … | exports.gives = nest('app.html.app') |
7 | 5 … | |
8 | 6 … | exports.needs = nest({ |
9 | - 'app.async.catchLinkClick': 'first', | |
10 | 7 … | 'app.html.tabs': 'first', |
11 | 8 … | 'app.page.errors': 'first', |
12 | - 'app.sync.catchKeyboardShortcut': 'first', | |
13 | 9 … | 'app.sync.goTo': 'first', |
14 | 10 … | 'app.sync.initialise': 'first', |
15 | 11 … | 'app.sync.window': 'reduce', |
16 | 12 … | 'history.obs.location': 'first', |
17 | 13 … | 'history.sync.push': 'first', |
18 | 14 … | 'router.sync.router': 'first', |
19 | - 'styles.css': 'reduce', | |
20 | 15 … | 'settings.sync.get': 'first', |
21 | 16 … | 'settings.sync.set': 'first' |
22 | 17 … | }) |
23 | 18 … | |
@@ -26,60 +21,18 @@ | ||
26 | 21 … | |
27 | 22 … | function app () { |
28 | 23 … | console.log('STARTING app') |
29 | 24 … | |
30 | - api.app.sync.initialise() | |
31 | - | |
32 | 25 … | window = api.app.sync.window(window) |
33 | 26 … | |
34 | - const css = values(api.styles.css()).join('\n') | |
35 | - insertCss(css) | |
27 … | + const initialTabs = ['/public', '/inbox', '/notifications'] // NB router converts these to { page: '/public' } | |
28 … | + const App = h('App', api.app.html.tabs(initialTabs)) | |
36 | 29 … | |
37 | - const initialTabs = ['/public', '/inbox', '/notifications'] | |
38 | - // NB router converts these to { page: '/public' } | |
39 | - const tabs = api.app.html.tabs(initialTabs) | |
30 … | + api.app.sync.initialise(App) | |
31 … | + // runs all the functions in app/sync/initialise | |
40 | 32 … | |
41 | - const App = h('App', tabs) | |
42 | - | |
43 | - // Catch user actions | |
44 | - api.app.sync.catchKeyboardShortcut(window, { tabs }) | |
45 | - api.app.async.catchLinkClick(App) | |
46 | - | |
47 | 33 … | api.history.obs.location()(loc => api.app.sync.goTo(loc || {})) |
48 | 34 … | |
49 | - // Catch errors | |
50 | - var { container: errorPage, addError } = api.router.sync.router('/errors') | |
51 | - window.addEventListener('error', ev => { | |
52 | - if (!tabs.has('/errors')) tabs.add(errorPage, true) | |
53 | - | |
54 | - addError(ev.error || ev) | |
55 | - }) | |
56 | - | |
57 | - /// /// TODO - extract this to keep patch-lite isolated from electron | |
58 | - const { getCurrentWebContents, getCurrentWindow } = electron.remote | |
59 | - window.addEventListener('resize', () => { | |
60 | - var wc = getCurrentWebContents() | |
61 | - wc && wc.getZoomFactor((zf) => { | |
62 | - api.settings.sync.set({ | |
63 | - electron: { | |
64 | - zoomFactor: zf, | |
65 | - windowBounds: getCurrentWindow().getBounds() | |
66 | - } | |
67 | - }) | |
68 | - }) | |
69 | - }) | |
70 | - | |
71 | - var zoomFactor = api.settings.sync.get('electron.zoomFactor') | |
72 | - if (zoomFactor) { getCurrentWebContents().setZoomFactor(zoomFactor) } | |
73 | - | |
74 | - var bounds = api.settings.sync.get('electron.windowBounds') | |
75 | - if (bounds) { getCurrentWindow().setBounds(bounds) } | |
76 | - /// /// | |
77 | - | |
78 | 35 … | return App |
79 | 36 … | } |
80 | 37 … | } |
81 | 38 … | |
82 | -function values (object) { | |
83 | - const keys = Object.keys(object) | |
84 | - return keys.map(k => object[k]) | |
85 | -} |
app/html/tabs.js | ||
---|---|---|
@@ -51,9 +51,15 @@ | ||
51 | 51 … | onSelect, |
52 | 52 … | onClose, |
53 | 53 … | append: h('div.navExtra', [ search, menu ]) |
54 | 54 … | }) |
55 | - _tabs.currentPage = () => _tabs.get(_tabs.selected[0]).firstChild | |
55 … | + _tabs.currentPage = () => { | |
56 … | + const currentPage = _tabs.get(_tabs.selected[0]) | |
57 … | + return currentPage && currentPage.firstChild | |
58 … | + } | |
59 … | + _tabs.nextTab = () => _tabs.currentPage() && _tabs.selectRelative(1) | |
60 … | + _tabs.previousTab = () => _tabs.currentPage() && _tabs.selectRelative(-1) | |
61 … | + _tabs.closeCurrentTab = () => _tabs.currentPage() && _tabs.remove(_tabs.selected[0]) | |
56 | 62 … | |
57 | 63 … | // # TODO: review - this works but is strange |
58 | 64 … | initialTabs.forEach(p => api.app.sync.goTo(p)) |
59 | 65 … | api.app.sync.goTo(initialTabs[0]) |
app/sync/initialise/electronState.js | ||
---|---|---|
@@ -1,0 +1,38 @@ | ||
1 … | +const nest = require('depnest') | |
2 … | +const electron = require('electron') | |
3 … | + | |
4 … | +exports.gives = nest('app.sync.initialise') | |
5 … | + | |
6 … | +exports.needs = nest({ | |
7 … | + 'settings.sync.get': 'first', | |
8 … | + 'settings.sync.set': 'first', | |
9 … | +}) | |
10 … | + | |
11 … | +exports.create = function (api) { | |
12 … | + return nest('app.sync.initialise', errorCatcher) | |
13 … | + | |
14 … | + function errorCatcher () { | |
15 … | + /// /// TODO - extract this to keep patch-lite isolated from electron | |
16 … | + const { getCurrentWebContents, getCurrentWindow } = electron.remote | |
17 … | + window.addEventListener('resize', () => { | |
18 … | + var wc = getCurrentWebContents() | |
19 … | + wc && wc.getZoomFactor((zf) => { | |
20 … | + api.settings.sync.set({ | |
21 … | + electron: { | |
22 … | + zoomFactor: zf, | |
23 … | + windowBounds: getCurrentWindow().getBounds() | |
24 … | + } | |
25 … | + }) | |
26 … | + }) | |
27 … | + }) | |
28 … | + | |
29 … | + var zoomFactor = api.settings.sync.get('electron.zoomFactor') | |
30 … | + if (zoomFactor) { getCurrentWebContents().setZoomFactor(zoomFactor) } | |
31 … | + | |
32 … | + var bounds = api.settings.sync.get('electron.windowBounds') | |
33 … | + if (bounds) { getCurrentWindow().setBounds(bounds) } | |
34 … | + /// /// | |
35 … | + } | |
36 … | +} | |
37 … | + | |
38 … | + |
app/sync/initialise/errorCatcher.js | ||
---|---|---|
@@ -1,0 +1,24 @@ | ||
1 … | +const nest = require('depnest') | |
2 … | + | |
3 … | +exports.gives = nest('app.sync.initialise') | |
4 … | + | |
5 … | +exports.needs = nest({ | |
6 … | + 'router.sync.router': 'first', | |
7 … | + 'app.html.tabs': 'first' | |
8 … | +}) | |
9 … | + | |
10 … | +exports.create = function (api) { | |
11 … | + return nest('app.sync.initialise', errorCatcher) | |
12 … | + | |
13 … | + function errorCatcher () { | |
14 … | + const tabs = api.app.html.tabs() | |
15 … | + | |
16 … | + var { container: errorPage, addError } = api.router.sync.router('/errors') | |
17 … | + window.addEventListener('error', ev => { | |
18 … | + if (!tabs.has('/errors')) tabs.add(errorPage, true) | |
19 … | + | |
20 … | + addError(ev.error || ev) | |
21 … | + }) | |
22 … | + } | |
23 … | +} | |
24 … | + |
app/sync/initialise/styles.js | ||
---|---|---|
@@ -1,0 +1,24 @@ | ||
1 … | +const nest = require('depnest') | |
2 … | +const insertCss = require('insert-css') | |
3 … | + | |
4 … | +exports.gives = nest('app.sync.initialise') | |
5 … | + | |
6 … | +exports.needs = nest({ | |
7 … | + 'styles.css': 'reduce', | |
8 … | +}) | |
9 … | + | |
10 … | + | |
11 … | +exports.create = function (api) { | |
12 … | + return nest('app.sync.initialise', styles) | |
13 … | + | |
14 … | + function styles () { | |
15 … | + const css = values(api.styles.css()).join('\n') | |
16 … | + insertCss(css) | |
17 … | + } | |
18 … | +} | |
19 … | + | |
20 … | +function values (object) { | |
21 … | + const keys = Object.keys(object) | |
22 … | + return keys.map(k => object[k]) | |
23 … | +} | |
24 … | + |
app/sync/initialise/userActionListeners.js | ||
---|---|---|
@@ -1,0 +1,35 @@ | ||
1 … | +const nest = require('depnest') | |
2 … | + | |
3 … | +exports.gives = nest('app.sync.initialise') | |
4 … | + | |
5 … | +exports.needs = nest({ | |
6 … | + 'app.async.catchLinkClick': 'first', | |
7 … | + 'app.sync.catchKeyboardShortcut': 'first', | |
8 … | + 'app.html.tabs': 'first', | |
9 … | +}) | |
10 … | + | |
11 … | + | |
12 … | +exports.create = function (api) { | |
13 … | + return nest('app.sync.initialise', userActionListeners) | |
14 … | + | |
15 … | + function userActionListeners (App) { | |
16 … | + const tabs = api.app.html.tabs() | |
17 … | + | |
18 … | + api.app.sync.catchKeyboardShortcut(window) | |
19 … | + api.app.async.catchLinkClick(App) | |
20 … | + | |
21 … | + electron.ipcRenderer.on('nextTab', () => { | |
22 … | + tabs.nextTab() | |
23 … | + }) | |
24 … | + | |
25 … | + electron.ipcRenderer.on('previousTab', () => { | |
26 … | + tabs.previousTab() | |
27 … | + }) | |
28 … | + | |
29 … | + electron.ipcRenderer.on('closeTab', () => { | |
30 … | + tabs.closeCurrentTab() | |
31 … | + }) | |
32 … | + | |
33 … | + } | |
34 … | +} | |
35 … | + |
index.js | |||
---|---|---|---|
@@ -21,18 +21,38 @@ | |||
21 | 21 … | { role: 'zoomout' }, | |
22 | 22 … | { type: 'separator' }, | |
23 | 23 … | { role: 'togglefullscreen' } | |
24 | 24 … | ] | |
25 | - if (process.platform === 'darwin') { | ||
26 | - var win = menu.find(x => x.label === 'Window') | ||
27 | - win.submenu = [ | ||
28 | - { role: 'minimize' }, | ||
29 | - { role: 'zoom' }, | ||
30 | - { role: 'close', label: 'Close' }, | ||
31 | - { type: 'separator' }, | ||
32 | - { role: 'front' } | ||
33 | - ] | ||
34 | - } | ||
25 … | + var win = menu.find(x => x.label === 'Window') | ||
26 … | + win.submenu = [ | ||
27 … | + { role: 'minimize' }, | ||
28 … | + { role: 'zoom' }, | ||
29 … | + { role: 'close', label: 'Close Window', accelerator: 'CmdOrCtrl+Shift+W' }, | ||
30 … | + { type: 'separator' }, | ||
31 … | + { | ||
32 … | + label: 'Close Tab', | ||
33 … | + accelerator: 'CmdOrCtrl+W', | ||
34 … | + click() { | ||
35 … | + windows.main.webContents.send('closeTab') | ||
36 … | + } | ||
37 … | + }, | ||
38 … | + { | ||
39 … | + label: 'Select Next Tab', | ||
40 … | + accelerator: 'CmdOrCtrl+Shift+]', | ||
41 … | + click() { | ||
42 … | + windows.main.webContents.send('nextTab') | ||
43 … | + } | ||
44 … | + }, | ||
45 … | + { | ||
46 … | + label: 'Select Previous Tab', | ||
47 … | + accelerator: 'CmdOrCtrl+Shift+[', | ||
48 … | + click() { | ||
49 … | + windows.main.webContents.send('previousTab') | ||
50 … | + } | ||
51 … | + }, | ||
52 … | + { type: 'separator' }, | ||
53 … | + { role: 'front' } | ||
54 … | + ] | ||
35 | 55 … | ||
36 | 56 … | Menu.setApplicationMenu(Menu.buildFromTemplate(menu)) | |
37 | 57 … | ||
38 | 58 … | startBackgroundProcess() |
Built with git-ssb-web