diff --git a/.yarnrc.yml b/.yarnrc.yml deleted file mode 100644 index 75d046a..0000000 --- a/.yarnrc.yml +++ /dev/null @@ -1,5 +0,0 @@ -enableMessageNames: false - -nodeLinker: node-modules - -yarnPath: .yarn/releases/yarn-3.6.1.cjs diff --git a/scripts/patch-desktop-app.mjs b/scripts/patch-desktop-app.mjs index 708171c..fefc114 100755 --- a/scripts/patch-desktop-app.mjs +++ b/scripts/patch-desktop-app.mjs @@ -61,6 +61,10 @@ const mainScript = ".webpack/main/index", replace(storeDeclaration, storeInterceptor); replace(updateDeclaration, updateInterceptor); + // conditionally create frameless windows + const titlebarStyle = `titleBarStyle:globalThis.__notionConfig?.titlebarStyle??"hiddenInset"`; + replace(`titleBarStyle:"hiddenInset"`, titlebarStyle); + return scriptContent; }, [rendererScript]: (scriptContent) => diff --git a/src/_assets/icons.svg.js b/src/_assets/icons.svg.js index 07a18e5..8a05cc6 100644 --- a/src/_assets/icons.svg.js +++ b/src/_assets/icons.svg.js @@ -7,5 +7,7 @@ const iconColour = ``, iconMonochrome = ``; -globalThis.__enhancerApi ??= {}; -Object.assign(globalThis.__enhancerApi, { iconColour, iconMonochrome }); +Object.assign((globalThis.__enhancerApi ??= {}), { + iconColour, + iconMonochrome, +}); diff --git a/src/_common/events.js b/src/_common/events.js index ea66dee..d01939d 100644 --- a/src/_common/events.js +++ b/src/_common/events.js @@ -155,8 +155,7 @@ document.addEventListener("keydown", (event) => { handleKeypress(event, keydownListeners); }); -globalThis.__enhancerApi ??= {}; -Object.assign(globalThis.__enhancerApi, { +Object.assign((globalThis.__enhancerApi ??= {}), { debounce, setState, useState, diff --git a/src/_common/markup.js b/src/_common/markup.js index 0d95a03..fe19ee7 100644 --- a/src/_common/markup.js +++ b/src/_common/markup.js @@ -587,8 +587,7 @@ const h = (type, props, ...children) => { }, html = htm.bind(h); -globalThis.__enhancerApi ??= {}; -Object.assign(globalThis.__enhancerApi, { +Object.assign((globalThis.__enhancerApi ??= {}), { html, extendProps, }); diff --git a/src/_common/registry.js b/src/_common/registry.js index c2123c4..1ceb5e8 100644 --- a/src/_common/registry.js +++ b/src/_common/registry.js @@ -79,8 +79,7 @@ const modDatabase = async (id) => { ); }; -globalThis.__enhancerApi ??= {}; -Object.assign(globalThis.__enhancerApi, { +Object.assign((globalThis.__enhancerApi ??= {}), { getMods, getProfile, isEnabled, diff --git a/src/_common/system.js b/src/_common/system.js index 248d208..409f497 100644 --- a/src/_common/system.js +++ b/src/_common/system.js @@ -146,8 +146,7 @@ const initDatabase = (namespace, fallbacks = {}) => { } else sendMessage("notion-enhancer", "reload-app"); }; -globalThis.__enhancerApi ??= {}; -Object.assign(globalThis.__enhancerApi, { +Object.assign((globalThis.__enhancerApi ??= {}), { platform, version, enhancerUrl, diff --git a/src/core/electron.cjs b/src/core/electron.cjs index 7273220..14b24ef 100644 --- a/src/core/electron.cjs +++ b/src/core/electron.cjs @@ -22,8 +22,7 @@ module.exports = async ({}, db) => { setPreference("isDraggableTabsEnabled", draggableTabs); // enable developer mode, access extra debug tools - globalThis.__notionConfig ??= {}; - Object.assign(globalThis.__notionConfig, { + Object.assign((globalThis.__notionConfig ??= {}), { env: developerMode ? "development" : "production", }); diff --git a/src/core/islands/Panel.mjs b/src/core/islands/Panel.mjs index ec6a28e..40a8b01 100644 --- a/src/core/islands/Panel.mjs +++ b/src/core/islands/Panel.mjs @@ -385,8 +385,7 @@ function Panel({ return $panel; } -globalThis.__enhancerApi ??= {}; -Object.assign(globalThis.__enhancerApi, { +Object.assign((globalThis.__enhancerApi ??= {}), { addPanelView, removePanelView, }); diff --git a/src/core/menu/islands/Footer.mjs b/src/core/menu/islands/Footer.mjs index 6c968ba..e22df9e 100644 --- a/src/core/menu/islands/Footer.mjs +++ b/src/core/menu/islands/Footer.mjs @@ -29,29 +29,26 @@ function Footer({ categories }) { ]; }); - const buttons = [...$categories.map(([, $btn]) => $btn), $reload], - updateFooter = () => { - const buttonsVisible = buttons.some(($el) => $el.style.display === ""); - setState({ footerOpen: buttonsVisible }); - }; useState(["view"], ([view]) => { + let footerOpen = $reload.style.display !== "none"; for (const [ids, $btn] of $categories) { - const modActive = ids.some((id) => id === view); - $btn.style.display = modActive ? "" : "none"; + const modInCategory = ids.some((id) => id === view); + if (modInCategory) footerOpen = true; + $btn.style.display = modInCategory ? "" : "none"; } - updateFooter(); + setState({ footerOpen }); }); useState(["databaseUpdated"], ([databaseUpdated]) => { $reload.style.display = databaseUpdated ? "" : "none"; - updateFooter(); + setState({ footerOpen: true }); }); return html``; } diff --git a/src/core/menu/islands/Mod.mjs b/src/core/menu/islands/Mod.mjs index 630953d..5bb0293 100644 --- a/src/core/menu/islands/Mod.mjs +++ b/src/core/menu/islands/Mod.mjs @@ -28,7 +28,8 @@ function Mod({ class="notion-enhancer--menu-mod flex items-stretch rounded-[4px] bg-[color:var(--theme--bg-secondary)] w-full py-[18px] px-[16px] border border-[color:var(--theme--fg-border)] cursor-pointer - transition duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]" + duration-[20ms] hover:bg-[color:var(--theme--bg-hover)] + transition &+.notion-enhancer--menu-option:mt-[24px]" > ${thumbnail ? html` ($view.style.display = "none"), duration); + $view.parentElement.style.overflow = "hidden"; + requestAnimationFrame(() => { + $view.style.transition = cssTransition; + $view.style.opacity = "0"; + setTimeout(() => ($view.style.display = "none"), duration); + }); } else if (!isVisible && nowActive) { setTimeout(() => { $view.style.opacity = "0"; $view.style.display = ""; - requestIdleCallback(() => { + requestAnimationFrame(() => { $view.style.transition = cssTransition; $view.style.opacity = "1"; - setState({ transitionInProgress: false }); + $view.parentElement.style.overflow = ""; }); }, duration); } @@ -48,28 +50,30 @@ function View({ id }, ...children) { const duration = 200, cssTransition = `opacity ${duration}ms, transform ${duration}ms`; if (isVisible && !nowActive) { - setState({ transitionInProgress: true }); - $view.style.transition = cssTransition; - $view.style.transform = `translateX(${ - transition === "slide-to-right" ? "-100%" : "100%" - })`; - $view.style.opacity = "0"; - setTimeout(() => { - $view.style.display = "none"; - $view.style.transform = ""; - }, duration); + $view.parentElement.style.overflow = "hidden"; + requestAnimationFrame(() => { + $view.style.transition = cssTransition; + $view.style.transform = `translateX(${ + transition === "slide-to-right" ? "-100%" : "100%" + })`; + $view.style.opacity = "0"; + setTimeout(() => { + $view.style.display = "none"; + $view.style.transform = ""; + }, duration); + }); } else if (!isVisible && nowActive) { $view.style.transform = `translateX(${ transition === "slide-to-right" ? "100%" : "-100%" })`; $view.style.opacity = "0"; $view.style.display = ""; - requestIdleCallback(() => { + requestAnimationFrame(() => { $view.style.transition = cssTransition; $view.style.transform = ""; $view.style.opacity = "1"; setTimeout(() => { - setState({ transitionInProgress: false }); + $view.parentElement.style.overflow = ""; }, duration); }); } diff --git a/src/core/menu/menu.mjs b/src/core/menu/menu.mjs index 66fdefd..7fac552 100644 --- a/src/core/menu/menu.mjs +++ b/src/core/menu/menu.mjs @@ -149,10 +149,7 @@ const renderMenu = async () => { `; useState(["footerOpen"], ([footerOpen]) => { - $main.style.height = footerOpen ? "100%" : "calc(100% + 33px)"; - }); - useState(["transitionInProgress"], ([transitionInProgress]) => { - $main.children[0].style.overflow = transitionInProgress ? "hidden" : ""; + $main.style.height = footerOpen ? "100%" : "calc(100% + 65px)"; }); const $skeleton = document.querySelector("#skeleton"); diff --git a/src/core/mod.json b/src/core/mod.json index da0295b..3c9797f 100644 --- a/src/core/mod.json +++ b/src/core/mod.json @@ -12,10 +12,7 @@ } ], "options": [ - { - "type": "heading", - "label": "Hotkeys" - }, + { "type": "heading", "label": "Hotkeys" }, { "type": "hotkey", "key": "openMenuHotkey", @@ -34,10 +31,7 @@ "description": "Toggles focus of the Notion window anywhere, even when your Notion app isn't active.", "value": "Ctrl+Shift+A" }, - { - "type": "heading", - "label": "Appearance" - }, + { "type": "heading", "label": "Appearance" }, { "type": "file", "key": "customStyles", @@ -56,10 +50,7 @@ "description": "Sets whether the notion-enhancer icon added to Notion's sidebar should be coloured or monochrome. The latter style will match the theme's icon colour for users who would like the icon to be less noticeable.", "values": ["Colour", "Monochrome"] }, - { - "type": "heading", - "label": "Experimental" - }, + { "type": "heading", "label": "Experimental" }, { "type": "toggle", "key": "draggableTabs", diff --git a/src/extensions/integrated-titlebar/client.mjs b/src/extensions/integrated-titlebar/client.mjs deleted file mode 100644 index f217e60..0000000 --- a/src/extensions/integrated-titlebar/client.mjs +++ /dev/null @@ -1,58 +0,0 @@ -/** - * notion-enhancer: integrated titlebar - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -'use strict'; - -import { createWindowButtons } from './buttons.mjs'; - -export default async function (api, db) { - const { web, registry, electron } = api, - tilingMode = await db.get(['tiling']), - dragareaHeight = await db.get(['dragarea_height']), - tabsEnabled = await registry.enabled('e1692c29-475e-437b-b7ff-3eee872e1a42'), - sidebarSelector = '.notion-sidebar', - panelSelector = '#enhancer--panel', - topbarSelector = '.notion-topbar', - topbarActionsSelector = '.notion-topbar-action-buttons'; - if (tilingMode || tabsEnabled) return; - - let sidebarWidth = '0px', - panelWidth = '0px'; - const updateDragareaOffsets = () => { - const $sidebar = document.querySelector(sidebarSelector), - newSidebarWidth = - !$sidebar || $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; - electron.sendMessageToHost('sidebar-width', sidebarWidth); - } - if (newPanelWidth !== panelWidth) { - panelWidth = newPanelWidth; - electron.sendMessageToHost('panel-width', panelWidth); - } - }; - web.addDocumentObserver(updateDragareaOffsets); - - await web.whenReady([topbarSelector, topbarActionsSelector]); - const $topbar = document.querySelector(topbarSelector), - $dragarea = web.html`
`; - $topbar.prepend($dragarea); - document.documentElement.style.setProperty( - '--integrated_titlebar--dragarea-height', - dragareaHeight + 'px' - ); - - const $topbarActions = document.querySelector(topbarActionsSelector), - $windowButtons = await createWindowButtons(api, db); - web.render($topbarActions.parentElement, $windowButtons); -} diff --git a/src/extensions/integrated-titlebar/createWindow.cjs b/src/extensions/integrated-titlebar/createWindow.cjs deleted file mode 100644 index 39e42e6..0000000 --- a/src/extensions/integrated-titlebar/createWindow.cjs +++ /dev/null @@ -1,18 +0,0 @@ -/** - * notion-enhancer: integrated titlebar - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -'use strict'; - -module.exports = function (api, db, __exports, __eval) { - __eval(` - const notionRectFromFocusedWindow = getRectFromFocusedWindow; - getRectFromFocusedWindow = (windowState) => { - const rect = notionRectFromFocusedWindow(windowState); - rect.frame = false; - return rect; - }; - `); -}; diff --git a/src/extensions/integrated-titlebar/mod.json b/src/extensions/integrated-titlebar/mod.json deleted file mode 100644 index b0bd9dc..0000000 --- a/src/extensions/integrated-titlebar/mod.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "Titlebar", - "id": "a5658d03-21c6-4088-bade-fa4780459133", - "environments": ["linux", "win32"], - "version": "0.11.1", - "description": "replaces the native window titlebar with buttons inset into the app.", - "preview": "integrated-titlebar.jpg", - "tags": ["extension", "layout"], - "authors": [ - { - "name": "dragonwocky", - "email": "thedragonring.bod@gmail.com", - "homepage": "https://dragonwocky.me/", - "avatar": "https://dragonwocky.me/avatar.jpg" - } - ], - "css": { - "frame": ["buttons.css"], - "client": ["buttons.css"], - "menu": ["buttons.css"] - }, - "js": { - "frame": ["frame.mjs"], - "client": ["client.mjs"], - "menu": ["menu.mjs"], - "electron": [ - { "source": "createWindow.cjs", "target": "main/createWindow.js" } - ] - }, - "options": [ - { - "type": "toggle", - "key": "tiling", - "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/src/extensions/integrated-titlebar/buttons.css b/src/extensions/titlebar/buttons.css similarity index 100% rename from src/extensions/integrated-titlebar/buttons.css rename to src/extensions/titlebar/buttons.css diff --git a/src/extensions/integrated-titlebar/buttons.mjs b/src/extensions/titlebar/buttons.mjs similarity index 100% rename from src/extensions/integrated-titlebar/buttons.mjs rename to src/extensions/titlebar/buttons.mjs diff --git a/src/extensions/titlebar/client.css b/src/extensions/titlebar/client.css new file mode 100644 index 0000000..28141c1 --- /dev/null +++ b/src/extensions/titlebar/client.css @@ -0,0 +1,13 @@ +/** + * notion-enhancer: titlebar + * (c) 2024 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-topbar { + user-select: none; + -webkit-app-region: drag; +} +.notion-topbar > div > * { + -webkit-app-region: no-drag; +} diff --git a/src/extensions/titlebar/client.mjs b/src/extensions/titlebar/client.mjs new file mode 100644 index 0000000..e51cd38 --- /dev/null +++ b/src/extensions/titlebar/client.mjs @@ -0,0 +1,58 @@ +/** + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +// import { createWindowButtons } from './buttons.mjs'; + +export default async function (api, db) { + // const { web, registry, electron } = api, + // tilingMode = await db.get(['tiling']), + // dragareaHeight = await db.get(['dragarea_height']), + // tabsEnabled = await registry.enabled('e1692c29-475e-437b-b7ff-3eee872e1a42'), + // sidebarSelector = '.notion-sidebar', + // panelSelector = '#enhancer--panel', + // topbarSelector = '.notion-topbar', + // topbarActionsSelector = '.notion-topbar-action-buttons'; + // if (tilingMode || tabsEnabled) return; + + // let sidebarWidth = '0px', + // panelWidth = '0px'; + // const updateDragareaOffsets = () => { + // const $sidebar = document.querySelector(sidebarSelector), + // newSidebarWidth = + // !$sidebar || $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; + // electron.sendMessageToHost('sidebar-width', sidebarWidth); + // } + // if (newPanelWidth !== panelWidth) { + // panelWidth = newPanelWidth; + // electron.sendMessageToHost('panel-width', panelWidth); + // } + // }; + // web.addDocumentObserver(updateDragareaOffsets); + + // await web.whenReady([topbarSelector, topbarActionsSelector]); + // const $topbar = document.querySelector(topbarSelector), + // $dragarea = web.html`
`; + // $topbar.prepend($dragarea); + // document.documentElement.style.setProperty( + // '--integrated_titlebar--dragarea-height', + // dragareaHeight + 'px' + // ); + + // const $topbarActions = document.querySelector(topbarActionsSelector), + // $windowButtons = await createWindowButtons(api, db); + // web.render($topbarActions.parentElement, $windowButtons); +} diff --git a/src/extensions/titlebar/electron.cjs b/src/extensions/titlebar/electron.cjs new file mode 100644 index 0000000..6e35175 --- /dev/null +++ b/src/extensions/titlebar/electron.cjs @@ -0,0 +1,14 @@ +/** + * notion-enhancer: titlebar + * (c) 2024 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +"use strict"; + +module.exports = async ({}, db) => { + const titlebarStyle = await db.get("titlebarStyle"); + Object.assign((globalThis.__notionConfig ??= {}), { + titlebarStyle: "hidden", + }); +}; diff --git a/src/extensions/integrated-titlebar/frame.mjs b/src/extensions/titlebar/frame.mjs similarity index 100% rename from src/extensions/integrated-titlebar/frame.mjs rename to src/extensions/titlebar/frame.mjs diff --git a/src/extensions/integrated-titlebar/integrated-titlebar.jpg b/src/extensions/titlebar/integrated-titlebar.jpg similarity index 100% rename from src/extensions/integrated-titlebar/integrated-titlebar.jpg rename to src/extensions/titlebar/integrated-titlebar.jpg diff --git a/src/extensions/integrated-titlebar/menu.mjs b/src/extensions/titlebar/menu.mjs similarity index 100% rename from src/extensions/integrated-titlebar/menu.mjs rename to src/extensions/titlebar/menu.mjs diff --git a/src/extensions/titlebar/mod.json b/src/extensions/titlebar/mod.json new file mode 100644 index 0000000..c3dbd3d --- /dev/null +++ b/src/extensions/titlebar/mod.json @@ -0,0 +1,52 @@ +{ + "id": "a5658d03-21c6-4088-bade-fa4780459133", + "name": "Titlebar", + "version": "0.11.1", + "environments": ["linux", "win32"], + "description": "Replaces the operating system's default window titlebar with buttons inset into the app.", + "thumbnail": "integrated-titlebar.jpg", + "tags": ["extension", "layout"], + "authors": [ + { + "name": "dragonwocky", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "options": [ + { + "type": "select", + "key": "titlebarStyle", + "description": "The integrated titlebar replaces the operating system's default window titlebar with buttons inset into the app's interface. Tiling window manager users may choose to fully disable the titlebar instead.", + "values": ["Integrated", "Disabled"] + }, + { "type": "heading", "label": "Icons" }, + { + "type": "file", + "key": "minimizeIcon", + "description": "Replaces the default icon used for the integrated titlebar's maximize button with an uploaded .svg image.", + "extensions": ["svg"] + }, + { + "type": "file", + "key": "maximizeIcon", + "description": "Replaces the default icon used for the integrated titlebar's maximize button with an uploaded .svg image.", + "extensions": ["svg"] + }, + { + "type": "file", + "key": "unmaximizeIcon", + "description": "Replaces the default icon used for the integrated titlebar's maximize button with an uploaded .svg image.", + "extensions": ["svg"] + }, + { + "type": "file", + "key": "closeIcon", + "description": "Replaces the default icon used for the integrated titlebar's maximize button with an uploaded .svg image.", + "extensions": ["svg"] + } + ], + "clientStyles": ["client.css"], + "clientScripts": ["client.mjs"], + "electronScripts": [[".webpack/main/index", "electron.cjs"]] +} diff --git a/src/load.mjs b/src/load.mjs index 7434474..5d499a1 100644 --- a/src/load.mjs +++ b/src/load.mjs @@ -7,10 +7,9 @@ "use strict"; export default (async () => { - if (globalThis.__getEnhancerApi) { - globalThis.__enhancerApi ??= {}; - Object.assign(globalThis.__enhancerApi, globalThis.__getEnhancerApi()); - } + Object.assign((globalThis.__enhancerApi ??= {}), { + ...(globalThis.__getEnhancerApi?.() ?? {}), + }); // prettier-ignore const { enhancerUrl, platform } = globalThis.__enhancerApi, diff --git a/src/registry.json b/src/registry.json index 1f3d22e..d4122ea 100644 --- a/src/registry.json +++ b/src/registry.json @@ -1 +1 @@ -["core", "themes/classic-dark"] +["core", "extensions/titlebar", "themes/classic-dark"] diff --git a/src/worker.js b/src/worker.js index f1aebeb..72ca146 100644 --- a/src/worker.js +++ b/src/worker.js @@ -175,5 +175,4 @@ if (IS_ELECTRON) { }); } -globalThis.__enhancerApi ??= {}; -Object.assign(globalThis.__enhancerApi, { queryDatabase }); +Object.assign((globalThis.__enhancerApi ??= {}), { queryDatabase });