diff --git a/extension/api/_.mjs b/extension/api/_.mjs index 9f48011..bd0e39d 100644 --- a/extension/api/_.mjs +++ b/extension/api/_.mjs @@ -10,14 +10,15 @@ /** environment-specific methods and constants */ export * as env from './env.mjs'; +/** environment-specific data persistence */ +export * as storage from './storage.mjs'; + /** helpers for formatting or parsing text */ export * as fmt from './fmt.mjs'; /** environment-specific filesystem reading */ export * as fs from './fs.mjs'; /** interactions with the enhancer's repository of mods */ export * as registry from './registry.mjs'; -/** environment-specific data persistence */ -export * as storage from './storage.mjs'; /** pattern and type validators */ export * as validation from './validation.mjs'; /** helpers for manipulation of a webpage */ diff --git a/extension/api/env.mjs b/extension/api/env.mjs index 43297fc..db7be08 100644 --- a/extension/api/env.mjs +++ b/extension/api/env.mjs @@ -30,8 +30,7 @@ export const supported = ['linux', 'win32', 'darwin', 'extension']; export const version = chrome.runtime.getManifest().version; /** open the enhancer's menu */ -export const openEnhancerMenu = () => - chrome.runtime.sendMessage({ action: 'openEnhancerMenu' }); +export const focusMenu = () => chrome.runtime.sendMessage({ action: 'focusMenu' }); /** focus an active notion tab */ export const focusNotion = () => chrome.runtime.sendMessage({ action: 'focusNotion' }); diff --git a/extension/api/fmt.mjs b/extension/api/fmt.mjs index 743cc6b..10d5092 100644 --- a/extension/api/fmt.mjs +++ b/extension/api/fmt.mjs @@ -11,6 +11,8 @@ * @module notion-enhancer/api/fmt */ +import * as web from './web.mjs'; + import '../dep/prism.min.js'; /** syntax highlighting using https://prismjs.com/ */ export const prism = Prism; @@ -19,7 +21,7 @@ Prism.hooks.add('complete', async (event) => { event.element.parentElement.removeAttribute('tabindex'); event.element.parentElement.parentElement .querySelector('.copy-to-clipboard-button') - .prepend(web.createElement(await web.getIcon('fa/regular/copy'))); + .prepend(web.html`${await web.icon('clipboard')}`); }); import '../dep/markdown-it.min.js'; @@ -27,7 +29,7 @@ import '../dep/markdown-it.min.js'; export const md = new markdownit({ linkify: true, highlight: (str, lang) => - web.html`
${web.escapeHtml(
+    web.html`
${web.escape(
       str
     )}
`, }); @@ -36,7 +38,7 @@ md.renderer.rules.code_block = (tokens, idx, options, env, slf) => { if (attrIdx === -1) { tokens[idx].attrPush(['class', 'match-braces language-plaintext']); } else tokens[idx].attrs[attrIdx][1] = 'match-braces language-plaintext'; - return web.html`${web.escapeHtml( + return web.html`${web.escape( tokens[idx].content )}
\n`; }; @@ -47,7 +49,7 @@ md.core.ruler.push( state.tokens.forEach(function (token, i) { if (token.type === 'heading_open') { const text = md.renderer.render(state.tokens[i + 1].children, md.options), - slug = fmt.slugger(text, slugs); + slug = slugger(text, slugs); slugs.add(slug); const attrIdx = token.attrIndex('id'); if (attrIdx === -1) { diff --git a/extension/api/registry.mjs b/extension/api/registry.mjs index 2a6215a..910d4ae 100644 --- a/extension/api/registry.mjs +++ b/extension/api/registry.mjs @@ -242,8 +242,7 @@ 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 (e) { - console.log(e); + } catch { _errors.push({ source: dir, message: 'invalid mod.json' }); } } diff --git a/extension/api/web.mjs b/extension/api/web.mjs index dd476f7..c4ab25c 100644 --- a/extension/api/web.mjs +++ b/extension/api/web.mjs @@ -18,7 +18,7 @@ const _hotkeyEventListeners = [], _documentObserverListeners = [], _documentObserverEvents = []; -let _$featherStylesheet, _$tooltip, _$tooltipStylesheet, _hotkeyEvent, _documentObserver; +let _$tooltip, _$tooltipStylesheet, _hotkeyEvent, _documentObserver; import '../dep/jscolor.min.js'; /** color picker with alpha channel using https://jscolor.com/ */ @@ -152,22 +152,13 @@ export const loadStylesheet = (path) => { * @returns {string} an svg string */ export const icon = (name, attrs = {}) => { - if (!_$featherStylesheet) { - _$featherStylesheet = html``; - render(document.head, _$featherStylesheet); - } - attrs.class = ((attrs.class || '') + ' enhancer--feather').trim(); - return ` `${escape(key)}="${escape(val)}"` - )}>`; + attrs.style = ( + (attrs.style || '') + + ';stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill:none;' + ).trim(); + return ` `${escape(key)}="${escape(val)}"`) + .join(' ')}>`; }; /** @@ -177,9 +168,9 @@ export const icon = (name, attrs = {}) => { */ export const tooltip = ($ref, text) => { if (!_$tooltip) { - _$tooltip = html`
`; - _$tooltipStylesheet = html``; diff --git a/extension/launcher.js b/extension/launcher.js index b307801..698e5ce 100644 --- a/extension/launcher.js +++ b/extension/launcher.js @@ -6,11 +6,12 @@ 'use strict'; -// only load if user is logged into notion and viewing a page -if ( - localStorage['LRU:KeyValueStore2:current-user-id'] && - location.pathname.split(/[/-]/g).reverse()[0].length === 32 -) { +const site = location.host.endsWith('.notion.site'), + loggedIn = localStorage['LRU:KeyValueStore2:current-user-id'], + page = location.pathname.split(/[/-]/g).reverse()[0].length === 32, + home = location.pathname === '/'; + +if (site || (loggedIn && (page || home))) { import(chrome.runtime.getURL('api/_.mjs')).then(async (api) => { const { registry, web } = api; for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { diff --git a/extension/manifest.json b/extension/manifest.json index 9b3101a..b57f399 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -19,15 +19,15 @@ "web_accessible_resources": [ { "resources": ["api/*", "dep/*", "icon/*", "repo/*"], - "matches": ["https://*.notion.so/*"] + "matches": ["https://*.notion.so/*", "https://*.notion.site/*"] } ], "content_scripts": [ { - "matches": ["https://*.notion.so/*"], + "matches": ["https://*.notion.so/*", "https://*.notion.site/*"], "js": ["launcher.js"] } ], "permissions": ["tabs", "storage", "unlimitedStorage"], - "host_permissions": ["https://*.notion.so/*", ""] + "host_permissions": ["https://*.notion.so/*", "https://*.notion.site/*", ""] } 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 8590126..1e38790 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 @@ -2,7 +2,7 @@ "name": "calendar-scroll", "id": "b1c7db33-dfee-489a-a76c-0dd66f7ed29a", "version": "0.2.0", - "description": "add a button to scroll down to the current week in fullpage/infinite-scroll calendars.", + "description": "add a button to jump down to the current week in fullpage/infinite-scroll calendars.", "tags": ["extension", "shortcut"], "authors": [ { diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/client.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/client.mjs index faf50f7..d7b15db 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/client.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/client.mjs @@ -7,8 +7,18 @@ 'use strict'; export default async function (api, db) { - const { env, fs, registry, web } = api, - sidebarSelector = '.notion-sidebar-container .notion-sidebar > div:nth-child(4)'; + const { env, fs, registry, web } = api; + + web.addHotkeyListener(await db.get(['hotkey']), env.focusMenu); + + const updateTheme = () => + db.set(['theme'], document.querySelector('.notion-dark-theme') ? 'dark' : 'light'); + web.addDocumentObserver((mutation) => { + if (mutation.target === document.body) updateTheme(); + }); + updateTheme(); + + const sidebarSelector = '.notion-sidebar-container .notion-sidebar > div:nth-child(4)'; await web.whenReady([sidebarSelector]); const $sidebarLink = web.html` `; - $sidebarLink.addEventListener('click', env.openEnhancerMenu); - web.addHotkeyListener(await db.get(['hotkey']), env.openEnhancerMenu); - - const updateTheme = () => - db.set(['theme'], document.querySelector('.notion-dark-theme') ? 'dark' : 'light'); - web.addDocumentObserver((mutation) => { - if (mutation.target === document.body) updateTheme(); - }); - updateTheme(); + $sidebarLink.addEventListener('click', env.focusMenu); const notifications = { cache: await db.get(['notifications'], []), 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 2391359..b2247b7 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/markdown.css +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/markdown.css @@ -4,64 +4,65 @@ * (https://notion-enhancer.github.io/) under the MIT license */ -.enhancer--markdown table { +.markdown table { border-spacing: 0; border: 1px solid var(--theme--ui_divider); } -.enhancer--markdown table th { +.markdown table th { text-align: left; } -.enhancer--markdown table th, -.enhancer--markdown table td { +.markdown table th, +.markdown table td { padding: 5px 8px 6px; border: 1px solid var(--theme--ui_divider); } -.enhancer--markdown h1 { +.markdown h1 { font-size: 1.875rem; margin: 1rem 0 0.5rem 0; } -.enhancer--markdown h2 { +.markdown h2 { font-size: 1.5rem; margin: 1rem 0 0.5rem 0; } -.enhancer--markdown h3 { +.markdown h3 { font-size: 1.25rem; margin: 1rem 0 0.5rem 0; } -.enhancer--markdown ul, -.enhancer--markdown ol { +.markdown ul, +.markdown ol { padding-left: 1.25rem; } -.enhancer--markdown li { +.markdown li { margin: 0.4rem 0; } -.enhancer--markdown ol li { +.markdown ol li { padding-left: 0.25rem; } -.enhancer--markdown blockquote { +.markdown blockquote { border-left: 2px solid currentColor; padding-left: 0.75rem; margin: 0.5rem 0; } -.enhancer--markdown hr { +.markdown hr { border: 0.5px solid var(--theme--ui_divider); } -.enhancer--markdown a { +.markdown.markdown-inline a { opacity: 0.7; text-decoration: none; border-bottom: 0.05em solid var(--theme--text_ui); } -.enhancer--markdown a:hover { +.markdown.markdown-inline a:hover { opacity: 0.9; } -.enhancer--markdown :not(pre) > code { +.markdown :not(pre) > code, +.markdown-inline code { padding: 0.2em 0.4em; border-radius: 3px; background: var(--theme--code_inline); color: var(--theme--code_inline-text); } -.enhancer--markdown pre { +.markdown pre { padding: 2em 1.25em; border-radius: 3px; tab-size: 2; @@ -70,8 +71,8 @@ background: var(--theme--code); color: var(--theme--code_plain); } -.enhancer--markdown pre, -.enhancer--markdown code { +.markdown pre, +.markdown.markdown-inline code { font-family: var(--theme--font_code); font-size: 0.796875rem; text-align: left; @@ -85,7 +86,7 @@ /* * https://prismjs.com/plugins/inline-color/ */ -.enhancer--markdown .inline-color-wrapper { +.markdown .inline-color-wrapper { /* * base64 svg (https://stackoverflow.com/a/21626701/7595472 - prevents visual glitches) * @@ -104,7 +105,7 @@ border: 0.5px solid var(--theme--code_plain); overflow: hidden; } -.enhancer--markdown .inline-color { +.markdown .inline-color { display: block; height: 120%; width: 120%; @@ -113,8 +114,8 @@ /* * https://prismjs.com/plugins/match-braces/ */ -.enhancer--markdown .token.punctuation.brace-hover, -.enhancer--markdown .token.punctuation.brace-selected { +.markdown .token.punctuation.brace-hover, +.markdown .token.punctuation.brace-selected { outline: solid 1px; } @@ -122,43 +123,43 @@ * https://prismjs.com/plugins/show-language/ * https://prismjs.com/plugins/copy-to-clipboard/ */ -.enhancer--markdown .code-toolbar { +.markdown .code-toolbar { position: relative; } -.enhancer--markdown .code-toolbar .toolbar-item { +.markdown .code-toolbar .toolbar-item { position: absolute; top: 0.35rem; display: inline-block; transition: opacity 200ms ease-in-out; opacity: 0; } -.enhancer--markdown .code-toolbar .toolbar-item:first-child { +.markdown .code-toolbar .toolbar-item:first-child { left: 0.8rem; } -.enhancer--markdown .code-toolbar .toolbar-item:last-child { +.markdown .code-toolbar .toolbar-item:last-child { right: 0.8rem; } -.enhancer--markdown .code-toolbar:hover .toolbar-item, -.enhancer--markdown .code-toolbar:focus-within .toolbar-item { +.markdown .code-toolbar:hover .toolbar-item, +.markdown .code-toolbar:focus-within .toolbar-item { opacity: 1; } -.enhancer--markdown .code-toolbar .toolbar-item > * { +.markdown .code-toolbar .toolbar-item > * { padding: 0.25rem 0.35rem; color: var(--theme--text_ui); font-size: 11px; font-family: inherit; } -.enhancer--markdown .code-toolbar .toolbar-item .copy-to-clipboard-button { +.markdown .code-toolbar .toolbar-item .copy-to-clipboard-button { border: none; background: none; cursor: pointer; border-radius: 3px; transition: background 100ms ease-in-out; } -.enhancer--markdown .code-toolbar .toolbar-item .copy-to-clipboard-button:hover { +.markdown .code-toolbar .toolbar-item .copy-to-clipboard-button:hover { background: var(--theme--button-hover); } -.enhancer--markdown .code-toolbar .toolbar-item .copy-to-clipboard-button svg { +.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.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs index 4f6bd32..873f44d 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs @@ -6,94 +6,14 @@ 'use strict'; -// 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})`, - 'highlight': `var(--theme--highlight_${color})`, - 'highlight-text': `var(--theme--highlight_${color}-text)`, - 'block': `var(--theme--block_${color})`, - 'block-text': `var(--theme--block_${color}-text)`, - 'tag': `var(--theme--tag_${color})`, - 'tag-text': `var(--theme--tag_${color}-text)`, - 'callout': `var(--theme--callout_${color})`, - 'callout-text': `var(--theme--callout_${color}-text)`, -}); - -setup({ - preflight: { - html: apply`w-full h-full`, - body: apply`w-full h-full bg-notion-bg font-sans text-foreground`, - }, - theme: { - fontFamily: { - sans: ['var(--theme--font_sans)'], - mono: ['var(--theme--font_mono)'], - }, - colors: { - 'notion': { - 'bg': 'var(--theme--bg)', - '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)', - 'foreground': 'var(--theme--text)', - '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)', - 'feature': 'var(--theme--ui_toggle-feature)', - }, - 'accent': { - 'blue': 'var(--theme--accent_blue)', - '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-hover': 'var(--theme--accent_red-hover)', - 'red-text': 'var(--theme--accent_red-text)', - }, - 'grey': mapColorVariables('grey'), - 'brown': mapColorVariables('brown'), - 'orange': mapColorVariables('orange'), - 'yellow': mapColorVariables('yellow'), - 'green': mapColorVariables('green'), - 'blue': mapColorVariables('blue'), - 'purple': mapColorVariables('purple'), - 'pink': mapColorVariables('pink'), - 'red': mapColorVariables('red'), - }, - extend: { - width: { - 'full-96': 'calc(100% - 24rem)', - }, - maxHeight: { - 'full-16': 'calc(100% - 4rem)', - 'full-32': 'calc(100% - 8rem)', - 'full-48': 'calc(100% - 12rem)', - }, - }, - }, -}); - // initialisation and external interactions import * as api from '../../api/_.mjs'; -import { render } from '../../api/web.mjs'; -const { env, fmt, fs, registry, storage, web } = api, +const { env, fmt, fs, registry, web } = api, db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'); +import { tw } from './styles.mjs'; + web.addHotkeyListener(await db.get(['hotkey']), env.focusNotion); for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { @@ -110,29 +30,25 @@ document.addEventListener('visibilitychange', loadTheme); loadTheme(); const notifications = { - $container: web.html`
`, + $container: web.html`
`, cache: await db.get(['notifications'], []), provider: [ env.welcomeNotification, ...(await fs.getJSON('https://notion-enhancer.github.io/notifications.json')), ], add({ icon, message, id = undefined, color = undefined, link = undefined }) { - const style = tw`p-2 ${ - color - ? `bg-${color}-tag text-${color}-tag-text border border-${color}-text hover:bg-${color}-text` - : '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` - ${message} - `, - web.html`${web.icon(icon, { class: tw`fill-current opacity-75 h-4 w-4 mx-2` })}` - ), + const $notification = link + ? web.html`` + : web.html``, resolve = async () => { if (id !== undefined) { notifications.cache.push(id); @@ -144,7 +60,16 @@ const notifications = { $notification.addEventListener('keyup', (event) => { if (['Enter', ' '].includes(event.key)) resolve(); }); - web.render(notifications.$container, $notification); + web.render( + notifications.$container, + web.render( + $notification, + web.html` + ${fmt.md.renderInline(message)} + `, + web.html`${web.icon(icon, { class: 'notification-icon' })}` + ) + ); return $notification; }, _changes: false, @@ -158,7 +83,7 @@ const notifications = { $notification.addEventListener('click', env.reload); }, }; -render(document.body, notifications.$container); +web.render(document.body, notifications.$container); for (const notification of notifications.provider) { if ( !notifications.cache.includes(notification.id) && @@ -182,135 +107,59 @@ if (errors.length) { // mod config -const $container = web.html`
`, - $nav = web.html``, - $main = web.html`
abc
`, - // $footer = web.html`
`, - $sidebar = web.html`
`; - -const notionNavStyle = tw`flex items-center font-semibold text-xl cursor-pointer select-none mr-4 - ml-4 sm:mb-4 md:w-full lg:(w-auto ml-0 mb-0)`, - $notion = web.html`

- ${(await fs.getText('icon/colour.svg')).replace( - /width="\d+" height="\d+"/, - `class="${tw`h-12 w-12 mr-5 sm:(h-6 w-6 mr-3)`}"` - )} - notion-enhancer -

`; -$notion.children[0].addEventListener('click', env.focusNotion); - -const navItemStyle = tw`ml-4 px-3 py-2 rounded-md text-sm font-medium bg-interactive hover:bg-interactive-hover`, - selectedNavItemStyle = tw`ml-4 px-3 py-2 rounded-md text-sm font-medium ring-1 ring-notion-divider bg-notion-secondary`; - -const $coreNavItem = web.html`core`, - $extensionsNavItem = web.html`extensions`, - $themesNavItem = web.html`themes`, - $supportNavItem = web.html`support`; - -web.render( - document.body, - web.render( - $container, - web.render( - 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)}`; - }, + title: (title) => web.html`

${web.escape(title)}

`, + version: (version) => web.html`v${web.escape(version)}`, tags: (tags) => { if (!tags.length) return ''; return web.render( - web.html`

`, + web.html`

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

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

`; - }, + description: (description) => web.html`

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

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

`, - ...authors.map(author) - ); + 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``; + toggle: (checked) => { + const $label = web.html``, + $input = web.html``; $label.addEventListener('keyup', (event) => { - if (['Enter', ' '].includes(event.key)) { - $input.checked = !$input.checked; - } + if (['Enter', ' '].includes(event.key)) $input.checked = !$input.checked; }); return web.render( $label, $input, - web.html`` + web.html`` ); }, }; components.mod = async (mod) => { - const $toggle = components.toggle(await registry.enabled(mod.id), { - customLabelStyle: 'flex w-full mt-auto', - customCheckStyle: 'ml-auto', - }); + const $toggle = components.toggle(await registry.enabled(mod.id)); $toggle.addEventListener('change', (event) => { registry.profile.set(['_mods', mod.id], event.target.checked); notifications.changes(); }); - 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.html`
`, web.render( - web.html`
`, + web.html`
`, mod.preview ? components.preview( mod.preview.startsWith('http') @@ -319,7 +168,7 @@ components.mod = async (mod) => { ) : '', web.render( - web.html`
`, + web.html`
`, web.render(components.title(mod.name), components.version(mod.version)), components.tags(mod.tags), components.description(mod.description), @@ -331,21 +180,18 @@ components.mod = async (mod) => { }; components.modList = async (category) => { - const $search = web.html``, - $list = 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`; + const query = $search.value.toLowerCase(); for (const $mod of $list.children) { const matches = !query || $mod.innerText.toLowerCase().includes(query); - $mod.classList[matches ? 'remove' : 'add'](hiddenStyle); + $mod.classList[matches ? 'remove' : 'add']('hidden'); } }); for (const mod of mods) { @@ -355,34 +201,72 @@ components.modList = async (category) => { } return web.render( web.html`
`, - web.render(web.html`
`, $search), + web.render(web.html`
`, $search), $list ); }; +const $main = web.html`
`, + $sidebar = web.html``; + +const $notionNavItem = web.html`

+ ${(await fs.getText('icon/colour.svg')).replace( + /width="\d+" height="\d+"/, + `class="nav-notion-icon"` + )} + notion-enhancer +

`; +$notionNavItem.children[0].addEventListener('click', env.focusNotion); + +const $coreNavItem = web.html`core`, + $extensionsNavItem = web.html`extensions`, + $themesNavItem = web.html`themes`, + $supportNavItem = web.html`support`; + +web.render( + document.body, + web.render( + web.html`
`, + web.render( + web.html`
`, + web.render( + web.html``, + $notionNavItem, + $coreNavItem, + $extensionsNavItem, + $themesNavItem, + $supportNavItem + ), + $main + ), + $sidebar + ) +); + +function selectNavItem($item) { + for (const $selected of document.querySelectorAll('.nav-item-selected')) { + $selected.className = 'nav-item'; + } + $item.className = 'nav-item-selected'; +} + import * as router from './router.mjs'; router.addView('core', async () => { - $extensionsNavItem.className = navItemStyle; - $themesNavItem.className = navItemStyle; - $coreNavItem.className = selectedNavItemStyle; web.empty($main); + selectNavItem($coreNavItem); return web.render($main, await components.modList('core')); }); router.addView('extensions', async () => { - $coreNavItem.className = navItemStyle; - $themesNavItem.className = navItemStyle; - $extensionsNavItem.className = selectedNavItemStyle; web.empty($main); + selectNavItem($extensionsNavItem); return web.render($main, await components.modList('extension')); }); router.addView('themes', async () => { - $coreNavItem.className = navItemStyle; - $extensionsNavItem.className = navItemStyle; - $themesNavItem.className = selectedNavItemStyle; web.empty($main); + selectNavItem($themesNavItem); return web.render($main, await components.modList('theme')); }); diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/styles.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/styles.mjs new file mode 100644 index 0000000..2160aa2 --- /dev/null +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/styles.mjs @@ -0,0 +1,137 @@ +/* + * notion-enhancer core: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +// 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})`, + 'highlight': `var(--theme--highlight_${color})`, + 'highlight-text': `var(--theme--highlight_${color}-text)`, + 'block': `var(--theme--block_${color})`, + 'block-text': `var(--theme--block_${color}-text)`, + 'tag': `var(--theme--tag_${color})`, + 'tag-text': `var(--theme--tag_${color}-text)`, + 'callout': `var(--theme--callout_${color})`, + 'callout-text': `var(--theme--callout_${color}-text)`, +}); + +const customClasses = { + 'notifications-container': apply`absolute bottom-0 right-0 px-4 py-3 max-w-full w-96`, + 'notification': ([color = 'default']) => + apply`p-2 ${ + color === 'default' + ? 'bg-tag text-tag-text hover:bg-interactive-hover border border-notion-divider' + : `bg-${color}-tag text-${color}-tag-text border border-${color}-text hover:bg-${color}-text` + } flex items-center rounded-full mt-3 shadow-md cursor-pointer`, + 'notification-text': apply`font-semibold mx-2 flex-auto`, + 'notification-icon': apply`fill-current opacity-75 h-4 w-4 mx-2`, + 'body-container': apply`flex w-full h-full overflow-hidden`, + 'content-container': apply`h-full w-full-96`, + 'nav': apply`px-4 py-3 flex flex-wrap items-center border-b border-notion-divider h-48 sm:h-32 lg:h-16`, + 'nav-notion': apply`flex items-center font-semibold text-xl cursor-pointer select-none mr-4 + ml-4 sm:mb-4 md:w-full lg:(w-auto ml-0 mb-0)`, + 'nav-notion-icon': apply`h-12 w-12 mr-5 sm:(h-6 w-6 mr-3)`, + 'nav-item': apply`ml-4 px-3 py-2 rounded-md text-sm font-medium bg-interactive hover:bg-interactive-hover`, + 'nav-item-selected': apply`ml-4 px-3 py-2 rounded-md text-sm font-medium ring-1 ring-notion-divider bg-notion-secondary`, + 'main': apply`transition px-4 py-3 overflow-y-auto max-h-full-48 sm:max-h-full-32 lg:max-h-full-16`, + 'sidebar': apply`h-full w-96 bg-notion-secondary border-l border-notion-divider`, + 'mods-list': apply`flex flex-wrap`, + 'mod-container': apply`w-full md:w-1/2 lg:w-1/3 xl:w-1/4 2xl:w-1/5 px-2.5 py-2.5 box-border`, + 'mod': apply`relative h-full w-full flex flex-col overflow-hidden rounded-lg shadow-lg + bg-notion-secondary border border-notion-divider`, + 'mod-body': apply`px-4 py-3 flex flex-col flex-auto`, + 'mod-preview': apply`object-cover w-full h-32`, + 'mod-title': apply`mb-2 text-xl font-semibold tracking-tight flex items-center`, + 'mod-version': apply`mt-px ml-3 p-1 font-normal text-xs leading-none bg-tag text-tag-text rounded`, + 'mod-tags': apply`text-foreground-ui mb-2 text-xs`, + 'mod-description': apply`mb-2 text-sm`, + 'mod-authors-container': apply`text-sm font-medium`, + 'mod-author': apply`flex items-center mb-2`, + 'mod-author-avatar': apply`inline object-cover w-5 h-5 rounded-full mr-2`, + 'toggle-box': apply`w-9 h-5 p-0.5 flex items-center bg-toggle-off rounded-full duration-300 ease-in-out`, + 'toggle-label': apply`relative text-sm`, + 'toggle-label-full': apply`relative text-sm flex w-full mt-auto`, + 'toggle-check': apply`appearance-none checked:sibling:(bg-toggle-on after::translate-x-4)`, + 'toggle-check-right': apply`appearance-none ml-auto checked:sibling:(bg-toggle-on after::translate-x-4)`, + 'toggle-feature': apply`after::(${pseudoContent} w-4 h-4 bg-toggle-feature rounded-full duration-300)`, + 'search-container': apply`mx-2.5 my-2.5`, + 'search': apply`transition block w-full px-3 py-2 text-sm rounded-md flex bg-notion-input text-foreground + hover:(ring ring-accent-blue-hover) focus:(outline-none ring ring-accent-blue-active)`, +}; + +setup({ + preflight: { + html: apply`w-full h-full`, + body: apply`w-full h-full bg-notion-bg font-sans text-foreground`, + }, + theme: { + fontFamily: { + sans: ['var(--theme--font_sans)'], + mono: ['var(--theme--font_mono)'], + }, + colors: { + 'notion': { + 'bg': 'var(--theme--bg)', + '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)', + 'foreground': 'var(--theme--text)', + '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)', + 'feature': 'var(--theme--ui_toggle-feature)', + }, + 'accent': { + 'blue': 'var(--theme--accent_blue)', + '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-hover': 'var(--theme--accent_red-hover)', + 'red-text': 'var(--theme--accent_red-text)', + }, + 'grey': mapColorVariables('grey'), + 'brown': mapColorVariables('brown'), + 'orange': mapColorVariables('orange'), + 'yellow': mapColorVariables('yellow'), + 'green': mapColorVariables('green'), + 'blue': mapColorVariables('blue'), + 'purple': mapColorVariables('purple'), + 'pink': mapColorVariables('pink'), + 'red': mapColorVariables('red'), + }, + extend: { + width: { + 'full-96': 'calc(100% - 24rem)', + }, + maxHeight: { + 'full-16': 'calc(100% - 4rem)', + 'full-32': 'calc(100% - 8rem)', + 'full-48': 'calc(100% - 12rem)', + }, + }, + }, + plugins: customClasses, +}); + +tw`hidden ${Object.keys(customClasses).join(' ')}`; + +export { tw }; diff --git a/extension/repo/tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2/mod.json b/extension/repo/tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2/mod.json index 1517326..6b5203e 100644 --- a/extension/repo/tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2/mod.json +++ b/extension/repo/tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2/mod.json @@ -2,7 +2,7 @@ "name": "tweaks", "id": "5174a483-c88d-4bf8-a95f-35cd330b76e2", "version": "0.2.0", - "description": "common style/layout changes and custom inserts.", + "description": "common style/layout changes and custom CSS insertion.", "tags": ["extension", "customisation"], "authors": [ { diff --git a/extension/worker.js b/extension/worker.js index b9ead42..df68e8a 100644 --- a/extension/worker.js +++ b/extension/worker.js @@ -6,47 +6,45 @@ 'use strict'; -function openEnhancerMenu() { - chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { - const enhancerMenuURL = chrome.runtime.getURL( - 'repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html' - ), - enhancerMenuTab = tabs.find((tab) => tab.url.startsWith(enhancerMenuURL)); - if (enhancerMenuTab) { - chrome.tabs.highlight({ 'tabs': enhancerMenuTab.index }); - } else chrome.tabs.create({ url: enhancerMenuURL }); - }); +async function focusMenu() { + const tabs = await chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }), + url = chrome.runtime.getURL('repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html'), + menu = tabs.find((tab) => tab.url.startsWith(url)); + if (menu) { + chrome.tabs.highlight({ 'tabs': menu.index }); + } else chrome.tabs.create({ url }); } -chrome.action.onClicked.addListener(openEnhancerMenu); +chrome.action.onClicked.addListener(focusMenu); -function focusNotion() { - chrome.tabs.query( - { url: 'https://*.notion.so/*', windowId: chrome.windows.WINDOW_ID_CURRENT }, - (tabs) => { - if (tabs.length) { - chrome.tabs.highlight({ 'tabs': tabs[0].index }); - } else chrome.tabs.create({ url: 'https://notion.so/' }); - } - ); +async function focusNotion() { + const tabs = await chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }), + notion = tabs.find( + (tab) => + new URL(tab.url).host.endsWith('.notion.so') || + new URL(tab.url).host.endsWith('.notion.site') + ); + if (notion) { + chrome.tabs.highlight({ 'tabs': notion.index }); + } else chrome.tabs.create({ url: 'https://notion.so/' }); } -function reload() { - chrome.tabs.query({ url: 'https://*.notion.so/*' }, (tabs) => { - (tabs || []).forEach((tab) => chrome.tabs.reload(tab.id)); - }); - chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { - const enhancerMenuURL = chrome.runtime.getURL( - 'repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html' - ), - enhancerMenuTabs = (tabs || []).filter((tab) => tab.url.startsWith(enhancerMenuURL)); - enhancerMenuTabs.forEach((tab) => chrome.tabs.reload(tab.id)); +async function reload() { + const tabs = await chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }), + menu = chrome.runtime.getURL('repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html'); + tabs.forEach((tab) => { + const url = new URL(tab.url), + matches = + url.host.endsWith('.notion.so') || + url.host.endsWith('.notion.site') || + tab.url.startsWith(menu); + if (matches) chrome.tabs.reload(tab.id); }); } chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { switch (request.action) { - case 'openEnhancerMenu': - openEnhancerMenu(); + case 'focusMenu': + focusMenu(); break; case 'focusNotion': focusNotion();