/** * notion-enhancer * (c) 2023 dragonwocky (https://dragonwocky.me/) * (https://notion-enhancer.github.io/) under the MIT license */ import { checkForUpdate, isDevelopmentBuild } from "../updateCheck.mjs"; import { Sidebar } from "./islands/Sidebar.mjs"; import { Footer } from "./islands/Footer.mjs"; import { Banner } from "./islands/Banner.mjs"; import { Onboarding } from "./islands/Onboarding.mjs"; import { Telemetry } from "./islands/Telemetry.mjs"; import { View } from "./islands/View.mjs"; import { List } from "./islands/List.mjs"; import { Mod } from "./islands/Mod.mjs"; import { Options } from "./islands/Options.mjs"; import { Profiles } from "./islands/Profiles.mjs"; let _apiImport, // _renderStarted, _stateHookedInto, _hotkeyRegistered; const categories = [ { icon: "palette", id: "themes", title: "Themes", description: `Themes override Notion's colour schemes. Dark themes require Notion to be in dark mode and light themes require Notion to be in light mode. To switch between dark mode and light mode, go to Settings & members → My notifications & settings → My settings → Appearance.`, }, { icon: "zap", id: "extensions", title: "Extensions", description: `Extensions add to the functionality and layout of the Notion client, interacting with and modifying existing interfaces.`, }, // { // icon: "plug", // id: "integrations", // title: "Integrations", // description: ` // Integrations access and modify Notion content. They interact directly with // https://www.notion.so/api/v3. Use at your own risk.`, // }, ], sidebar = [ "notion-enhancer", { id: "welcome", title: "Welcome", icon: "notion-enhancer", }, { icon: "message-circle", title: "Community", href: "https://discord.gg/sFWPXtA", }, { icon: "clock", title: "Changelog", href: "https://notion-enhancer.github.io/about/changelog/", }, { icon: "book", title: "Documentation", href: "https://notion-enhancer.github.io/", }, { icon: "github", title: "Source Code", href: "https://github.com/notion-enhancer", }, "Settings", { id: "core", title: "Core", icon: "sliders-horizontal", disableUntilAgreedToTerms: true, }, ...categories.map((c) => ({ id: c.id, title: c.title, icon: c.icon, disableUntilAgreedToTerms: true, })), ]; const renderMenu = async () => { const { html, setState, useState } = globalThis.__enhancerApi, { getMods, isEnabled, setEnabled } = globalThis.__enhancerApi, [theme, icon] = useState(["theme", "icon"]); if (!theme || !icon || _renderStarted) return; if (icon === "Monochrome") sidebar[1].icon += "?mask"; _renderStarted = true; const mods = await getMods(); for (let i = 0; i < categories.length; i++) { const { id } = categories[i]; categories[i].mods = mods.filter(({ _src }) => _src.startsWith(`${id}/`)); categories[i].view = html`<${View} id=${id}> <${List} ...${categories[i]} /> `; } for (let i = 0; i < mods.length; i++) { const options = mods[i].options?.filter((opt) => opt.type !== "heading"); if (mods[i]._src === "core" || !options?.length) continue; const _get = () => isEnabled(mods[i].id), _set = async (enabled) => { await setEnabled(mods[i].id, enabled); setState({ rerender: true }); }; mods[i].view = html`<${View} id=${mods[i].id}> <${Mod} ...${{ ...mods[i], options: [], _get, _set }} /> <${Options} mod=${mods[i]} /> `; } const $sidebar = html`<${Sidebar} items=${sidebar} categories=${categories} />`, $main = html`
<${View} id="welcome"> <${Banner} updateAvailable=${await checkForUpdate()} isDevelopmentBuild=${await isDevelopmentBuild()} /> <${Onboarding} /> <${View} id="core"> <${Options} mod=${mods.find(({ _src }) => _src === "core")} /> <${Telemetry} /> <${Profiles} /> ${[...categories, ...mods] .filter(({ view }) => view) .map(({ view }) => view)}
<${Footer} categories=${categories} />
`; useState(["footerOpen"], ([footerOpen]) => { $main.style.height = footerOpen ? "100%" : "calc(100% + 65px)"; }); const $skeleton = document.querySelector("#skeleton"); $skeleton.replaceWith($sidebar, $main); }, registerHotkey = ([hotkey]) => { const { addKeyListener, setState, useState } = globalThis.__enhancerApi; if (!hotkey || _hotkeyRegistered) return; _hotkeyRegistered = true; addKeyListener(hotkey, (event) => { event.preventDefault(); const msg = { channel: "notion-enhancer", action: "open-menu" }; parent?.postMessage(msg, "*"); }); addKeyListener("Escape", () => { const [popupOpen] = useState(["popupOpen"]); if (document.activeElement?.tagName === "INPUT") { document.activeElement.blur(); } else if (!popupOpen) { const msg = { channel: "notion-enhancer", action: "close-menu" }; parent?.postMessage(msg, "*"); } else setState({ rerender: true }); }); }, updateTheme = ([theme]) => { if (theme === "dark") document.body.classList.add("dark"); if (theme === "light") document.body.classList.remove("dark"); }; const importApi = () => { return (_apiImport ??= (async () => { const api = globalThis.__enhancerApi; if (typeof api === "undefined") await import("../../api/system.js"); await import("../../load.mjs").then((i) => i.default); })()); }, hookIntoState = () => { if (_stateHookedInto) return; _stateHookedInto = true; const { useState } = globalThis.__enhancerApi; useState(["theme"], updateTheme); useState(["hotkey"], registerHotkey); useState(["rerender"], renderMenu); }; addEventListener("message", async (event) => { if (event.data?.channel !== "notion-enhancer") return; await importApi().then(hookIntoState); const { setState, useState } = globalThis.__enhancerApi; setState({ rerender: true, hotkey: event.data?.hotkey ?? useState(["hotkey"])[0], theme: event.data?.theme ?? useState(["theme"])[0], icon: event.data?.icon ?? useState(["icon"])[0], }); });