diff --git a/src/common/system.js b/src/common/system.js index 409f497..409c9a0 100644 --- a/src/common/system.js +++ b/src/common/system.js @@ -42,7 +42,7 @@ const connectToPort = () => { // from worker to client if (IS_RENDERER) { const { ipcRenderer } = require("electron"); - ipcRenderer.on(channel, listener); + ipcRenderer.on(channel, (event, message) => listener(message)); } else if (!IS_ELECTRON) { const onMessage = (msg) => { if (msg?.channel !== channel || msg?.invocation) return; diff --git a/src/core/islands/Panel.mjs b/src/core/islands/Panel.mjs index 64512a1..9285d08 100644 --- a/src/core/islands/Panel.mjs +++ b/src/core/islands/Panel.mjs @@ -125,8 +125,7 @@ function Panel({ class="border-(l-1 [color:var(--theme--fg-border)]) w-0 group-&[data-pinned]/panel:(w-[var(--panel--width,0)]) h-[calc(100vh-45px)] bottom-0) absolute right-0 z-20 bg-[color:var(--theme--bg-primary)] group-&[data-peeked]/panel:( - w-[var(--panel--width,0)] h-[calc(100vh-120px)] bottom-[60px] rounded-l-[8px] border-(t-1 b-1) - shadow-[rgba(15,15,15,0.1)_0px_0px_0px_1px,rgba(15,15,15,0.2)_0px_3px_6px,rgba(15,15,15,0.4)_0px_9px_24px])" + w-[var(--panel--width,0)] h-[calc(100vh-120px)] bottom-[60px] rounded-l-[8px] border-(t-1 b-1))" >
{ + if (document.contains($topbarToggle)) return; + document.querySelector(topbarFavorite)?.after($topbarToggle); + }; + addMutationListener(topbarFavorite, addToTopbar); + addToTopbar(topbarFavorite); let preDragWidth, dragStartX, _animatedAt; const getWidth = async (width) => { @@ -175,8 +181,9 @@ function Panel({ borderBottomWidth: "1px", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", - boxShadow: - "rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.2) 0px 3px 6px, rgba(15, 15, 15, 0.4) 0px 9px 24px", + boxShadow: document.body.classList.contains("dark") + ? "rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.2) 0px 3px 6px, rgba(15, 15, 15, 0.4) 0px 9px 24px" + : "rgba(15, 15, 15, 0.05) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 3px 6px, rgba(15, 15, 15, 0.2) 0px 9px 24px", }, pinAnimation = { height: "calc(100vh - 45px)", @@ -247,7 +254,6 @@ function Panel({ `, $toggleTooltipClick = html``, $toggleTooltip = html`<${Tooltip} - class="text-start" onbeforeshow=${() => { $toggleTooltipClick.innerText = isPinned() ? "Close sidebar" @@ -255,20 +261,10 @@ function Panel({ }} >${$toggleTooltipClick}
${hotkey} - `, - alignTooltipBelow = ($target) => { - const rect = $target.getBoundingClientRect(); - return { - x: rect.right, - y: rect.bottom + $toggleTooltip.clientHeight / 2 + 6, - }; - }; - $resizeTooltip.attach($resizeHandle, () => { - const rect = $resizeHandle.getBoundingClientRect(); - return { x: rect.x - 6 }; - }); - $toggleTooltip.attach($topbarToggle, () => alignTooltipBelow($topbarToggle)); - $toggleTooltip.attach($panelToggle, () => alignTooltipBelow($panelToggle)); + `; + $resizeTooltip.attach($resizeHandle, "left"); + $toggleTooltip.attach($topbarToggle, "bottom"); + $toggleTooltip.attach($panelToggle, "bottom"); // hovering over the peek trigger will temporarily // pop out an interactive preview of the panel diff --git a/src/core/islands/Tooltip.mjs b/src/core/islands/Tooltip.mjs index 34d94e0..0306fe5 100644 --- a/src/core/islands/Tooltip.mjs +++ b/src/core/islands/Tooltip.mjs @@ -26,19 +26,26 @@ function Tooltip(props, ...children) { ${children}
`; + // can pass each coord as a number or a function $tooltip.show = (x, y) => { const $notionApp = document.querySelector(notionApp); if (!document.contains($tooltip)) $notionApp?.append($tooltip); if ($tooltip.hasAttribute("open")) return; + $tooltip.onbeforeshow?.(); + const edgePadding = 12, + { clientHeight, clientWidth } = document.documentElement; requestAnimationFrame(() => { - $tooltip.onbeforeshow?.(); - $tooltip.setAttribute("open", true); - x -= $tooltip.clientWidth; - if (x < 0) x = $tooltip.clientWidth + 12; - y -= $tooltip.clientHeight / 2; - if (y < 0) y = $tooltip.clientHeight + 12; + if (typeof x === "function") x = x(); + if (typeof y === "function") y = y(); + if (x < edgePadding) x = $tooltip.clientWidth + edgePadding; + if (x + $tooltip.clientWidth > clientWidth + edgePadding) + x = clientWidth - $tooltip.clientWidth - edgePadding; + if (y < edgePadding) y = $tooltip.clientHeight + edgePadding; + if (y + $tooltip.clientHeight > clientHeight + edgePadding) + y = clientHeight - $tooltip.clientHeight - edgePadding; $tooltip.style.left = `${x}px`; $tooltip.style.top = `${y}px`; + $tooltip.setAttribute("open", true); $tooltip.onshow?.(); }); }; @@ -49,11 +56,30 @@ function Tooltip(props, ...children) { $tooltip.onhide?.(); }, 200); }; - $tooltip.attach = ($target, calcPos) => { + $tooltip.attach = ($target, alignment = "") => { $target.addEventListener("mouseover", (event) => { setTimeout(() => { if (!$target.matches(":hover")) return; - const { x = event.clientX, y = event.clientY } = calcPos?.(event) ?? {}; + const x = () => { + const rect = $target.getBoundingClientRect(); + if (["top", "bottom"].includes(alignment)) { + return rect.left + rect.width / 2 - $tooltip.clientWidth / 2; + } else if (alignment === "left") { + return rect.left - $tooltip.clientWidth - 6; + } else if (alignment === "right") { + return rect.right + 6; + } else return event.clientX; + }, + y = () => { + const rect = $target.getBoundingClientRect(); + if (["left", "right"].includes(alignment)) { + return event.clientY - $tooltip.clientHeight / 2; + } else if (alignment === "top") { + return rect.top - $tooltip.clientHeight - 6; + } else if (alignment === "bottom") { + return rect.bottom + 6; + } else return event.clientY; + }; $tooltip.show(x, y); }, 200); }); diff --git a/src/core/islands/TopbarButton.mjs b/src/core/islands/TopbarButton.mjs index 27bd018..9e28298 100644 --- a/src/core/islands/TopbarButton.mjs +++ b/src/core/islands/TopbarButton.mjs @@ -6,34 +6,24 @@ "use strict"; -function TopbarButton({ icon, ...props }) { +function TopbarButton({ icon, ...props }, ...children) { const { html, extendProps, addMutationListener } = globalThis.__enhancerApi; extendProps(props, { tabindex: 0, role: "button", class: `notion-enhancer--topbar-button + text-[color:var(--theme--fg-primary)] mr-[2px] user-select-none h-[28px] w-[33px] duration-[20ms] - transition inline-flex items-center justify-center mr-[2px] + transition inline-flex items-center justify-center rounded-[3px] hover:bg-[color:var(--theme--bg-hover)] &[data-active]:bg-[color:var(--theme--bg-hover)]`, }); - const topbarFavorite = ".notion-topbar-favorite-button", - $button = html``, - addToTopbar = (after = topbarFavorite) => { - if (document.contains($button)) return; - document.querySelector(after)?.after($button); - }; - $button.addToTopbar = (after = topbarFavorite) => { - addMutationListener(after, () => addToTopbar(after)); - addToTopbar(after); - }; - return $button; + return html``; } export { TopbarButton }; diff --git a/src/extensions/titlebar/buttons.mjs b/src/extensions/titlebar/buttons.mjs index da307f5..9786d93 100644 --- a/src/extensions/titlebar/buttons.mjs +++ b/src/extensions/titlebar/buttons.mjs @@ -1,73 +1,49 @@ /** - * notion-enhancer: integrated titlebar - * (c) 2021 dragonwocky (https://dragonwocky.me/) + * notion-enhancer: titlebar + * (c) 2024 dragonwocky (https://dragonwocky.me/) * (https://notion-enhancer.github.io/) under the MIT license */ -'use strict'; +"use strict"; -export const createWindowButtons = async ({ electron, 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(); +import { Tooltip } from "../../core/islands/Tooltip.mjs"; +import { TopbarButton } from "../../core/islands/TopbarButton.mjs"; - 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 minimizeLabel = "Minimize window", + maximizeLabel = "Maximize window", + unmaximizeLabel = "Unmaximize window", + closeLabel = "Close window"; - const $windowButtons = web.html`
`, - $minimize = web.html``, - $maximize = web.html``, - $unmaximize = web.html``, - $close = web.html``; - components.addTooltip($minimize, '**Minimize window**'); - components.addTooltip($maximize, '**Maximize window**'); - components.addTooltip($unmaximize, '**Unmaximize window**'); - components.addTooltip($close, '**Close window**'); - - $minimize.addEventListener('click', () => electron.browser.minimize()); - $maximize.addEventListener('click', () => electron.browser.maximize()); - $unmaximize.addEventListener('click', () => electron.browser.unmaximize()); - $close.addEventListener('click', () => electron.browser.close()); - electron.browser.on('maximize', () => { - $maximize.replaceWith($unmaximize); - }); - electron.browser.on('unmaximize', () => { - $unmaximize.replaceWith($maximize); - }); - - web.render( - $windowButtons, - $minimize, - electron.browser.isMaximized() ? $unmaximize : $maximize, - $close - ); - return $windowButtons; +const createWindowButtons = () => { + const { html } = globalThis.__enhancerApi, + $minimize = html`<${TopbarButton} + aria-label="${minimizeLabel}" + icon="minus" + />`, + $maximize = html`<${TopbarButton} + aria-label="${maximizeLabel}" + icon="maximize" + />`, + $unmaximize = html`<${TopbarButton} + aria-label="${unmaximizeLabel}" + icon="minimize" + />`, + $close = html`<${TopbarButton} + class="!hover:(bg-red-600 text-white)" + aria-label="${closeLabel}" + icon="x" + />`; + html`<${Tooltip}>${minimizeLabel}`.attach($minimize, "bottom"); + html`<${Tooltip}>${maximizeLabel}`.attach($maximize, "bottom"); + html`<${Tooltip}>${unmaximizeLabel}`.attach($unmaximize, "bottom"); + html`<${Tooltip}>${closeLabel}`.attach($close, "bottom"); + return html`
${$minimize}${$maximize}${$unmaximize}${$close}
`; }; + +if (globalThis.IS_TABS) { + const appendAfter = ".hide-scrollbar", + $buttons = createWindowButtons(); + document.querySelector(appendAfter)?.after($buttons); +} + +export { createWindowButtons }; diff --git a/src/extensions/titlebar/client.css b/src/extensions/titlebar/client.css index b70a6ac..d0bbd5c 100644 --- a/src/extensions/titlebar/client.css +++ b/src/extensions/titlebar/client.css @@ -9,7 +9,10 @@ user-select: none; -webkit-app-region: drag; } -.notion-tabs :is([aria-label], [draggable]), +.notion-tabs :is([aria-label], [draggable], .notion-enhancer--topbar-button), .notion-topbar > div > * { -webkit-app-region: no-drag; } +.notion-tabs .notion-enhancer--topbar-button:last-child { + margin-right: 12px; +} diff --git a/src/extensions/titlebar/client.mjs b/src/extensions/titlebar/client.mjs index 88b13a5..41cfda7 100644 --- a/src/extensions/titlebar/client.mjs +++ b/src/extensions/titlebar/client.mjs @@ -1,31 +1,24 @@ /** - * notion-enhancer: integrated titlebar - * (c) 2021 dragonwocky (https://dragonwocky.me/) + * notion-enhancer: titlebar + * (c) 2024 dragonwocky (https://dragonwocky.me/) * (https://notion-enhancer.github.io/) under the MIT license */ "use strict"; -import { TopbarButton } from "../../core/islands/TopbarButton.mjs"; +import { createWindowButtons } from "./buttons.mjs"; -// import { createWindowButtons } from './buttons.mjs'; - -export default async function ({ html }, db) { - const topbarMore = ".notion-topbar-more-button"; - const $minimizeButton = html`<${TopbarButton} - aria-label="Minimize window" - icon="minus" - />`, - $maximizeButton = html`<${TopbarButton} - aria-label="Maximize window" - icon="maximize" - />`, - $unmaximizeButton = html`<${TopbarButton} - aria-label="Unmaximize window" - icon="minimize" - />`, - $closeButton = html`<${TopbarButton} aria-label="Close window" icon="x" />`; - $closeButton.addToTopbar(topbarMore); - $maximizeButton.addToTopbar(topbarMore); - $minimizeButton.addToTopbar(topbarMore); -} +export default (api, db) => { + const { onMessage, addMutationListener } = api, + $buttons = createWindowButtons(), + topbarMore = ".notion-topbar-more-button", + addToTopbar = () => document.querySelector(topbarMore)?.after($buttons), + showIfNoTabBar = async () => { + const { isShowingTabBar } = await __electronApi.electronAppFeatures.get(); + $buttons.style.display = isShowingTabBar ? "none" : ""; + }; + __electronApi?.electronAppFeatures.addListener(showIfNoTabBar); + showIfNoTabBar(); + addMutationListener(topbarMore, addToTopbar); + addToTopbar(topbarMore); +}; diff --git a/src/extensions/titlebar/tabs.cjs b/src/extensions/titlebar/tabs.cjs index 3cd804f..98ccc32 100644 --- a/src/extensions/titlebar/tabs.cjs +++ b/src/extensions/titlebar/tabs.cjs @@ -8,17 +8,27 @@ module.exports = async ({ whenReady }, db) => { const api = await whenReady(), - { addMutationListener } = api, - tabSelector = ".hide-scrollbar > div > div", + { html, addMutationListener } = api, + { enhancerUrl, onMessage, sendMessage } = api, titlebarStyle = await db.get("titlebarStyle"); // only make area draggable if tabs are visible: // otherwise dragarea overlaps regular app topbar + const tabSelector = ".hide-scrollbar > div > div"; addMutationListener(".hide-scrollbar", () => { const tabCount = document.querySelectorAll(tabSelector).length; if (tabCount > 1) document.body.classList.add("notion-tabs"); else document.body.classList.remove("notion-tabs"); }); + onMessage("tabs:set-state", (state) => { + if (state.themeMode === "dark") document.body.classList.add("dark"); + else document.body.classList.remove("dark"); + }); + if (titlebarStyle === "Disabled") return; + const $buttonsScript = document.createElement("script"); + $buttonsScript.type = "module"; + $buttonsScript.src = enhancerUrl("extensions/titlebar/buttons.mjs"); + document.head.append($buttonsScript); }; diff --git a/src/load.mjs b/src/load.mjs index e4a0977..3d026fb 100644 --- a/src/load.mjs +++ b/src/load.mjs @@ -18,6 +18,7 @@ export default (async () => { IS_MENU = location.href.startsWith(enhancerUrl("core/menu/index.html")), IS_TABS = /\/app\/\.webpack\/renderer\/(draggable_)?tabs\/index.html$/.test(location.href), IS_ELECTRON = ['linux', 'win32', 'darwin'].includes(platform); + if (IS_TABS) globalThis.IS_TABS = true; if (!IS_MENU && !IS_TABS) { if (!signedIn || !pageLoaded) return; diff --git a/src/worker.js b/src/worker.js index 72ca146..2547da5 100644 --- a/src/worker.js +++ b/src/worker.js @@ -121,7 +121,7 @@ if (IS_ELECTRON) { const { namespace, query, args } = message.data; return queryDatabase(namespace, query, args); }); - ipcMain.on("notion-enhancer", ({ sender }, message) => { + ipcMain.on("notion-enhancer", ({}, message) => { if (message === "reload-app") reloadApp(); }); } else {