mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-11 15:59:03 +00:00
refactor: move state.mjs into markup.mjs for use in notion interface
This commit is contained in:
parent
bb5853e866
commit
45e0be7d62
@ -8,6 +8,7 @@ import { checkForUpdate } from "./updateCheck.mjs";
|
|||||||
import { sendTelemetryPing } from "./sendTelemetry.mjs";
|
import { sendTelemetryPing } from "./sendTelemetry.mjs";
|
||||||
import { Modal, Frame } from "./islands/Modal.mjs";
|
import { Modal, Frame } from "./islands/Modal.mjs";
|
||||||
import { MenuButton } from "./islands/MenuButton.mjs";
|
import { MenuButton } from "./islands/MenuButton.mjs";
|
||||||
|
import { TopbarButton } from "./islands/TopbarButton.mjs";
|
||||||
import { Panel } from "./islands/Panel.mjs";
|
import { Panel } from "./islands/Panel.mjs";
|
||||||
|
|
||||||
const shouldLoadThemeOverrides = async (db) => {
|
const shouldLoadThemeOverrides = async (db) => {
|
||||||
@ -118,10 +119,11 @@ const insertMenu = async (db) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertPanel = async (db) => {
|
const insertPanel = async (api, db) => {
|
||||||
const notionFrame = ".notion-frame",
|
const notionFrame = ".notion-frame",
|
||||||
{ html, addKeyListener, addMutationListener } = globalThis.__enhancerApi,
|
notionTopbarBtn = ".notion-topbar-more-button",
|
||||||
togglePanelHotkey = await db.get("togglePanelHotkey");
|
togglePanelHotkey = await db.get("togglePanelHotkey"),
|
||||||
|
{ html } = api;
|
||||||
|
|
||||||
const $panel = html`<${Panel}
|
const $panel = html`<${Panel}
|
||||||
_getWidth=${() => db.get("sidePanelWidth")}
|
_getWidth=${() => db.get("sidePanelWidth")}
|
||||||
@ -129,28 +131,45 @@ const insertPanel = async (db) => {
|
|||||||
_getOpen=${() => db.get("sidePanelOpen")}
|
_getOpen=${() => db.get("sidePanelOpen")}
|
||||||
_setOpen=${(open) => db.set("sidePanelOpen", open)}
|
_setOpen=${(open) => db.set("sidePanelOpen", open)}
|
||||||
/>`,
|
/>`,
|
||||||
|
togglePanel = () => {
|
||||||
|
if ($panel.hasAttribute("open")) $panel.close();
|
||||||
|
else $panel.open();
|
||||||
|
};
|
||||||
|
|
||||||
|
const $panelTopbarBtn = html`<${TopbarButton}
|
||||||
|
aria-label="Open side panel"
|
||||||
|
icon="panel-right"
|
||||||
|
onclick=${togglePanel}
|
||||||
|
/>`,
|
||||||
appendToDom = () => {
|
appendToDom = () => {
|
||||||
const $frame = document.querySelector(notionFrame);
|
const $frame = document.querySelector(notionFrame);
|
||||||
if (!$frame) return;
|
if (!$frame) return;
|
||||||
if (!$frame.contains($panel)) $frame.append($panel);
|
if (!$frame.contains($panel)) $frame.append($panel);
|
||||||
if (!$frame.style.flexDirection !== "row")
|
if (!$frame.style.flexDirection !== "row")
|
||||||
$frame.style.flexDirection = "row";
|
$frame.style.flexDirection = "row";
|
||||||
|
if (!document.contains($panelTopbarBtn)) {
|
||||||
|
const $notionTopbarBtn = document.querySelector(notionTopbarBtn);
|
||||||
|
$notionTopbarBtn?.before($panelTopbarBtn);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
addMutationListener(notionFrame, appendToDom);
|
api.addMutationListener(`${notionFrame}, ${notionTopbarBtn}`, appendToDom);
|
||||||
|
api.useState(["panelOpen"], ([panelOpen]) => {
|
||||||
|
if (panelOpen) $panelTopbarBtn.setAttribute("data-active", true);
|
||||||
|
else $panelTopbarBtn.removeAttribute("data-active");
|
||||||
|
});
|
||||||
appendToDom();
|
appendToDom();
|
||||||
|
|
||||||
addKeyListener(togglePanelHotkey, (event) => {
|
api.addKeyListener(togglePanelHotkey, (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if ($panel.hasAttribute("open")) $panel.close();
|
togglePanel();
|
||||||
else $panel.open();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async (api, db) =>
|
export default async (api, db) =>
|
||||||
Promise.all([
|
Promise.all([
|
||||||
insertMenu(db),
|
insertMenu(db),
|
||||||
insertPanel(db),
|
insertPanel(api, db),
|
||||||
insertCustomStyles(db),
|
insertCustomStyles(db),
|
||||||
loadThemeOverrides(db),
|
loadThemeOverrides(db),
|
||||||
sendTelemetryPing(),
|
sendTelemetryPing(),
|
||||||
|
@ -4,17 +4,6 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// <div class="absolute top-0">
|
|
||||||
// <svg
|
|
||||||
// role="graphics-symbol"
|
|
||||||
// viewBox="0 0 16 16"
|
|
||||||
// class="doubleChevronRight"
|
|
||||||
// style="width: 16px; height: 16px; display: block; fill: rgba(255, 255, 255, 0.443); flex-shrink: 0;"
|
|
||||||
// >
|
|
||||||
// <path d="M2.25781 14.1211C2.47656 14.1211 2.66797 14.0391 2.81836 13.8887L8.14355 8.67969C8.32812 8.49512 8.41699 8.29688 8.41699 8.06445C8.41699 7.8252 8.32812 7.62012 8.14355 7.44922L2.81836 2.24023C2.66797 2.08984 2.4834 2.00781 2.25781 2.00781C1.81348 2.00781 1.46484 2.35645 1.46484 2.80078C1.46484 3.0127 1.55371 3.21777 1.7041 3.375L6.50977 8.05762L1.7041 12.7539C1.55371 12.9043 1.46484 13.1094 1.46484 13.3281C1.46484 13.7725 1.81348 14.1211 2.25781 14.1211ZM8.36914 14.1211C8.58789 14.1211 8.77246 14.0391 8.92285 13.8887L14.2549 8.67969C14.4395 8.49512 14.5283 8.29688 14.5283 8.06445C14.5283 7.8252 14.4326 7.62012 14.2549 7.44922L8.92285 2.24023C8.77246 2.08984 8.58789 2.00781 8.36914 2.00781C7.9248 2.00781 7.56934 2.35645 7.56934 2.80078C7.56934 3.0127 7.66504 3.21777 7.81543 3.375L12.6211 8.05762L7.81543 12.7539C7.66504 12.9043 7.56934 13.1094 7.56934 13.3281C7.56934 13.7725 7.9248 14.1211 8.36914 14.1211Z"></path>
|
|
||||||
// </svg>
|
|
||||||
// </div>;
|
|
||||||
|
|
||||||
import { Tooltip } from "./Tooltip.mjs";
|
import { Tooltip } from "./Tooltip.mjs";
|
||||||
|
|
||||||
function PanelView(props) {
|
function PanelView(props) {
|
||||||
@ -36,9 +25,8 @@ function Panel({
|
|||||||
maxWidth = 640,
|
maxWidth = 640,
|
||||||
...props
|
...props
|
||||||
}) {
|
}) {
|
||||||
const { html, extendProps } = globalThis.__enhancerApi,
|
const { html, ...api } = globalThis.__enhancerApi;
|
||||||
{ addMutationListener, removeMutationListener } = globalThis.__enhancerApi;
|
api.extendProps(props, {
|
||||||
extendProps(props, {
|
|
||||||
class: `notion-enhancer--side-panel order-2 shrink-0
|
class: `notion-enhancer--side-panel order-2 shrink-0
|
||||||
transition-[width] open:w-[var(--side\\_panel--width)]
|
transition-[width] open:w-[var(--side\\_panel--width)]
|
||||||
w-0 border-l-1 border-[color:var(--theme--fg-border)]
|
w-0 border-l-1 border-[color:var(--theme--fg-border)]
|
||||||
@ -65,21 +53,22 @@ function Panel({
|
|||||||
d="M2.25781 14.1211C2.47656 14.1211 2.66797 14.0391 2.81836 13.8887L8.14355 8.67969C8.32812 8.49512 8.41699 8.29688 8.41699 8.06445C8.41699 7.8252 8.32812 7.62012 8.14355 7.44922L2.81836 2.24023C2.66797 2.08984 2.4834 2.00781 2.25781 2.00781C1.81348 2.00781 1.46484 2.35645 1.46484 2.80078C1.46484 3.0127 1.55371 3.21777 1.7041 3.375L6.50977 8.05762L1.7041 12.7539C1.55371 12.9043 1.46484 13.1094 1.46484 13.3281C1.46484 13.7725 1.81348 14.1211 2.25781 14.1211ZM8.36914 14.1211C8.58789 14.1211 8.77246 14.0391 8.92285 13.8887L14.2549 8.67969C14.4395 8.49512 14.5283 8.29688 14.5283 8.06445C14.5283 7.8252 14.4326 7.62012 14.2549 7.44922L8.92285 2.24023C8.77246 2.08984 8.58789 2.00781 8.36914 2.00781C7.9248 2.00781 7.56934 2.35645 7.56934 2.80078C7.56934 3.0127 7.66504 3.21777 7.81543 3.375L12.6211 8.05762L7.81543 12.7539C7.66504 12.9043 7.56934 13.1094 7.56934 13.3281C7.56934 13.7725 7.9248 14.1211 8.36914 14.1211Z"
|
d="M2.25781 14.1211C2.47656 14.1211 2.66797 14.0391 2.81836 13.8887L8.14355 8.67969C8.32812 8.49512 8.41699 8.29688 8.41699 8.06445C8.41699 7.8252 8.32812 7.62012 8.14355 7.44922L2.81836 2.24023C2.66797 2.08984 2.4834 2.00781 2.25781 2.00781C1.81348 2.00781 1.46484 2.35645 1.46484 2.80078C1.46484 3.0127 1.55371 3.21777 1.7041 3.375L6.50977 8.05762L1.7041 12.7539C1.55371 12.9043 1.46484 13.1094 1.46484 13.3281C1.46484 13.7725 1.81348 14.1211 2.25781 14.1211ZM8.36914 14.1211C8.58789 14.1211 8.77246 14.0391 8.92285 13.8887L14.2549 8.67969C14.4395 8.49512 14.5283 8.29688 14.5283 8.06445C14.5283 7.8252 14.4326 7.62012 14.2549 7.44922L8.92285 2.24023C8.77246 2.08984 8.58789 2.00781 8.36914 2.00781C7.9248 2.00781 7.56934 2.35645 7.56934 2.80078C7.56934 3.0127 7.66504 3.21777 7.81543 3.375L12.6211 8.05762L7.81543 12.7539C7.66504 12.9043 7.56934 13.1094 7.56934 13.3281C7.56934 13.7725 7.9248 14.1211 8.36914 14.1211Z"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>`,
|
</div>`;
|
||||||
$panel = html`<aside ...${props}>
|
|
||||||
${$resizeHandle}
|
const $panel = html`<aside ...${props}>
|
||||||
<div
|
${$resizeHandle}
|
||||||
class="flex justify-between items-center
|
<div
|
||||||
|
class="flex justify-between items-center
|
||||||
border-(b [color:var(--theme--fg-border)])"
|
border-(b [color:var(--theme--fg-border)])"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="font-size: 14px; color: rgba(255, 255, 255, 0.81); font-weight: 500; display: flex; align-items: center; padding: 12px 12px 12px 16px;"
|
||||||
>
|
>
|
||||||
<div
|
Comments
|
||||||
style="font-size: 14px; color: rgba(255, 255, 255, 0.81); font-weight: 500; display: flex; align-items: center; padding: 12px 12px 12px 16px;"
|
|
||||||
>
|
|
||||||
Comments
|
|
||||||
</div>
|
|
||||||
${$chevronClose}
|
|
||||||
</div>
|
</div>
|
||||||
</aside>`;
|
${$chevronClose}
|
||||||
|
</div>
|
||||||
|
</aside>`;
|
||||||
|
|
||||||
let preDragWidth,
|
let preDragWidth,
|
||||||
userDragActive,
|
userDragActive,
|
||||||
@ -139,33 +128,9 @@ function Panel({
|
|||||||
options = { duration: 150, easing: "cubic-bezier(0.4, 0, 0.2, 1)" };
|
options = { duration: 150, easing: "cubic-bezier(0.4, 0, 0.2, 1)" };
|
||||||
$notionHelp.style.setProperty("right", destination);
|
$notionHelp.style.setProperty("right", destination);
|
||||||
$notionHelp.animate(keyframes, options);
|
$notionHelp.animate(keyframes, options);
|
||||||
removeMutationListener(repositionHelp);
|
api.removeMutationListener(repositionHelp);
|
||||||
};
|
};
|
||||||
addMutationListener(notionHelp, repositionHelp);
|
api.addMutationListener(notionHelp, repositionHelp);
|
||||||
|
|
||||||
const notionTopbarMore = ".notion-topbar-more-button",
|
|
||||||
$topbarPanelButton = html`<button
|
|
||||||
aria-label="Open side panel"
|
|
||||||
class="user-select-none h-[28px] w-[33px] duration-[20ms]
|
|
||||||
transition inline-flex items-center justify-center mr-[2px]
|
|
||||||
rounded-[3px] hover:bg-[color:var(--theme--bg-hover)]
|
|
||||||
[data-active]:bg-[color:var(--theme--bg-hover)]"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="i-panel-right w-[20px] h-[20px] fill-[color:var(--theme--fg-secondary)]"
|
|
||||||
/>
|
|
||||||
</button>`,
|
|
||||||
appendToDom = () => {
|
|
||||||
if (document.contains($topbarPanelButton)) return;
|
|
||||||
const $notionTopbarMore = document.querySelector(notionTopbarMore);
|
|
||||||
$notionTopbarMore?.before($topbarPanelButton);
|
|
||||||
};
|
|
||||||
$topbarPanelButton.addEventListener("click", () => {
|
|
||||||
if ($panel.hasAttribute("open")) $panel.close();
|
|
||||||
else $panel.open();
|
|
||||||
});
|
|
||||||
addMutationListener(notionTopbarMore, appendToDom);
|
|
||||||
appendToDom();
|
|
||||||
|
|
||||||
$panel.resize = async (width) => {
|
$panel.resize = async (width) => {
|
||||||
$tooltip.hide();
|
$tooltip.hide();
|
||||||
@ -181,7 +146,7 @@ function Panel({
|
|||||||
$panel.open = () => {
|
$panel.open = () => {
|
||||||
$panel.setAttribute("open", true);
|
$panel.setAttribute("open", true);
|
||||||
$panel.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = 0));
|
$panel.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = 0));
|
||||||
$topbarPanelButton.setAttribute("data-active", true);
|
api.setState({ panelOpen: true });
|
||||||
$panel.onopen?.();
|
$panel.onopen?.();
|
||||||
_setOpen(true);
|
_setOpen(true);
|
||||||
$panel.resize();
|
$panel.resize();
|
||||||
@ -191,8 +156,8 @@ function Panel({
|
|||||||
$panel.onbeforeclose?.();
|
$panel.onbeforeclose?.();
|
||||||
$panel.removeAttribute("open");
|
$panel.removeAttribute("open");
|
||||||
$panel.style.pointerEvents = "auto";
|
$panel.style.pointerEvents = "auto";
|
||||||
$topbarPanelButton.removeAttribute("data-active");
|
|
||||||
$panel.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = -1));
|
$panel.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = -1));
|
||||||
|
api.setState({ panelOpen: false });
|
||||||
repositionHelp();
|
repositionHelp();
|
||||||
_setOpen(false);
|
_setOpen(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
26
src/core/islands/TopbarButton.mjs
Normal file
26
src/core/islands/TopbarButton.mjs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* notion-enhancer
|
||||||
|
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||||
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
|
*/
|
||||||
|
|
||||||
|
function TopbarButton({ icon, ...props }, ...children) {
|
||||||
|
const { html, extendProps } = globalThis.__enhancerApi;
|
||||||
|
extendProps(props, {
|
||||||
|
tabindex: 0,
|
||||||
|
role: "button",
|
||||||
|
class: `notion-enhancer--topbar-button
|
||||||
|
user-select-none h-[28px] w-[33px] duration-[20ms]
|
||||||
|
transition inline-flex items-center justify-center mr-[2px]
|
||||||
|
rounded-[3px] hover:bg-[color:var(--theme--bg-hover)]
|
||||||
|
[data-active]:bg-[color:var(--theme--bg-hover)]`,
|
||||||
|
});
|
||||||
|
return html`<button ...${props}>
|
||||||
|
<i
|
||||||
|
class="i-${icon} w-[20px] h-[20px]
|
||||||
|
fill-[color:var(--theme--fg-secondary)]"
|
||||||
|
/>
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { TopbarButton };
|
@ -7,7 +7,6 @@
|
|||||||
import { Popup } from "./Popup.mjs";
|
import { Popup } from "./Popup.mjs";
|
||||||
import { Button } from "./Button.mjs";
|
import { Button } from "./Button.mjs";
|
||||||
import { Description } from "./Description.mjs";
|
import { Description } from "./Description.mjs";
|
||||||
import { useState } from "../state.mjs";
|
|
||||||
|
|
||||||
const updateGuide =
|
const updateGuide =
|
||||||
"https://notion-enhancer.github.io/getting-started/updating/",
|
"https://notion-enhancer.github.io/getting-started/updating/",
|
||||||
@ -69,7 +68,7 @@ function Circle(rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Banner({ updateAvailable, isDevelopmentBuild }) {
|
function Banner({ updateAvailable, isDevelopmentBuild }) {
|
||||||
const { html, version, initDatabase } = globalThis.__enhancerApi,
|
const { html, version, initDatabase, useState } = globalThis.__enhancerApi,
|
||||||
$version = html`<button
|
$version = html`<button
|
||||||
class="text-[12px] py-[2px] px-[6px] mt-[2px]
|
class="text-[12px] py-[2px] px-[6px] mt-[2px]
|
||||||
font-medium leading-tight tracking-wide rounded-[3px]
|
font-medium leading-tight tracking-wide rounded-[3px]
|
||||||
|
@ -4,11 +4,10 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
|
|
||||||
function Checkbox({ _get, _set, _requireReload = true, ...props }) {
|
function Checkbox({ _get, _set, _requireReload = true, ...props }) {
|
||||||
let _initialValue;
|
let _initialValue;
|
||||||
const { html, extendProps } = globalThis.__enhancerApi,
|
const { html, extendProps } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
$input = html`<input
|
$input = html`<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="hidden checked:sibling:(px-px
|
class="hidden checked:sibling:(px-px
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
import { Button } from "./Button.mjs";
|
import { Button } from "./Button.mjs";
|
||||||
|
|
||||||
function Footer({ categories }) {
|
function Footer({ categories }) {
|
||||||
const { html, reloadApp } = globalThis.__enhancerApi,
|
const { html, reloadApp } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
$reload = html`<${Button}
|
$reload = html`<${Button}
|
||||||
class="ml-auto"
|
class="ml-auto"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
|
|
||||||
const updateHotkey = (event) => {
|
const updateHotkey = (event) => {
|
||||||
const keys = [];
|
const keys = [];
|
||||||
for (const modifier of ["metaKey", "ctrlKey", "altKey", "shiftKey"]) {
|
for (const modifier of ["metaKey", "ctrlKey", "altKey", "shiftKey"]) {
|
||||||
@ -76,7 +74,8 @@ function Input({
|
|||||||
...props
|
...props
|
||||||
}) {
|
}) {
|
||||||
let $filename, $clear;
|
let $filename, $clear;
|
||||||
const { html, extendProps } = globalThis.__enhancerApi;
|
const { html, extendProps } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi;
|
||||||
Coloris({ format: "rgb" });
|
Coloris({ format: "rgb" });
|
||||||
|
|
||||||
type ??= "text";
|
type ??= "text";
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState } from "../state.mjs";
|
|
||||||
import { Description } from "./Description.mjs";
|
import { Description } from "./Description.mjs";
|
||||||
import { Input } from "./Input.mjs";
|
import { Input } from "./Input.mjs";
|
||||||
import { Mod } from "./Mod.mjs";
|
import { Mod } from "./Mod.mjs";
|
||||||
@ -38,7 +37,7 @@ function Search({ items, itemType }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function List({ id, mods, description }) {
|
function List({ id, mods, description }) {
|
||||||
const { html, isEnabled, setEnabled } = globalThis.__enhancerApi,
|
const { html, isEnabled, setEnabled, setState } = globalThis.__enhancerApi,
|
||||||
$mods = mods.map((mod) => {
|
$mods = mods.map((mod) => {
|
||||||
const _get = () => isEnabled(mod.id),
|
const _get = () => isEnabled(mod.id),
|
||||||
_set = async (enabled) => {
|
_set = async (enabled) => {
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState } from "../state.mjs";
|
|
||||||
import { Description } from "./Description.mjs";
|
import { Description } from "./Description.mjs";
|
||||||
import { Toggle } from "./Toggle.mjs";
|
import { Toggle } from "./Toggle.mjs";
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ function Mod({
|
|||||||
_set,
|
_set,
|
||||||
_src,
|
_src,
|
||||||
}) {
|
}) {
|
||||||
const { html, enhancerUrl } = globalThis.__enhancerApi,
|
const { html, enhancerUrl, setState } = globalThis.__enhancerApi,
|
||||||
toggleId = Math.random().toString(36).slice(2, 5);
|
toggleId = Math.random().toString(36).slice(2, 5);
|
||||||
|
|
||||||
return html`<label
|
return html`<label
|
||||||
|
@ -9,13 +9,13 @@ import { Description } from "./Description.mjs";
|
|||||||
import { Checkbox } from "./Checkbox.mjs";
|
import { Checkbox } from "./Checkbox.mjs";
|
||||||
import { Button } from "./Button.mjs";
|
import { Button } from "./Button.mjs";
|
||||||
import { Tile } from "./Tile.mjs";
|
import { Tile } from "./Tile.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/",
|
||||||
tsAndCs = "https://notion-enhancer.github.io/about/terms-and-conditions/";
|
tsAndCs = "https://notion-enhancer.github.io/about/terms-and-conditions/";
|
||||||
|
|
||||||
function Onboarding() {
|
function Onboarding() {
|
||||||
const { html, version, initDatabase } = globalThis.__enhancerApi,
|
const { html, version, initDatabase } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
$submitAgreement = html`<${Button}
|
$submitAgreement = html`<${Button}
|
||||||
icon="arrow-right"
|
icon="arrow-right"
|
||||||
class="ml-auto"
|
class="ml-auto"
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState } from "../state.mjs";
|
|
||||||
import { Heading } from "./Heading.mjs";
|
import { Heading } from "./Heading.mjs";
|
||||||
import { Description } from "./Description.mjs";
|
import { Description } from "./Description.mjs";
|
||||||
import { Input } from "./Input.mjs";
|
import { Input } from "./Input.mjs";
|
||||||
@ -78,7 +77,7 @@ function Option({ _get, _set, ...opt }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Options({ mod }) {
|
function Options({ mod }) {
|
||||||
const { html, modDatabase } = globalThis.__enhancerApi;
|
const { html, modDatabase, setState } = globalThis.__enhancerApi;
|
||||||
return filterOptionsForRender(mod.options).map((opt) => {
|
return filterOptionsForRender(mod.options).map((opt) => {
|
||||||
opt.label ??= camelToSentenceCase(opt.key);
|
opt.label ??= camelToSentenceCase(opt.key);
|
||||||
if (opt.type === "heading") return html`<${Heading}>${opt.label}<//>`;
|
if (opt.type === "heading") return html`<${Heading}>${opt.label}<//>`;
|
||||||
|
@ -4,10 +4,9 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
|
|
||||||
function Popup({ trigger, ...props }, ...children) {
|
function Popup({ trigger, ...props }, ...children) {
|
||||||
const { html, extendProps } = globalThis.__enhancerApi;
|
const { html, extendProps } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi;
|
||||||
extendProps(props, {
|
extendProps(props, {
|
||||||
class: `notion-enhancer--menu-popup
|
class: `notion-enhancer--menu-popup
|
||||||
group absolute top-0 left-0 w-full h-full
|
group absolute top-0 left-0 w-full h-full
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
import { Heading } from "./Heading.mjs";
|
import { Heading } from "./Heading.mjs";
|
||||||
import { Description } from "./Description.mjs";
|
import { Description } from "./Description.mjs";
|
||||||
import { Checkbox } from "./Checkbox.mjs";
|
import { Checkbox } from "./Checkbox.mjs";
|
||||||
@ -14,6 +13,7 @@ import { Popup } from "./Popup.mjs";
|
|||||||
|
|
||||||
function Profile({ id }) {
|
function Profile({ id }) {
|
||||||
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
profile = initDatabase([id]),
|
profile = initDatabase([id]),
|
||||||
db = initDatabase();
|
db = initDatabase();
|
||||||
|
|
||||||
@ -180,6 +180,7 @@ function Profile({ id }) {
|
|||||||
|
|
||||||
function Profiles() {
|
function Profiles() {
|
||||||
const { html, initDatabase } = globalThis.__enhancerApi,
|
const { html, initDatabase } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
$input = html`<${Input} icon="file-cog" />`,
|
$input = html`<${Input} icon="file-cog" />`,
|
||||||
$list = html`<ul></ul>`;
|
$list = html`<ul></ul>`;
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
import { Popup } from "./Popup.mjs";
|
import { Popup } from "./Popup.mjs";
|
||||||
|
|
||||||
function Option({ value, _get, _set }) {
|
function Option({ value, _get, _set }) {
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
$selected = html`<i class="ml-auto i-check w-[16px] h-[16px]"></i>`,
|
$selected = html`<i class="ml-auto i-check w-[16px] h-[16px]"></i>`,
|
||||||
$option = html`<div
|
$option = html`<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@ -36,6 +36,7 @@ function Option({ value, _get, _set }) {
|
|||||||
function Select({ values, _get, _set, _requireReload = true, ...props }) {
|
function Select({ values, _get, _set, _requireReload = true, ...props }) {
|
||||||
let _initialValue;
|
let _initialValue;
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
// dir="rtl" overflows to the left during transition
|
// dir="rtl" overflows to the left during transition
|
||||||
$select = html`<div
|
$select = html`<div
|
||||||
dir="rtl"
|
dir="rtl"
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
import { Description } from "./Description.mjs";
|
import { Description } from "./Description.mjs";
|
||||||
|
|
||||||
function SidebarHeading({}, ...children) {
|
function SidebarHeading({}, ...children) {
|
||||||
const { html } = globalThis.__enhancerApi;
|
const { html } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi;
|
||||||
return html`<h2
|
return html`<h2
|
||||||
class="flex items-center font-semibold leading-none
|
class="flex items-center font-semibold leading-none
|
||||||
text-([12px] [color:var(--theme--fg-secondary)])
|
text-([12px] [color:var(--theme--fg-secondary)])
|
||||||
@ -20,6 +20,7 @@ function SidebarHeading({}, ...children) {
|
|||||||
|
|
||||||
function SidebarButton({ id, icon, ...props }, ...children) {
|
function SidebarButton({ id, icon, ...props }, ...children) {
|
||||||
const { html, extendProps } = globalThis.__enhancerApi,
|
const { html, extendProps } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
$btn = html`<${props["href"] ? "a" : "button"}
|
$btn = html`<${props["href"] ? "a" : "button"}
|
||||||
class="flex items-center select-none text-[14px]
|
class="flex items-center select-none text-[14px]
|
||||||
min-h-[27px] px-[12px] my-px last:mb-[12px] w-full
|
min-h-[27px] px-[12px] my-px last:mb-[12px] w-full
|
||||||
@ -53,6 +54,7 @@ function SidebarButton({ id, icon, ...props }, ...children) {
|
|||||||
function Sidebar({ items, categories }) {
|
function Sidebar({ items, categories }) {
|
||||||
const { html, version } = globalThis.__enhancerApi,
|
const { html, version } = globalThis.__enhancerApi,
|
||||||
{ initDatabase, isEnabled } = globalThis.__enhancerApi,
|
{ initDatabase, isEnabled } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
$agreeToUnlock = html`<span
|
$agreeToUnlock = html`<span
|
||||||
class="pt-[2px] pb-[5px] px-[15px] text-[12px]
|
class="pt-[2px] pb-[5px] px-[15px] text-[12px]
|
||||||
inline-block text-[color:var(--theme--fg-red)]"
|
inline-block text-[color:var(--theme--fg-red)]"
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { collectTelemetryData } from "../../sendTelemetry.mjs";
|
import { collectTelemetryData } from "../../sendTelemetry.mjs";
|
||||||
import { useState, setState } from "../state.mjs";
|
|
||||||
import { Option } from "./Options.mjs";
|
import { Option } from "./Options.mjs";
|
||||||
|
|
||||||
const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/";
|
const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/";
|
||||||
function Telemetry() {
|
function Telemetry() {
|
||||||
const { html, initDatabase } = globalThis.__enhancerApi,
|
const { html, initDatabase } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
_get = async () => {
|
_get = async () => {
|
||||||
// defaults to true, must be explicitly set to false to disable
|
// defaults to true, must be explicitly set to false to disable
|
||||||
return (await initDatabase().get("telemetryEnabled")) ?? true;
|
return (await initDatabase().get("telemetryEnabled")) ?? true;
|
||||||
|
@ -4,11 +4,10 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
|
|
||||||
function Toggle({ _get, _set, _requireReload = true, ...props }) {
|
function Toggle({ _get, _set, _requireReload = true, ...props }) {
|
||||||
let _initialValue;
|
let _initialValue;
|
||||||
const { html, extendProps } = globalThis.__enhancerApi,
|
const { html, extendProps } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
$input = html`<input
|
$input = html`<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="hidden checked:sibling:children:(
|
class="hidden checked:sibling:children:(
|
||||||
|
@ -4,10 +4,9 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "../state.mjs";
|
|
||||||
|
|
||||||
function View({ id }, ...children) {
|
function View({ id }, ...children) {
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
|
{ setState, useState } = globalThis.__enhancerApi,
|
||||||
// set padding on last child to maintain pad on overflow
|
// set padding on last child to maintain pad on overflow
|
||||||
$view = html`<article
|
$view = html`<article
|
||||||
id=${id}
|
id=${id}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setState, useState } from "./state.mjs";
|
|
||||||
import { checkForUpdate, isDevelopmentBuild } from "../updateCheck.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";
|
||||||
@ -17,6 +16,11 @@ import { Mod } from "./islands/Mod.mjs";
|
|||||||
import { Options } from "./islands/Options.mjs";
|
import { Options } from "./islands/Options.mjs";
|
||||||
import { Profiles } from "./islands/Profiles.mjs";
|
import { Profiles } from "./islands/Profiles.mjs";
|
||||||
|
|
||||||
|
let _apiImported = false,
|
||||||
|
_renderStarted = false,
|
||||||
|
_stateHookedInto = false,
|
||||||
|
_hotkeyRegistered = false;
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
{
|
{
|
||||||
icon: "palette",
|
icon: "palette",
|
||||||
@ -85,118 +89,126 @@ const categories = [
|
|||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
|
||||||
const render = async () => {
|
const renderMenu = async () => {
|
||||||
const { html, getMods } = globalThis.__enhancerApi,
|
const { html, ...api } = globalThis.__enhancerApi,
|
||||||
{ isEnabled, setEnabled } = globalThis.__enhancerApi,
|
[theme, icon] = api.useState(["theme", "icon"]);
|
||||||
[icon, renderStarted] = useState(["icon", "renderStarted"]);
|
if (!theme || !icon || _renderStarted) return;
|
||||||
if (!html || !getMods || !icon || renderStarted) return;
|
if (icon === "Monochrome") sidebar[1].icon += "?mask";
|
||||||
if (icon === "Monochrome") sidebar[1].icon += "?mask";
|
_renderStarted = true;
|
||||||
setState({ renderStarted: true });
|
|
||||||
|
|
||||||
const mods = await getMods();
|
const mods = await api.getMods();
|
||||||
for (let i = 0; i < categories.length; i++) {
|
for (let i = 0; i < categories.length; i++) {
|
||||||
const { id } = categories[i];
|
const { id } = categories[i];
|
||||||
categories[i].mods = mods.filter(({ _src }) => _src.startsWith(`${id}/`));
|
categories[i].mods = mods.filter(({ _src }) => _src.startsWith(`${id}/`));
|
||||||
categories[i].view = html`<${View} id=${id}>
|
categories[i].view = html`<${View} id=${id}>
|
||||||
<${List} ...${categories[i]} />
|
<${List} ...${categories[i]} />
|
||||||
<//>`;
|
<//>`;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < mods.length; i++) {
|
for (let i = 0; i < mods.length; i++) {
|
||||||
const options = mods[i].options?.filter((opt) => opt.type !== "heading");
|
const options = mods[i].options?.filter((opt) => opt.type !== "heading");
|
||||||
if (mods[i]._src === "core" || !options?.length) continue;
|
if (mods[i]._src === "core" || !options?.length) continue;
|
||||||
const _get = () => isEnabled(mods[i].id),
|
const _get = () => api.isEnabled(mods[i].id),
|
||||||
_set = async (enabled) => {
|
_set = async (enabled) => {
|
||||||
await setEnabled(mods[i].id, enabled);
|
await api.setEnabled(mods[i].id, enabled);
|
||||||
setState({ rerender: true });
|
api.setState({ rerender: true });
|
||||||
};
|
};
|
||||||
mods[i].view = html`<${View} id=${mods[i].id}>
|
mods[i].view = html`<${View} id=${mods[i].id}>
|
||||||
<!-- passing an empty options array hides the settings button -->
|
<!-- passing an empty options array hides the settings button -->
|
||||||
<${Mod} ...${{ ...mods[i], options: [], _get, _set }} />
|
<${Mod} ...${{ ...mods[i], options: [], _get, _set }} />
|
||||||
<${Options} mod=${mods[i]} />
|
<${Options} mod=${mods[i]} />
|
||||||
<//>`;
|
<//>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const $sidebar = html`<${Sidebar}
|
const $sidebar = html`<${Sidebar}
|
||||||
items=${sidebar}
|
items=${sidebar}
|
||||||
categories=${categories}
|
categories=${categories}
|
||||||
/>`,
|
/>`,
|
||||||
$main = html`
|
$main = html`
|
||||||
<main class="flex-(& col) overflow-hidden transition-[height]">
|
<main class="flex-(& col) overflow-hidden transition-[height]">
|
||||||
<!-- wrappers necessary for transitions and breakpoints -->
|
<!-- wrappers necessary for transitions and breakpoints -->
|
||||||
<div class="grow overflow-auto">
|
<div class="grow overflow-auto">
|
||||||
<div class="relative h-full w-full">
|
<div class="relative h-full w-full">
|
||||||
<${View} id="welcome">
|
<${View} id="welcome">
|
||||||
<${Banner}
|
<${Banner}
|
||||||
updateAvailable=${await checkForUpdate()}
|
updateAvailable=${await checkForUpdate()}
|
||||||
isDevelopmentBuild=${await isDevelopmentBuild()}
|
isDevelopmentBuild=${await isDevelopmentBuild()}
|
||||||
/>
|
/>
|
||||||
<${Onboarding} />
|
<${Onboarding} />
|
||||||
<//>
|
<//>
|
||||||
<${View} id="core">
|
<${View} id="core">
|
||||||
<${Options} mod=${mods.find(({ _src }) => _src === "core")} />
|
<${Options} mod=${mods.find(({ _src }) => _src === "core")} />
|
||||||
<${Telemetry} />
|
<${Telemetry} />
|
||||||
<${Profiles} />
|
<${Profiles} />
|
||||||
<//>
|
<//>
|
||||||
${[...categories, ...mods]
|
${[...categories, ...mods]
|
||||||
.filter(({ view }) => view)
|
.filter(({ view }) => view)
|
||||||
.map(({ view }) => view)}
|
.map(({ view }) => view)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<${Footer} categories=${categories} />
|
||||||
<${Footer} categories=${categories} />
|
</main>
|
||||||
</main>
|
`;
|
||||||
`;
|
api.useState(["footerOpen"], ([footerOpen]) => {
|
||||||
useState(["footerOpen"], ([footerOpen]) => {
|
$main.style.height = footerOpen ? "100%" : "calc(100% + 33px)";
|
||||||
$main.style.height = footerOpen ? "100%" : "calc(100% + 33px)";
|
});
|
||||||
});
|
api.useState(["transitionInProgress"], ([transitionInProgress]) => {
|
||||||
useState(["transitionInProgress"], ([transitionInProgress]) => {
|
$main.children[0].style.overflow = transitionInProgress ? "hidden" : "";
|
||||||
$main.children[0].style.overflow = transitionInProgress ? "hidden" : "";
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const $skeleton = document.querySelector("#skeleton");
|
const $skeleton = document.querySelector("#skeleton");
|
||||||
$skeleton.replaceWith($sidebar, $main);
|
$skeleton.replaceWith($sidebar, $main);
|
||||||
};
|
},
|
||||||
|
registerHotkey = ([hotkey]) => {
|
||||||
window.addEventListener("focus", () => {
|
const api = globalThis.__enhancerApi;
|
||||||
setState({ focus: true, rerender: true });
|
if (!hotkey || _hotkeyRegistered) return;
|
||||||
});
|
_hotkeyRegistered = true;
|
||||||
window.addEventListener("message", (event) => {
|
api.addKeyListener(hotkey, (event) => {
|
||||||
if (event.data?.channel !== "notion-enhancer") return;
|
event.preventDefault();
|
||||||
const [hotkey, theme, icon] = useState(["hotkey", "theme", "icon"]);
|
const msg = { channel: "notion-enhancer", action: "open-menu" };
|
||||||
setState({
|
|
||||||
rerender: true,
|
|
||||||
hotkey: event.data?.hotkey ?? hotkey,
|
|
||||||
theme: event.data?.theme ?? theme,
|
|
||||||
icon: event.data?.icon ?? icon,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
useState(["hotkey"], ([hotkey]) => {
|
|
||||||
const { addKeyListener } = globalThis.__enhancerApi ?? {},
|
|
||||||
[hotkeyRegistered] = useState(["hotkeyRegistered"]),
|
|
||||||
[renderStarted] = useState(["renderStarted"]);
|
|
||||||
if (!hotkey || !addKeyListener || hotkeyRegistered || !renderStarted) return;
|
|
||||||
setState({ 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 (!popupOpen) {
|
|
||||||
const msg = { channel: "notion-enhancer", action: "close-menu" };
|
|
||||||
parent?.postMessage(msg, "*");
|
parent?.postMessage(msg, "*");
|
||||||
} else setState({ rerender: true });
|
});
|
||||||
|
api.addKeyListener("Escape", () => {
|
||||||
|
const [popupOpen] = api.useState(["popupOpen"]);
|
||||||
|
if (!popupOpen) {
|
||||||
|
const msg = { channel: "notion-enhancer", action: "close-menu" };
|
||||||
|
parent?.postMessage(msg, "*");
|
||||||
|
} else api.setState({ rerender: true });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateTheme = ([theme]) => {
|
||||||
|
if (theme === "dark") document.body.classList.add("dark");
|
||||||
|
if (theme === "light") document.body.classList.remove("dark");
|
||||||
|
};
|
||||||
|
|
||||||
|
const importApi = async () => {
|
||||||
|
if (_apiImported) return;
|
||||||
|
_apiImported = true;
|
||||||
|
const api = globalThis.__enhancerApi;
|
||||||
|
if (typeof api === "undefined") await import("../../shared/system.js");
|
||||||
|
await import("../../load.mjs").then((i) => i.default);
|
||||||
|
},
|
||||||
|
hookIntoState = () => {
|
||||||
|
if (_stateHookedInto) return;
|
||||||
|
_stateHookedInto = true;
|
||||||
|
const api = globalThis.__enhancerApi;
|
||||||
|
api.useState(["rerender"], renderMenu);
|
||||||
|
api.useState(["hotkey"], registerHotkey);
|
||||||
|
api.useState(["theme"], updateTheme);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("focus", async () => {
|
||||||
|
await importApi().then(hookIntoState);
|
||||||
|
const api = globalThis.__enhancerApi;
|
||||||
|
api.setState({ focus: true, rerender: true });
|
||||||
|
});
|
||||||
|
window.addEventListener("message", async (event) => {
|
||||||
|
if (event.data?.channel !== "notion-enhancer") return;
|
||||||
|
await importApi().then(hookIntoState);
|
||||||
|
const api = globalThis.__enhancerApi;
|
||||||
|
api.setState({
|
||||||
|
rerender: true,
|
||||||
|
hotkey: event.data?.hotkey ?? api.useState(["hotkey"]),
|
||||||
|
theme: event.data?.theme ?? api.useState(["theme"]),
|
||||||
|
icon: event.data?.icon ?? api.useState(["icon"]),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
useState(["theme"], ([theme]) => {
|
|
||||||
if (theme === "dark") document.body.classList.add("dark");
|
|
||||||
if (theme === "light") document.body.classList.remove("dark");
|
|
||||||
});
|
|
||||||
|
|
||||||
useState(["rerender"], async () => {
|
|
||||||
const [theme, icon] = useState(["theme", "icon"]);
|
|
||||||
if (!theme || !icon) return;
|
|
||||||
if (typeof globalThis.__enhancerApi === "undefined")
|
|
||||||
await import("../../shared/system.js");
|
|
||||||
(await import("../../load.mjs")).default.then(render);
|
|
||||||
});
|
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* notion-enhancer
|
|
||||||
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
|
||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
|
||||||
*/
|
|
||||||
|
|
||||||
const _state = {},
|
|
||||||
_subscribers = [];
|
|
||||||
|
|
||||||
const setState = (state) => {
|
|
||||||
Object.assign(_state, state);
|
|
||||||
const updates = Object.keys(state);
|
|
||||||
_subscribers
|
|
||||||
.filter(([keys]) => updates.some((key) => keys.includes(key)))
|
|
||||||
.forEach(([keys, callback]) => callback(keys.map((key) => _state[key])));
|
|
||||||
},
|
|
||||||
useState = (keys, callback) => {
|
|
||||||
const state = keys.map((key) => _state[key]);
|
|
||||||
if (callback) _subscribers.push([keys, callback]);
|
|
||||||
callback?.(state);
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { setState, useState };
|
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
// jump to
|
||||||
|
|
||||||
const { twind, htm, lucide } = globalThis,
|
const { twind, htm, lucide } = globalThis,
|
||||||
{ readFile, iconColour, iconMonochrome } = globalThis.__enhancerApi;
|
{ readFile, iconColour, iconMonochrome } = globalThis.__enhancerApi;
|
||||||
|
|
||||||
@ -71,6 +73,14 @@ const encodeSvg = (svg) =>
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// at-runtime utility class evaluation w/ twind:
|
||||||
|
// - feature parity w/ tailwind v3
|
||||||
|
// - useful for building self-contained components
|
||||||
|
// (mods can extend interfaces w/out needing to
|
||||||
|
// import additional stylesheets)
|
||||||
|
// - integrated with lucide to render icons w/out
|
||||||
|
// complex markup, e.g. `<i class="i-bookmark" />`
|
||||||
twind.install({
|
twind.install({
|
||||||
darkMode: "class",
|
darkMode: "class",
|
||||||
rules: [[/^i-((?:\w|-)+)(?:\?(mask|bg|auto))?$/, presetIcons]],
|
rules: [[/^i-((?:\w|-)+)(?:\?(mask|bg|auto))?$/, presetIcons]],
|
||||||
@ -87,14 +97,6 @@ twind.install({
|
|||||||
["\\[.+]", (match) => "&" + match.input],
|
["\\[.+]", (match) => "&" + match.input],
|
||||||
["([a-z-]+):", ({ 1: $1 }) => "&::" + $1],
|
["([a-z-]+):", ({ 1: $1 }) => "&::" + $1],
|
||||||
],
|
],
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
backgroundImage: {
|
|
||||||
"brand-gradient":
|
|
||||||
"linear-gradient(225deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 100%)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
|
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
|
||||||
@ -535,7 +537,11 @@ const svgElements = [
|
|||||||
"zoomAndPan",
|
"zoomAndPan",
|
||||||
];
|
];
|
||||||
|
|
||||||
// html`<div class=${className}></div>`
|
// enables use of the jsx-like htm syntax
|
||||||
|
// for building components and interfaces
|
||||||
|
// with tagged templates. instantiates dom
|
||||||
|
// elements directly, does not use a vdom.
|
||||||
|
// e.g. html`<div class=${className}></div>`
|
||||||
const h = (type, props, ...children) => {
|
const h = (type, props, ...children) => {
|
||||||
children = children.flat(Infinity);
|
children = children.flat(Infinity);
|
||||||
// html`<${Component} attr="value">Click Me<//>`
|
// html`<${Component} attr="value">Click Me<//>`
|
||||||
@ -557,24 +563,61 @@ const h = (type, props, ...children) => {
|
|||||||
elem.append(...children);
|
elem.append(...children);
|
||||||
return elem;
|
return elem;
|
||||||
},
|
},
|
||||||
|
// combines instance-provided element props
|
||||||
|
// with a template of element props such that
|
||||||
|
// island/component/template props handlers
|
||||||
|
// and styles can be preserved and extended
|
||||||
|
// rather than overwritten
|
||||||
|
extendProps = (props, extend) => {
|
||||||
|
for (const key in extend) {
|
||||||
|
const { [key]: userProvided } = props;
|
||||||
|
if (typeof extend[key] === "function") {
|
||||||
|
props[key] = (...args) => {
|
||||||
|
extend[key](...args);
|
||||||
|
userProvided?.(...args);
|
||||||
|
};
|
||||||
|
} else if (key === "class") {
|
||||||
|
if (userProvided) props[key] += " ";
|
||||||
|
if (!userProvided) props[key] = "";
|
||||||
|
props[key] += extend[key];
|
||||||
|
} else props[key] = extend[key] ?? userProvided;
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
},
|
||||||
html = htm.bind(h);
|
html = htm.bind(h);
|
||||||
|
|
||||||
const extendProps = (props, extend) => {
|
// provides basic key/value reactivity:
|
||||||
for (const key in extend) {
|
// this is shared between all active mods,
|
||||||
const { [key]: userProvided } = props;
|
// i.e. mods can read and update other mods'
|
||||||
if (typeof extend[key] === "function") {
|
// reactive states. this enables interop
|
||||||
props[key] = (...args) => {
|
// between a mod's component islands and
|
||||||
extend[key](...args);
|
// supports inter-mod communication if so
|
||||||
userProvided?.(...args);
|
// required. caution should be used in
|
||||||
};
|
// naming keys to avoid conflicts
|
||||||
} else if (key === "class") {
|
const _state = {},
|
||||||
if (userProvided) props[key] += " ";
|
_subscribers = [],
|
||||||
if (!userProvided) props[key] = "";
|
setState = (state) => {
|
||||||
props[key] += extend[key];
|
Object.assign(_state, state);
|
||||||
} else props[key] = extend[key] ?? userProvided;
|
const updates = Object.keys(state);
|
||||||
}
|
_subscribers
|
||||||
return props;
|
.filter(([keys]) => updates.some((key) => keys.includes(key)))
|
||||||
};
|
.forEach(([keys, callback]) => callback(keys.map((key) => _state[key])));
|
||||||
|
},
|
||||||
|
// useState(["keyA", "keyB"]) => returns [valueA, valueB]
|
||||||
|
// useState(["keyA", "keyB"], callback) => registers callback
|
||||||
|
// to be triggered after each update to either keyA or keyB,
|
||||||
|
// with [valueA, valueB] passed to the callback's first arg
|
||||||
|
useState = (keys, callback) => {
|
||||||
|
const state = keys.map((key) => _state[key]);
|
||||||
|
if (callback) _subscribers.push([keys, callback]);
|
||||||
|
callback?.(state);
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
globalThis.__enhancerApi ??= {};
|
globalThis.__enhancerApi ??= {};
|
||||||
Object.assign(globalThis.__enhancerApi, { html, extendProps });
|
Object.assign(globalThis.__enhancerApi, {
|
||||||
|
html,
|
||||||
|
extendProps,
|
||||||
|
setState,
|
||||||
|
useState,
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user