diff --git a/extension/CHANGELOG.md b/extension/CHANGELOG.md index 48aec82..0a676cf 100644 --- a/extension/CHANGELOG.md +++ b/extension/CHANGELOG.md @@ -8,19 +8,16 @@ a complete rework of the enhancer including a port to the browser as a chrome ex - new: cross-environment mod loader structure. - new: notifications sourced from an online endpoint for sending global user alerts. - new: simplify user installs by depending on the chrome web store and [notion-repackaged](https://github.com/notion-enhancer/notion-repackaged). -- improved: split the core mod into the theming & menu mods. +- new: separate menu profiles for mod configurations. +- improved: split the core mod into separate mods for specific features. - improved: theming variables that are more specific, less laggy, and less complicated. - improved: merged bracketed-links into tweaks. +- improved: a redesigned menu with separate categories for mods and a sidebar for configuring options. - removed: integrated scrollbar tweak (notion now includes by default). - removed: js insert. css insert moved to tweaks mod. - removed: majority of layout and font size variables - better to leave former to notion and use `ctrl +` for latter. - bugfix: bypass csp restrictions. -#### todo - -- improved: a redesigned menu with a better overview of all mods. -- new: separate menu profiles for mod configurations. - **below this point the enhancer was desktop-only. in v0.11.0 it was been ported to also** **run as a chrome extension. changes made to both are indicated above.** diff --git a/extension/api/env.mjs b/extension/api/env.mjs index 71d8355..2d5dbe4 100644 --- a/extension/api/env.mjs +++ b/extension/api/env.mjs @@ -42,7 +42,7 @@ export const reloadTabs = () => chrome.runtime.sendMessage({ action: 'reloadTabs /** a notification displayed when the menu is opened for the first time */ export const welcomeNotification = { id: '84e2d49b-c3dc-44b4-a154-cf589676bfa0', - // color: 'blue', + color: 'purple', icon: 'message-circle', message: 'Welcome! Come chat with us on Discord.', link: 'https://discord.gg/sFWPXtA', diff --git a/extension/api/registry.mjs b/extension/api/registry.mjs index e9719eb..872affc 100644 --- a/extension/api/registry.mjs +++ b/extension/api/registry.mjs @@ -38,20 +38,25 @@ async function validate(mod) { const check = async ( key, value, - type, + types, { extension = '', - error = `invalid ${key} (${extension ? `${extension} ` : ''}${type}): ${JSON.stringify( + error = `invalid ${key} (${extension ? `${extension} ` : ''}${types}): ${JSON.stringify( value )}`, optional = false, } = {} ) => { - const test = await is( - type === 'file' && value ? `repo/${mod._dir}/${value}` : value, - type, - { extension } - ); + let test; + for (const type of types.split('|')) { + if (type === 'file') { + test = + value && !value.startsWith('http') + ? await is(`repo/${mod._dir}/${value}`, type, { extension }) + : false; + } else test = await is(value, type, { extension }); + if (test) break; + } if (!test) { if (optional && (await is(value, 'undefined'))) return true; if (error) _errors.push({ source: mod._dir, message: error }); @@ -72,11 +77,23 @@ async function validate(mod) { return mod.environments.map((tag) => check('environments.env', tag, 'env')); }), check('description', mod.description, 'string'), - // file doubles for url here - check('preview', mod.preview, 'file', { optional: true }), - check('tags', mod.tags, 'array').then((passed) => - passed ? mod.tags.map((tag) => check('tags.tag', tag, 'string')) : 0 - ), + check('preview', mod.preview, 'file|url', { optional: true }), + check('tags', mod.tags, 'array').then((passed) => { + if (!passed) return false; + const containsCategory = mod.tags.filter((tag) => + ['core', 'extension', 'theme'].includes(tag) + ).length; + if (!containsCategory) { + _errors.push({ + source: mod._dir, + message: `invalid tags (must contain at least one of 'core', 'extension', or 'theme'): ${JSON.stringify( + mod.tags + )}`, + }); + return false; + } + return mod.tags.map((tag) => check('tags.tag', tag, 'string')); + }), check('authors', mod.authors, 'array').then((passed) => { if (!passed) return false; return mod.authors.map((author) => [ @@ -219,7 +236,8 @@ export const list = async (filter = (mod) => true) => { const mod = await getJSON(`repo/${dir}/mod.json`); mod._dir = dir; if (await validate(mod)) _cache.push(mod); - } catch { + } catch (e) { + console.log(e); _errors.push({ source: dir, message: 'invalid mod.json' }); } } diff --git a/extension/api/web.mjs b/extension/api/web.mjs index 017e810..dd476f7 100644 --- a/extension/api/web.mjs +++ b/extension/api/web.mjs @@ -50,6 +50,12 @@ export const whenReady = (selectors = []) => { }); }; +/** + * parse the current location search params into a usable form + * @returns {map} a map of the url search params + */ +export const queryParams = () => new URLSearchParams(window.location.search); + /** * replace special html characters with escaped versions * @param {string} str diff --git a/extension/dep/twind-content.mjs b/extension/dep/twind-content.mjs new file mode 100644 index 0000000..ca4c2bf --- /dev/null +++ b/extension/dep/twind-content.mjs @@ -0,0 +1,43 @@ +/** + * Twind v0.16.16 + * @license MIT + * @source https://unpkg.com/@twind/content@0.1.0/content.js?module + */ + +import { directive as o } from './twind.mjs'; +var c = new Set([ + 'open-quote', + 'close-quote', + 'no-open-quote', + 'no-close-quote', + 'normal', + 'none', + 'inherit', + 'initial', + 'unset', + ]), + n = (t) => t.join('-'), + s = (t) => { + switch (t[0]) { + case 'data': + return `attr(${n(t)})`; + case 'attr': + case 'counter': + return `${t[0]}(${n(t.slice(1))})`; + case 'var': + return `var(--${n(t)})`; + case void 0: + return 'attr(data-content)'; + default: + return JSON.stringify(n(t)); + } + }, + i = (t, { theme: r }) => { + let e = Array.isArray(t) ? n(t) : t; + return { + content: + (e && r('content', [e], '')) || (c.has(e) && e) || (Array.isArray(t) ? s(t) : e), + }; + }, + u = (t, r) => (Array.isArray(t) ? i(t, r) : o(i, t)); +export { u as content }; diff --git a/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/client.mjs b/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/client.mjs index b8ff0a7..4c640f6 100644 --- a/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/client.mjs +++ b/extension/repo/bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f/client.mjs @@ -1,41 +1,33 @@ /* - * notion-enhancer core: bypass-preview + * notion-enhancer: bypass-preview * (c) 2021 dragonwocky (https://dragonwocky.me/) * (https://notion-enhancer.github.io/) under the MIT license */ 'use strict'; -import { web } from '../../api/_.mjs'; +export default async function (api, db) { + const { web } = api; + await web.whenReady(); -web.whenReady().then(async () => { - const openAsPage = document.querySelector( - '.notion-peek-renderer [style*="height: 45px;"] a' - ); - if (openAsPage) openAsPage.click(); -}); - -function getCurrentPage() { - const previewID = location.search - .slice(1) - .split('&') - .map((opt) => opt.split('=')) - .find((opt) => opt[0] === 'p'); - if (previewID) return { type: 'preview', id: previewID[1] }; - return { type: 'page', id: location.pathname.split(/(-|\/)/g).reverse()[0] }; -} -let lastPage = getCurrentPage(); -web.addDocumentObserver((event) => { - const currentPage = getCurrentPage(); - if (currentPage.id !== lastPage.id || currentPage.type !== lastPage.type) { - const openAsPage = document.querySelector( - '.notion-peek-renderer [style*="height: 45px;"] a' - ); - if (openAsPage) { - if (currentPage.id === lastPage.id && currentPage.type === 'preview') { - history.back(); - } else openAsPage.click(); - } - lastPage = getCurrentPage(); + let _lastPage = {}; + function getCurrentPage() { + if (web.queryParams().get('p')) return { type: 'preview', id: web.queryParams().get('p') }; + return { type: 'page', id: location.pathname.split(/(-|\/)/g).reverse()[0] }; } -}); + + web.addDocumentObserver((event) => { + const currentPage = getCurrentPage(); + if (currentPage.id !== _lastPage.id || currentPage.type !== _lastPage.type) { + const openAsPage = document.querySelector( + '.notion-peek-renderer [style*="height: 45px;"] a' + ); + if (openAsPage) { + if (currentPage.id === _lastPage.id && currentPage.type === 'preview') { + history.back(); + } else openAsPage.click(); + } + _lastPage = getCurrentPage(); + } + }); +} diff --git a/extension/repo/calendar-scroll@b1c7db33-dfee-489a-a76c-0dd66f7ed29a/mod.json b/extension/repo/calendar-scroll@b1c7db33-dfee-489a-a76c-0dd66f7ed29a/mod.json index f86b47b..8590126 100644 --- a/extension/repo/calendar-scroll@b1c7db33-dfee-489a-a76c-0dd66f7ed29a/mod.json +++ b/extension/repo/calendar-scroll@b1c7db33-dfee-489a-a76c-0dd66f7ed29a/mod.json @@ -13,7 +13,7 @@ } ], "js": { - "client": ["client.mjs?"] + "client": ["client.mjs"] }, "css": { "client": ["client.css"] diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/markdown.css b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/markdown.css index 1ab8d7c..2391359 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/markdown.css +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/markdown.css @@ -4,68 +4,64 @@ * (https://notion-enhancer.github.io/) under the MIT license */ -::selection { - background: var(--theme--selected); -} - -.markdown table { +.enhancer--markdown table { border-spacing: 0; - border: 1px solid var(--theme--divider); + border: 1px solid var(--theme--ui_divider); } -.markdown table th { +.enhancer--markdown table th { text-align: left; } -.markdown table th, -.markdown table td { +.enhancer--markdown table th, +.enhancer--markdown table td { padding: 5px 8px 6px; - border: 1px solid var(--theme--divider); + border: 1px solid var(--theme--ui_divider); } -.markdown h1 { - font-size: var(--theme--font_heading1-size); +.enhancer--markdown h1 { + font-size: 1.875rem; margin: 1rem 0 0.5rem 0; } -.markdown h2 { - font-size: var(--theme--font_heading2-size); +.enhancer--markdown h2 { + font-size: 1.5rem; margin: 1rem 0 0.5rem 0; } -.markdown h3 { - font-size: var(--theme--font_heading3-size); +.enhancer--markdown h3 { + font-size: 1.25rem; margin: 1rem 0 0.5rem 0; } -.markdown ul, -.markdown ol { +.enhancer--markdown ul, +.enhancer--markdown ol { padding-left: 1.25rem; } -.markdown li { +.enhancer--markdown li { margin: 0.4rem 0; } -.markdown ol li { +.enhancer--markdown ol li { padding-left: 0.25rem; } -.markdown blockquote { +.enhancer--markdown blockquote { border-left: 2px solid currentColor; padding-left: 0.75rem; margin: 0.5rem 0; } -.markdown hr { - border: 0.5px solid var(--theme--divider); +.enhancer--markdown hr { + border: 0.5px solid var(--theme--ui_divider); } -.markdown a { +.enhancer--markdown a { opacity: 0.7; text-decoration: none; - border-bottom: 0.05em solid currentColor; + border-bottom: 0.05em solid var(--theme--text_ui); } -.markdown a:hover { - opacity: 1; +.enhancer--markdown a:hover { + opacity: 0.9; } -.markdown :not(pre) > code { +.enhancer--markdown :not(pre) > code { padding: 0.2em 0.4em; border-radius: 3px; background: var(--theme--code_inline); color: var(--theme--code_inline-text); } -.markdown pre { +.enhancer--markdown pre { padding: 2em 1.25em; border-radius: 3px; tab-size: 2; @@ -74,10 +70,10 @@ background: var(--theme--code); color: var(--theme--code_plain); } -.markdown pre, -.markdown code { +.enhancer--markdown pre, +.enhancer--markdown code { font-family: var(--theme--font_code); - font-size: var(--theme--font_code-size); + font-size: 0.796875rem; text-align: left; word-spacing: normal; word-break: normal; @@ -89,7 +85,7 @@ /* * https://prismjs.com/plugins/inline-color/ */ -.inline-color-wrapper { +.enhancer--markdown .inline-color-wrapper { /* * base64 svg (https://stackoverflow.com/a/21626701/7595472 - prevents visual glitches) * @@ -108,7 +104,7 @@ border: 0.5px solid var(--theme--code_plain); overflow: hidden; } -.inline-color { +.enhancer--markdown .inline-color { display: block; height: 120%; width: 120%; @@ -117,8 +113,8 @@ /* * https://prismjs.com/plugins/match-braces/ */ -.token.punctuation.brace-hover, -.token.punctuation.brace-selected { +.enhancer--markdown .token.punctuation.brace-hover, +.enhancer--markdown .token.punctuation.brace-selected { outline: solid 1px; } @@ -126,43 +122,43 @@ * https://prismjs.com/plugins/show-language/ * https://prismjs.com/plugins/copy-to-clipboard/ */ -.code-toolbar { +.enhancer--markdown .code-toolbar { position: relative; } -.code-toolbar .toolbar-item { +.enhancer--markdown .code-toolbar .toolbar-item { position: absolute; top: 0.35rem; display: inline-block; transition: opacity 200ms ease-in-out; opacity: 0; } -.code-toolbar .toolbar-item:first-child { +.enhancer--markdown .code-toolbar .toolbar-item:first-child { left: 0.8rem; } -.code-toolbar .toolbar-item:last-child { +.enhancer--markdown .code-toolbar .toolbar-item:last-child { right: 0.8rem; } -.code-toolbar:hover .toolbar-item, -.code-toolbar:focus-within .toolbar-item { +.enhancer--markdown .code-toolbar:hover .toolbar-item, +.enhancer--markdown .code-toolbar:focus-within .toolbar-item { opacity: 1; } -.code-toolbar .toolbar-item > * { +.enhancer--markdown .code-toolbar .toolbar-item > * { padding: 0.25rem 0.35rem; - color: var(--theme--text_property); - font-size: var(--theme--font_ui_small-size); + color: var(--theme--text_ui); + font-size: 11px; font-family: inherit; } -.code-toolbar .toolbar-item .copy-to-clipboard-button { +.enhancer--markdown .code-toolbar .toolbar-item .copy-to-clipboard-button { border: none; background: none; cursor: pointer; border-radius: 3px; transition: background 100ms ease-in-out; } -.code-toolbar .toolbar-item .copy-to-clipboard-button:hover { +.enhancer--markdown .code-toolbar .toolbar-item .copy-to-clipboard-button:hover { background: var(--theme--button-hover); } -.code-toolbar .toolbar-item .copy-to-clipboard-button svg { +.enhancer--markdown .code-toolbar .toolbar-item .copy-to-clipboard-button svg { width: 1em; margin-right: 0.5em; } 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 879e49c..ecf5bee 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.css +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.css @@ -4,6 +4,10 @@ * (https://notion-enhancer.github.io/) under the MIT license */ +::selection { + background: var(--theme--accent_blue-selection); +} + ::-webkit-scrollbar { width: 10px; height: 10px; diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs index afb4a1b..149ab3a 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs @@ -9,6 +9,8 @@ // css-in-js for better component generation import { tw, apply, setup } from '../../dep/twind.mjs'; +import { content } from '../../dep/twind-content.mjs'; +const pseudoContent = content('""'); const mapColorVariables = (color) => ({ 'text': `var(--theme--text_${color})`, @@ -38,13 +40,16 @@ setup({ 'secondary': 'var(--theme--bg_secondary)', 'popup': 'var(--theme--bg_popup)', 'divider': 'var(--theme--ui_divider)', + 'input': 'var(--theme--ui_input)', }, 'icon': 'var(--theme--icon)', - 'icon_ui': 'var(--theme--icon_ui)', + 'icon-ui': 'var(--theme--icon_ui)', 'foreground': 'var(--theme--text)', - 'foreground_ui': 'var(--theme--text_ui)', + 'foreground-ui': 'var(--theme--text_ui)', 'interactive': 'var(--theme--ui_interactive)', 'interactive-hover': 'var(--theme--ui_interactive-hover)', + 'tag': 'var(--theme--tag_default)', + 'tag-text': 'var(--theme--tag_default-text)', 'toggle': { 'on': 'var(--theme--ui_toggle-on)', 'off': 'var(--theme--ui_toggle-off)', @@ -52,9 +57,12 @@ setup({ }, 'accent': { 'blue': 'var(--theme--accent_blue)', - 'blue-contrast': 'var(--theme--accent_blue-text)', + 'blue-hover': 'var(--theme--accent_blue-hover)', + 'blue-active': 'var(--theme--accent_blue-active)', + 'blue-text': 'var(--theme--accent_blue-text)', 'red': 'var(--theme--accent_red)', - 'red-contrast': 'var(--theme--accent_red-text)', + 'red-hover': 'var(--theme--accent_red-hover)', + 'red-text': 'var(--theme--accent_red-text)', }, 'grey': mapColorVariables('grey'), 'brown': mapColorVariables('brown'), @@ -66,6 +74,11 @@ setup({ 'pink': mapColorVariables('pink'), 'red': mapColorVariables('red'), }, + extend: { + maxHeight: { + 'full-16': 'calc(100% - 4rem)', + }, + }, }, }); @@ -73,8 +86,9 @@ setup({ import * as api from '../../api/_.mjs'; import { render } from '../../api/web.mjs'; -const { env, fs, registry, web } = api, - db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'); +const { env, fmt, fs, registry, storage, web } = api, + db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'), + profile = await storage.get(['currentprofile'], 'default'); web.addHotkeyListener(await db.get(['hotkey']), env.focusNotion); @@ -102,25 +116,29 @@ const notifications = { const style = tw`p-2 ${ color ? `bg-${color}-tag text-${color}-tag-text border border-${color}-text hover:bg-${color}-text` - : 'bg-notion-popup text-foreground hover:bg-interactive-hover border border-notion-divider' + : 'bg-tag text-tag-text hover:bg-interactive-hover border border-notion-divider' } flex items-center rounded-full mt-3 shadow-md cursor-pointer`, $notification = web.render( link ? web.html`` - : web.html``, + : web.html``, web.html` ${message} `, web.html`${web.icon(icon, { class: tw`fill-current opacity-75 h-4 w-4 mx-2` })}` - ); - $notification.addEventListener('click', async () => { - if (id !== undefined) { - notifications.cache.push(id); - await db.set(['notifications'], notifications.cache); - } - $notification.remove(); + ), + resolve = async () => { + if (id !== undefined) { + notifications.cache.push(id); + await db.set(['notifications'], notifications.cache); + } + $notification.remove(); + }; + $notification.addEventListener('click', resolve); + $notification.addEventListener('keyup', (event) => { + if (['Enter', ' '].includes(event.key)) resolve(); }); web.render(notifications.$container, $notification); }, @@ -149,10 +167,10 @@ if (errors.length) { // mod config -const $container = web.html`
`, - $nav = web.html``, - $main = web.html`
abc
`, - $footer = web.html`
`, +const $container = web.html`
`, + $nav = web.html``, + $main = web.html`
abc
`, + // $footer = web.html`
`, $sidebar = web.html`
`; const $notion = web.html`

@@ -167,7 +185,8 @@ $notion.children[0].addEventListener('click', env.focusNotion); const navItemStyle = tw`px-3 py-2 rounded-md text-sm font-medium bg-interactive hover:bg-interactive-hover`, selectedNavItemStyle = tw`px-3 py-2 rounded-md text-sm font-medium ring-1 ring-notion-divider bg-notion-secondary`; -const $extensionsNavItem = web.html`extensions`, +const $coreNavItem = web.html`core`, + $extensionsNavItem = web.html`extensions`, $themesNavItem = web.html`themes`, $supportNavItem = web.html`support`; @@ -176,27 +195,177 @@ web.render( web.render( $container, web.render( - web.html`
`, - web.render($nav, $notion, $extensionsNavItem, $themesNavItem, $supportNavItem), - $main, - $footer + web.html`
`, + web.render( + $nav, + $notion, + $coreNavItem, + $extensionsNavItem, + $themesNavItem, + $supportNavItem + ), + $main + // $footer ), $sidebar ) ); +const components = { + preview: (url) => web.html``, + title: (title) => { + const style = tw`mb-2 text-xl font-semibold tracking-tight flex items-center`; + return web.html`

${web.escape(title)}

`; + }, + version: (version) => { + const style = tw`mt-px ml-3 p-1 font-normal text-xs leading-none bg-tag text-tag-text rounded`; + return web.html`v${web.escape(version)}`; + }, + tags: (tags) => { + if (!tags.length) return ''; + return web.render( + web.html`

`, + tags.map((tag) => `#${web.escape(tag)}`).join(' ') + ); + }, + description: (description) => { + return web.html`

+ ${fmt.md.renderInline(description)} +

`; + }, + authors: (authors) => { + const author = (author) => web.html` + ${web.escape(author.name)}'s avatar ${web.escape(author.name)} + `; + return web.render( + web.html`

`, + ...authors.map(author) + ); + }, + toggle: ( + checked, + { + customLabelStyle = '', + customCheckStyle = '', + customBoxStyle = '', + customFeatureStyle = '', + } + ) => { + const checkStyle = tw`appearance-none checked:sibling:(bg-toggle-on after::translate-x-4) ${customCheckStyle}`, + boxStyle = tw`w-9 h-5 p-0.5 flex items-center bg-toggle-off rounded-full duration-300 ease-in-out ${customBoxStyle}`, + featureStyle = tw`after::(${pseudoContent} w-4 h-4 bg-toggle-feature rounded-full duration-300) ${customFeatureStyle}`, + $label = web.html``, + $input = web.html``; + $label.addEventListener('keyup', (event) => { + if (['Enter', ' '].includes(event.key)) { + $input.checked = !$input.checked; + } + }); + return web.render( + $label, + $input, + web.html`` + ); + }, +}; + +components.mod = async (mod) => { + const $toggle = components.toggle(await registry.enabled(mod.id), { + customLabelStyle: 'flex w-full mt-auto', + customCheckStyle: 'ml-auto', + }); + $toggle.addEventListener('change', (event) => { + storage.set(['profiles', profile, '_mods', mod.id], event.target.checked); + }); + const style = tw`relative h-full w-full flex flex-col overflow-hidden rounded-lg shadow-lg + bg-notion-secondary border border-notion-divider`; + return web.render( + web.html`
`, + web.render( + web.html`
`, + mod.preview + ? components.preview( + mod.preview.startsWith('http') + ? mod.preview + : fs.localPath(`repo/${mod._dir}/${mod.preview}`) + ) + : '', + web.render( + web.html`
`, + web.render(components.title(mod.name), components.version(mod.version)), + components.tags(mod.tags), + components.description(mod.description), + components.authors(mod.authors), + mod.environments.includes(env.name) && !registry.core.includes(mod.id) ? $toggle : '' + ) + ) + ); +}; + +components.modList = async (category) => { + const $search = web.html``, + $list = web.html`
`, + mods = await registry.list( + (mod) => mod.environments.includes(env.name) && mod.tags.includes(category) + ); + web.addHotkeyListener(['/'], () => $search.focus()); + $search.addEventListener('input', (event) => { + const query = $search.value.toLowerCase(), + hiddenStyle = tw`hidden`; + for (const $mod of $list.children) { + const matches = !query || $mod.innerText.toLowerCase().includes(query); + $mod.classList[matches ? 'remove' : 'add'](hiddenStyle); + } + }); + for (const mod of mods) { + mod.tags = mod.tags.filter((tag) => tag !== category); + web.render($list, await components.mod(mod)); + mod.tags.unshift(category); + } + return web.render( + web.html`
`, + web.render(web.html`
`, $search), + $list + ); +}; + import * as router from './router.mjs'; -router.addView('extensions', () => { +router.addView('core', async () => { + $extensionsNavItem.className = navItemStyle; + $themesNavItem.className = navItemStyle; + $coreNavItem.className = selectedNavItemStyle; + web.empty($main); + return web.render($main, await components.modList('core')); +}); + +router.addView('extensions', async () => { + $coreNavItem.className = navItemStyle; $themesNavItem.className = navItemStyle; $extensionsNavItem.className = selectedNavItemStyle; web.empty($main); - web.render($main, 123); + return web.render($main, await components.modList('extension')); }); -router.addView('themes', () => { + +router.addView('themes', async () => { + $coreNavItem.className = navItemStyle; $extensionsNavItem.className = navItemStyle; $themesNavItem.className = selectedNavItemStyle; web.empty($main); - web.render($main, 456); + return web.render($main, await components.modList('theme')); }); -router.listen('extensions', $main); + +router.loadView('extensions', $main); 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 e9248c7..a7ac01b 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/mod.json +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/mod.json @@ -14,7 +14,7 @@ ], "css": { "client": ["client.css"], - "menu": ["menu.css"] + "menu": ["menu.css", "markdown.css"] }, "js": { "client": ["client.mjs"] diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.mjs index 2575fb9..110608b 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.mjs @@ -8,8 +8,6 @@ import { web } from '../../api/_.mjs'; -export const queryParams = () => new URLSearchParams(window.location.search); - let _defaultView = '', $viewRoot; const _views = new Map(); @@ -26,7 +24,7 @@ function router(event) { const anchor = event.path.find((anchor) => anchor.nodeName === 'A'); if (location.search !== anchor.getAttribute('href')) { window.history.pushState(null, null, anchor.href); - listen(); + loadView(); } } function navigator(event) { @@ -38,33 +36,24 @@ function navigator(event) { history.replaceState({ search: location.search, hash }, null, `#${hash}`); } -export async function listen(defaultView = null, $elem = null) { +export async function loadView(defaultView = null) { if (defaultView) _defaultView = defaultView; - if ($elem) $viewRoot = $elem; - if (!$viewRoot) throw new Error('no view root set.'); if (!_defaultView) throw new Error('no view root set.'); - const query = queryParams(), + const query = web.queryParams(), fallbackView = () => { window.history.replaceState(null, null, `?view=${_defaultView}`); - return listen(); + return loadView(); }; if (!query.get('view') || document.body.dataset.view !== query.get('view')) { if (_views.get(query.get('view'))) { - $viewRoot.style.opacity = 0; - const loadFunc = _views.get(query.get('view'))(); - setTimeout(async () => { - await loadFunc; - requestAnimationFrame(() => { - $viewRoot.style.opacity = ''; - }); - }, 200); + await _views.get(query.get('view'))(); } else return fallbackView(); } else return fallbackView(); } window.addEventListener('popstate', (event) => { - if (event.state) listen(); + if (event.state) loadView(); document.getElementById(location.hash.slice(1))?.scrollIntoView(true); document.documentElement.scrollTop = 0; });