feat(core): implement dev mode toggle in electron app

+ refactor: merge components/ into islands/ for consistency
This commit is contained in:
dragonwocky 2023-08-04 13:13:06 +10:00
parent e762e68f7f
commit 4f07420c4a
Signed by: dragonwocky
GPG Key ID: 7998D08F7D7BD7A8
28 changed files with 143 additions and 146 deletions

View File

@ -4,25 +4,26 @@
* (https://notion-enhancer.github.io/) under the MIT license * (https://notion-enhancer.github.io/) under the MIT license
*/ */
import { checkForUpdate } from "./update.mjs"; import { checkForUpdate } from "./updateCheck.mjs";
import { sendTelemetryPing } from "./telemetry.mjs"; import { sendTelemetryPing } from "./sendTelemetry.mjs";
import { Frame, Modal, Button } from "./components.mjs"; import { Modal, Frame } from "./islands/Modal.mjs";
import { MenuButton } from "./islands/MenuButton.mjs";
const doThemeOverride = async (db) => { const shouldLoadThemeOverrides = async (db) => {
const { getMods, isEnabled } = globalThis.__enhancerApi, const { getMods, isEnabled } = globalThis.__enhancerApi,
loadThemeOverrides = await db.get("loadThemeOverrides"); loadThemeOverrides = await db.get("loadThemeOverrides");
if (loadThemeOverrides === "Enabled") return true; if (loadThemeOverrides === "Enabled") return true;
if (loadThemeOverrides === "Disabled") return false; if (loadThemeOverrides === "Disabled") return false;
// prettier-ignore // prettier-ignore
// loadThemeOverrides === "Auto"
return (await getMods(async (mod) => { return (await getMods(async (mod) => {
// loadThemeOverrides === "Auto"
if (!mod._src.startsWith("themes/")) return false; if (!mod._src.startsWith("themes/")) return false;
return await isEnabled(mod.id); return await isEnabled(mod.id);
})).length; })).length;
}, },
overrideThemes = async (db) => { loadThemeOverrides = async (db) => {
const { html, enhancerUrl } = globalThis.__enhancerApi; const { html, enhancerUrl } = globalThis.__enhancerApi;
if (!(await doThemeOverride(db))) return; if (!(await shouldLoadThemeOverrides(db))) return;
document.head.append(html`<link document.head.append(html`<link
rel="stylesheet" rel="stylesheet"
href=${enhancerUrl("core/theme.css")} href=${enhancerUrl("core/theme.css")}
@ -37,36 +38,36 @@ const doThemeOverride = async (db) => {
</style>`); </style>`);
}; };
const initMenu = async (db) => { const insertMenu = async (db) => {
const notionSidebar = `.notion-sidebar-container .notion-sidebar > :nth-child(3) > div > :nth-child(2)`, const notionSidebar = `.notion-sidebar-container .notion-sidebar > :nth-child(3) > div > :nth-child(2)`,
notionSettingsAndMembers = `${notionSidebar} > [role="button"]:nth-child(3)`, notionSettingsAndMembers = `${notionSidebar} > [role="button"]:nth-child(3)`,
{ html, addKeyListener, addMutationListener } = globalThis.__enhancerApi, { html, addKeyListener, addMutationListener } = globalThis.__enhancerApi,
{ platform, enhancerUrl, onMessage } = globalThis.__enhancerApi, { platform, enhancerUrl, onMessage } = globalThis.__enhancerApi,
menuButtonIconStyle = await db.get("menuButtonIconStyle"), menuButtonIconStyle = await db.get("menuButtonIconStyle"),
openMenuHotkey = await db.get("openMenuHotkey"), openMenuHotkey = await db.get("openMenuHotkey"),
renderPing = { menuPing = {
channel: "notion-enhancer", channel: "notion-enhancer",
hotkey: openMenuHotkey, hotkey: openMenuHotkey,
icon: menuButtonIconStyle, icon: menuButtonIconStyle,
}; };
let _contentWindow; let _contentWindow;
const sendThemePing = () => { const updateMenuTheme = () => {
const darkMode = document.body.classList.contains("dark"), const darkMode = document.body.classList.contains("dark"),
notionTheme = darkMode ? "dark" : "light"; notionTheme = darkMode ? "dark" : "light";
if (renderPing.theme === notionTheme) return; if (menuPing.theme === notionTheme) return;
renderPing.theme = notionTheme; menuPing.theme = notionTheme;
_contentWindow?.postMessage?.(renderPing, "*"); _contentWindow?.postMessage?.(menuPing, "*");
}, },
sendRenderPing = (contentWindow) => { triggerMenuRender = (contentWindow) => {
_contentWindow ??= contentWindow; _contentWindow ??= contentWindow;
if (!$modal.hasAttribute("open")) return; if (!$modal.hasAttribute("open")) return;
delete renderPing.theme;
_contentWindow?.focus?.(); _contentWindow?.focus?.();
sendThemePing(); delete menuPing.theme;
updateMenuTheme();
}; };
const $modal = html`<${Modal} onopen=${sendRenderPing}> const $modal = html`<${Modal} onopen=${triggerMenuRender}>
<${Frame} <${Frame}
title="notion-enhancer menu" title="notion-enhancer menu"
src="${enhancerUrl("core/menu/index.html")}" src="${enhancerUrl("core/menu/index.html")}"
@ -76,52 +77,50 @@ const initMenu = async (db) => {
const apiKey = "__enhancerApi"; const apiKey = "__enhancerApi";
this.contentWindow[apiKey] = globalThis[apiKey]; this.contentWindow[apiKey] = globalThis[apiKey];
} }
sendRenderPing(this.contentWindow); triggerMenuRender(this.contentWindow);
}} }}
/> />
<//>`, <//>`,
$button = html`<${Button} $button = html`<${MenuButton}
onclick=${$modal.open} onclick=${$modal.open}
notifications=${(await checkForUpdate()) ? 1 : 0} notifications=${(await checkForUpdate()) ? 1 : 0}
themeOverridesLoaded=${await doThemeOverride(db)} themeOverridesLoaded=${await shouldLoadThemeOverrides(db)}
icon="notion-enhancer${menuButtonIconStyle === "Monochrome" icon="notion-enhancer${menuButtonIconStyle === "Monochrome"
? "?mask" ? "?mask"
: " text-[16px]"}" : " text-[16px]"}"
>notion-enhancer >notion-enhancer
<//>`; <//>`;
const insertMenu = () => { const appendToDom = () => {
if (!document.contains($modal)) document.body.append($modal); if (!document.contains($modal)) document.body.append($modal);
if (!document.querySelector(notionSidebar)?.contains($button)) { if (!document.querySelector(notionSidebar)?.contains($button)) {
document.querySelector(notionSettingsAndMembers)?.after($button); document.querySelector(notionSettingsAndMembers)?.after($button);
} }
}; };
addMutationListener(notionSidebar, insertMenu); addMutationListener(notionSidebar, appendToDom);
insertMenu(); addMutationListener("body", updateMenuTheme);
appendToDom();
addMutationListener("body", sendThemePing);
window.addEventListener("focus", sendRenderPing);
addKeyListener(openMenuHotkey, (event) => { addKeyListener(openMenuHotkey, (event) => {
event.preventDefault(); event.preventDefault();
$modal.open(); $modal.open();
}); });
window.addEventListener("focus", triggerMenuRender);
window.addEventListener("message", (event) => { window.addEventListener("message", (event) => {
// from embedded menu
if (event.data?.channel !== "notion-enhancer") return; if (event.data?.channel !== "notion-enhancer") return;
if (event.data?.action === "close-menu") $modal.close(); if (event.data?.action === "close-menu") $modal.close();
if (event.data?.action === "open-menu") $modal.open(); if (event.data?.action === "open-menu") $modal.open();
}); });
onMessage("notion-enhancer", (message) => { onMessage("notion-enhancer", (message) => {
// from worker
if (message === "open-menu") $modal.open(); if (message === "open-menu") $modal.open();
}); });
}; };
export default async (api, db) => { export default async (api, db) =>
const { sendMessage } = globalThis.__enhancerApi; Promise.all([
await Promise.all([ insertMenu(db),
overrideThemes(db),
insertCustomStyles(db), insertCustomStyles(db),
initMenu(db), loadThemeOverrides(db),
sendTelemetryPing(), sendTelemetryPing(),
]); ]).then(() => api.sendMessage("notion-enhancer", "load-complete"));
sendMessage("notion-enhancer", "load-complete");
};

View File

@ -1,33 +0,0 @@
{
"__comment": "pseudo-mod to allow configuration of api-provided components",
"name": "components",
"id": "36a2ffc9-27ff-480e-84a7-c7700a7d232d",
"version": "0.2.0",
"description": "shared notion-style elements.",
"tags": ["core"],
"authors": [
{
"name": "dragonwocky",
"email": "thedragonring.bod@gmail.com",
"homepage": "https://dragonwocky.me/",
"avatar": "https://dragonwocky.me/avatar.jpg"
},
{
"name": "CloudHill",
"email": "rh.cloudhill@gmail.com",
"homepage": "https://github.com/CloudHill",
"avatar": "https://avatars.githubusercontent.com/u/54142180"
}
],
"js": {},
"css": {},
"options": [
{
"type": "hotkey",
"key": "panel.hotkey",
"label": "toggle panel hotkey",
"value": "Ctrl+Alt+\\",
"tooltip": "**opens/closes the side panel in notion** if a mod that uses it has been enabled"
}
]
}

View File

@ -0,0 +1,43 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function MenuButton(
{ icon, notifications, themeOverridesLoaded, ...props },
...children
) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
tabindex: 0,
role: "button",
class: `notion-enhancer--menu-button
flex select-none cursor-pointer rounded-[3px]
text-[14px] my-px mx-[4px] py-[2px] px-[10px]
transition hover:bg-[color:var(--theme--bg-hover)]`,
});
return html`<div ...${props}>
<div class="flex items-center justify-center w-[22px] h-[22px] mr-[8px]">
<i class="i-${icon}"></i>
</div>
<div>${children}</div>
<div class="ml-auto my-auto${notifications > 0 ? "" : " hidden"}">
<!-- accents are squashed into one variable for theming:
use rgb to match notion if overrides not loaded -->
<div
class="flex justify-center w-[16px] h-[16px] font-semibold
text-([10px] [color:var(--theme--accent-secondary\\_contrast)])
bg-[color:var(--theme--accent-secondary)] rounded-[3px] mb-[2px]
dark:bg-[color:${themeOverridesLoaded
? "var(--theme--accent-secondary)"
: "rgb(180,65,60)"}]"
>
<span class="ml-[-0.5px]">${notifications}</span>
</div>
</div>
</div>`;
}
export { MenuButton };

View File

@ -4,18 +4,6 @@
* (https://notion-enhancer.github.io/) under the MIT license * (https://notion-enhancer.github.io/) under the MIT license
*/ */
function Frame(props) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
class: `rounded-[5px] w-[1150px] h-[calc(100vh-100px)]
max-w-[calc(100vw-100px)] max-h-[715px] overflow-hidden
bg-[color:var(--theme--bg-primary)] drop-shadow-xl
group-open:(pointer-events-auto opacity-100 scale-100)
transition opacity-0 scale-95`,
});
return html`<iframe ...${props}></iframe>`;
}
function Modal(props, ...children) { function Modal(props, ...children) {
const { html, extendProps, addKeyListener } = globalThis.__enhancerApi; const { html, extendProps, addKeyListener } = globalThis.__enhancerApi;
extendProps(props, { extendProps(props, {
@ -56,40 +44,16 @@ function Modal(props, ...children) {
return $modal; return $modal;
} }
function Button( function Frame(props) {
{ icon, notifications, themeOverridesLoaded, ...props },
...children
) {
const { html, extendProps } = globalThis.__enhancerApi; const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, { extendProps(props, {
tabindex: 0, class: `rounded-[5px] w-[1150px] h-[calc(100vh-100px)]
role: "button", max-w-[calc(100vw-100px)] max-h-[715px] overflow-hidden
class: `notion-enhancer--menu-button bg-[color:var(--theme--bg-primary)] drop-shadow-xl
flex select-none cursor-pointer rounded-[3px] group-open:(pointer-events-auto opacity-100 scale-100)
text-[14px] my-px mx-[4px] py-[2px] px-[10px] transition opacity-0 scale-95`,
transition hover:bg-[color:var(--theme--bg-hover)]`,
}); });
return html`<div ...${props}> return html`<iframe ...${props}></iframe>`;
<div class="flex items-center justify-center w-[22px] h-[22px] mr-[8px]">
<i class="i-${icon}"></i>
</div>
<div>${children}</div>
<div class="ml-auto my-auto${notifications > 0 ? "" : " hidden"}">
<!-- accents are squashed into one variable for theming:
use rgb to match notion if overrides not loaded -->
<div
class="flex justify-center w-[16px] h-[16px] font-semibold
text-([10px] [color:var(--theme--accent-secondary\\_contrast)])
bg-[color:var(--theme--accent-secondary)] rounded-[3px] mb-[2px]
dark:bg-[color:${themeOverridesLoaded
? "var(--theme--accent-secondary)"
: "rgb(180,65,60)"}]"
>
<span class="ml-[-0.5px]">${notifications}</span>
</div>
</div>
</div>`;
} }
export { Frame, Modal, Button }; export { Modal, Frame };

View File

@ -0,0 +1,12 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Panel(props) {
const { html, extendProps } = globalThis.__enhancerApi;
return html`<iframe ...${props}></iframe>`;
}
export { Panel };

View File

@ -4,9 +4,9 @@
* (https://notion-enhancer.github.io/) under the MIT license * (https://notion-enhancer.github.io/) under the MIT license
*/ */
import { Popup } from "../components/Popup.mjs"; import { Popup } from "./Popup.mjs";
import { Button } from "../components/Button.mjs"; import { Button } from "./Button.mjs";
import { Description } from "../components/Description.mjs"; import { Description } from "./Description.mjs";
import { useState } from "../state.mjs"; import { useState } from "../state.mjs";
const updateGuide = const updateGuide =

View File

@ -5,7 +5,7 @@
*/ */
import { setState, useState } from "../state.mjs"; import { setState, useState } from "../state.mjs";
import { Button } from "../components/Button.mjs"; import { Button } from "./Button.mjs";
function Footer({ categories }) { function Footer({ categories }) {
const { html, reloadApp } = globalThis.__enhancerApi, const { html, reloadApp } = globalThis.__enhancerApi,

View File

@ -5,8 +5,8 @@
*/ */
import { setState } from "../state.mjs"; import { setState } from "../state.mjs";
import { Description } from "../components/Description.mjs"; import { Description } from "./Description.mjs";
import { Input } from "../components/Input.mjs"; import { Input } from "./Input.mjs";
import { Mod } from "./Mod.mjs"; import { Mod } from "./Mod.mjs";
function Search({ items, itemType }) { function Search({ items, itemType }) {

View File

@ -5,8 +5,8 @@
*/ */
import { setState } from "../state.mjs"; import { setState } from "../state.mjs";
import { Description } from "../components/Description.mjs"; import { Description } from "./Description.mjs";
import { Toggle } from "../components/Toggle.mjs"; import { Toggle } from "./Toggle.mjs";
function Mod({ function Mod({
id, id,

View File

@ -4,11 +4,11 @@
* (https://notion-enhancer.github.io/) under the MIT license * (https://notion-enhancer.github.io/) under the MIT license
*/ */
import { Heading } from "../components/Heading.mjs"; import { Heading } from "./Heading.mjs";
import { Description } from "../components/Description.mjs"; import { Description } from "./Description.mjs";
import { Checkbox } from "../components/Checkbox.mjs"; import { Checkbox } from "./Checkbox.mjs";
import { Button } from "../components/Button.mjs"; import { Button } from "./Button.mjs";
import { Tile } from "../components/Tile.mjs"; import { Tile } from "./Tile.mjs";
import { setState, useState } from "../state.mjs"; import { setState, useState } from "../state.mjs";
const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/", const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/",

View File

@ -5,11 +5,11 @@
*/ */
import { setState } from "../state.mjs"; import { setState } from "../state.mjs";
import { Heading } from "../components/Heading.mjs"; import { Heading } from "./Heading.mjs";
import { Description } from "../components/Description.mjs"; import { Description } from "./Description.mjs";
import { Input } from "../components/Input.mjs"; import { Input } from "./Input.mjs";
import { Select } from "../components/Select.mjs"; import { Select } from "./Select.mjs";
import { Toggle } from "../components/Toggle.mjs"; import { Toggle } from "./Toggle.mjs";
const camelToSentenceCase = (string) => const camelToSentenceCase = (string) =>
string[0].toUpperCase() + string[0].toUpperCase() +
@ -22,16 +22,18 @@ const camelToSentenceCase = (string) =>
// ignore platform-specific options // ignore platform-specific options
if (opt.platforms && !opt.platforms.includes(platform)) return options; if (opt.platforms && !opt.platforms.includes(platform)) return options;
// replace consective headings // replace consective headings
opt.autoremove ??= true; opt._autoremoveIfSectionEmpty ??= true;
const prev = options[options.length - 1], const prev = options[options.length - 1],
canReplacePrev = prev?.autoremove && prev?.type === opt.type; canReplacePrev =
prev?._autoremoveIfSectionEmpty && prev?.type === opt.type;
if (opt.type === "heading" && canReplacePrev) { if (opt.type === "heading" && canReplacePrev) {
options[options.length - 1] = opt; options[options.length - 1] = opt;
} else options.push(opt); } else options.push(opt);
return options; return options;
}, []); }, []);
// remove trailing heading // remove trailing heading
return options.at(-1)?.type === "heading" && options.at(-1)?.autoremove return options.at(-1)?.type === "heading" &&
options.at(-1)?._autoremoveIfSectionEmpty
? options.slice(0, -1) ? options.slice(0, -1)
: options; : options;
}; };

View File

@ -5,12 +5,12 @@
*/ */
import { setState, useState } from "../state.mjs"; import { setState, useState } from "../state.mjs";
import { Heading } from "../components/Heading.mjs"; import { Heading } from "./Heading.mjs";
import { Description } from "../components/Description.mjs"; import { Description } from "./Description.mjs";
import { Checkbox } from "../components/Checkbox.mjs"; import { Checkbox } from "./Checkbox.mjs";
import { Button } from "../components/Button.mjs"; import { Button } from "./Button.mjs";
import { Input } from "../components/Input.mjs"; import { Input } from "./Input.mjs";
import { Popup } from "../components/Popup.mjs"; import { Popup } from "./Popup.mjs";
function Profile({ id }) { function Profile({ id }) {
const { html, getProfile, initDatabase } = globalThis.__enhancerApi, const { html, getProfile, initDatabase } = globalThis.__enhancerApi,

View File

@ -5,7 +5,7 @@
*/ */
import { setState, useState } from "../state.mjs"; import { setState, useState } from "../state.mjs";
import { Description } from "../components/Description.mjs"; import { Description } from "./Description.mjs";
function SidebarHeading({}, ...children) { function SidebarHeading({}, ...children) {
const { html } = globalThis.__enhancerApi; const { html } = globalThis.__enhancerApi;

View File

@ -4,7 +4,7 @@
* (https://notion-enhancer.github.io/) under the MIT license * (https://notion-enhancer.github.io/) under the MIT license
*/ */
import { collectTelemetryData } from "../../telemetry.mjs"; import { collectTelemetryData } from "../../sendTelemetry.mjs";
import { useState, setState } from "../state.mjs"; import { useState, setState } from "../state.mjs";
import { Option } from "./Options.mjs"; import { Option } from "./Options.mjs";

View File

@ -5,7 +5,7 @@
*/ */
import { setState, useState } from "./state.mjs"; import { setState, useState } from "./state.mjs";
import { checkForUpdate, isDevelopmentBuild } from "../update.mjs"; import { checkForUpdate, isDevelopmentBuild } from "../updateCheck.mjs";
import { Sidebar } from "./islands/Sidebar.mjs"; import { Sidebar } from "./islands/Sidebar.mjs";
import { Footer } from "./islands/Footer.mjs"; import { Footer } from "./islands/Footer.mjs";
import { Banner } from "./islands/Banner.mjs"; import { Banner } from "./islands/Banner.mjs";

View File

@ -1,7 +1,7 @@
{ {
"id": "0f0bf8b6-eae6-4273-b307-8fc43f2ee082", "id": "0f0bf8b6-eae6-4273-b307-8fc43f2ee082",
"name": "notion-enhancer", "name": "notion-enhancer",
"version": "0.11.1-dev", "version": "0.11.1",
"description": "an enhancer/customiser for the all-in-one productivity workspace notion.so", "description": "an enhancer/customiser for the all-in-one productivity workspace notion.so",
"tags": ["core"], "tags": ["core"],
"authors": [ "authors": [
@ -35,7 +35,7 @@
{ {
"type": "select", "type": "select",
"key": "loadThemeOverrides", "key": "loadThemeOverrides",
"description": "Loads the styling required for a theme to customise Notion's interface. Disabling this will increase client performance.", "description": "Loads the styling required for a theme to customise Notion's interface. Disabling this may increase client performance, but will also disable all themes.",
"values": ["Auto", "Enabled", "Disabled"] "values": ["Auto", "Enabled", "Disabled"]
}, },
{ {
@ -47,16 +47,17 @@
{ {
"type": "heading", "type": "heading",
"label": "Advanced", "label": "Advanced",
"autoremove": false "_autoremoveIfSectionEmpty": false
}, },
{ {
"type": "toggle", "type": "toggle",
"key": "developerMode", "key": "developerMode",
"description": "Activates built-in debugging tools accessible through the application menu in desktop environments and restores access to the DevTools console via the <kbd>Ctrl+Shift+I</kbd> hotkey.", "description": "Activates built-in debugging tools accessible through the application menu.",
"platforms": ["linux", "win32", "darwin"],
"value": false "value": false
} }
], ],
"clientStyles": ["variables.css"], "clientStyles": ["variables.css"],
"clientScripts": ["client.mjs"], "clientScripts": ["client.mjs"],
"electronScripts": [] "electronScripts": [{ "source": "notionConfig.cjs", "target": "config.js" }]
} }

View File

@ -0,0 +1,9 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
module.exports = async (api, db, __exports, __eval) => {
if (await db.get("developerMode")) __exports.default.env = "development";
};