mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-12 00:09:03 +00:00
feat(menu): animate slide b/w mod lists <-> options, add enabled mods to sidebar
This commit is contained in:
parent
530be53e70
commit
f57e5b7f9b
@ -9,7 +9,7 @@ import { setState, useState, getState } from "./state.mjs";
|
|||||||
function Sidebar({}, ...children) {
|
function Sidebar({}, ...children) {
|
||||||
const { html } = globalThis.__enhancerApi;
|
const { html } = globalThis.__enhancerApi;
|
||||||
return html`<aside
|
return html`<aside
|
||||||
class="notion-enhancer--menu-sidebar min-w-[224.14px] max-w-[250px]
|
class="notion-enhancer--menu-sidebar z-10 row-span-1
|
||||||
h-full overflow-y-auto bg-[color:var(--theme--bg-secondary)]"
|
h-full overflow-y-auto bg-[color:var(--theme--bg-secondary)]"
|
||||||
>
|
>
|
||||||
${children}
|
${children}
|
||||||
@ -27,22 +27,24 @@ function SidebarSection({}, ...children) {
|
|||||||
</h2>`;
|
</h2>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarButton({ icon, ...props }, ...children) {
|
function SidebarButton({ id, icon, ...props }, ...children) {
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
iconSize = icon.startsWith("notion-enhancer")
|
$icon = icon
|
||||||
? "w-[17px] h-[17px] ml-[1.5px] mr-[9.5px]"
|
? html`<i
|
||||||
: "w-[18px] h-[18px] ml-px mr-[9px]",
|
class="i-${icon} ${icon.startsWith("notion-enhancer")
|
||||||
|
? "w-[17px] h-[17px] ml-[1.5px] mr-[9.5px]"
|
||||||
|
: "w-[18px] h-[18px] ml-px mr-[9px]"}"
|
||||||
|
></i>`
|
||||||
|
: "",
|
||||||
$el = html`<${props.href ? "a" : "button"}
|
$el = html`<${props.href ? "a" : "button"}
|
||||||
class="flex select-none cursor-pointer w-full
|
class="flex select-none cursor-pointer w-full
|
||||||
items-center py-[5px] px-[15px] text-[14px] last:mb-[12px]
|
items-center py-[5px] px-[15px] text-[14px] last:mb-[12px]
|
||||||
transition hover:bg-[color:var(--theme--bg-hover)]"
|
transition hover:bg-[color:var(--theme--bg-hover)]"
|
||||||
...${props}
|
...${props}
|
||||||
>
|
>${$icon}
|
||||||
<i class="i-${icon} ${iconSize}"></i>
|
|
||||||
<span class="leading-[20px]">${children}</span>
|
<span class="leading-[20px]">${children}</span>
|
||||||
<//>`;
|
<//>`;
|
||||||
if (!props.href) {
|
if (!props.href) {
|
||||||
const id = $el.innerText;
|
|
||||||
$el.onclick ??= () => setState({ transition: "fade", view: id });
|
$el.onclick ??= () => setState({ transition: "fade", view: id });
|
||||||
useState(["view"], ([view = "welcome"]) => {
|
useState(["view"], ([view = "welcome"]) => {
|
||||||
const active = view.toLowerCase() === id.toLowerCase();
|
const active = view.toLowerCase() === id.toLowerCase();
|
||||||
@ -55,11 +57,10 @@ function SidebarButton({ icon, ...props }, ...children) {
|
|||||||
|
|
||||||
function View({ id }, ...children) {
|
function View({ id }, ...children) {
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
duration = 100,
|
|
||||||
$el = html`<article
|
$el = html`<article
|
||||||
id=${id}
|
id=${id}
|
||||||
class="notion-enhancer--menu-view h-full
|
class="notion-enhancer--menu-view h-full w-full
|
||||||
grow overflow-y-auto px-[60px] py-[36px]"
|
absolute overflow-y-auto px-[60px] py-[36px]"
|
||||||
>
|
>
|
||||||
${children}
|
${children}
|
||||||
</article>`;
|
</article>`;
|
||||||
@ -67,21 +68,55 @@ function View({ id }, ...children) {
|
|||||||
const [transition] = getState(["transition"]),
|
const [transition] = getState(["transition"]),
|
||||||
isVisible = $el.style.display !== "none",
|
isVisible = $el.style.display !== "none",
|
||||||
nowActive = view.toLowerCase() === id.toLowerCase();
|
nowActive = view.toLowerCase() === id.toLowerCase();
|
||||||
if (transition === "fade") {
|
switch (transition) {
|
||||||
$el.style.opacity = "0";
|
case "fade": {
|
||||||
$el.style.transition = `opacity ${duration}ms`;
|
const duration = 100;
|
||||||
if (isVisible && !nowActive) {
|
$el.style.transition = `opacity ${duration}ms`;
|
||||||
setTimeout(() => ($el.style.display = "none"), duration);
|
$el.style.opacity = "0";
|
||||||
} else if (!isVisible && nowActive) {
|
if (isVisible && !nowActive) {
|
||||||
setTimeout(() => {
|
setTimeout(() => ($el.style.display = "none"), duration);
|
||||||
$el.style.display = "";
|
} else if (!isVisible && nowActive) {
|
||||||
requestIdleCallback(() => ($el.style.opacity = "1"));
|
setTimeout(() => {
|
||||||
}, duration);
|
$el.style.display = "";
|
||||||
|
requestIdleCallback(() => ($el.style.opacity = "1"));
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
case "slide-to-left":
|
||||||
$el.style.transition = "";
|
case "slide-to-right": {
|
||||||
$el.style.opacity = nowActive ? "1" : "0";
|
const duration = 200,
|
||||||
$el.style.display = nowActive ? "" : "none";
|
cssTransition = `opacity ${duration}ms, transform ${duration}ms`,
|
||||||
|
transformOut = `translateX(${
|
||||||
|
transition === "slide-to-right" ? "-100%" : "100%"
|
||||||
|
})`,
|
||||||
|
transformIn = `translateX(${
|
||||||
|
transition === "slide-to-right" ? "100%" : "-100%"
|
||||||
|
})`;
|
||||||
|
if (isVisible && !nowActive) {
|
||||||
|
$el.style.transition = cssTransition;
|
||||||
|
$el.style.transform = transformOut;
|
||||||
|
$el.style.opacity = "0";
|
||||||
|
setTimeout(() => {
|
||||||
|
$el.style.display = "none";
|
||||||
|
$el.style.transform = "";
|
||||||
|
}, duration);
|
||||||
|
} else if (!isVisible && nowActive) {
|
||||||
|
$el.style.transform = transformIn;
|
||||||
|
$el.style.opacity = "0";
|
||||||
|
$el.style.display = "";
|
||||||
|
requestIdleCallback(() => {
|
||||||
|
$el.style.transition = cssTransition;
|
||||||
|
$el.style.transform = "";
|
||||||
|
$el.style.opacity = "1";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
$el.style.transition = "";
|
||||||
|
$el.style.opacity = nowActive ? "1" : "0";
|
||||||
|
$el.style.display = nowActive ? "" : "none";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return $el;
|
return $el;
|
||||||
@ -153,7 +188,7 @@ function Mod({
|
|||||||
class="flex items-center p-[4px] rounded-[4px] transition
|
class="flex items-center p-[4px] rounded-[4px] transition
|
||||||
text-[color:var(--theme--fg-secondary)] my-auto mr-[8px]
|
text-[color:var(--theme--fg-secondary)] my-auto mr-[8px]
|
||||||
duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]"
|
duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]"
|
||||||
onclick=${() => setState({ transition: "none", view: id })}
|
onclick=${() => setState({ transition: "slide-to-right", view: id })}
|
||||||
>
|
>
|
||||||
<i class="i-settings w-[18px] h-[18px]"></i>
|
<i class="i-settings w-[18px] h-[18px]"></i>
|
||||||
</button>`
|
</button>`
|
||||||
@ -260,6 +295,20 @@ function Option({ type, value, description, _get, _set, ...props }) {
|
|||||||
<//>`;
|
<//>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Button({ icon, ...props }, children) {
|
||||||
|
const { html } = globalThis.__enhancerApi;
|
||||||
|
return html`<button
|
||||||
|
class="mt-[14px] first:mt-0 mb-[14px] last:mb-0
|
||||||
|
flex items-center h-[32px] px-[12px] rounded-[4px]
|
||||||
|
cursor-pointer border-(& [color:var(--theme--fg-border)])
|
||||||
|
transition duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]"
|
||||||
|
...${props}
|
||||||
|
>
|
||||||
|
<i class="i-${icon} w-[18px] h-[18px]"></i>
|
||||||
|
<span class="ml-[8px] text-[14px]">${children}</span>
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
function Input({ size, icon, transparent, onrerender, ...props }) {
|
function Input({ size, icon, transparent, onrerender, ...props }) {
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
$input = html`<input
|
$input = html`<input
|
||||||
@ -626,4 +675,13 @@ function Toggle({ _get, _set, ...props }) {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Sidebar, SidebarSection, SidebarButton, View, List, Mod, Option };
|
export {
|
||||||
|
Sidebar,
|
||||||
|
SidebarSection,
|
||||||
|
SidebarButton,
|
||||||
|
View,
|
||||||
|
List,
|
||||||
|
Mod,
|
||||||
|
Button,
|
||||||
|
Option,
|
||||||
|
};
|
||||||
|
@ -10,7 +10,9 @@
|
|||||||
<script src="../../vendor/coloris.min.js" type="module"></script>
|
<script src="../../vendor/coloris.min.js" type="module"></script>
|
||||||
<script src="./menu.mjs" type="module" defer></script>
|
<script src="./menu.mjs" type="module" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
<!-- prettier-ignore -->
|
||||||
<body
|
<body
|
||||||
class="flex flex-row w-screen h-screen text-[color:var(--theme--fg-primary)] font-[family:var(--theme--font-sans)]"
|
class="grid grid-rows-1 grid-cols-[224.14px auto] w-screen h-screen
|
||||||
|
text-[color:var(--theme--fg-primary)] font-[family:var(--theme--font-sans)]"
|
||||||
></body>
|
></body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -12,56 +12,59 @@ import {
|
|||||||
View,
|
View,
|
||||||
List,
|
List,
|
||||||
Mod,
|
Mod,
|
||||||
|
Button,
|
||||||
Option,
|
Option,
|
||||||
} from "./components.mjs";
|
} from "./components.mjs";
|
||||||
|
|
||||||
const renderOptions = async (mod) => {
|
const compatibleMods = (mods) => {
|
||||||
const { html, platform, getProfile } = globalThis.__enhancerApi,
|
const { platform } = globalThis.__enhancerApi;
|
||||||
{ optionDefaults, initDatabase } = globalThis.__enhancerApi,
|
return mods.filter((mod) => {
|
||||||
profile = await getProfile();
|
const required =
|
||||||
const db = initDatabase([profile, mod.id], await optionDefaults(mod.id));
|
mod.id &&
|
||||||
let options = mod.options.reduce((options, opt, i) => {
|
mod.name &&
|
||||||
if (!opt.key && (opt.type !== "heading" || !opt.label)) return options;
|
mod.version &&
|
||||||
if (opt.platforms && !opt.platforms.includes(platform)) return options;
|
mod.description &&
|
||||||
const prevOpt = options[options.length - 1];
|
mod.thumbnail &&
|
||||||
// no consective headings
|
mod.authors,
|
||||||
if (opt.type === "heading" && prevOpt?.type === opt.type) {
|
compatible = !mod.platforms || mod.platforms.includes(platform);
|
||||||
options[options.length - 1] = opt;
|
return required && compatible;
|
||||||
} else options.push(opt);
|
|
||||||
return options;
|
|
||||||
}, []);
|
|
||||||
// no empty/end headings e.g. if section is platform-specific
|
|
||||||
if (options[options.length - 1]?.type === "heading") options.pop();
|
|
||||||
options = options.map(async (opt) => {
|
|
||||||
if (opt.type === "heading") return html`<${Option} ...${opt} />`;
|
|
||||||
const _get = () => db.get(opt.key),
|
|
||||||
_set = async (value) => {
|
|
||||||
await db.set(opt.key, value);
|
|
||||||
setState({ rerender: true });
|
|
||||||
};
|
|
||||||
return html`<${Option} ...${{ ...opt, _get, _set }} />`;
|
|
||||||
});
|
});
|
||||||
return Promise.all(options);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const compatibleMods = (mods) => {
|
const renderSidebar = (items, categories) => {
|
||||||
const { platform } = globalThis.__enhancerApi;
|
const { html, isEnabled } = globalThis.__enhancerApi,
|
||||||
return mods.filter((mod) => {
|
$sidebar = html`<${Sidebar}>
|
||||||
const required =
|
${items.map((item) => {
|
||||||
mod.id &&
|
if (typeof item === "object") {
|
||||||
mod.name &&
|
const { title, ...props } = item;
|
||||||
mod.version &&
|
return html`<${SidebarButton} ...${props}>${title}<//>`;
|
||||||
mod.description &&
|
} else return html`<${SidebarSection}>${item}<//>`;
|
||||||
mod.thumbnail &&
|
})}
|
||||||
mod.authors,
|
<//>`;
|
||||||
compatible = !mod.platforms || mod.platforms.includes(platform);
|
for (const { title, mods } of categories) {
|
||||||
return required && compatible;
|
const $title = html`<${SidebarSection}>${title}<//>`,
|
||||||
});
|
$mods = mods.map((mod) => [
|
||||||
|
mod.id,
|
||||||
|
html`<${SidebarButton} id=${mod.id}>${mod.name}<//>`,
|
||||||
|
]);
|
||||||
|
$sidebar.append($title, ...$mods.map(([, $btn]) => $btn));
|
||||||
|
useState(["rerender"], async () => {
|
||||||
|
let sectionVisible = false;
|
||||||
|
for (const [id, $btn] of $mods) {
|
||||||
|
if (await isEnabled(id)) {
|
||||||
|
$btn.style.display = "";
|
||||||
|
sectionVisible = true;
|
||||||
|
} else $btn.style.display = "none";
|
||||||
|
}
|
||||||
|
$title.style.display = sectionVisible ? "" : "none";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return $sidebar;
|
||||||
},
|
},
|
||||||
renderList = async (mods, description) => {
|
renderList = async (mods, description) => {
|
||||||
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
||||||
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
|
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
|
||||||
mods = compatibleMods(mods).map(async (mod) => {
|
mods = mods.map(async (mod) => {
|
||||||
const _get = () => enabledMods.get(mod.id),
|
const _get = () => enabledMods.get(mod.id),
|
||||||
_set = async (enabled) => {
|
_set = async (enabled) => {
|
||||||
await enabledMods.set(mod.id, enabled);
|
await enabledMods.set(mod.id, enabled);
|
||||||
@ -73,10 +76,38 @@ const compatibleMods = (mods) => {
|
|||||||
${await Promise.all(mods)}
|
${await Promise.all(mods)}
|
||||||
<//>`;
|
<//>`;
|
||||||
},
|
},
|
||||||
renderOptionViews = async (parentView, mods) => {
|
renderOptions = async (mod) => {
|
||||||
|
const { html, platform, getProfile } = globalThis.__enhancerApi,
|
||||||
|
{ optionDefaults, initDatabase } = globalThis.__enhancerApi,
|
||||||
|
profile = await getProfile();
|
||||||
|
const db = initDatabase([profile, mod.id], await optionDefaults(mod.id));
|
||||||
|
let options = mod.options.reduce((options, opt, i) => {
|
||||||
|
if (!opt.key && (opt.type !== "heading" || !opt.label)) return options;
|
||||||
|
if (opt.platforms && !opt.platforms.includes(platform)) return options;
|
||||||
|
const prevOpt = options[options.length - 1];
|
||||||
|
// no consective headings
|
||||||
|
if (opt.type === "heading" && prevOpt?.type === opt.type) {
|
||||||
|
options[options.length - 1] = opt;
|
||||||
|
} else options.push(opt);
|
||||||
|
return options;
|
||||||
|
}, []);
|
||||||
|
// no empty/end headings e.g. if section is platform-specific
|
||||||
|
if (options[options.length - 1]?.type === "heading") options.pop();
|
||||||
|
options = options.map(async (opt) => {
|
||||||
|
if (opt.type === "heading") return html`<${Option} ...${opt} />`;
|
||||||
|
const _get = () => db.get(opt.key),
|
||||||
|
_set = async (value) => {
|
||||||
|
await db.set(opt.key, value);
|
||||||
|
setState({ rerender: true });
|
||||||
|
};
|
||||||
|
return html`<${Option} ...${{ ...opt, _get, _set }} />`;
|
||||||
|
});
|
||||||
|
return Promise.all(options);
|
||||||
|
},
|
||||||
|
renderMods = async (category, mods) => {
|
||||||
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
const { html, getProfile, initDatabase } = globalThis.__enhancerApi,
|
||||||
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
|
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
|
||||||
mods = compatibleMods(mods)
|
mods = mods
|
||||||
.filter((mod) => {
|
.filter((mod) => {
|
||||||
return mod.options?.filter((opt) => opt.type !== "heading").length;
|
return mod.options?.filter((opt) => opt.type !== "heading").length;
|
||||||
})
|
})
|
||||||
@ -87,9 +118,17 @@ const compatibleMods = (mods) => {
|
|||||||
setState({ rerender: true });
|
setState({ rerender: true });
|
||||||
};
|
};
|
||||||
return html`<${View} id=${mod.id}>
|
return html`<${View} id=${mod.id}>
|
||||||
|
<${Button}
|
||||||
|
icon="chevron-left"
|
||||||
|
onclick=${() => {
|
||||||
|
setState({ transition: "slide-to-left", view: category.id });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${category.title}
|
||||||
|
<//>
|
||||||
<${Mod} ...${{ ...mod, options: [], _get, _set }} />
|
<${Mod} ...${{ ...mod, options: [], _get, _set }} />
|
||||||
${await renderOptions(mod)}<//
|
${await renderOptions(mod)}
|
||||||
>`;
|
<//>`;
|
||||||
});
|
});
|
||||||
return Promise.all(mods);
|
return Promise.all(mods);
|
||||||
};
|
};
|
||||||
@ -101,11 +140,42 @@ const render = async () => {
|
|||||||
if (!html || !getCore || !icon || renderStarted) return;
|
if (!html || !getCore || !icon || renderStarted) return;
|
||||||
setState({ renderStarted: true });
|
setState({ renderStarted: true });
|
||||||
|
|
||||||
const sidebar = [
|
const categories = [
|
||||||
|
{
|
||||||
|
icon: "palette",
|
||||||
|
id: "themes",
|
||||||
|
title: "Themes",
|
||||||
|
description: `Themes override Notion's colour schemes. To switch between
|
||||||
|
dark mode and light mode, go to <span class="py-[2px] px-[4px] rounded-[3px]
|
||||||
|
bg-[color:var(--theme--bg-hover)]">Settings & members → My notifications &
|
||||||
|
settings → My settings → Appearance</span>.`,
|
||||||
|
mods: compatibleMods(await getThemes()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "zap",
|
||||||
|
id: "extensions",
|
||||||
|
title: "Extensions",
|
||||||
|
description: `Extensions add to the functionality and layout of the Notion
|
||||||
|
client, interacting with and modifying existing interfaces.`,
|
||||||
|
mods: compatibleMods(await getExtensions()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "plug",
|
||||||
|
id: "integrations",
|
||||||
|
title: "Integrations",
|
||||||
|
description: `<span class="text-[color:var(--theme--fg-red)]">
|
||||||
|
Integrations access and modify Notion content. They interact directly with
|
||||||
|
<span class="py-[2px] px-[4px] rounded-[3px] bg-[color:var(--theme--bg-hover)]">
|
||||||
|
https://www.notion.so/api/v3</span>. Use at your own risk.</span>`,
|
||||||
|
mods: compatibleMods(await getIntegrations()),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sidebar = [
|
||||||
"notion-enhancer",
|
"notion-enhancer",
|
||||||
{
|
{
|
||||||
icon: `notion-enhancer${icon === "Monochrome" ? "?mask" : ""}`,
|
id: "welcome",
|
||||||
title: "Welcome",
|
title: "Welcome",
|
||||||
|
icon: `notion-enhancer${icon === "Monochrome" ? "?mask" : ""}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "message-circle",
|
icon: "message-circle",
|
||||||
@ -133,53 +203,22 @@ const render = async () => {
|
|||||||
href: "https://github.com/sponsors/dragonwocky",
|
href: "https://github.com/sponsors/dragonwocky",
|
||||||
},
|
},
|
||||||
"Settings",
|
"Settings",
|
||||||
{ icon: "sliders-horizontal", title: "Core" },
|
{ icon: "sliders-horizontal", id: "core", title: "Core" },
|
||||||
{ icon: "palette", title: "Themes" },
|
...categories.map((c) => ({ icon: c.icon, id: c.id, title: c.title })),
|
||||||
{ icon: "zap", title: "Extensions" },
|
];
|
||||||
{ icon: "plug", title: "Integrations" },
|
|
||||||
],
|
// view wrapper necessary for transitions
|
||||||
$sidebar = html`<${Sidebar}>
|
const $views = html`<div class="relative overflow-hidden">
|
||||||
${sidebar.map((item) => {
|
<${View} id="welcome">welcome<//>
|
||||||
if (typeof item === "object") {
|
<${View} id="core">${await renderOptions(await getCore())}<//>
|
||||||
const { title, ...props } = item;
|
</div>`;
|
||||||
return html`<${SidebarButton} ...${props}>${title}<//>`;
|
for (const { id, title, description, mods } of categories) {
|
||||||
} else return html`<${SidebarSection}>${item}<//>`;
|
const $list = await renderList(mods, description),
|
||||||
})}
|
$mods = await renderMods({ id, title }, mods);
|
||||||
<//>`;
|
$views.append(html`<${View} id=${id}>${$list}<//>`, ...$mods);
|
||||||
document.body.append(
|
|
||||||
$sidebar,
|
|
||||||
html`<${View} id="welcome">welcome<//>`,
|
|
||||||
html`<${View} id="core">${await renderOptions(await getCore())}<//>`
|
|
||||||
);
|
|
||||||
for (const { id, mods, description } of [
|
|
||||||
{
|
|
||||||
id: "themes",
|
|
||||||
mods: await getThemes(),
|
|
||||||
description: `Themes override Notion's colour schemes. To switch between
|
|
||||||
dark mode and light mode, go to <span class="py-[2px] px-[4px] rounded-[3px]
|
|
||||||
bg-[color:var(--theme--bg-hover)]">Settings & members → My notifications &
|
|
||||||
settings → My settings → Appearance</span>.`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "extensions",
|
|
||||||
mods: await getExtensions(),
|
|
||||||
description: `Extensions add to the functionality and layout of the Notion
|
|
||||||
client, interacting with and modifying existing interfaces.`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "integrations",
|
|
||||||
mods: await getIntegrations(),
|
|
||||||
description: `<span class="text-[color:var(--theme--fg-red)]">
|
|
||||||
Integrations access and modify Notion content. They interact directly with
|
|
||||||
<span class="py-[2px] px-[4px] rounded-[3px] bg-[color:var(--theme--bg-hover)]">
|
|
||||||
https://www.notion.so/api/v3</span>. Use at your own risk.</span>`,
|
|
||||||
},
|
|
||||||
]) {
|
|
||||||
document.body.append(
|
|
||||||
html`<${View} id=${id}>${await renderList(mods, description)}<//>`,
|
|
||||||
...(await renderOptionViews(id, mods))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.body.append(renderSidebar(sidebar, categories), $views);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("focus", () => setState({ rerender: true }));
|
window.addEventListener("focus", () => setState({ rerender: true }));
|
||||||
|
Loading…
Reference in New Issue
Block a user