This commit is contained in:
runargs 2020-10-15 13:04:41 -04:00
commit e040699b8e
12 changed files with 727 additions and 125 deletions

View File

@ -13,13 +13,14 @@ a flexibility update.
higher up on the list = higher priority of application = loaded last in order to override others. higher up on the list = higher priority of application = loaded last in order to override others.
(excluding the core, which though pinned to the top of the list is always loaded first so theming (excluding the core, which though pinned to the top of the list is always loaded first so theming
variables can be modified.) variables can be modified.)
- new: in-page columns disabled/wrapped and wider pages when the window is narrower than 600px - new: in-page columns are disabled/wrapped and pages are wider when
for improved responsiveness. the window is narrower than 600px for improved responsiveness.
- new: relaunch button in tray menu. - new: relaunch button in tray menu.
- new: a core mod option to make transitions snappy/0s. - new: a core mod option to make transitions snappy/0s.
- new: a core mod option for a default page id/url (all new windows will load it instead of the - new: a core mod option for a default page id/url (all new windows will load it instead of the
normal "most recent" page). normal "most recent" page).
- new: css variables for increasing line spacing/paragraph margins. - new: css variables for increasing line spacing/paragraph margins.
- new: patch the notion:// url scheme/protocol to work on linux.
- improved: menu will now respect integrated titlebar setting. - improved: menu will now respect integrated titlebar setting.
- improved: use keyup listeners instead of a globalShortcut for the enhancements menu toggle. - improved: use keyup listeners instead of a globalShortcut for the enhancements menu toggle.
- bugfix: removed messenger emoji set as the provider no longer supports it. - bugfix: removed messenger emoji set as the provider no longer supports it.
@ -34,7 +35,10 @@ a flexibility update.
- bugfix: remove focus mode footer from neutral theme. - bugfix: remove focus mode footer from neutral theme.
- bugfix: improvements to the colour theming, particularly to make real- and fake-light/dark - bugfix: improvements to the colour theming, particularly to make real- and fake-light/dark
modes (as applied by the night shift extension) look consistent. modes (as applied by the night shift extension) look consistent.
relevant variables (assuming all are prefixed by `--theme_[dark|light]--`):
`box-shadow`, `box-shadow_strong`, `select_input`, and `ui-border`
- bugfix: font sizing applied to overlays/previews. - bugfix: font sizing applied to overlays/previews.
- bugfix: removed typo in variable name for brown text.
- tweak: sticky table/list rows. - tweak: sticky table/list rows.
- theme: "material ocean" = an oceanic colour palette. - theme: "material ocean" = an oceanic colour palette.
- theme: "dracula" = a theme based on the popular dracula color palette - theme: "dracula" = a theme based on the popular dracula color palette
@ -50,6 +54,7 @@ a fork of notion-deb-builder that does generate an app.asar has been created and
- bugfix: night shift working on macOS. - bugfix: night shift working on macOS.
- bugfix: windows are properly hidden/shown on macOS. - bugfix: windows are properly hidden/shown on macOS.
- extension: "tweaks" = common layout changes. - extension: "tweaks" = common layout changes.
- update themes to new variables.
### v0.9.1 (2020-09-26) ### v0.9.1 (2020-09-26)

View File

@ -114,8 +114,16 @@ team to take to heart for future improvements."
## features ## features
once applied, modules can be configured via the graphical menu, which is opened from most of the enhancer's functionality is split into configurable enhancement modules,
the tray/menubar icon or with `OPTION/ALT+E`. but some basic improvements are built in by default:
- the notion:// url scheme/protocol is patched to work on linux.
- in-page columns are disabled/wrapped and pages are wider when
the window is narrower than 600px for improved responsiveness.
- a tray/menubar icon: links relevant to the enhancer + buttons to manage notion windows.
once applied, modules can be configured via the graphical menu,
which is opened from the tray/menubar icon or with `OPTION/ALT+E`.
![](https://user-images.githubusercontent.com/16874139/93692603-954fd980-fb38-11ea-9d52-82ac53449d33.png) ![](https://user-images.githubusercontent.com/16874139/93692603-954fd980-fb38-11ea-9d52-82ac53449d33.png)

View File

@ -14,18 +14,20 @@ module.exports = (store, __exports) => {
/\\/g, /\\/g,
'/' '/'
)}/app/helpers/notionIpc.js`), )}/app/helpers/notionIpc.js`),
{ toKeyEvent } = require('keyboardevent-from-electron-accelerator'); { toKeyEvent } = require('keyboardevent-from-electron-accelerator'),
tabsEnabled = (store('mods')['e1692c29-475e-437b-b7ff-3eee872e1a42'] || {})
.enabled;
document.defaultView.addEventListener('keyup', (event) => { document.defaultView.addEventListener('keyup', (event) => {
// additional hotkeys // additional hotkeys
if (event.code === 'F5') location.reload(); if (event.key === 'F5') location.reload();
// open menu on hotkey toggle // open menu on hotkey toggle
const hotkey = toKeyEvent(store().menu_toggle); const hotkey = toKeyEvent(store().menu_toggle);
let triggered = true; let triggered = true;
for (let prop in hotkey) for (let prop in hotkey)
if (hotkey[prop] !== event[prop]) triggered = false; if (hotkey[prop] !== event[prop]) triggered = false;
if (triggered) electron.ipcRenderer.send('enhancer:open-menu'); if (triggered) electron.ipcRenderer.send('enhancer:open-menu');
if ((store('mods')['e1692c29-475e-437b-b7ff-3eee872e1a42'] || {}).enabled) { if (tabsEnabled) {
// switch between tabs via key modifier // switch between tabs via key modifier
const select_tab_modifier = toKeyEvent( const select_tab_modifier = toKeyEvent(
store('e1692c29-475e-437b-b7ff-3eee872e1a42').select_modifier store('e1692c29-475e-437b-b7ff-3eee872e1a42').select_modifier
@ -33,7 +35,22 @@ module.exports = (store, __exports) => {
let triggered = true; let triggered = true;
for (let prop in select_tab_modifier) for (let prop in select_tab_modifier)
if (select_tab_modifier[prop] !== event[prop]) triggered = false; if (select_tab_modifier[prop] !== event[prop]) triggered = false;
if (triggered && [1, 2, 3, 4, 5, 6, 7, 8, 9].includes(+event.key)) if (
triggered &&
[
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'ArrowRight',
'ArrowLeft',
].includes(event.key)
)
electron.ipcRenderer.sendToHost('enhancer:select-tab', event.key); electron.ipcRenderer.sendToHost('enhancer:select-tab', event.key);
// create/close tab keybindings // create/close tab keybindings
const new_tab_keybinding = toKeyEvent( const new_tab_keybinding = toKeyEvent(
@ -49,7 +66,6 @@ module.exports = (store, __exports) => {
triggered = true; triggered = true;
for (let prop in close_tab_keybinding) for (let prop in close_tab_keybinding)
if (close_tab_keybinding[prop] !== event[prop]) triggered = false; if (close_tab_keybinding[prop] !== event[prop]) triggered = false;
console.log(triggered, event);
if (triggered) electron.ipcRenderer.sendToHost('enhancer:close-tab'); if (triggered) electron.ipcRenderer.sendToHost('enhancer:close-tab');
} }
}); });
@ -70,11 +86,7 @@ module.exports = (store, __exports) => {
document.body.classList.add('snappy-transitions'); document.body.classList.add('snappy-transitions');
// frameless // frameless
if ( if (store().frameless && !store().tiling_mode && !tabsEnabled) {
store().frameless &&
!store().tiling_mode &&
!(store('mods')['e1692c29-475e-437b-b7ff-3eee872e1a42'] || {}).enabled
) {
document.body.classList.add('frameless'); document.body.classList.add('frameless');
// draggable area // draggable area
document document
@ -87,9 +99,7 @@ module.exports = (store, __exports) => {
} }
// window buttons // window buttons
if ( if (!tabsEnabled) {
!(store('mods')['e1692c29-475e-437b-b7ff-3eee872e1a42'] || {}).enabled
) {
const buttons = require('./buttons.js')(store); const buttons = require('./buttons.js')(store);
document document
.querySelector('.notion-topbar > div[style*="display: flex"]') .querySelector('.notion-topbar > div[style*="display: flex"]')
@ -186,9 +196,7 @@ module.exports = (store, __exports) => {
'--theme--code_inline-background', '--theme--code_inline-background',
].map((rule) => [rule, getStyle(rule)]) ].map((rule) => [rule, getStyle(rule)])
); );
if ( if (tabsEnabled) {
(store('mods')['e1692c29-475e-437b-b7ff-3eee872e1a42'] || {}).enabled
) {
electron.ipcRenderer.sendToHost( electron.ipcRenderer.sendToHost(
'enhancer:set-tab-theme', 'enhancer:set-tab-theme',
[ [
@ -214,7 +222,7 @@ module.exports = (store, __exports) => {
); );
electron.ipcRenderer.on('enhancer:get-menu-theme', setThemeVars); electron.ipcRenderer.on('enhancer:get-menu-theme', setThemeVars);
if ((store('mods')['e1692c29-475e-437b-b7ff-3eee872e1a42'] || {}).enabled) { if (tabsEnabled) {
let tab_title = ''; let tab_title = '';
__electronApi.setWindowTitle = (title) => { __electronApi.setWindowTitle = (title) => {
if (tab_title !== title) { if (tab_title !== title) {

View File

@ -79,7 +79,7 @@ module.exports = (store, __exports) => {
}); });
electron.app.on('before-quit', () => (intended_quit = true)); electron.app.on('before-quit', () => (intended_quit = true));
window.loadURL(__exports.getIndexUrl(relativeUrl)); window.loadURL(__exports.getIndexUrl(relativeUrl));
window.webContents.openDevTools(); // window.webContents.openDevTools();
return window; return window;
}; };
return __exports.createWindow; return __exports.createWindow;

View File

@ -153,7 +153,7 @@
font-family: var(--theme--font_code) !important; font-family: var(--theme--font_code) !important;
} }
.notion-frame .notion-page-block div[placeholder='Untitled'], .notion-frame .notion-page-block div[placeholder='Untitled'],
.notion-overlay-container .notion-page-block div[placeholder='Untitled']{ .notion-overlay-container .notion-page-block div[placeholder='Untitled'] {
font-size: calc( font-size: calc(
var(--theme--font_body-size) * (var(--theme--font_heading1-size) / 1em) var(--theme--font_body-size) * (var(--theme--font_heading1-size) / 1em)
) !important; ) !important;
@ -174,15 +174,19 @@
) !important; ) !important;
} }
.notion-frame .notion-scroller.vertical.horizontal [style*='font-size: 14px'], .notion-frame .notion-scroller.vertical.horizontal [style*='font-size: 14px'],
.notion-overlay-container .notion-scroller.vertical [style*='font-size: 14px']{ .notion-overlay-container .notion-scroller.vertical [style*='font-size: 14px'] {
font-size: var(--theme--font_label-size) !important; font-size: var(--theme--font_label-size) !important;
} }
.notion-frame .notion-scroller.vertical.horizontal .notion-page-content, .notion-frame .notion-scroller.vertical.horizontal .notion-page-content,
.notion-overlay-container .notion-scroller.vertical .notion-page-content { .notion-overlay-container .notion-scroller.vertical .notion-page-content {
font-size: var(--theme--font_body-size) !important; font-size: var(--theme--font_body-size) !important;
} }
.notion-frame .notion-scroller.vertical.horizontal .notion-page-content[style*='font-size: 14px'], .notion-frame
.notion-overlay-container .notion-scroller.vertical .notion-page-content[style*='font-size: 14px'] { .notion-scroller.vertical.horizontal
.notion-page-content[style*='font-size: 14px'],
.notion-overlay-container
.notion-scroller.vertical
.notion-page-content[style*='font-size: 14px'] {
font-size: var(--theme--font_body-size_small) !important; font-size: var(--theme--font_body-size_small) !important;
} }
.notion-code-block [placeholder=' '] { .notion-code-block [placeholder=' '] {
@ -557,7 +561,7 @@
.notion-body:not(.dark) .notion-body:not(.dark)
[style*='color: rgb(100, 71, 58); fill: rgb(100, 71, 58);'] { [style*='color: rgb(100, 71, 58); fill: rgb(100, 71, 58);'] {
fill: var(--theme--text_brown) !important; fill: var(--theme--text_brown) !important;
color: var(--theme--text_brown-text) !important; color: var(--theme--text_brown) !important;
} }
.notion-body.dark [style*='background: rgba(147, 114, 100, 0.5)'], .notion-body.dark [style*='background: rgba(147, 114, 100, 0.5)'],
.notion-body:not(.dark) [style*='background: rgba(140, 46, 0, 0.2)'] { .notion-body:not(.dark) [style*='background: rgba(140, 46, 0, 0.2)'] {

View File

@ -118,7 +118,7 @@ window['__start'] = async () => {
const $popup = document.querySelector('#popup'); const $popup = document.querySelector('#popup');
document.addEventListener('keyup', (event) => { document.addEventListener('keyup', (event) => {
if (event.code === 'F5') location.reload(); if (event.key === 'F5') location.reload();
// further-configuration popup // further-configuration popup
if ( if (
$popup.classList.contains('visible') && $popup.classList.contains('visible') &&
@ -126,12 +126,13 @@ window['__start'] = async () => {
) )
$popup.classList.remove('visible'); $popup.classList.remove('visible');
// close window on hotkey toggle // close window on hotkey toggle
console.log();
const hotkey = toKeyEvent(coreStore().menu_toggle); const hotkey = toKeyEvent(coreStore().menu_toggle);
let triggered = true; let triggered = true;
for (let prop in hotkey) for (let prop in hotkey)
if (hotkey[prop] !== event[prop]) triggered = false; if (hotkey[prop] !== event[prop]) triggered = false;
if (triggered) electron.remote.getCurrentWindow().close(); if (triggered || ((event.ctrlKey || event.metaKey) && event.key === 'w'))
electron.remote.getCurrentWindow().close();
console.log(event.ctrlKey, event.key);
// focus search // focus search
const meta = const meta =
!(event.ctrlKey || event.metaKey) && !event.altKey && !event.shiftKey; !(event.ctrlKey || event.metaKey) && !event.altKey && !event.shiftKey;

View File

@ -83,7 +83,8 @@ module.exports = {
], ],
hacks: { hacks: {
'main/main.js': require('./tray.js'), 'main/main.js': require('./tray.js'),
'main/createWindow.js': require('./create.js'), 'main/systemMenu.js': require('./systemMenu.js'),
'main/createWindow.js': require('./createWindow.js'),
'renderer/index.js': require('./render.js'), 'renderer/index.js': require('./render.js'),
'renderer/preload.js': require('./client.js'), 'renderer/preload.js': require('./client.js'),
}, },

View File

@ -42,7 +42,8 @@ module.exports = (store, __exports) => {
searching: false, searching: false,
searchingPeekView: false, searchingPeekView: false,
zoomFactor: 1, zoomFactor: 1,
tabs: new Map([[0, true]]), tabs: new Map([[0, ['notion.so', true]]]),
slideIn: new Set(),
}; };
this.$titlebar = null; this.$titlebar = null;
this.$dragging = null; this.$dragging = null;
@ -74,15 +75,13 @@ module.exports = (store, __exports) => {
// draggable re-ordering // draggable re-ordering
const getTab = ($el) => { const getTab = ($el) => {
if ($el.tagName !== 'BUTTON') $el = $el.parentElement;
if ($el.innerText === '+') if ($el.innerText === '+')
return [null, document.querySelector('.tab.new')]; return [null, document.querySelector('.tab.new')];
if ($el.innerText === '×') $el = $el.parentElement; const tab = Object.entries(this.views.tabs).find(
if (!$el.innerText.endsWith('×')) $el = $el.parentElement;
return (
Object.entries(this.views.tabs).find(
([id, $node]) => $node === $el ([id, $node]) => $node === $el
) || []
); );
return tab ? [+tab[0], tab[1]] : [];
}; };
document.addEventListener('dragstart', (event) => { document.addEventListener('dragstart', (event) => {
if (!this.$titlebar) return; if (!this.$titlebar) return;
@ -110,12 +109,30 @@ module.exports = (store, __exports) => {
.forEach((el) => el.classList.remove('dragged-over')); .forEach((el) => el.classList.remove('dragged-over'));
const from = getTab(this.views.tabs[+this.$dragging]), const from = getTab(this.views.tabs[+this.$dragging]),
to = getTab(event.target); to = getTab(event.target);
if (!from[1].classList.contains('new') && from[0] !== to[0]) if (from[0] !== to[0]) {
to[1].parentElement.insertBefore(from[1], to[1]); if (to[1].classList.contains('new')) {
from[1].classList.remove('slideIn'); const list = new Map(this.state.tabs);
list.delete(from[0]);
list.set(from[0], this.state.tabs.get(from[0]));
this.setState({ tabs: list });
} else {
const list = [...this.state.tabs],
fromIndex = list.findIndex(
([id, [title, open]]) => id === from[0]
),
toIndex = list.findIndex(([id, [title, open]]) => id === to[0]);
list.splice(
toIndex > fromIndex ? toIndex - 1 : toIndex,
0,
list.splice(fromIndex, 1)[0]
);
this.setState({ tabs: new Map(list) });
}
}
this.$dragging = null; this.$dragging = null;
}); });
document.addEventListener('keyup', (event) => { document.addEventListener('keyup', (event) => {
if (!electron.remote.getCurrentWindow().isFocused()) return;
// switch between tabs via key modifier // switch between tabs via key modifier
const select_tab_modifier = toKeyEvent( const select_tab_modifier = toKeyEvent(
store('e1692c29-475e-437b-b7ff-3eee872e1a42').select_modifier store('e1692c29-475e-437b-b7ff-3eee872e1a42').select_modifier
@ -123,7 +140,22 @@ module.exports = (store, __exports) => {
let triggered = true; let triggered = true;
for (let prop in select_tab_modifier) for (let prop in select_tab_modifier)
if (select_tab_modifier[prop] !== event[prop]) triggered = false; if (select_tab_modifier[prop] !== event[prop]) triggered = false;
if (triggered && [1, 2, 3, 4, 5, 6, 7, 8, 9].includes(+event.key)) if (
triggered &&
[
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'ArrowRight',
'ArrowLeft',
].includes(event.key)
)
this.selectTab(event.key); this.selectTab(event.key);
// create/close tab keybindings // create/close tab keybindings
const new_tab_keybinding = toKeyEvent( const new_tab_keybinding = toKeyEvent(
@ -139,8 +171,8 @@ module.exports = (store, __exports) => {
triggered = true; triggered = true;
for (let prop in close_tab_keybinding) for (let prop in close_tab_keybinding)
if (close_tab_keybinding[prop] !== event[prop]) triggered = false; if (close_tab_keybinding[prop] !== event[prop]) triggered = false;
console.log(triggered, event); if (triggered && document.querySelector('.tab.current .close'))
if (triggered) this.closeTab(this.views.current.id); document.querySelector('.tab.current .close').click();
}); });
} }
@ -186,14 +218,31 @@ module.exports = (store, __exports) => {
newTab() { newTab() {
let id = 0; let id = 0;
const list = new Map(this.state.tabs); const list = new Map(this.state.tabs);
while (this.state.tabs.get(id)) id++; while (this.state.tabs.get(id) && this.state.tabs.get(id)[1]) id++;
list.delete(id); list.delete(id);
this.openTab(id, list, true); this.openTab(id, list, true);
} }
openTab(id, state = new Map(this.state.tabs), load) { openTab(id, state = new Map(this.state.tabs), load) {
if (!id && id !== 0) return; if (!id && id !== 0) {
if (state.get(this.views.current.id)[1]) return;
const currentIndex = [...state].findIndex(
([id, [title, open]]) => id === this.views.current.id
);
id = ([...state].find(
([id, [title, open]], tabIndex) => open && tabIndex > currentIndex
) || [...state].find(([id, [title, open]]) => open))[0];
}
const current_src = this.views.current.$el().src;
this.views.current.id = id; this.views.current.id = id;
this.setState({ tabs: state.set(id, true) }, async () => { this.setState(
{
tabs: state.set(id, [
state.get(id) ? state.get(id)[0] : 'notion.so',
true,
]),
slideIn: load ? this.state.slideIn.add(id) : this.state.slideIn,
},
async () => {
this.focusTab(); this.focusTab();
if (load) { if (load) {
await new Promise((res, rej) => { await new Promise((res, rej) => {
@ -217,27 +266,28 @@ module.exports = (store, __exports) => {
this.views.html[id].loadURL( this.views.html[id].loadURL(
store().default_page store().default_page
? idToNotionURL(store().default_page) ? idToNotionURL(store().default_page)
: this.views.current.$el().src : current_src
); );
this.views.html[id].getWebContents().openDevTools(); setTimeout(() => {
const slideIn = new Set(this.state.slideIn);
slideIn.delete(id);
this.setState({ slideIn });
}, 100);
// this.views.html[id].getWebContents().openDevTools();
} }
}); }
);
} }
closeTab(id) { closeTab(id) {
if ((!id && id !== 0) || !this.state.tabs.get(id)) return; if ((!id && id !== 0) || !this.state.tabs.get(id)) return;
const list = new Map(this.state.tabs); const list = new Map(this.state.tabs);
list.delete(id); list.set(id, [list.get(id)[0], false]);
list.set(id, false); if (![...list].filter(([id, [title, open]]) => open).length)
if (![...list].filter(([id, open]) => open).length)
return electron.remote.getCurrentWindow().close(); return electron.remote.getCurrentWindow().close();
while ( this.openTab(
!list.get(this.views.current.id) || this.views.current.id === id ? null : this.views.current.id,
this.views.current.id === id list
) { );
this.views.current.id = this.views.current.id - 1;
if (this.views.current.id < 0) this.views.current.id = list.size - 1;
}
this.setState({ tabs: list }, this.focusTab.bind(this));
} }
focusTab() { focusTab() {
if (this.views.active === this.views.current.id) return; if (this.views.active === this.views.current.id) return;
@ -257,6 +307,15 @@ module.exports = (store, __exports) => {
} }
} }
selectTab(num) { selectTab(num) {
if (num === 'ArrowLeft') {
const prev = document.querySelector('.tab.current')
.previousElementSibling;
if (prev) prev.click();
} else if (num === 'ArrowRight') {
const next = document.querySelector('.tab.current')
.nextElementSibling;
if (next && !next.classList.contains('new')) next.click();
} else {
num = +num; num = +num;
if (num == 9) { if (num == 9) {
document document
@ -271,6 +330,7 @@ module.exports = (store, __exports) => {
document.querySelector('#tabs').children[num - 1].click(); document.querySelector('#tabs').children[num - 1].click();
} }
} }
}
communicateWithView(event) { communicateWithView(event) {
switch (event.channel) { switch (event.channel) {
@ -279,9 +339,15 @@ module.exports = (store, __exports) => {
document.body.style.setProperty(style[0], style[1]); document.body.style.setProperty(style[0], style[1]);
break; break;
case 'enhancer:set-tab-title': case 'enhancer:set-tab-title':
if (this.views.tabs[event.target.id]) { if (this.state.tabs.get(+event.target.id)) {
this.views.tabs[event.target.id].children[0].innerText = this.setState({
event.args[0]; tabs: new Map(
this.state.tabs.set(+event.target.id, [
event.args[0],
this.state.tabs.get(+event.target.id)[1],
])
),
});
const electronWindow = electron.remote.getCurrentWindow(); const electronWindow = electron.remote.getCurrentWindow();
if ( if (
event.target.id == this.views.current.id && event.target.id == this.views.current.id &&
@ -297,8 +363,8 @@ module.exports = (store, __exports) => {
this.newTab(); this.newTab();
break; break;
case 'enhancer:close-tab': case 'enhancer:close-tab':
if (event.target.id == this.views.current.id) if (document.querySelector('.tab.current .close'))
this.closeTab(+event.target.id); document.querySelector('.tab.current .close').click();
break; break;
} }
} }
@ -562,25 +628,25 @@ module.exports = (store, __exports) => {
'div', 'div',
{ id: 'tabs' }, { id: 'tabs' },
...[...this.state.tabs] ...[...this.state.tabs]
.filter(([id, open]) => open) .filter(([id, [title, open]]) => open)
.map(([id, open]) => .map(([id, [title, open]]) =>
React.createElement( React.createElement(
'button', 'button',
{ {
draggable: true,
className: className:
'tab slideIn' + 'tab' +
(id === this.views.current.id ? ' current' : ''), (id === this.views.current.id ? ' current' : '') +
(this.state.slideIn.has(id) ? ' slideIn' : ''),
draggable: true,
onClick: (e) => { onClick: (e) => {
if (!e.target.classList.contains('close')) if (!e.target.classList.contains('close'))
this.openTab(id); this.openTab(id);
}, },
ref: ($tab) => { ref: ($tab) => {
this.views.tabs[id] = $tab; this.views.tabs[id] = $tab;
this.focusTab();
}, },
}, },
React.createElement('span', {}, 'notion.so'), React.createElement('span', {}, title),
React.createElement( React.createElement(
'span', 'span',
{ {
@ -608,7 +674,7 @@ module.exports = (store, __exports) => {
} }
renderNotionContainer() { renderNotionContainer() {
this.views.react = Object.fromEntries( this.views.react = Object.fromEntries(
[...this.state.tabs].map(([id, open]) => { [...this.state.tabs].map(([id, [title, open]]) => {
return [ return [
id, id,
this.views.react[id] || this.views.react[id] ||

476
mods/core/systemMenu.js Normal file
View File

@ -0,0 +1,476 @@
/*
* notion-enhancer
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* under the MIT license
*/
'use strict';
module.exports = (store, __exports) => {
const electron = require('electron'),
fs = require('fs-extra'),
{ __notion } = require('../../pkg/helpers.js'),
createWindow = require(`${__notion}/app/main/createWindow.js`),
config = require(`${__notion}/app/config.js`),
notion_intl = require(`${__notion}/app/shared/notion-intl/index.js`),
localizationHelper = require(`${__notion}/app/helpers/localizationHelper.js`),
isMac = process.platform === 'darwin',
// why is it inversed? i have no idea, but for some reason this is what works
tabsEnabled = !(store('mods')['e1692c29-475e-437b-b7ff-3eee872e1a42'] || {})
.enabled,
menuMessages = notion_intl.defineMessages({
fileMenuTitle: {
id: 'desktopTopbar.fileMenu.title',
defaultMessage: 'File',
},
editMenuTitle: {
id: 'desktopTopbar.editMenu.title',
defaultMessage: 'Edit',
},
viewMenuTitle: {
id: 'desktopTopbar.viewMenu.title',
defaultMessage: 'View',
},
windowMenuTitle: {
id: 'desktopTopbar.windowMenu.title',
defaultMessage: 'Window',
},
helpTitle: {
id: 'desktopTopbar.helpMenu.title',
defaultMessage: 'Help',
},
newWindow: {
id: 'desktopTopbar.fileMenu.newWindow',
defaultMessage: 'New Window',
},
closeWindow: {
id: 'desktopTopbar.fileMenu.close',
defaultMessage: 'Close Window',
},
quit: {
id: 'desktopTopbar.fileMenu.quit',
defaultMessage: 'Exit',
},
undo: {
id: 'desktopTopbar.editMenu.undo',
defaultMessage: 'Undo',
},
redo: {
id: 'desktopTopbar.editMenu.redo',
defaultMessage: 'Redo',
},
cut: {
id: 'desktopTopbar.editMenu.cut',
defaultMessage: 'Cut',
},
copy: {
id: 'desktopTopbar.editMenu.copy',
defaultMessage: 'Copy',
},
paste: {
id: 'desktopTopbar.editMenu.paste',
defaultMessage: 'Paste',
},
selectAll: {
id: 'desktopTopbar.editMenu.selectAll',
defaultMessage: 'Select All',
},
startSpeaking: {
id: 'desktopTopbar.editMenu.speech.startSpeaking',
defaultMessage: 'Start Speaking',
},
stopSpeaking: {
id: 'desktopTopbar.editMenu.speech.stopSpeaking',
defaultMessage: 'Stop Speaking',
},
speech: {
id: 'desktopTopbar.editMenu.speech',
defaultMessage: 'Speech',
},
reload: {
id: 'desktopTopbar.viewMenu.reload',
defaultMessage: 'Reload',
},
togglefullscreen: {
id: 'desktopTopbar.viewMenu.togglefullscreen',
defaultMessage: 'Toggle Full Screen',
},
toggleDevTools: {
id: 'desktopTopbar.toggleDevTools',
defaultMessage: 'Toggle Developer Tools',
},
toggleWindowDevTools: {
id: 'desktopTopbar.toggleWindowDevTools',
defaultMessage: 'Toggle Window Developer Tools',
},
maximize: {
id: 'desktopTopbar.windowMenu.maximize',
defaultMessage: 'Maximize',
},
minimize: {
id: 'desktopTopbar.windowMenu.minimize',
defaultMessage: 'Minimize',
},
zoom: {
id: 'desktopTopbar.windowMenu.zoom',
defaultMessage: 'Zoom',
},
front: {
id: 'desktopTopbar.windowMenu.front',
defaultMessage: 'Front',
},
close: {
id: 'desktopTopbar.windowMenu.close',
defaultMessage: 'Close',
},
help: {
id: 'desktopTopbar.helpMenu.openHelpAndSupport',
defaultMessage: 'Open Help & Support',
},
reset: {
id: 'desktopTopbar.appMenu.resetAppAndClearData',
defaultMessage: 'Reset App & Clear Local Data',
},
about: {
id: 'desktopTopbar.appMenu.about',
defaultMessage: 'About Notion',
},
services: {
id: 'desktopTopbar.appMenu.services',
defaultMessage: 'Services',
},
hide: { id: 'desktopTopbar.appMenu.hide', defaultMessage: 'Hide Notion' },
hideOthers: {
id: 'desktopTopbar.appMenu.hideOthers',
defaultMessage: 'Hide Others',
},
unhide: {
id: 'desktopTopbar.appMenu.unhide',
defaultMessage: 'Show All',
},
quitMac: { id: 'desktopTopbar.appMenu.quit', defaultMessage: 'Quit' },
}),
escapeAmpersand = (message) => message.replace(/&/g, '&&');
__exports.setupSystemMenu = (locale) => {
const intl = localizationHelper.createIntlShape(locale),
fileMenu = {
role: 'fileMenu',
label: escapeAmpersand(intl.formatMessage(menuMessages.fileMenuTitle)),
submenu: isMac
? [
{
label: escapeAmpersand(
intl.formatMessage(menuMessages.newWindow)
),
accelerator: 'CmdOrCtrl+Shift+N',
click: () => createWindow.createWindow(),
},
...(tabsEnabled
? [
{
role: 'close',
label: escapeAmpersand(
intl.formatMessage(menuMessages.closeWindow)
),
},
]
: []),
]
: [
{
label: escapeAmpersand(
intl.formatMessage(menuMessages.newWindow)
),
accelerator: 'CmdOrCtrl+Shift+N',
click: () => createWindow.createWindow(),
},
...(tabsEnabled
? [
{
role: 'quit',
label: escapeAmpersand(
intl.formatMessage(menuMessages.quit)
),
},
]
: []),
],
},
editMenu = {
role: 'editMenu',
label: escapeAmpersand(intl.formatMessage(menuMessages.editMenuTitle)),
submenu: isMac
? [
{
role: 'undo',
label: escapeAmpersand(intl.formatMessage(menuMessages.undo)),
},
{
role: 'redo',
label: escapeAmpersand(intl.formatMessage(menuMessages.redo)),
},
{ type: 'separator' },
{
role: 'cut',
label: escapeAmpersand(intl.formatMessage(menuMessages.cut)),
},
{
role: 'copy',
label: escapeAmpersand(intl.formatMessage(menuMessages.copy)),
},
{
role: 'paste',
label: escapeAmpersand(intl.formatMessage(menuMessages.paste)),
},
{
role: 'selectAll',
label: escapeAmpersand(
intl.formatMessage(menuMessages.selectAll)
),
},
{ type: 'separator' },
{
label: escapeAmpersand(intl.formatMessage(menuMessages.speech)),
submenu: [
{
role: 'startSpeaking',
label: escapeAmpersand(
intl.formatMessage(menuMessages.startSpeaking)
),
},
{
role: 'stopSpeaking',
label: escapeAmpersand(
intl.formatMessage(menuMessages.stopSpeaking)
),
},
],
},
]
: [
{
role: 'undo',
label: escapeAmpersand(intl.formatMessage(menuMessages.undo)),
},
{
role: 'redo',
label: escapeAmpersand(intl.formatMessage(menuMessages.redo)),
},
{ type: 'separator' },
{
role: 'cut',
label: escapeAmpersand(intl.formatMessage(menuMessages.cut)),
},
{
role: 'copy',
label: escapeAmpersand(intl.formatMessage(menuMessages.copy)),
},
{
role: 'paste',
label: escapeAmpersand(intl.formatMessage(menuMessages.paste)),
},
{ type: 'separator' },
{
role: 'selectAll',
label: escapeAmpersand(
intl.formatMessage(menuMessages.selectAll)
),
},
],
},
viewMenu = {
role: 'viewMenu',
label: escapeAmpersand(intl.formatMessage(menuMessages.viewMenuTitle)),
submenu: [
{
label: escapeAmpersand(intl.formatMessage(menuMessages.reload)),
accelerator: 'CmdOrCtrl+R',
click() {
const focusedWebContents = electron.webContents.getFocusedWebContents();
if (focusedWebContents) {
if (focusedWebContents.hostWebContents) {
for (const webContentsInstance of electron.webContents.getAllWebContents()) {
if (
webContentsInstance.hostWebContents ===
focusedWebContents.hostWebContents
) {
webContentsInstance.reload();
}
}
} else {
focusedWebContents.reload();
}
}
},
},
{
label: escapeAmpersand(
intl.formatMessage(menuMessages.toggleDevTools)
),
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
click() {
let focusedWebContents = electron.webContents.getFocusedWebContents();
if (focusedWebContents) {
const focusedWebContentsUrl = focusedWebContents.getURL();
if (
focusedWebContentsUrl.startsWith('file://') &&
focusedWebContentsUrl.endsWith('/search.html')
) {
const notionWebviewWebContents = electron.webContents
.getAllWebContents()
.find(
(webContentsInstance) =>
webContentsInstance.hostWebContents ===
focusedWebContents.hostWebContents &&
webContentsInstance !== focusedWebContents
);
if (notionWebviewWebContents) {
focusedWebContents = notionWebviewWebContents;
}
}
focusedWebContents.toggleDevTools();
}
},
},
{
label: escapeAmpersand(
intl.formatMessage(menuMessages.toggleWindowDevTools)
),
accelerator: isMac ? 'Shift+Alt+Command+I' : 'Alt+Ctrl+Shift+I',
visible: false,
click(menuItem, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.toggleDevTools();
}
},
},
{ type: 'separator' },
{
role: 'togglefullscreen',
label: escapeAmpersand(
intl.formatMessage(menuMessages.togglefullscreen)
),
},
],
},
windowMenu = {
role: 'windowMenu',
label: escapeAmpersand(
intl.formatMessage(menuMessages.windowMenuTitle)
),
submenu: isMac
? [
{
role: 'minimize',
label: escapeAmpersand(
intl.formatMessage(menuMessages.minimize)
),
},
{
role: 'zoom',
label: escapeAmpersand(intl.formatMessage(menuMessages.zoom)),
},
{ type: 'separator' },
{
role: 'front',
label: escapeAmpersand(intl.formatMessage(menuMessages.front)),
},
]
: [
{
role: 'minimize',
label: escapeAmpersand(
intl.formatMessage(menuMessages.minimize)
),
},
{
label: escapeAmpersand(
intl.formatMessage(menuMessages.maximize)
),
click(item, focusedWindow) {
if (focusedWindow) {
if (focusedWindow.isMaximized()) {
focusedWindow.unmaximize();
} else {
focusedWindow.maximize();
}
}
},
},
...(tabsEnabled
? [
{
role: 'close',
label: escapeAmpersand(
intl.formatMessage(menuMessages.close)
),
},
]
: []),
],
},
helpMenu = {
role: 'help',
label: escapeAmpersand(intl.formatMessage(menuMessages.helpTitle)),
submenu: [
{
label: escapeAmpersand(intl.formatMessage(menuMessages.help)),
click() {
electron.shell.openExternal(config.default.baseURL + '/help');
},
},
],
},
appMenu = {
role: 'appMenu',
submenu: [
{
role: 'about',
label: escapeAmpersand(intl.formatMessage(menuMessages.about)),
},
{ type: 'separator' },
{
label: escapeAmpersand(intl.formatMessage(menuMessages.reset)),
async click(item, focusedWindow) {
await fs.remove(electron.app.getPath('userData'));
electron.app.relaunch();
electron.app.exit();
},
},
{ type: 'separator' },
{
role: 'services',
label: escapeAmpersand(intl.formatMessage(menuMessages.services)),
},
{ type: 'separator' },
{
role: 'hide',
label: escapeAmpersand(intl.formatMessage(menuMessages.hide)),
},
{
role: 'hideOthers',
label: escapeAmpersand(intl.formatMessage(menuMessages.hideOthers)),
},
{
role: 'unhide',
label: escapeAmpersand(intl.formatMessage(menuMessages.unhide)),
},
...(tabsEnabled
? [
{ type: 'separator' },
{
role: 'quit',
label: escapeAmpersand(
intl.formatMessage(menuMessages.quitMac)
),
},
]
: []),
],
},
template = [fileMenu, editMenu, viewMenu, windowMenu, helpMenu];
if (isMac) template.unshift(appMenu);
const menu = electron.Menu.buildFromTemplate(template);
electron.Menu.setApplicationMenu(menu);
};
};

View File

@ -33,7 +33,7 @@ module.exports = (store, __exports) => {
// menu // menu
electron.ipcMain.on('enhancer:open-menu', (event, arg) => { electron.ipcMain.on('enhancer:open-menu', (event, arg) => {
openExtensionMenu(); openEnhancerMenu();
}); });
electron.ipcMain.on('enhancer:set-menu-theme', (event, arg) => { electron.ipcMain.on('enhancer:set-menu-theme', (event, arg) => {
if (!enhancer_menu) return; if (!enhancer_menu) return;
@ -81,13 +81,13 @@ module.exports = (store, __exports) => {
}; };
} }
function openExtensionMenu() { function openEnhancerMenu() {
if (enhancer_menu) return enhancer_menu.show(); if (enhancer_menu) return enhancer_menu.show();
const window_state = require(`${helpers.__notion.replace( const window_state = require(`${helpers.__notion.replace(
/\\/g, /\\/g,
'/' '/'
)}/app/node_modules/electron-window-state/index.js`)({ )}/app/node_modules/electron-window-state/index.js`)({
file: 'menu-windowstate.json', file: 'menu.windowstate.json',
path: helpers.__data, path: helpers.__data,
defaultWidth: 275, defaultWidth: 275,
defaultHeight: 600, defaultHeight: 600,
@ -106,7 +106,7 @@ module.exports = (store, __exports) => {
width: window_state.width, width: window_state.width,
height: window_state.height, height: window_state.height,
webPreferences: { webPreferences: {
preload: path.resolve(`${__dirname}/menu.js`), preload: path.resolve(`${__dirname}/enhancerMenu.js`),
nodeIntegration: true, nodeIntegration: true,
session: electron.session.fromPartition('persist:notion'), session: electron.session.fromPartition('persist:notion'),
}, },
@ -116,6 +116,7 @@ module.exports = (store, __exports) => {
window_state.saveState(enhancer_menu); window_state.saveState(enhancer_menu);
enhancer_menu = null; enhancer_menu = null;
}); });
// enhancer_menu.webContents.openDevTools();
} }
// tray // tray
@ -165,13 +166,13 @@ module.exports = (store, __exports) => {
type: 'normal', type: 'normal',
label: 'Enhancements', label: 'Enhancements',
accelerator: store().menu_toggle, accelerator: store().menu_toggle,
click: openExtensionMenu, click: openEnhancerMenu,
}, },
{ {
type: 'normal', type: 'normal',
label: 'New Window', label: 'New Window',
click: () => { click: () => {
require('./create.js')( require('./createWindow.js')(
store, store,
require(path.resolve( require(path.resolve(
`${helpers.__notion}/app/main/createWindow.js` `${helpers.__notion}/app/main/createWindow.js`

View File

@ -19,9 +19,19 @@ module.exports = {
options: [ options: [
{ {
key: 'select_modifier', key: 'select_modifier',
label: 'tab select modifier (key+1, +2, +3, ... +9):', label:
'tab select modifier (key+1, +2, +3, ... +9 and key+left/right arrows):',
type: 'select', type: 'select',
value: ['Alt', 'Command', 'Control', 'Shift', 'Super'], value: [
'Alt',
'Command',
'Control',
'Super',
'Alt+Shift',
'Command+Shift',
'Control+Shift',
'Super+Shift',
],
}, },
{ {
key: 'new_tab', key: 'new_tab',

View File

@ -90,18 +90,40 @@ module.exports = async function ({ overwrite_version, friendly_errors } = {}) {
filter: (stats) => stats.isFile() && stats.path.endsWith('.js'), filter: (stats) => stats.isFile() && stats.path.endsWith('.js'),
} }
)) { )) {
insertion_target = path.resolve( const insertion_file = path.resolve(
`${helpers.__notion}/app/${insertion_target}` `${helpers.__notion}/app/${insertion_target}`
); );
if (insertion_target === 'main/main.js') {
// https://github.com/dragonwocky/notion-enhancer/issues/160
// patch the notion:// url scheme/protocol to work on linux
fs.readFile(insertion_file, 'utf8', (err, data) => {
if (err) throw err;
fs.writeFile(
insertion_file,
`${data.replace(
/process.platform === "win32"/g,
'process.platform === "win32" || process.platform === "linux"'
)}\n\n//notion-enhancer\nrequire('${helpers.realpath(
__dirname
)}/loader.js')(__filename, exports);`,
'utf8',
(err) => {
if (err) throw err;
}
);
});
} else {
fs.appendFile( fs.appendFile(
insertion_target, insertion_file,
`\n\n//notion-enhancer\nrequire('${helpers.realpath( `\n\n//notion-enhancer\nrequire('${helpers.realpath(
__dirname __dirname
)}/loader.js')(__filename, exports);` )}/loader.js')(__filename, exports);`
); );
} }
}
// not resolved, nothing depends on it so it's just a "let it do its thing" // not resolved, nothing else in application depends on it
// so it's just a "let it do its thing"
console.info(' ...recording enhancement version.'); console.info(' ...recording enhancement version.');
fs.outputFile( fs.outputFile(
path.resolve(`${helpers.__notion}/app/ENHANCER_VERSION.txt`), path.resolve(`${helpers.__notion}/app/ENHANCER_VERSION.txt`),