diff --git a/extension/icons/monstr/party.svg b/extension/icons/monstr/party.svg new file mode 100644 index 0000000..ff65557 --- /dev/null +++ b/extension/icons/monstr/party.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/extension/repo/CHANGELOG.md b/extension/repo/CHANGELOG.md index 733c86a..f67ac04 100644 --- a/extension/repo/CHANGELOG.md +++ b/extension/repo/CHANGELOG.md @@ -5,12 +5,13 @@ - improved: split the core mod into the theming & menu mods. - improved: new larger menu layout, with individual mod pages. - 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: js insert. css insert moved to tweaks mod. +- ported: tweaks, bypass-preview. #### todo -- tag sort - documentation e.g. \_file - complete/bugfix theming variables - color pickers diff --git a/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/client.js b/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/client.js index 68955c2..e4e9f3f 100644 --- a/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/client.js +++ b/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/client.js @@ -4,6 +4,8 @@ * (https://notion-enhancer.github.io/) under the MIT license */ +'use strict'; + import { web } from '../../api.js'; web.whenReady().then(async () => { @@ -23,7 +25,7 @@ function getCurrentPage() { return { type: 'page', id: location.pathname.split(/(-|\/)/g).reverse()[0] }; } let lastPage = getCurrentPage(); -web.observeDocument((event) => { +web.addDocumentObserver((event) => { const currentPage = getCurrentPage(); if (currentPage.id !== lastPage.id || currentPage.type !== lastPage.type) { const openAsPage = document.querySelector( diff --git a/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/mod.json b/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/mod.json index b51c1fd..f77d96b 100644 --- a/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/mod.json +++ b/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/mod.json @@ -1,7 +1,7 @@ { "name": "bypass-preview", "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", "tags": ["extension", "automation"], "authors": [ diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/README.md b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/README.md index 8679d5a..c9b8a11 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/README.md +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/README.md @@ -1 +1,3 @@ # menu + +[theming mod link test](?view=mod&id=0f0bf8b6-eae6-4273-b307-8fc43f2ee082) diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/client.js b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/client.js index 28de427..df9b13d 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/client.js +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/client.js @@ -25,6 +25,7 @@ web.whenReady([sidebarSelector]).then(async () => { list: await fs.getJSON('https://notion-enhancer.github.io/notifications.json'), dismissed: await storage.get(_id, 'notifications', []), }; + console.log($enhancerSidebarElement); notifications.waiting = notifications.list.filter( ({ id }) => !notifications.dismissed.includes(id) ); @@ -48,4 +49,4 @@ web.whenReady([sidebarSelector]).then(async () => { setTheme(); 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); diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.css b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.css index 0fea1fc..65f60c3 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.css +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.css @@ -11,6 +11,7 @@ -webkit-text-size-adjust: 100%; font-size: inherit; font-family: inherit; + fill: currentColor; } html { @@ -43,7 +44,7 @@ header > * { margin: 0 1.25rem 0.1em 0; font-size: var(--theme--font_heading1-size); } -header h1 a:not([data-view-active]) { +header h1 a { text-decoration: none; } header h1 img { @@ -80,7 +81,7 @@ main { main { grid-template-columns: 1fr 1fr; } - [data-view='mod'] main > .documentation--buttons { + main > .action--buttons { grid-column: span 2; } [data-view='mod'] main .library--card, @@ -97,7 +98,7 @@ main { main { grid-template-columns: 1fr 1fr 1fr; } - [data-view='mod'] main > .documentation--buttons { + main > .action--buttons { grid-column: span 3; } [data-view='mod'] main > .documentation--body { @@ -108,7 +109,7 @@ main { main { grid-template-columns: 1fr 1fr 1fr 1fr; } - [data-view='mod'] main > .documentation--buttons { + main > .action--buttons { grid-column: span 4; } [data-view='mod'] main > .documentation--body { @@ -119,7 +120,7 @@ main { main { grid-template-columns: 1fr 1fr 1fr 1fr 1fr; } - [data-view='mod'] main > .documentation--buttons { + main > .action--buttons { grid-column: span 5; } [data-view='mod'] main > .documentation--body { @@ -136,7 +137,7 @@ main article img { max-width: 100%; } -.documentation--buttons, +.action--buttons, .library--expand { margin: 0; display: flex; @@ -146,14 +147,14 @@ main article img { .library--expand a { margin-left: auto; } -.documentation--buttons a, +.action--buttons a, .library--expand a { border-radius: 3px; padding: 0.35rem 0.45rem; text-decoration: none; display: flex; } -.documentation--buttons .documentation--reload { +.action--buttons .action--alert { cursor: pointer; border-radius: 3px; padding: 0.35rem 0.45rem; @@ -164,31 +165,32 @@ main article img { opacity: 0; transition: opacity 200ms ease-in-out; } -.documentation--buttons .documentation--reload[data-triggered] { +.action--buttons .action--alert[data-triggered] { pointer-events: all; opacity: 1; } -.documentation--buttons .documentation--reload[data-triggered]:hover { +.action--buttons .action--alert[data-triggered]:hover { background: none; color: var(--theme--block_grey-text); box-shadow: var(--theme--block_grey) 0px 0px 0px 1px inset; } -.documentation--buttons span, +.action--buttons span, .library--expand span { color: var(--theme--text_property); } -.documentation--buttons a:hover, +.action--buttons a:hover, +.action--buttons a.action--active, .library--expand a:hover { background: var(--theme--button-hover); } -.documentation--buttons svg, +.action--buttons svg, .library--expand svg { width: 1em; height: 1em; padding-top: 2px; margin-right: 0.3rem; } -.documentation--buttons svg *, +.action--buttons svg *, .library--expand svg * { fill: var(--theme--text_property); } @@ -269,7 +271,7 @@ label p > span:not([class]), label > span:not([class]) { font-size: 1rem; } -label [data-icon='fa/question-circle'] { +label [data-icon='fa/solid/question-circle'] { height: var(--theme--font_ui_small-size); width: var(--theme--font_ui_small-size); margin-left: 0.25em; @@ -306,7 +308,7 @@ label [data-icon='fa/question-circle'] { height: 0.8rem; width: 0.8rem; left: 0.325rem; - top: 0.225rem; + top: 0.2rem; position: absolute; border-radius: 100%; background: var(--theme--toggle_dot); @@ -419,22 +421,7 @@ label [data-icon='fa/question-circle'] { overflow-x: auto; } -.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; -} -.tooltip p { - margin: 0.25rem 0; -} - -.notification--list { +.notifications { position: absolute; bottom: 1.5rem; right: 1.5rem; @@ -459,7 +446,7 @@ label [data-icon='fa/question-circle'] { transform-origin: 100% 100%; opacity: 0; } -.notification svg { +.notification :not(.notification--dismiss) > svg { height: 1.5rem; width: 1.5rem; margin-top: 0.25rem; @@ -478,27 +465,20 @@ label [data-icon='fa/question-circle'] { right: 0.75rem; background: none; border: none; - padding: 0.25rem 0.35rem; - font-size: var(--theme--font_body-size); + padding: 0.15rem 0 0.15rem 0.5rem; + width: var(--theme--font_body-size); color: currentColor; cursor: pointer; transition: opacity 200ms ease-in-out; opacity: 0; } +.notification .notification--dismiss svg { + width: 100%; +} .notification:hover .notification--dismiss, .notification:focus-within .notification--dismiss { 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 { background: transparent; diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html index 9d659f7..4ee54bf 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html @@ -8,21 +8,23 @@

- - library + + library

- website + + website

- source code + + source code +

+

+ + support

-

support

-
- diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.js b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.js index fb29842..db00430 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.js +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.js @@ -9,112 +9,124 @@ const _id = 'a6621988-551d-495a-97d8-3c568bca2e9e'; 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 || []) { 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 - .querySelector('img[data-view-target="notion"]') - .addEventListener('click', env.focusNotion); -web.hotkeyListener(await storage.get(_id, 'hotkey.focustoggle'), env.focusNotion); +document.querySelector('img[data-notion]').addEventListener('click', env.focusNotion); +web.addHotkeyListener(await storage.get(_id, 'hotkey.focustoggle'), env.focusNotion); +web.addDocumentObserver(web.loadIcons); -const tooltips = { - $el: document.querySelector('.tooltip'), - add($parent, selector, text) { - text = fmt.md.render(text); - $parent.addEventListener('mouseover', (event) => { - if (event.target.matches(selector) || event.target.matches(`${selector} *`)) { - this.$el.innerHTML = text; - this.$el.style.display = 'block'; - } - }); - $parent.addEventListener('mousemove', (event) => { - this.$el.style.top = event.clientY - this.$el.clientHeight + 'px'; - this.$el.style.left = - event.clientX < window.innerWidth / 2 ? event.clientX + 20 + 'px' : ''; - }); - $parent.addEventListener('mouseout', (event) => { - if (event.target.matches(selector) || event.target.matches(`${selector} *`)) { - this.$el.style.display = ''; - } +const notifications = { + $el: web.createElement(web.html``), + push({ heading, message, icon, color }, onDismiss = () => {}) { + const $notif = web.createElement(web.html` + `); + this.$el.append($notif); + setTimeout(() => { + $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 = {}; -components.card = { - preview: ({ preview = '' }) => - preview - ? web.createElement(web.html``) - : '', - async name({ name, id, version, tags }) { - if (registry.CORE.includes(id)) - return web.createElement(web.html`

- - ${web.escapeHtml(name)} - v${web.escapeHtml(version)} - -

`); - const $el = web.createElement(web.html``); 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; }, async text(id, { key, label, tooltip }) { const state = await storage.get(id, key), - opt = web.createElement(web.html``); +

${web.escapeHtml(label)} + ${tooltip ? web.html`` : ''}

+ + `); opt.querySelector('textarea').addEventListener('input', (event) => { event.target.style.removeProperty('--txt--scroll-height'); event.target.style.setProperty( @@ -207,52 +213,54 @@ components.options = { ); }); 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; }, async number(id, { key, label, tooltip }) { const state = await storage.get(id, key), - opt = web.createElement(web.html``); +

${web.escapeHtml(label)} + ${tooltip ? web.html`` : ''}

+ + `); 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; }, async file(id, { key, label, tooltip, extensions }) { const state = await storage.get(id, key), - opt = web.createElement(web.html``); + +

${web.escapeHtml(label)} + +

+

+ + ${web.escapeHtml(state || 'choose file...')} +

+ `); opt.addEventListener('change', (event) => { const file = event.target.files[0], reader = new FileReader(); - opt.querySelector('.library--file_path').innerText = file.name; - reader.onload = (progress) => { + opt.querySelector('.library--file_path').innerText = file.name; + reader.onload = (progress) => { storage.set(id, key, file.name); storage.set(id, `_file.${key}`, progress.currentTarget.result); }; @@ -272,281 +280,190 @@ components.options = { opt.addEventListener('click', (event) => { document.documentElement.scrollTop = 0; }); - tooltips.add( - opt, - '[data-tooltip]', - `${ - tooltip ? `${tooltip}\n\n` : '' - }**warning:** browser extensions do not have true filesystem access, - so file content is only saved on selection. re-select files to apply edits.` + web.addTooltip( + opt.querySelector('[data-tooltip]'), + `${tooltip ? `${tooltip}\n\n` : ''}**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; }, - 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`
`), - 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, - buttons({ _dir }) { - const $el = web.createElement(web.html`

- - - back to library - - - - view source code - - -

`); - storage.onChange(() => { - const $reload = $el.querySelector('.documentation--reload'); - if (document.body.contains($el) && !$reload.dataset.triggered) { - $reload.dataset.triggered = true; - this._reloadTriggered = true; - } - }); - $el.querySelector('.documentation--reload').addEventListener('click', env.reloadTabs); - return $el; + `); + $reload.addEventListener('click', env.reloadTabs); + } + if (this._reloadTriggered) { + $fragment.querySelector('.action--buttons').append($reload); + await new Promise((res, rej) => requestAnimationFrame(res)); + $reload.dataset.triggered = true; + } }, - readme: async (mod) => { - const readme = web.createElement(web.html`
- ${ - (await fs.isFile(`repo/${mod._dir}/README.md`)) - ? fmt.md.render(await fs.getText(`repo/${mod._dir}/README.md`)) - : '' - } -
`); - fmt.Prism.highlightAllUnder(readme); - return readme; + async clearFilters($fragment = document) { + let $clearFilters = $fragment.querySelector('[data-clear-filters]'); + if (!$clearFilters) { + $clearFilters = web.createElement(web.html` + + + clear filters + `); + } + 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 = { - $container: document.querySelector('main'), - _router(event) { - event.preventDefault(); - 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(); +storage.addChangeListener(async (event) => { + actionButtons._reloadTriggered = true; + actionButtons.reload(); + router.load(); - const search = new Map( - location.search - .slice(1) - .split('&') - .map((query) => query.split('=')) - ); - switch (search.get('view')) { - case 'mod': - const mod = (await registry.get()).find((mod) => mod.id === search.get('id')); - if (mod) { - await this.mod(mod); - break; - } - case 'library': - await this.library(); - break; - default: - window.history.replaceState( - { search: '?view=library', hash: '' }, - null, - '?view=library' - ); - return this._load(); + if (event.namespace === '_mods' && event.new === true) { + const enabledTheme = (await registry.get()).find((mod) => mod.id === event.key); + if ( + enabledTheme.tags.includes('theme') && + (await storage.get(_id, 'themes.autoresolve', true)) + ) { + for (const theme of await registry.get( + (mod) => + mod.tags.includes('theme') && + mod.id !== enabledTheme.id && + ((mod.tags.includes('dark') && enabledTheme.tags.includes('dark')) || + (mod.tags.includes('light') && enabledTheme.tags.includes('light'))) + )) { + if (document.body.dataset.view === 'library') { + const $toggle = document.getElementById(`enable--${theme.id}`); + if ($toggle.checked) $toggle.click(); + } else storage.set('_mods', theme.id, false); + } } + } +}); - setTimeout(() => { - document.getElementById(location.hash.slice(1))?.scrollIntoView(true); - document.documentElement.scrollTop = 0; - }, 50); - document - .querySelectorAll('img') - .forEach((img) => (img.onerror = (event) => event.target.remove())); - document - .querySelectorAll('a[href^="?"]') - .forEach((a) => a.addEventListener('click', this._router)); - document - .querySelectorAll('a[href^="#"]') - .forEach((a) => a.addEventListener('click', this._navigator)); - document.querySelectorAll('[data-icon]').forEach((icon) => - fs.getText(`icons/${icon.dataset.icon}.svg`).then((svg) => { - svg = web.createElement(svg); - for (const attr of icon.attributes) { - svg.setAttribute(attr.name, attr.value); - } - icon.replaceWith(svg); - }) - ); - }, - 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; +router.addView( + 'library', + async () => { + const $fragment = web.createFragment(web.html` +

+ + + themes + + + + extensions + + + + enabled + + + + disabled + +

`); for (const mod of await registry.get( (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; }, -}; -views._router = views._router.bind(views); -views._navigator = views._navigator.bind(views); -views._load(); -window.addEventListener('popstate', (event) => { - if (event.state) views._load(); -}); - -const notifications = { - $list: document.querySelector('.notification--list'), - push({ heading, message = '', type = 'information' }, onDismiss = () => {}) { - let svg = '', - className = 'notification'; - switch (type) { - case 'celebration': - svg = web.html``; - className += ' celebration'; - break; - case 'information': - svg = web.html``; - className += ' information'; - break; - case 'warning': - svg = web.html``; - className += ' warning'; - break; + async (search = router.getSearch()) => { + for (const [filter, active] of [ + ['tag=theme', search.get('tag') === 'theme'], + ['tag=extension', search.get('tag') === 'extension'], + ['enabled', search.has('enabled')], + ['disabled', search.has('disabled')], + ]) { + document + .querySelector(`.action--buttons > [href="?view=library&${filter}"]`) + .classList[active ? 'add' : 'remove']('action--active'); } - const $notif = web.createElement(web.html``); - $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) { + for (const card of document.querySelectorAll('main > .library--card')) { + const { tags } = (await registry.get()).find((mod) => mod.id === card.dataset.mod), + isEnabled = await registry.isEnabled(card.dataset.mod); if ( - notification.heading && - notification.appears_on && - (notification.appears_on.versions.includes('*') || - notification.appears_on.versions.includes(env.version)) && - notification.appears_on.extension + (search.has('tag') ? tags.includes(search.get('tag')) : true) && + (search.has('enabled') && search.has('disabled') + ? true + : search.has('enabled') + ? isEnabled + : search.has('disabled') + ? !isEnabled + : true) ) { - this.push(notification, async () => { - const dismissed = await storage.get(_id, 'notifications', []); - storage.set('_notifications', 'external', [ - ...new Set([...dismissed, notification.id]), - ]); - }); - } + card.style.display = ''; + } else card.style.display = 'none'; } + 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` +

+ + + back to library + + + + view source code + +

`); + const $card = await components.card(mod); + $card.querySelector('.library--expand').remove(); + if (mod.options && mod.options.length) { + const options = web.createElement(web.html`
`); + 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` +
+ ${ + (await fs.isFile(`repo/${mod._dir}/README.md`)) + ? fmt.md.render(await fs.getText(`repo/${mod._dir}/README.md`)) + : '' + } +
`) + ); + fmt.Prism.highlightAllUnder($fragment); + actionButtons.reload($fragment); + return $fragment; }, -}; -for (const error of await registry.errors()) { - notifications.push({ - heading: `error: ${error.source}`, - message: error.message, - type: 'warning', - }); -} -notifications.fetch(); + () => { + if (document.querySelector('[data-mod]').dataset.mod !== router.getSearch().get('id')) + router.load(true); + } +); -async function theme() { - document.documentElement.className = `notion-${ - (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); -// }); +router.setDefaultView('library'); +router.load(); diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/mod.json b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/mod.json index baad3de..6439d83 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/mod.json +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/mod.json @@ -13,8 +13,9 @@ } ], "css": { - "client": ["client.css"], - "menu": ["menu.css", "markdown.css"] + "frame": ["tooltips.css"], + "client": ["client.css", "tooltips.css"], + "menu": ["menu.css", "markdown.css", "tooltips.css"] }, "js": { "client": ["client.js"] diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.js b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.js new file mode 100644 index 0000000..280fca1 --- /dev/null +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.js @@ -0,0 +1,100 @@ +/* + * notion-enhancer core: menu + * (c) 2021 dragonwocky (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); + }); +}); diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/tooltips.css b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/tooltips.css new file mode 100644 index 0000000..dc40542 --- /dev/null +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/tooltips.css @@ -0,0 +1,20 @@ +/* + * notion-enhancer core: tooltips + * (c) 2021 dragonwocky (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; +} diff --git a/extension/repo/tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2/client.js b/extension/repo/tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2/client.js index bcd7965..234862b 100644 --- a/extension/repo/tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2/client.js +++ b/extension/repo/tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2/client.js @@ -14,9 +14,8 @@ web.whenReady().then(async () => { if (cssInsert) { document.body.append( web.createElement( - web.html`` + web.html` + ` ) ); }