From 7f53445ab0e2d71838c216d0eca26cd566b3ac66 Mon Sep 17 00:00:00 2001 From: dragonwocky Date: Thu, 2 Dec 2021 17:58:24 +1100 Subject: [PATCH] desktop-only extension: integrated titlebar (inc. customisable icons) --- repo/gruvbox-light/variables.css | 2 +- repo/integrated-titlebar/buttons.css | 58 ++++++++++++++ repo/integrated-titlebar/buttons.mjs | 69 +++++++++++++++++ repo/integrated-titlebar/client.mjs | 43 ++++++++++- repo/integrated-titlebar/createWindow.cjs | 90 ++++------------------ repo/integrated-titlebar/menu.mjs | 20 ++++- repo/integrated-titlebar/mod.json | 40 +++++++++- repo/integrated-titlebar/rendererIndex.cjs | 41 ++++++++++ repo/light+/variables.css | 2 +- repo/pinky-boom/variables.css | 2 +- repo/theming/variables.css | 2 +- 11 files changed, 289 insertions(+), 80 deletions(-) create mode 100644 repo/integrated-titlebar/buttons.mjs create mode 100644 repo/integrated-titlebar/rendererIndex.cjs diff --git a/repo/gruvbox-light/variables.css b/repo/gruvbox-light/variables.css index 7622732..ccfb8ca 100644 --- a/repo/gruvbox-light/variables.css +++ b/repo/gruvbox-light/variables.css @@ -6,7 +6,7 @@ * (https://notion-enhancer.github.io/) under the MIT license */ -:root { +:root:not(.dark) { /* * variables are named for consistency with gruvbox dark, * but the _light variants are actually darker here diff --git a/repo/integrated-titlebar/buttons.css b/repo/integrated-titlebar/buttons.css index e69de29..85d0799 100644 --- a/repo/integrated-titlebar/buttons.css +++ b/repo/integrated-titlebar/buttons.css @@ -0,0 +1,58 @@ +/* + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.integrated_titlebar--dragarea { + width: 100%; + display: block; + -webkit-app-region: drag; + background: var(--theme--bg_secondary); +} + +.integrated_titlebar--buttons { + display: flex; + align-items: center; +} +.integrated_titlebar--buttons[data-in-enhancer-menu] { + margin: -0.5rem 0 0.75rem auto; +} + +.integrated_titlebar--buttons button { + user-select: none; + transition: background 20ms ease-in 0s; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + border-radius: 3px; + height: 28px; + width: 33px; + padding: 0 0.25px 0 0; + + margin-left: 2px; + border: none; + background: transparent; + font-size: 18px; +} +.integrated_titlebar--buttons button svg { + display: block; + width: 20px; + height: 20px; +} + +.integrated_titlebar--buttons button:focus, +.integrated_titlebar--buttons button:hover { + background: var(--theme--ui_interactive-hover); +} +.integrated_titlebar--buttons button:active { + background: var(--theme--ui_interactive-active); +} +#integrated_titlebar--close:focus, +#integrated_titlebar--close:hover, +#integrated_titlebar--close:active { + background: var(--theme--accent_red); + color: var(--theme--accent_red-text); +} diff --git a/repo/integrated-titlebar/buttons.mjs b/repo/integrated-titlebar/buttons.mjs new file mode 100644 index 0000000..0c77cd0 --- /dev/null +++ b/repo/integrated-titlebar/buttons.mjs @@ -0,0 +1,69 @@ +/* + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export const createWindowButtons = async ({ web, components }, db) => { + let minimizeIcon = (await db.get(['minimize_icon'])) || (await components.feather('minus')), + maximizeIcon = (await db.get(['maximize_icon'])) || (await components.feather('maximize')), + unmaximizeIcon = + (await db.get(['unmaximize_icon'])) || (await components.feather('minimize')), + closeIcon = await db.get(['close_icon'], await components.feather('x')); + minimizeIcon = minimizeIcon.trim(); + maximizeIcon = maximizeIcon.trim(); + unmaximizeIcon = unmaximizeIcon.trim(); + closeIcon = closeIcon.trim(); + + minimizeIcon = + minimizeIcon.startsWith('') + ? minimizeIcon + : web.escape(minimizeIcon); + maximizeIcon = + maximizeIcon.startsWith('') + ? maximizeIcon + : web.escape(maximizeIcon); + unmaximizeIcon = + unmaximizeIcon.startsWith('') + ? unmaximizeIcon + : web.escape(unmaximizeIcon); + closeIcon = + closeIcon.startsWith('') + ? closeIcon + : web.escape(closeIcon); + + const $windowButtons = web.html`
`, + $minimize = web.html``, + $maximize = web.html``, + $unmaximize = web.html``, + $close = web.html``; + + $minimize.addEventListener('click', () => __enhancerElectronApi.browser.minimize()); + $maximize.addEventListener('click', () => __enhancerElectronApi.browser.maximize()); + $unmaximize.addEventListener('click', () => __enhancerElectronApi.browser.unmaximize()); + $close.addEventListener('click', () => __enhancerElectronApi.browser.close()); + __enhancerElectronApi.browser.on('maximize', () => { + $maximize.replaceWith($unmaximize); + }); + __enhancerElectronApi.browser.on('unmaximize', () => { + $unmaximize.replaceWith($maximize); + }); + + web.render( + $windowButtons, + $minimize, + __enhancerElectronApi.browser.isMaximized() ? $unmaximize : $maximize, + $close + ); + return $windowButtons; +}; diff --git a/repo/integrated-titlebar/client.mjs b/repo/integrated-titlebar/client.mjs index 7966334..de0eff3 100644 --- a/repo/integrated-titlebar/client.mjs +++ b/repo/integrated-titlebar/client.mjs @@ -6,6 +6,47 @@ 'use strict'; +import { createWindowButtons } from './buttons.mjs'; + export default async function (api, db) { - // + const { web } = api, + tilingMode = await db.get(['tiling']), + dragareaHeight = await db.get(['dragarea_height']), + sidebarSelector = '.notion-sidebar', + panelSelector = '#enhancer--panel', + topbarSelector = '.notion-topbar', + topbarActionsSelector = '.notion-topbar-action-buttons'; + if (tilingMode) return; + + let sidebarWidth = '0px', + panelWidth = '0px'; + const updateDragareaOffsets = () => { + const $sidebar = document.querySelector(sidebarSelector), + newSidebarWidth = $sidebar.style.height === 'auto' ? '0px' : $sidebar.style.width, + $panel = document.querySelector(panelSelector), + newPanelWidth = + $panel && $panel.dataset.enhancerPanelPinned === 'true' + ? window + .getComputedStyle(document.documentElement) + .getPropertyValue('--component--panel-width') + : '0px'; + if (newSidebarWidth !== sidebarWidth) { + sidebarWidth = newSidebarWidth; + __enhancerElectronApi.sendMessageToHost('sidebar-width', sidebarWidth); + } + if (newPanelWidth !== panelWidth) { + panelWidth = newPanelWidth; + __enhancerElectronApi.sendMessageToHost('panel-width', panelWidth); + } + }; + web.addDocumentObserver(updateDragareaOffsets); + + await web.whenReady([topbarSelector, topbarActionsSelector]); + const $topbar = document.querySelector(topbarSelector), + $dragarea = web.html`
`; + $topbar.prepend($dragarea); + + const $topbarActions = document.querySelector(topbarActionsSelector), + $windowButtons = await createWindowButtons(api, db); + web.render($topbarActions.parentElement, $windowButtons); } diff --git a/repo/integrated-titlebar/createWindow.cjs b/repo/integrated-titlebar/createWindow.cjs index 6fde278..c74047a 100644 --- a/repo/integrated-titlebar/createWindow.cjs +++ b/repo/integrated-titlebar/createWindow.cjs @@ -6,77 +6,21 @@ 'use strict'; -module.exports = async function (api, db, __exports) { - const notionCreateWindow = __exports.createWindow; - // __exports.createWindow = (relativeUrl = '', args) => { - // const windowState = require('electron-window-state').default({ - // defaultWidth: 1320, - // defaultHeight: 860, - // }); - // const focusedWindow = electron_1.BrowserWindow.getFocusedWindow(); - // const rect = getRectFromFocusedWindow(windowState); - // const windowCreationArgs = Object.assign(Object.assign({}, rect), { - // show: false, - // backgroundColor: '#ffffff', - // titleBarStyle: 'hiddenInset', - // autoHideMenuBar: true, - // webPreferences: { - // preload: path_1.default.resolve(__dirname, '../renderer/index.js'), - // webviewTag: true, - // session: electron_1.session.fromPartition(constants_1.electronSessionPartition), - // enableRemoteModule: true, - // }, - // }); - // const { window, warmed } = getNextWindow(windowCreationArgs); - // window.setMenuBarVisibility(false); - // warmWindowState.warmedWindow = undefined; - // window.once('ready-to-show', () => { - // if (args && args.isLocalhost) { - // return; - // } - // if (!warmed) { - // window.show(); - // } - // }); - // if (warmed) { - // if (warmWindowState.warmedLoaded) { - // notionIpc.sendMainToNotionWindow(window, 'notion:navigate-to-url', relativeUrl); - // } else { - // void window.loadURL(urlHelpers_1.getIndexUrl(relativeUrl)); - // } - // window.setBounds(getRectFromFocusedWindow(windowState)); - // window.show(); - // } else { - // void window.loadURL(urlHelpers_1.getIndexUrl(relativeUrl)); - // } - // if (focusedWindow) { - // if (focusedWindow.isFullScreen()) { - // window.setFullScreen(true); - // } - // } else { - // if (windowState.isFullScreen) { - // window.setFullScreen(true); - // } - // } - // window.on('close', () => { - // windowState.saveState(window); - // if (process.platform === 'win32') { - // const currentWindows = electron_1.BrowserWindow.getAllWindows(); - // const hasNoOtherOpenWindows = currentWindows.every((currentWindow) => - // Boolean( - // currentWindow.id === window.id || - // (warmWindowState.warmedWindow && - // currentWindow.id === warmWindowState.warmedWindow.id) - // ) - // ); - // if (hasNoOtherOpenWindows) { - // electron_2.app.quit(); - // } - // } - // }); - // setImmediate(() => { - // warmWindowState.warmedLoaded = false; - // }); - // return window; - // }; +module.exports = async function (api, db, __exports, __eval) { + __eval(` + const notionRectFromFocusedWindow = getRectFromFocusedWindow; + getRectFromFocusedWindow = (windowState) => { + const rect = notionRectFromFocusedWindow(windowState); + rect.frame = false; + return rect; + }; + `); }; + +// hide on open +// maybe only do once to hide first window? + +// exports.createWindow = (...args) => { +// warmWindowState.warmedWindow = true; +// createWindow(...args); +// }; diff --git a/repo/integrated-titlebar/menu.mjs b/repo/integrated-titlebar/menu.mjs index 7966334..62a5466 100644 --- a/repo/integrated-titlebar/menu.mjs +++ b/repo/integrated-titlebar/menu.mjs @@ -6,6 +6,24 @@ 'use strict'; +import { createWindowButtons } from './buttons.mjs'; + export default async function (api, db) { - // + const { web } = api, + tilingMode = await db.get(['tiling']), + dragareaHeight = await db.get(['dragarea_height']), + bodyContainerSelector = '.body-container', + sidebarSelector = '.sidebar'; + if (tilingMode) return; + + await web.whenReady([bodyContainerSelector, sidebarSelector]); + const $bodyContainer = document.querySelector(bodyContainerSelector), + $dragarea = web.html`
`; + document.body.prepend($dragarea); + $bodyContainer.style.height = `calc(100% - ${dragareaHeight}px)`; + + const $sidebar = document.querySelector(sidebarSelector), + $windowButtons = await createWindowButtons(api, db); + $windowButtons.dataset.inEnhancerMenu = true; + $sidebar.prepend($windowButtons); } diff --git a/repo/integrated-titlebar/mod.json b/repo/integrated-titlebar/mod.json index 2a79050..cae80a0 100644 --- a/repo/integrated-titlebar/mod.json +++ b/repo/integrated-titlebar/mod.json @@ -20,7 +20,10 @@ "js": { "client": ["client.mjs"], "menu": ["menu.mjs"], - "electron": [{ "source": "createWindow.cjs", "target": "main/createWindow.js" }] + "electron": [ + { "source": "createWindow.cjs", "target": "main/createWindow.js" }, + { "source": "rendererIndex.cjs", "target": "renderer/index.js" } + ] }, "options": [ { @@ -29,6 +32,41 @@ "label": "tiling window manager mode", "tooltip": "**completely remove the close/minimise/maximise buttons** (only for advanced use, not recommended)", "value": false + }, + { + "type": "number", + "key": "dragarea_height", + "label": "dragarea height (px)", + "tooltip": "**the height of the rectangle added to the top of the window, used to drag/move the window around** (dragging is not possible in a frameless window without this bar)", + "value": 12 + }, + { + "type": "text", + "key": "minimize_icon", + "label": "minimize window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + }, + { + "type": "text", + "key": "maximize_icon", + "label": "maximize window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + }, + { + "type": "text", + "key": "unmaximize_icon", + "label": "unmaximize window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + }, + { + "type": "text", + "key": "close_icon", + "label": "close window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" } ] } diff --git a/repo/integrated-titlebar/rendererIndex.cjs b/repo/integrated-titlebar/rendererIndex.cjs new file mode 100644 index 0000000..6aa2eb9 --- /dev/null +++ b/repo/integrated-titlebar/rendererIndex.cjs @@ -0,0 +1,41 @@ +/* + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function (api, db, __exports, __eval) { + const dragareaSelector = '[style*="-webkit-app-region: drag;"]'; + + await new Promise((res, rej) => { + let isReadyInt; + isReadyInt = setInterval(isReadyTest, 100); + function isReadyTest() { + if (document.querySelector(dragareaSelector)) { + clearInterval(isReadyInt); + res(true); + } + } + isReadyTest(); + }); + + const tilingMode = await db.get(['tiling']), + dragareaHeight = await db.get(['dragarea_height']); + + const dragarea = document.querySelector(dragareaSelector); + dragarea.style.top = '2px'; + dragarea.style.height = tilingMode ? '0' : `${dragareaHeight}px`; + + document.getElementById('notion').addEventListener('ipc-message', (event) => { + switch (event.channel) { + case 'notion-enhancer:sidebar-width': + dragarea.style.left = event.args[0]; + break; + case 'notion-enhancer:panel-width': + dragarea.style.right = event.args[0]; + break; + } + }); +}; diff --git a/repo/light+/variables.css b/repo/light+/variables.css index 8ce9f46..20a11f8 100644 --- a/repo/light+/variables.css +++ b/repo/light+/variables.css @@ -5,7 +5,7 @@ * (https://notion-enhancer.github.io/) under the MIT license */ -:root { +:root:not(.dark) { --theme--accent_blue: var(--light_plus--accent_blue, rgb(46, 170, 220)); --theme--accent_blue-selection: var( --light_plus--accent_blue-selection, diff --git a/repo/pinky-boom/variables.css b/repo/pinky-boom/variables.css index 5d92b3a..95a6094 100644 --- a/repo/pinky-boom/variables.css +++ b/repo/pinky-boom/variables.css @@ -5,7 +5,7 @@ * (https://notion-enhancer.github.io/) under the MIT license */ -:root { +:root:not(.dark) { --pinky_boom--brown: #a52a2a80; --pinky_boom--orange: #ffa60080; --pinky_boom--yellow: #ffff0080; diff --git a/repo/theming/variables.css b/repo/theming/variables.css index 42add87..ff97211 100644 --- a/repo/theming/variables.css +++ b/repo/theming/variables.css @@ -31,7 +31,7 @@ --theme--accent_red-text: #fff; } -:root { +:root:not(.dark) { --theme--bg: #fff; --theme--bg_secondary: rgb(247, 246, 243); --theme--bg_card: #fff;