mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-09 15:09:02 +00:00
add menu filters + unspaghetti menu code
This commit is contained in:
parent
4e3f921ee3
commit
a55482d62d
2
extension/icons/monstr/party.svg
Normal file
2
extension/icons/monstr/party.svg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<!-- https://iconmonstr.com/party-5-svg/ -->
|
||||||
|
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path fill="currentColor" d="M4.823 21.933l2.734-1.171-3.241-8.847-1.561 4.372 2.068 5.646zm-2.594-4.174l-2.229 6.241 3.903-1.672-1.674-4.569zm6.248 2.609l2.934-1.258-3.482-9.141-2.215-1.969-.872 2.443 3.635 9.925zm7.523-3.224l-6.453-5.736 2.785 7.308 3.668-1.572zm-.826-5.003l2.201-1.445c.23-.152.295-.462.143-.693-.152-.232-.463-.295-.692-.143l-2.201 1.445c-.231.151-.295.461-.144.692.096.147.256.226.418.226.095 0 .19-.026.275-.082m-2.993-4.312l1.112-2.388c.117-.25.008-.548-.242-.664-.251-.116-.548-.009-.665.242l-1.111 2.388c-.117.25-.008.547.242.664l.211.047c.189 0 .368-.107.453-.289m-2.627.709c1.539-2.963 1.644-5.73.314-8.222-.09-.169-.263-.265-.442-.265-.37 0-.621.398-.44.736 1.166 2.184 1.058 4.637-.32 7.29-.127.245-.031.547.214.674.073.038.152.057.23.057.18 0 .355-.099.444-.27m6.505 6.095c2.017-1.434 4.463-1.64 7.272-.613.327.119.672-.123.672-.47 0-.203-.125-.395-.328-.47-3.136-1.147-5.894-.9-8.196.738-.224.16-.277.472-.117.698.098.136.251.209.407.209.101 0 .202-.03.29-.092m3.757-6.757l-1.697.014.938 1.415-.511 1.618 1.635-.455 1.381.986.073-1.696 1.365-1.009-1.591-.592-.538-1.61-1.055 1.329zm-7.307 3.624c.276-.009.492-.24.483-.517-.056-1.627.36-1.937 1.377-2.051 1.689-.191 1.785-1.312 1.842-1.982.053-.637.071-.851.773-.903.63-.046 1.331-.16 1.76-.659.461-.538.466-1.358.402-2.164-.021-.276-.266-.478-.537-.459-.275.021-.481.262-.459.537.062.787.011 1.23-.165 1.434-.149.174-.48.271-1.074.314-1.553.114-1.644 1.179-1.697 1.816-.057.668-.082.973-.956 1.071-2.075.234-2.315 1.619-2.266 3.08.01.27.231.483.5.483h.017m7.842-8.675c0 1.006.818 1.824 1.825 1.824 1.006 0 1.824-.818 1.824-1.824 0-1.007-.818-1.825-1.824-1.825-1.007 0-1.825.818-1.825 1.825m-6.623-2.841c1.104 0 2 .897 2 2 0 1.104-.896 2-2 2-1.103 0-2-.896-2-2 0-1.103.897-2 2-2"/></svg>
|
After Width: | Height: | Size: 1.9 KiB |
@ -5,12 +5,13 @@
|
|||||||
- improved: split the core mod into the theming & menu mods.
|
- improved: split the core mod into the theming & menu mods.
|
||||||
- improved: new larger menu layout, with individual mod pages.
|
- improved: new larger menu layout, with individual mod pages.
|
||||||
- improved: merged bracketed-links into tweaks.
|
- improved: merged bracketed-links into tweaks.
|
||||||
|
- improved: replaced confusing all-tag filters with themes/extensions/enabled/disabled filters.
|
||||||
- removed: integrated scrollbar tweak (notion now includes by default).
|
- removed: integrated scrollbar tweak (notion now includes by default).
|
||||||
- removed: js insert. css insert moved to tweaks mod.
|
- removed: js insert. css insert moved to tweaks mod.
|
||||||
|
- ported: tweaks, bypass-preview.
|
||||||
|
|
||||||
#### todo
|
#### todo
|
||||||
|
|
||||||
- tag sort
|
|
||||||
- documentation e.g. \_file
|
- documentation e.g. \_file
|
||||||
- complete/bugfix theming variables
|
- complete/bugfix theming variables
|
||||||
- color pickers
|
- color pickers
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
import { web } from '../../api.js';
|
import { web } from '../../api.js';
|
||||||
|
|
||||||
web.whenReady().then(async () => {
|
web.whenReady().then(async () => {
|
||||||
@ -23,7 +25,7 @@ function getCurrentPage() {
|
|||||||
return { type: 'page', id: location.pathname.split(/(-|\/)/g).reverse()[0] };
|
return { type: 'page', id: location.pathname.split(/(-|\/)/g).reverse()[0] };
|
||||||
}
|
}
|
||||||
let lastPage = getCurrentPage();
|
let lastPage = getCurrentPage();
|
||||||
web.observeDocument((event) => {
|
web.addDocumentObserver((event) => {
|
||||||
const currentPage = getCurrentPage();
|
const currentPage = getCurrentPage();
|
||||||
if (currentPage.id !== lastPage.id || currentPage.type !== lastPage.type) {
|
if (currentPage.id !== lastPage.id || currentPage.type !== lastPage.type) {
|
||||||
const openAsPage = document.querySelector(
|
const openAsPage = document.querySelector(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "bypass-preview",
|
"name": "bypass-preview",
|
||||||
"id": "cb6fd684-f113-4a7a-9423-8f0f0cff069f",
|
"id": "cb6fd684-f113-4a7a-9423-8f0f0cff069f",
|
||||||
"description": "go straight to the normal full view when opening a page..",
|
"description": "go straight to the normal full view when opening a page.",
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"tags": ["extension", "automation"],
|
"tags": ["extension", "automation"],
|
||||||
"authors": [
|
"authors": [
|
||||||
|
@ -1 +1,3 @@
|
|||||||
# menu
|
# menu
|
||||||
|
|
||||||
|
[theming mod link test](?view=mod&id=0f0bf8b6-eae6-4273-b307-8fc43f2ee082)
|
||||||
|
@ -25,6 +25,7 @@ web.whenReady([sidebarSelector]).then(async () => {
|
|||||||
list: await fs.getJSON('https://notion-enhancer.github.io/notifications.json'),
|
list: await fs.getJSON('https://notion-enhancer.github.io/notifications.json'),
|
||||||
dismissed: await storage.get(_id, 'notifications', []),
|
dismissed: await storage.get(_id, 'notifications', []),
|
||||||
};
|
};
|
||||||
|
console.log($enhancerSidebarElement);
|
||||||
notifications.waiting = notifications.list.filter(
|
notifications.waiting = notifications.list.filter(
|
||||||
({ id }) => !notifications.dismissed.includes(id)
|
({ id }) => !notifications.dismissed.includes(id)
|
||||||
);
|
);
|
||||||
@ -48,4 +49,4 @@ web.whenReady([sidebarSelector]).then(async () => {
|
|||||||
setTheme();
|
setTheme();
|
||||||
document.querySelector(sidebarSelector).appendChild($enhancerSidebarElement);
|
document.querySelector(sidebarSelector).appendChild($enhancerSidebarElement);
|
||||||
});
|
});
|
||||||
web.hotkeyListener(await storage.get(_id, 'hotkey.focustoggle'), env.openEnhancerMenu);
|
web.addHotkeyListener(await storage.get(_id, 'hotkey.focustoggle'), env.openEnhancerMenu);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@ -43,7 +44,7 @@ header > * {
|
|||||||
margin: 0 1.25rem 0.1em 0;
|
margin: 0 1.25rem 0.1em 0;
|
||||||
font-size: var(--theme--font_heading1-size);
|
font-size: var(--theme--font_heading1-size);
|
||||||
}
|
}
|
||||||
header h1 a:not([data-view-active]) {
|
header h1 a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
header h1 img {
|
header h1 img {
|
||||||
@ -80,7 +81,7 @@ main {
|
|||||||
main {
|
main {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
[data-view='mod'] main > .documentation--buttons {
|
main > .action--buttons {
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
}
|
}
|
||||||
[data-view='mod'] main .library--card,
|
[data-view='mod'] main .library--card,
|
||||||
@ -97,7 +98,7 @@ main {
|
|||||||
main {
|
main {
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
}
|
}
|
||||||
[data-view='mod'] main > .documentation--buttons {
|
main > .action--buttons {
|
||||||
grid-column: span 3;
|
grid-column: span 3;
|
||||||
}
|
}
|
||||||
[data-view='mod'] main > .documentation--body {
|
[data-view='mod'] main > .documentation--body {
|
||||||
@ -108,7 +109,7 @@ main {
|
|||||||
main {
|
main {
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
}
|
}
|
||||||
[data-view='mod'] main > .documentation--buttons {
|
main > .action--buttons {
|
||||||
grid-column: span 4;
|
grid-column: span 4;
|
||||||
}
|
}
|
||||||
[data-view='mod'] main > .documentation--body {
|
[data-view='mod'] main > .documentation--body {
|
||||||
@ -119,7 +120,7 @@ main {
|
|||||||
main {
|
main {
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||||
}
|
}
|
||||||
[data-view='mod'] main > .documentation--buttons {
|
main > .action--buttons {
|
||||||
grid-column: span 5;
|
grid-column: span 5;
|
||||||
}
|
}
|
||||||
[data-view='mod'] main > .documentation--body {
|
[data-view='mod'] main > .documentation--body {
|
||||||
@ -136,7 +137,7 @@ main article img {
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.documentation--buttons,
|
.action--buttons,
|
||||||
.library--expand {
|
.library--expand {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -146,14 +147,14 @@ main article img {
|
|||||||
.library--expand a {
|
.library--expand a {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
.documentation--buttons a,
|
.action--buttons a,
|
||||||
.library--expand a {
|
.library--expand a {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 0.35rem 0.45rem;
|
padding: 0.35rem 0.45rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.documentation--buttons .documentation--reload {
|
.action--buttons .action--alert {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 0.35rem 0.45rem;
|
padding: 0.35rem 0.45rem;
|
||||||
@ -164,31 +165,32 @@ main article img {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 200ms ease-in-out;
|
transition: opacity 200ms ease-in-out;
|
||||||
}
|
}
|
||||||
.documentation--buttons .documentation--reload[data-triggered] {
|
.action--buttons .action--alert[data-triggered] {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.documentation--buttons .documentation--reload[data-triggered]:hover {
|
.action--buttons .action--alert[data-triggered]:hover {
|
||||||
background: none;
|
background: none;
|
||||||
color: var(--theme--block_grey-text);
|
color: var(--theme--block_grey-text);
|
||||||
box-shadow: var(--theme--block_grey) 0px 0px 0px 1px inset;
|
box-shadow: var(--theme--block_grey) 0px 0px 0px 1px inset;
|
||||||
}
|
}
|
||||||
.documentation--buttons span,
|
.action--buttons span,
|
||||||
.library--expand span {
|
.library--expand span {
|
||||||
color: var(--theme--text_property);
|
color: var(--theme--text_property);
|
||||||
}
|
}
|
||||||
.documentation--buttons a:hover,
|
.action--buttons a:hover,
|
||||||
|
.action--buttons a.action--active,
|
||||||
.library--expand a:hover {
|
.library--expand a:hover {
|
||||||
background: var(--theme--button-hover);
|
background: var(--theme--button-hover);
|
||||||
}
|
}
|
||||||
.documentation--buttons svg,
|
.action--buttons svg,
|
||||||
.library--expand svg {
|
.library--expand svg {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
margin-right: 0.3rem;
|
margin-right: 0.3rem;
|
||||||
}
|
}
|
||||||
.documentation--buttons svg *,
|
.action--buttons svg *,
|
||||||
.library--expand svg * {
|
.library--expand svg * {
|
||||||
fill: var(--theme--text_property);
|
fill: var(--theme--text_property);
|
||||||
}
|
}
|
||||||
@ -269,7 +271,7 @@ label p > span:not([class]),
|
|||||||
label > span:not([class]) {
|
label > span:not([class]) {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
label [data-icon='fa/question-circle'] {
|
label [data-icon='fa/solid/question-circle'] {
|
||||||
height: var(--theme--font_ui_small-size);
|
height: var(--theme--font_ui_small-size);
|
||||||
width: var(--theme--font_ui_small-size);
|
width: var(--theme--font_ui_small-size);
|
||||||
margin-left: 0.25em;
|
margin-left: 0.25em;
|
||||||
@ -306,7 +308,7 @@ label [data-icon='fa/question-circle'] {
|
|||||||
height: 0.8rem;
|
height: 0.8rem;
|
||||||
width: 0.8rem;
|
width: 0.8rem;
|
||||||
left: 0.325rem;
|
left: 0.325rem;
|
||||||
top: 0.225rem;
|
top: 0.2rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
background: var(--theme--toggle_dot);
|
background: var(--theme--toggle_dot);
|
||||||
@ -419,22 +421,7 @@ label [data-icon='fa/question-circle'] {
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip {
|
.notifications {
|
||||||
position: absolute;
|
|
||||||
background: var(--theme--tooltip);
|
|
||||||
color: var(--theme--tooltip-text);
|
|
||||||
font-size: var(--theme--font_ui_small-size);
|
|
||||||
padding: 0.15rem 0.4rem;
|
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
|
|
||||||
border-radius: 3px;
|
|
||||||
max-width: 20rem;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.tooltip p {
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification--list {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 1.5rem;
|
bottom: 1.5rem;
|
||||||
right: 1.5rem;
|
right: 1.5rem;
|
||||||
@ -459,7 +446,7 @@ label [data-icon='fa/question-circle'] {
|
|||||||
transform-origin: 100% 100%;
|
transform-origin: 100% 100%;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.notification svg {
|
.notification :not(.notification--dismiss) > svg {
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
@ -478,27 +465,20 @@ label [data-icon='fa/question-circle'] {
|
|||||||
right: 0.75rem;
|
right: 0.75rem;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.25rem 0.35rem;
|
padding: 0.15rem 0 0.15rem 0.5rem;
|
||||||
font-size: var(--theme--font_body-size);
|
width: var(--theme--font_body-size);
|
||||||
color: currentColor;
|
color: currentColor;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: opacity 200ms ease-in-out;
|
transition: opacity 200ms ease-in-out;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
.notification .notification--dismiss svg {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
.notification:hover .notification--dismiss,
|
.notification:hover .notification--dismiss,
|
||||||
.notification:focus-within .notification--dismiss {
|
.notification:focus-within .notification--dismiss {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.notification.celebration,
|
|
||||||
.notification.information {
|
|
||||||
background: var(--theme--block_blue);
|
|
||||||
color: var(--theme--block_blue-text);
|
|
||||||
}
|
|
||||||
.notification.warning,
|
|
||||||
.notification.danger {
|
|
||||||
background: var(--theme--block_red);
|
|
||||||
color: var(--theme--block_red-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
@ -8,21 +8,23 @@
|
|||||||
<body data-view>
|
<body data-view>
|
||||||
<header>
|
<header>
|
||||||
<h1>
|
<h1>
|
||||||
<img data-view-target="notion" alt="" width="24" src="../../icons/colour.svg" />
|
<img data-notion alt="" width="24" src="../../icons/colour.svg" />
|
||||||
<a href="?view=library" data-view-target="library">library</a>
|
<a href="?view=library">library</a>
|
||||||
</h1>
|
</h1>
|
||||||
<h1>
|
<h1>
|
||||||
<i data-icon="fa/info"></i><a href="https://notion-enhancer.github.io/">website</a>
|
<i data-icon="fa/solid/info"></i>
|
||||||
|
<a href="https://notion-enhancer.github.io/">website</a>
|
||||||
</h1>
|
</h1>
|
||||||
<h1>
|
<h1>
|
||||||
<span data-icon="fa/code"></span
|
<i data-icon="fa/solid/code"></i>
|
||||||
><a href="https://github.com/notion-enhancer/extension">source code</a>
|
<a href="https://github.com/notion-enhancer/extension">source code</a>
|
||||||
|
</h1>
|
||||||
|
<h1>
|
||||||
|
<i data-icon="fa/brands/discord"></i>
|
||||||
|
<a href="https://discord.gg/sFWPXtA">support</a>
|
||||||
</h1>
|
</h1>
|
||||||
<h1><i data-icon="fa/discord"></i><a href="https://discord.gg/sFWPXtA">support</a></h1>
|
|
||||||
</header>
|
</header>
|
||||||
<main></main>
|
<main></main>
|
||||||
<section class="tooltip"></section>
|
|
||||||
<footer class="notification--list"></footer>
|
|
||||||
<script src="./menu.js" type="module"></script>
|
<script src="./menu.js" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -9,112 +9,124 @@
|
|||||||
const _id = 'a6621988-551d-495a-97d8-3c568bca2e9e';
|
const _id = 'a6621988-551d-495a-97d8-3c568bca2e9e';
|
||||||
import { env, storage, web, fmt, fs, registry } from '../../api.js';
|
import { env, storage, web, fmt, fs, registry } from '../../api.js';
|
||||||
|
|
||||||
for (const mod of await registry.get((mod) => registry.enabled(mod.id))) {
|
for (const mod of await registry.get((mod) => registry.isEnabled(mod.id))) {
|
||||||
for (const sheet of mod.css?.menu || []) {
|
for (const sheet of mod.css?.menu || []) {
|
||||||
web.loadStyleset(`repo/${mod._dir}/${sheet}`);
|
web.loadStyleset(`repo/${mod._dir}/${sheet}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function loadTheme() {
|
||||||
|
document.documentElement.className = `notion-${
|
||||||
|
(await storage.get(_id, 'theme')) || 'dark'
|
||||||
|
}-theme`;
|
||||||
|
}
|
||||||
|
window.addEventListener('focus', loadTheme);
|
||||||
|
loadTheme();
|
||||||
|
|
||||||
document
|
document.querySelector('img[data-notion]').addEventListener('click', env.focusNotion);
|
||||||
.querySelector('img[data-view-target="notion"]')
|
web.addHotkeyListener(await storage.get(_id, 'hotkey.focustoggle'), env.focusNotion);
|
||||||
.addEventListener('click', env.focusNotion);
|
web.addDocumentObserver(web.loadIcons);
|
||||||
web.hotkeyListener(await storage.get(_id, 'hotkey.focustoggle'), env.focusNotion);
|
|
||||||
|
|
||||||
const tooltips = {
|
const notifications = {
|
||||||
$el: document.querySelector('.tooltip'),
|
$el: web.createElement(web.html`<footer class="notifications"></footer>`),
|
||||||
add($parent, selector, text) {
|
push({ heading, message, icon, color }, onDismiss = () => {}) {
|
||||||
text = fmt.md.render(text);
|
const $notif = web.createElement(web.html`
|
||||||
$parent.addEventListener('mouseover', (event) => {
|
<div role="alert" class="notification" style="
|
||||||
if (event.target.matches(selector) || event.target.matches(`${selector} *`)) {
|
background: var(--theme--block_${web.escapeHtml(color)});
|
||||||
this.$el.innerHTML = text;
|
color: var(--theme--block_${web.escapeHtml(color)}-text);
|
||||||
this.$el.style.display = 'block';
|
">
|
||||||
}
|
<div><i data-icon="${web.escapeHtml(icon)}"></i></div>
|
||||||
});
|
<div>
|
||||||
$parent.addEventListener('mousemove', (event) => {
|
<h3>${web.escapeHtml(heading)}</h3>
|
||||||
this.$el.style.top = event.clientY - this.$el.clientHeight + 'px';
|
<p>${fmt.md.renderInline(message)}</p>
|
||||||
this.$el.style.left =
|
</div>
|
||||||
event.clientX < window.innerWidth / 2 ? event.clientX + 20 + 'px' : '';
|
<button class="notification--dismiss"><i data-icon="fa/solid/times"></i></button>
|
||||||
});
|
</div>`);
|
||||||
$parent.addEventListener('mouseout', (event) => {
|
this.$el.append($notif);
|
||||||
if (event.target.matches(selector) || event.target.matches(`${selector} *`)) {
|
setTimeout(() => {
|
||||||
this.$el.style.display = '';
|
$notif.style.opacity = 1;
|
||||||
}
|
}, 100);
|
||||||
|
$notif.querySelector('.notification--dismiss').addEventListener('click', (event) => {
|
||||||
|
$notif.style.opacity = 0;
|
||||||
|
$notif.style.transform = 'scaleY(0)';
|
||||||
|
$notif.style.marginTop = `-${
|
||||||
|
$notif.offsetHeight / parseFloat(getComputedStyle(document.documentElement).fontSize)
|
||||||
|
}rem`;
|
||||||
|
setTimeout(() => $notif.remove(), 400);
|
||||||
|
onDismiss();
|
||||||
});
|
});
|
||||||
|
return $notif;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
document.body.append(notifications.$el);
|
||||||
|
for (const error of await registry.errors()) {
|
||||||
|
notifications.push({
|
||||||
|
heading: `error: ${error.source}`,
|
||||||
|
message: error.message,
|
||||||
|
color: 'red',
|
||||||
|
icon: 'fa/solid/exclamation-triangle',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const notification of await (async () => {
|
||||||
|
const dismissed = await storage.get('_notifications', 'dismissed', []);
|
||||||
|
return (await fs.getJSON('https://notion-enhancer.github.io/notifications.json'))
|
||||||
|
.sort((a, b) => b.id - a.id)
|
||||||
|
.filter(({ id }) => !dismissed.includes(id));
|
||||||
|
})()) {
|
||||||
|
if (
|
||||||
|
(!notification.versions || notification.versions.includes(env.version)) &&
|
||||||
|
(!notification.environments || notification.environments.includes(env.name))
|
||||||
|
) {
|
||||||
|
notifications.push(notification, async () => {
|
||||||
|
const dismissed = await storage.get('_notifications', 'dismissed', []);
|
||||||
|
storage.set('_notifications', 'dismissed', [
|
||||||
|
...new Set([...dismissed, notification.id]),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import * as router from './router.js';
|
||||||
|
|
||||||
const components = {};
|
const components = {};
|
||||||
components.card = {
|
components.card = async (mod) => {
|
||||||
preview: ({ preview = '' }) =>
|
const $card = web.createElement(web.html`
|
||||||
preview
|
<article class="library--card" data-mod='${mod.id}'>
|
||||||
? web.createElement(web.html`<img
|
${
|
||||||
alt=""
|
mod.preview
|
||||||
class="library--preview"
|
? web.html`<img
|
||||||
src="${web.escapeHtml(preview)}"
|
alt=""
|
||||||
/>`)
|
class="library--preview"
|
||||||
: '',
|
src="${web.escapeHtml(mod.preview)}"
|
||||||
async name({ name, id, version, tags }) {
|
/>`
|
||||||
if (registry.CORE.includes(id))
|
: ''
|
||||||
return web.createElement(web.html`<div class="library--title"><h2>
|
}
|
||||||
<span>
|
<div>
|
||||||
${web.escapeHtml(name)}
|
<label
|
||||||
<span class="library--version">v${web.escapeHtml(version)}</span>
|
for="enable--${web.escapeHtml(mod.id)}"
|
||||||
</span>
|
|
||||||
</h2></div>`);
|
|
||||||
const $el = web.createElement(web.html`<label
|
|
||||||
for="enable--${web.escapeHtml(id)}"
|
|
||||||
class="library--title library--toggle_label"
|
class="library--title library--toggle_label"
|
||||||
>
|
>
|
||||||
<input type="checkbox" id="enable--${web.escapeHtml(id)}"
|
<input type="checkbox" id="enable--${web.escapeHtml(mod.id)}"
|
||||||
${(await storage.get('_enabled', id, false)) ? 'checked' : ''}/>
|
${(await registry.isEnabled(mod.id)) ? 'checked' : ''}/>
|
||||||
<h2>
|
<h2>
|
||||||
<span>
|
<span>
|
||||||
${web.escapeHtml(name)}
|
${web.escapeHtml(mod.name)}
|
||||||
<span class="library--version">v${web.escapeHtml(version)}</span>
|
<span class="library--version">v${web.escapeHtml(mod.version)}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="library--toggle"></span>
|
${
|
||||||
</h2>
|
registry.CORE.includes(mod.id) ? '' : web.html`<span class="library--toggle"></span>`
|
||||||
</label>`);
|
|
||||||
$el.addEventListener('change', async (event) => {
|
|
||||||
storage.set('_enabled', id, event.target.checked);
|
|
||||||
if (
|
|
||||||
event.target.checked &&
|
|
||||||
tags.includes('theme') &&
|
|
||||||
(await storage.get(_id, 'themes.autoresolve', true))
|
|
||||||
) {
|
|
||||||
const themes = await registry.get(
|
|
||||||
(mod) =>
|
|
||||||
mod.tags.includes('theme') &&
|
|
||||||
mod.id !== id &&
|
|
||||||
((mod.tags.includes('dark') && tags.includes('dark')) ||
|
|
||||||
(mod.tags.includes('light') && tags.includes('light')))
|
|
||||||
);
|
|
||||||
for (const theme of themes) {
|
|
||||||
if (document.body.dataset.view === 'library') {
|
|
||||||
const $toggle = document.getElementById(`enable--${theme.id}`);
|
|
||||||
if ($toggle.checked) $toggle.click();
|
|
||||||
} else storage.set('_enabled', theme.id, false);
|
|
||||||
}
|
}
|
||||||
}
|
</h2>
|
||||||
});
|
</label>
|
||||||
return $el;
|
<ul class="library--tags">
|
||||||
},
|
${mod.tags.map((tag) => web.html`<li>#${web.escapeHtml(tag)}</li>`).join('')}
|
||||||
tags: ({ tags = [] }) =>
|
</ul>
|
||||||
web.createElement(web.html`<ul class="library--tags">
|
<p class="library--description markdown">${fmt.md.renderInline(mod.description)}</p>
|
||||||
${tags.map((tag) => web.html`<li>#${web.escapeHtml(tag)}</li>`).join('')}
|
<ul class="library--authors">
|
||||||
</ul>`),
|
${mod.authors
|
||||||
description: ({ description }) =>
|
|
||||||
web.createElement(
|
|
||||||
web.html`<p class="library--description markdown">${fmt.md.renderInline(
|
|
||||||
description
|
|
||||||
)}</p>`
|
|
||||||
),
|
|
||||||
authors: ({ authors }) =>
|
|
||||||
web.createElement(web.html`<ul class="library--authors">
|
|
||||||
${authors
|
|
||||||
.map(
|
.map(
|
||||||
(author) =>
|
(author) =>
|
||||||
web.html`<li>
|
web.html`
|
||||||
|
<li>
|
||||||
<a href="${web.escapeHtml(author.url)}">
|
<a href="${web.escapeHtml(author.url)}">
|
||||||
<img alt="" src="${web.escapeHtml(author.icon)}" />
|
<img alt="" src="${web.escapeHtml(author.icon)}" />
|
||||||
<span>${web.escapeHtml(author.name)}</span>
|
<span>${web.escapeHtml(author.name)}</span>
|
||||||
@ -122,83 +134,77 @@ components.card = {
|
|||||||
</li>`
|
</li>`
|
||||||
)
|
)
|
||||||
.join('')}
|
.join('')}
|
||||||
</ul>`),
|
</ul>
|
||||||
expand: async ({ id }) =>
|
<p class="library--expand">
|
||||||
web.createElement(
|
<a href="?view=mod&id=${web.escapeHtml(mod.id)}">
|
||||||
web.html`<p class="library--expand">
|
<span><i data-icon="fa/solid/long-arrow-alt-right"></i></span>
|
||||||
<a href="?view=mod&id=${web.escapeHtml(id)}">
|
<span>settings & documentation</span>
|
||||||
<span><i data-icon="fa/long-arrow-alt-right"></i></span>
|
</a>
|
||||||
<span>settings & documentation</span>
|
</p>
|
||||||
</a>
|
</div>
|
||||||
</p>`
|
</article>`);
|
||||||
),
|
$card.querySelector('.library--title input').addEventListener('change', async (event) => {
|
||||||
async _generate(mod) {
|
storage.set('_mods', mod.id, event.target.checked);
|
||||||
const card = web.createElement(web.html`<article class="library--card"></article>`),
|
});
|
||||||
body = web.createElement(web.html`<div></div>`);
|
return $card;
|
||||||
card.append(this.preview(mod));
|
|
||||||
body.append(await this.name(mod));
|
|
||||||
body.append(this.tags(mod));
|
|
||||||
body.append(this.description(mod));
|
|
||||||
body.append(this.authors(mod));
|
|
||||||
body.append(await this.expand(mod));
|
|
||||||
card.append(body);
|
|
||||||
return card;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
components.options = {
|
components.options = {
|
||||||
async toggle(id, { key, label, tooltip }) {
|
async toggle(id, { key, label, tooltip }) {
|
||||||
const state = await storage.get(id, key),
|
const state = await storage.get(id, key),
|
||||||
opt = web.createElement(web.html`<label
|
opt = web.createElement(web.html`
|
||||||
for="toggle--${web.escapeHtml(`${id}.${key}`)}"
|
<label
|
||||||
class="library--toggle_label"
|
for="toggle--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
|
class="library--toggle_label"
|
||||||
>
|
>
|
||||||
<input type="checkbox" id="toggle--${web.escapeHtml(`${id}.${key}`)}"
|
<input type="checkbox" id="toggle--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
${state ? 'checked' : ''}/>
|
${state ? 'checked' : ''}/>
|
||||||
<p>
|
<p>
|
||||||
<span data-tooltip>${web.escapeHtml(label)}
|
<span data-tooltip>${web.escapeHtml(label)}
|
||||||
${tooltip ? web.html`<i data-icon="fa/question-circle"></i>` : ''}</span>
|
${tooltip ? web.html`<i data-icon="fa/solid/question-circle"></i>` : ''}</span>
|
||||||
<span class="library--toggle"></span>
|
<span class="library--toggle"></span>
|
||||||
</p>
|
</p>
|
||||||
</label>`);
|
</label>`);
|
||||||
opt.addEventListener('change', (event) => storage.set(id, key, event.target.checked));
|
opt.addEventListener('change', (event) => storage.set(id, key, event.target.checked));
|
||||||
if (tooltip) tooltips.add(opt, '[data-tooltip]', tooltip);
|
if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
|
||||||
return opt;
|
return opt;
|
||||||
},
|
},
|
||||||
async select(id, { key, label, tooltip, values }) {
|
async select(id, { key, label, tooltip, values }) {
|
||||||
const state = await storage.get(id, key),
|
const state = await storage.get(id, key),
|
||||||
opt = web.createElement(web.html`<label
|
opt = web.createElement(web.html`
|
||||||
|
<label
|
||||||
for="select--${web.escapeHtml(`${id}.${key}`)}"
|
for="select--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
class="library--select_label"
|
class="library--select_label"
|
||||||
>
|
>
|
||||||
<p><span data-tooltip>${web.escapeHtml(label)}
|
<p><span data-tooltip>${web.escapeHtml(label)}
|
||||||
${tooltip ? web.html`<i data-icon="fa/question-circle"></i>` : ''}</span></p>
|
${tooltip ? web.html`<i data-icon="fa/solid/question-circle"></i>` : ''}</span></p>
|
||||||
<p class="library--select">
|
<p class="library--select">
|
||||||
<span><i data-icon="fa/caret-down"></i></span>
|
<span><i data-icon="fa/solid/caret-down"></i></span>
|
||||||
<select id="select--${web.escapeHtml(`${id}.${key}`)}">
|
<select id="select--${web.escapeHtml(`${id}.${key}`)}">
|
||||||
${values.map(
|
${values.map(
|
||||||
(value) =>
|
(value) =>
|
||||||
web.html`<option value="${web.escapeHtml(value)}"
|
web.html`<option value="${web.escapeHtml(value)}"
|
||||||
${value === state ? 'selected' : ''}>
|
${value === state ? 'selected' : ''}>
|
||||||
${web.escapeHtml(value)}</option>`
|
${web.escapeHtml(value)}</option>`
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
</label>`);
|
</label>`);
|
||||||
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
|
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
|
||||||
if (tooltip) tooltips.add(opt, '[data-tooltip]', tooltip);
|
if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
|
||||||
return opt;
|
return opt;
|
||||||
},
|
},
|
||||||
async text(id, { key, label, tooltip }) {
|
async text(id, { key, label, tooltip }) {
|
||||||
const state = await storage.get(id, key),
|
const state = await storage.get(id, key),
|
||||||
opt = web.createElement(web.html`<label
|
opt = web.createElement(web.html`
|
||||||
for="text--${web.escapeHtml(`${id}.${key}`)}"
|
<label
|
||||||
class="library--text_label"
|
for="text--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
|
class="library--text_label"
|
||||||
>
|
>
|
||||||
<p><span data-tooltip>${web.escapeHtml(label)}
|
<p><span data-tooltip>${web.escapeHtml(label)}
|
||||||
${tooltip ? web.html`<i data-icon="fa/question-circle"></i>` : ''}</span></p>
|
${tooltip ? web.html`<i data-icon="fa/solid/question-circle"></i>` : ''}</span></p>
|
||||||
<textarea id="text--${web.escapeHtml(`${id}.${key}`)}"
|
<textarea id="text--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
rows="1">${web.escapeHtml(state)}</textarea>
|
rows="1">${web.escapeHtml(state)}</textarea>
|
||||||
</label>`);
|
</label>`);
|
||||||
opt.querySelector('textarea').addEventListener('input', (event) => {
|
opt.querySelector('textarea').addEventListener('input', (event) => {
|
||||||
event.target.style.removeProperty('--txt--scroll-height');
|
event.target.style.removeProperty('--txt--scroll-height');
|
||||||
event.target.style.setProperty(
|
event.target.style.setProperty(
|
||||||
@ -207,52 +213,54 @@ components.options = {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
|
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
|
||||||
if (tooltip) tooltips.add(opt, '[data-tooltip]', tooltip);
|
if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
|
||||||
return opt;
|
return opt;
|
||||||
},
|
},
|
||||||
async number(id, { key, label, tooltip }) {
|
async number(id, { key, label, tooltip }) {
|
||||||
const state = await storage.get(id, key),
|
const state = await storage.get(id, key),
|
||||||
opt = web.createElement(web.html`<label
|
opt = web.createElement(web.html`
|
||||||
for="number--${web.escapeHtml(`${id}.${key}`)}"
|
<label
|
||||||
class="library--number_label"
|
for="number--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
|
class="library--number_label"
|
||||||
>
|
>
|
||||||
<p><span data-tooltip>${web.escapeHtml(label)}
|
<p><span data-tooltip>${web.escapeHtml(label)}
|
||||||
${tooltip ? web.html`<i data-icon="fa/question-circle"></i>` : ''}</span></p>
|
${tooltip ? web.html`<i data-icon="fa/solid/question-circle"></i>` : ''}</span></p>
|
||||||
<input id="number--${web.escapeHtml(`${id}.${key}`)}"
|
<input id="number--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
type="number" value="${web.escapeHtml(state.toString())}"/>
|
type="number" value="${web.escapeHtml(state.toString())}"/>
|
||||||
</label>`);
|
</label>`);
|
||||||
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
|
opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
|
||||||
if (tooltip) tooltips.add(opt, '[data-tooltip]', tooltip);
|
if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
|
||||||
return opt;
|
return opt;
|
||||||
},
|
},
|
||||||
async file(id, { key, label, tooltip, extensions }) {
|
async file(id, { key, label, tooltip, extensions }) {
|
||||||
const state = await storage.get(id, key),
|
const state = await storage.get(id, key),
|
||||||
opt = web.createElement(web.html`<label
|
opt = web.createElement(web.html`
|
||||||
for="file--${web.escapeHtml(`${id}.${key}`)}"
|
<label
|
||||||
class="library--file_label"
|
for="file--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
|
class="library--file_label"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
id="file--${web.escapeHtml(`${id}.${key}`)}"
|
id="file--${web.escapeHtml(`${id}.${key}`)}"
|
||||||
${web.escapeHtml(
|
${web.escapeHtml(
|
||||||
extensions && extensions.length
|
extensions && extensions.length
|
||||||
? ` accept=${web.escapeHtml(extensions.join(','))}`
|
? ` accept=${web.escapeHtml(extensions.join(','))}`
|
||||||
: ''
|
: ''
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<p class="library--file_title"><span data-tooltip>${web.escapeHtml(label)}
|
<p class="library--file_title"><span data-tooltip>${web.escapeHtml(label)}
|
||||||
<i data-icon="fa/question-circle"></i></span>
|
<i data-icon="fa/solid/question-circle"></i></span>
|
||||||
<span class="library--file_remove"><i data-icon="fa/minus"></i></span></p>
|
<span class="library--file_remove"><i data-icon="fa/solid/minus"></i></span></p>
|
||||||
<p class="library--file">
|
<p class="library--file">
|
||||||
<span><i data-icon="fa/file"></i></span>
|
<span><i data-icon="fa/solid/file"></i></span>
|
||||||
<span class="library--file_path">${web.escapeHtml(state || 'choose file...')}</span>
|
<span class="library--file_path">${web.escapeHtml(state || 'choose file...')}</span>
|
||||||
</p>
|
</p>
|
||||||
</label>`);
|
</label>`);
|
||||||
opt.addEventListener('change', (event) => {
|
opt.addEventListener('change', (event) => {
|
||||||
const file = event.target.files[0],
|
const file = event.target.files[0],
|
||||||
reader = new FileReader();
|
reader = new FileReader();
|
||||||
opt.querySelector('.library--file_path').innerText = file.name;
|
opt.querySelector('.library--file_path').innerText = file.name;
|
||||||
reader.onload = (progress) => {
|
reader.onload = (progress) => {
|
||||||
storage.set(id, key, file.name);
|
storage.set(id, key, file.name);
|
||||||
storage.set(id, `_file.${key}`, progress.currentTarget.result);
|
storage.set(id, `_file.${key}`, progress.currentTarget.result);
|
||||||
};
|
};
|
||||||
@ -272,281 +280,190 @@ components.options = {
|
|||||||
opt.addEventListener('click', (event) => {
|
opt.addEventListener('click', (event) => {
|
||||||
document.documentElement.scrollTop = 0;
|
document.documentElement.scrollTop = 0;
|
||||||
});
|
});
|
||||||
tooltips.add(
|
web.addTooltip(
|
||||||
opt,
|
opt.querySelector('[data-tooltip]'),
|
||||||
'[data-tooltip]',
|
`${tooltip ? `${tooltip}\n\n` : ''}**warning:** ${
|
||||||
`${
|
'browser extensions do not have true filesystem access, ' +
|
||||||
tooltip ? `${tooltip}\n\n` : ''
|
'so file content is only saved on selection. re-select files to apply edits.'
|
||||||
}**warning:** browser extensions do not have true filesystem access,
|
}`
|
||||||
so file content is only saved on selection. re-select files to apply edits.`
|
|
||||||
);
|
);
|
||||||
return opt;
|
return opt;
|
||||||
},
|
},
|
||||||
async _generate(mod) {
|
|
||||||
const card = await components.card._generate(mod);
|
|
||||||
card.querySelector('.library--expand').remove();
|
|
||||||
if (mod.options && mod.options.length) {
|
|
||||||
const options = web.createElement(web.html`<div class="library--options"></div>`),
|
|
||||||
inputs = await Promise.all(
|
|
||||||
mod.options
|
|
||||||
.filter((opt) => !opt.environments || opt.environments.includes(env.name))
|
|
||||||
.map((opt) => this[opt.type](mod.id, opt))
|
|
||||||
);
|
|
||||||
inputs.forEach((opt) => options.append(opt));
|
|
||||||
card.append(options);
|
|
||||||
}
|
|
||||||
return card;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
components.documentation = {
|
|
||||||
|
const actionButtons = {
|
||||||
_reloadTriggered: false,
|
_reloadTriggered: false,
|
||||||
buttons({ _dir }) {
|
async reload($fragment = document) {
|
||||||
const $el = web.createElement(web.html`<p class="documentation--buttons">
|
let $reload = $fragment.querySelector('[data-reload]');
|
||||||
<a href="?view=library">
|
if (!$reload) {
|
||||||
<span><i data-icon="fa/long-arrow-alt-left"></i></span>
|
$reload = web.createElement(web.html`
|
||||||
<span>back to library</span>
|
<button class="action--alert" data-reload>
|
||||||
</a>
|
<span><i data-icon="fa/solid/redo"></i></span>
|
||||||
<a
|
|
||||||
href="https://github.com/notion-enhancer/extension/tree/main/repo/${encodeURIComponent(
|
|
||||||
_dir
|
|
||||||
)}"
|
|
||||||
>
|
|
||||||
<span><i data-icon="fa/code"></i></span>
|
|
||||||
<span>view source code</span>
|
|
||||||
</a>
|
|
||||||
<button class="documentation--reload"${this._reloadTriggered ? ' data-triggered' : ''}>
|
|
||||||
<span><i data-icon="fa/redo"></i></span>
|
|
||||||
<span>reload tabs to apply changes</span>
|
<span>reload tabs to apply changes</span>
|
||||||
</button>
|
</button>`);
|
||||||
</p>`);
|
$reload.addEventListener('click', env.reloadTabs);
|
||||||
storage.onChange(() => {
|
}
|
||||||
const $reload = $el.querySelector('.documentation--reload');
|
if (this._reloadTriggered) {
|
||||||
if (document.body.contains($el) && !$reload.dataset.triggered) {
|
$fragment.querySelector('.action--buttons').append($reload);
|
||||||
$reload.dataset.triggered = true;
|
await new Promise((res, rej) => requestAnimationFrame(res));
|
||||||
this._reloadTriggered = true;
|
$reload.dataset.triggered = true;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
$el.querySelector('.documentation--reload').addEventListener('click', env.reloadTabs);
|
|
||||||
return $el;
|
|
||||||
},
|
},
|
||||||
readme: async (mod) => {
|
async clearFilters($fragment = document) {
|
||||||
const readme = web.createElement(web.html`<article class="documentation--body markdown">
|
let $clearFilters = $fragment.querySelector('[data-clear-filters]');
|
||||||
${
|
if (!$clearFilters) {
|
||||||
(await fs.isFile(`repo/${mod._dir}/README.md`))
|
$clearFilters = web.createElement(web.html`
|
||||||
? fmt.md.render(await fs.getText(`repo/${mod._dir}/README.md`))
|
<a class="action--alert" href="?view=library" data-clear-filters>
|
||||||
: ''
|
<span><i data-icon="fa/solid/times"></i></span>
|
||||||
}
|
<span>clear filters</span>
|
||||||
</article>`);
|
</a>`);
|
||||||
fmt.Prism.highlightAllUnder(readme);
|
}
|
||||||
return readme;
|
const search = router.getSearch();
|
||||||
|
if (search.get('tag') || search.has('enabled') || search.has('disabled')) {
|
||||||
|
$fragment.querySelector('.action--buttons').append($clearFilters);
|
||||||
|
await new Promise((res, rej) => requestAnimationFrame(res));
|
||||||
|
$clearFilters.dataset.triggered = true;
|
||||||
|
} else $clearFilters.remove();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const views = {
|
storage.addChangeListener(async (event) => {
|
||||||
$container: document.querySelector('main'),
|
actionButtons._reloadTriggered = true;
|
||||||
_router(event) {
|
actionButtons.reload();
|
||||||
event.preventDefault();
|
router.load();
|
||||||
let anchor,
|
|
||||||
i = 0;
|
|
||||||
do {
|
|
||||||
anchor = event.path[i];
|
|
||||||
i++;
|
|
||||||
} while (anchor.nodeName !== 'A');
|
|
||||||
if (location.search !== anchor.getAttribute('href')) {
|
|
||||||
window.history.pushState(
|
|
||||||
{ search: anchor.getAttribute('href'), hash: '' },
|
|
||||||
'',
|
|
||||||
anchor.href
|
|
||||||
);
|
|
||||||
this._load();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_navigator(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const hash = event.target.getAttribute('href').slice(1);
|
|
||||||
document.getElementById(hash).scrollIntoView(true);
|
|
||||||
document.documentElement.scrollTop = 0;
|
|
||||||
history.replaceState({ search: location.search, hash }, null, `#${hash}`);
|
|
||||||
},
|
|
||||||
_reset() {
|
|
||||||
document
|
|
||||||
.querySelectorAll('a[href^="?"]')
|
|
||||||
.forEach((a) => a.removeEventListener('click', this._router));
|
|
||||||
document
|
|
||||||
.querySelectorAll('a[href^="#"]')
|
|
||||||
.forEach((a) => a.removeEventListener('click', this._navigator));
|
|
||||||
this.$container.style.opacity = 0;
|
|
||||||
return new Promise((res, rej) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$container.innerHTML = '';
|
|
||||||
this.$container.style.opacity = '';
|
|
||||||
document.body.dataset.view = '';
|
|
||||||
document
|
|
||||||
.querySelector('[data-view-target][data-view-active]')
|
|
||||||
?.removeAttribute('data-view-active');
|
|
||||||
res();
|
|
||||||
}, 200);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async _load() {
|
|
||||||
await this._reset();
|
|
||||||
|
|
||||||
const search = new Map(
|
if (event.namespace === '_mods' && event.new === true) {
|
||||||
location.search
|
const enabledTheme = (await registry.get()).find((mod) => mod.id === event.key);
|
||||||
.slice(1)
|
if (
|
||||||
.split('&')
|
enabledTheme.tags.includes('theme') &&
|
||||||
.map((query) => query.split('='))
|
(await storage.get(_id, 'themes.autoresolve', true))
|
||||||
);
|
) {
|
||||||
switch (search.get('view')) {
|
for (const theme of await registry.get(
|
||||||
case 'mod':
|
(mod) =>
|
||||||
const mod = (await registry.get()).find((mod) => mod.id === search.get('id'));
|
mod.tags.includes('theme') &&
|
||||||
if (mod) {
|
mod.id !== enabledTheme.id &&
|
||||||
await this.mod(mod);
|
((mod.tags.includes('dark') && enabledTheme.tags.includes('dark')) ||
|
||||||
break;
|
(mod.tags.includes('light') && enabledTheme.tags.includes('light')))
|
||||||
}
|
)) {
|
||||||
case 'library':
|
if (document.body.dataset.view === 'library') {
|
||||||
await this.library();
|
const $toggle = document.getElementById(`enable--${theme.id}`);
|
||||||
break;
|
if ($toggle.checked) $toggle.click();
|
||||||
default:
|
} else storage.set('_mods', theme.id, false);
|
||||||
window.history.replaceState(
|
}
|
||||||
{ search: '?view=library', hash: '' },
|
|
||||||
null,
|
|
||||||
'?view=library'
|
|
||||||
);
|
|
||||||
return this._load();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
router.addView(
|
||||||
document.getElementById(location.hash.slice(1))?.scrollIntoView(true);
|
'library',
|
||||||
document.documentElement.scrollTop = 0;
|
async () => {
|
||||||
}, 50);
|
const $fragment = web.createFragment(web.html`
|
||||||
document
|
<p class="action--buttons">
|
||||||
.querySelectorAll('img')
|
<a href="?view=library&tag=theme">
|
||||||
.forEach((img) => (img.onerror = (event) => event.target.remove()));
|
<span><i data-icon="fa/solid/palette"></i></span>
|
||||||
document
|
<span>themes</span>
|
||||||
.querySelectorAll('a[href^="?"]')
|
</a>
|
||||||
.forEach((a) => a.addEventListener('click', this._router));
|
<a href="?view=library&tag=extension">
|
||||||
document
|
<span><i data-icon="fa/solid/plus"></i></span>
|
||||||
.querySelectorAll('a[href^="#"]')
|
<span>extensions</span>
|
||||||
.forEach((a) => a.addEventListener('click', this._navigator));
|
</a>
|
||||||
document.querySelectorAll('[data-icon]').forEach((icon) =>
|
<a href="?view=library&enabled">
|
||||||
fs.getText(`icons/${icon.dataset.icon}.svg`).then((svg) => {
|
<span><i data-icon="fa/solid/toggle-on"></i></span>
|
||||||
svg = web.createElement(svg);
|
<span>enabled</span>
|
||||||
for (const attr of icon.attributes) {
|
</a>
|
||||||
svg.setAttribute(attr.name, attr.value);
|
<a href="?view=library&disabled">
|
||||||
}
|
<span><i data-icon="fa/solid/toggle-off"></i></span>
|
||||||
icon.replaceWith(svg);
|
<span>disabled</span>
|
||||||
})
|
</a>
|
||||||
);
|
</p>`);
|
||||||
},
|
|
||||||
async mod(mod) {
|
|
||||||
document.body.dataset.view = 'mod';
|
|
||||||
document.querySelector('header [data-view-target="library"]').dataset.active = true;
|
|
||||||
this.$container.append(components.documentation.buttons(mod));
|
|
||||||
this.$container.append(await components.options._generate(mod));
|
|
||||||
this.$container.append(await components.documentation.readme(mod));
|
|
||||||
},
|
|
||||||
async library() {
|
|
||||||
document.body.dataset.view = 'library';
|
|
||||||
document.querySelector('header [data-view-target="library"]').dataset.active = true;
|
|
||||||
for (const mod of await registry.get(
|
for (const mod of await registry.get(
|
||||||
(mod) => !mod.environments || mod.environments.includes(env.name)
|
(mod) => !mod.environments || mod.environments.includes(env.name)
|
||||||
)) {
|
)) {
|
||||||
this.$container.append(await components.card._generate(mod));
|
$fragment.append(await components.card(mod));
|
||||||
}
|
}
|
||||||
|
actionButtons.reload($fragment);
|
||||||
|
actionButtons.clearFilters($fragment);
|
||||||
|
return $fragment;
|
||||||
},
|
},
|
||||||
};
|
async (search = router.getSearch()) => {
|
||||||
views._router = views._router.bind(views);
|
for (const [filter, active] of [
|
||||||
views._navigator = views._navigator.bind(views);
|
['tag=theme', search.get('tag') === 'theme'],
|
||||||
views._load();
|
['tag=extension', search.get('tag') === 'extension'],
|
||||||
window.addEventListener('popstate', (event) => {
|
['enabled', search.has('enabled')],
|
||||||
if (event.state) views._load();
|
['disabled', search.has('disabled')],
|
||||||
});
|
]) {
|
||||||
|
document
|
||||||
const notifications = {
|
.querySelector(`.action--buttons > [href="?view=library&${filter}"]`)
|
||||||
$list: document.querySelector('.notification--list'),
|
.classList[active ? 'add' : 'remove']('action--active');
|
||||||
push({ heading, message = '', type = 'information' }, onDismiss = () => {}) {
|
|
||||||
let svg = '',
|
|
||||||
className = 'notification';
|
|
||||||
switch (type) {
|
|
||||||
case 'celebration':
|
|
||||||
svg = web.html`<i data-icon="monster/party"></i>`;
|
|
||||||
className += ' celebration';
|
|
||||||
break;
|
|
||||||
case 'information':
|
|
||||||
svg = web.html`<i data-icon="fa/info"></i>`;
|
|
||||||
className += ' information';
|
|
||||||
break;
|
|
||||||
case 'warning':
|
|
||||||
svg = web.html`<i data-icon="fa/exclamation-triangle"></i>`;
|
|
||||||
className += ' warning';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
const $notif = web.createElement(web.html`<div role="alert" class="${className}">
|
for (const card of document.querySelectorAll('main > .library--card')) {
|
||||||
<div>${svg}</div>
|
const { tags } = (await registry.get()).find((mod) => mod.id === card.dataset.mod),
|
||||||
<div>
|
isEnabled = await registry.isEnabled(card.dataset.mod);
|
||||||
<h3>${web.escapeHtml(heading)}</h3>
|
|
||||||
<p>${fmt.md.renderInline(message)}</p>
|
|
||||||
</div>
|
|
||||||
<button class="notification--dismiss">×</button>
|
|
||||||
</div>`);
|
|
||||||
$notif.querySelector('.notification--dismiss').addEventListener('click', (event) => {
|
|
||||||
$notif.style.opacity = 0;
|
|
||||||
$notif.style.transform = 'scaleY(0)';
|
|
||||||
$notif.style.marginTop = `-${
|
|
||||||
$notif.offsetHeight / parseFloat(getComputedStyle(document.documentElement).fontSize)
|
|
||||||
}rem`;
|
|
||||||
setTimeout(() => $notif.remove(), 400);
|
|
||||||
onDismiss();
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
$notif.style.opacity = 1;
|
|
||||||
}, 100);
|
|
||||||
return this.$list.append($notif);
|
|
||||||
},
|
|
||||||
async fetch() {
|
|
||||||
const notifications = {
|
|
||||||
list: await fs.getJSON('https://notion-enhancer.github.io/notifications.json'),
|
|
||||||
dismissed: await storage.get(_id, 'notifications', []),
|
|
||||||
};
|
|
||||||
notifications.list = notifications.list.sort((a, b) => b.id - a.id);
|
|
||||||
notifications.waiting = notifications.list.filter(
|
|
||||||
({ id }) => !notifications.dismissed.includes(id)
|
|
||||||
);
|
|
||||||
for (const notification of notifications.waiting) {
|
|
||||||
if (
|
if (
|
||||||
notification.heading &&
|
(search.has('tag') ? tags.includes(search.get('tag')) : true) &&
|
||||||
notification.appears_on &&
|
(search.has('enabled') && search.has('disabled')
|
||||||
(notification.appears_on.versions.includes('*') ||
|
? true
|
||||||
notification.appears_on.versions.includes(env.version)) &&
|
: search.has('enabled')
|
||||||
notification.appears_on.extension
|
? isEnabled
|
||||||
|
: search.has('disabled')
|
||||||
|
? !isEnabled
|
||||||
|
: true)
|
||||||
) {
|
) {
|
||||||
this.push(notification, async () => {
|
card.style.display = '';
|
||||||
const dismissed = await storage.get(_id, 'notifications', []);
|
} else card.style.display = 'none';
|
||||||
storage.set('_notifications', 'external', [
|
|
||||||
...new Set([...dismissed, notification.id]),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
actionButtons.clearFilters();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.addView(
|
||||||
|
'mod',
|
||||||
|
async () => {
|
||||||
|
const mod = (await registry.get()).find((mod) => mod.id === router.getSearch().get('id'));
|
||||||
|
if (!mod) return false;
|
||||||
|
const $fragment = web.createFragment(web.html`
|
||||||
|
<p class="action--buttons">
|
||||||
|
<a href="?view=library">
|
||||||
|
<span><i data-icon="fa/solid/long-arrow-alt-left"></i></span>
|
||||||
|
<span>back to library</span>
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/notion-enhancer/extension/tree/main/repo/${encodeURIComponent(
|
||||||
|
mod._dir
|
||||||
|
)}">
|
||||||
|
<span><i data-icon="fa/solid/code"></i></span>
|
||||||
|
<span>view source code</span>
|
||||||
|
</a>
|
||||||
|
</p>`);
|
||||||
|
const $card = await components.card(mod);
|
||||||
|
$card.querySelector('.library--expand').remove();
|
||||||
|
if (mod.options && mod.options.length) {
|
||||||
|
const options = web.createElement(web.html`<div class="library--options"></div>`);
|
||||||
|
mod.options
|
||||||
|
.filter((opt) => !opt.environments || opt.environments.includes(env.name))
|
||||||
|
.forEach(async (opt) =>
|
||||||
|
options.append(await components.options[opt.type](mod.id, opt))
|
||||||
|
);
|
||||||
|
$card.append(options);
|
||||||
|
}
|
||||||
|
$fragment.append(
|
||||||
|
$card,
|
||||||
|
web.createElement(web.html`
|
||||||
|
<article class="documentation--body markdown">
|
||||||
|
${
|
||||||
|
(await fs.isFile(`repo/${mod._dir}/README.md`))
|
||||||
|
? fmt.md.render(await fs.getText(`repo/${mod._dir}/README.md`))
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
</article>`)
|
||||||
|
);
|
||||||
|
fmt.Prism.highlightAllUnder($fragment);
|
||||||
|
actionButtons.reload($fragment);
|
||||||
|
return $fragment;
|
||||||
},
|
},
|
||||||
};
|
() => {
|
||||||
for (const error of await registry.errors()) {
|
if (document.querySelector('[data-mod]').dataset.mod !== router.getSearch().get('id'))
|
||||||
notifications.push({
|
router.load(true);
|
||||||
heading: `error: ${error.source}`,
|
}
|
||||||
message: error.message,
|
);
|
||||||
type: 'warning',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
notifications.fetch();
|
|
||||||
|
|
||||||
async function theme() {
|
router.setDefaultView('library');
|
||||||
document.documentElement.className = `notion-${
|
router.load();
|
||||||
(await storage.get(_id, 'theme')) || 'dark'
|
|
||||||
}-theme`;
|
|
||||||
}
|
|
||||||
window.addEventListener('focus', theme);
|
|
||||||
theme();
|
|
||||||
|
|
||||||
// registry.errors().then((err) => {
|
|
||||||
// document.querySelector('[data-section="alerts"]').innerHTML = JSON.stringify(err);
|
|
||||||
// });
|
|
||||||
|
@ -13,8 +13,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"client": ["client.css"],
|
"frame": ["tooltips.css"],
|
||||||
"menu": ["menu.css", "markdown.css"]
|
"client": ["client.css", "tooltips.css"],
|
||||||
|
"menu": ["menu.css", "markdown.css", "tooltips.css"]
|
||||||
},
|
},
|
||||||
"js": {
|
"js": {
|
||||||
"client": ["client.js"]
|
"client": ["client.js"]
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* notion-enhancer core: menu
|
||||||
|
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||||
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { web } from '../../api.js';
|
||||||
|
|
||||||
|
export const getSearch = () =>
|
||||||
|
new Map(
|
||||||
|
location.search
|
||||||
|
.slice(1)
|
||||||
|
.split('&')
|
||||||
|
.map((query) => query.split('='))
|
||||||
|
);
|
||||||
|
|
||||||
|
let defaultView = '';
|
||||||
|
const views = new Map(),
|
||||||
|
filters = new Map();
|
||||||
|
|
||||||
|
export function setDefaultView(name) {
|
||||||
|
defaultView = name;
|
||||||
|
}
|
||||||
|
export function addView(name, loader, filter = () => {}) {
|
||||||
|
views.set(name, loader);
|
||||||
|
filters.set(name, filter);
|
||||||
|
}
|
||||||
|
export function removeView(name) {
|
||||||
|
views.delete(name);
|
||||||
|
filters.delete(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function router(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const anchor = event.path.find((anchor) => anchor.nodeName === 'A');
|
||||||
|
if (location.search !== anchor.getAttribute('href')) {
|
||||||
|
window.history.pushState(
|
||||||
|
{ search: anchor.getAttribute('href'), hash: '' },
|
||||||
|
'',
|
||||||
|
anchor.href
|
||||||
|
);
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function navigator(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const anchor = event.path.find((anchor) => anchor.nodeName === 'A'),
|
||||||
|
hash = anchor.getAttribute('href').slice(1);
|
||||||
|
document.getElementById(hash).scrollIntoView(true);
|
||||||
|
document.documentElement.scrollTop = 0;
|
||||||
|
history.replaceState({ search: location.search, hash }, null, `#${hash}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load(force = false) {
|
||||||
|
const $container = document.querySelector('main'),
|
||||||
|
search = getSearch(),
|
||||||
|
fallbackView = () =>
|
||||||
|
window.history.replaceState(
|
||||||
|
{ search: `?view=${defaultView}`, hash: '' },
|
||||||
|
null,
|
||||||
|
`?view=${defaultView}`
|
||||||
|
);
|
||||||
|
if (force || !search.get('view') || document.body.dataset.view !== search.get('view')) {
|
||||||
|
if (views.get(search.get('view'))) {
|
||||||
|
const $body = await (views.get(search.get('view')) || (() => void 0))();
|
||||||
|
if ($body) {
|
||||||
|
$container.style.opacity = 0;
|
||||||
|
await new Promise((res, rej) =>
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.dataset.view = search.get('view');
|
||||||
|
$container.innerHTML = '';
|
||||||
|
$container.append($body);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
$container.style.opacity = '';
|
||||||
|
setTimeout(res, 200);
|
||||||
|
});
|
||||||
|
}, 200)
|
||||||
|
);
|
||||||
|
} else return fallbackView();
|
||||||
|
} else return fallbackView();
|
||||||
|
}
|
||||||
|
if (filters.get(search.get('view'))) filters.get(search.get('view'))(search);
|
||||||
|
}
|
||||||
|
window.addEventListener('popstate', (event) => {
|
||||||
|
if (event.state) load();
|
||||||
|
document.getElementById(location.hash.slice(1))?.scrollIntoView(true);
|
||||||
|
document.documentElement.scrollTop = 0;
|
||||||
|
});
|
||||||
|
web.addDocumentObserver((mutation) => {
|
||||||
|
mutation.target.querySelectorAll('a[href^="?"]').forEach((a) => {
|
||||||
|
a.removeEventListener('click', router);
|
||||||
|
a.addEventListener('click', router);
|
||||||
|
});
|
||||||
|
mutation.target.querySelectorAll('a[href^="#"]').forEach((a) => {
|
||||||
|
a.removeEventListener('click', navigator);
|
||||||
|
a.addEventListener('click', navigator);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* notion-enhancer core: tooltips
|
||||||
|
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||||
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
|
*/
|
||||||
|
|
||||||
|
.enhancer--tooltip {
|
||||||
|
position: absolute;
|
||||||
|
background: var(--theme--tooltip);
|
||||||
|
color: var(--theme--tooltip-text);
|
||||||
|
font-size: var(--theme--font_ui_small-size);
|
||||||
|
padding: 0.15rem 0.4rem;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) !important;
|
||||||
|
border-radius: 3px;
|
||||||
|
max-width: 20rem;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.enhancer--tooltip p {
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
}
|
@ -14,9 +14,8 @@ web.whenReady().then(async () => {
|
|||||||
if (cssInsert) {
|
if (cssInsert) {
|
||||||
document.body.append(
|
document.body.append(
|
||||||
web.createElement(
|
web.createElement(
|
||||||
web.html`<style id="${await storage.get(_id, 'insert.css')}@${_id}">
|
web.html`
|
||||||
${cssInsert}
|
<style id="${await storage.get(_id, 'insert.css')}@${_id}">${cssInsert}</style>`
|
||||||
</style>`
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user