diff --git a/extension/api/registry-validation.mjs b/extension/api/registry-validation.mjs index afe7819..48add10 100644 --- a/extension/api/registry-validation.mjs +++ b/extension/api/registry-validation.mjs @@ -148,6 +148,22 @@ const check = async ( }); tests.push(test); } + if (mod.js.hook) { + if (mod.tags.includes('core')) { + const test = check(mod, 'js.hook', mod.js.hook, 'file', { + extension: '.mjs', + }); + tests.push(test); + } else { + registry._errors.push({ + source: mod._dir, + message: `js.hook (only core mods can register hooks): ${JSON.stringify( + mod.tags + )}`, + }); + tests.push(false); + } + } return tests; }); }, diff --git a/extension/api/registry.mjs b/extension/api/registry.mjs index d55e0dd..54ee095 100644 --- a/extension/api/registry.mjs +++ b/extension/api/registry.mjs @@ -25,6 +25,7 @@ export const _cache = [], export const core = [ 'a6621988-551d-495a-97d8-3c568bca2e9e', '0f0bf8b6-eae6-4273-b307-8fc43f2ee082', + '36a2ffc9-27ff-480e-84a7-c7700a7d232d', ]; /** diff --git a/extension/api/web.mjs b/extension/api/web.mjs index ad8d32e..e116b44 100644 --- a/extension/api/web.mjs +++ b/extension/api/web.mjs @@ -17,7 +17,7 @@ const _hotkeyEventListeners = [], _documentObserverListeners = [], _documentObserverEvents = []; -let _$tooltip, _$tooltipStylesheet, _hotkeyEvent, _documentObserver; +let _hotkeyEvent, _documentObserver; import '../dep/jscolor.min.js'; /** color picker with alpha channel using https://jscolor.com/ */ @@ -160,52 +160,6 @@ export const icon = (name, attrs = {}) => { .join(' ')}>`; }; -/** - * add a tooltip to show extra information on hover - * @param {HTMLElement} $ref - the element that will trigger the tooltip when hovered - * @param {string} text - the markdown content of the tooltip - */ -export const tooltip = ($ref, text) => { - if (!_$tooltip) { - _$tooltip = html`
`; - _$tooltipStylesheet = html``; - render(document.head, _$tooltipStylesheet); - render(document.body, _$tooltip); - } - text = fmt.md.render(text); - $ref.addEventListener('mouseover', (event) => { - _$tooltip.innerHTML = text; - _$tooltip.style.display = 'block'; - }); - $ref.addEventListener('mousemove', (event) => { - _$tooltip.style.top = event.clientY - _$tooltip.clientHeight + 'px'; - _$tooltip.style.left = event.clientX - _$tooltip.clientWidth + 'px'; - }); - $ref.addEventListener('mouseout', (event) => { - _$tooltip.style.display = ''; - }); -}; - /** * register a hotkey listener to the page * @param {array} keys - the combination of keys that will trigger the hotkey. diff --git a/extension/launcher.js b/extension/launcher.js index 5319194..cf7173c 100644 --- a/extension/launcher.js +++ b/extension/launcher.js @@ -13,16 +13,28 @@ page = location.pathname.split(/[/-]/g).reverse()[0].length === 32; if (site || page) { - import(chrome.runtime.getURL('api/_.mjs')).then(async (api) => { - const { registry, web } = api; + import(chrome.runtime.getURL('api/_.mjs')).then(async ({ ...api }) => { + const { fs, registry, web } = api, + insert = async (mod) => { + for (const sheet of mod.css?.client || []) { + web.loadStylesheet(`repo/${mod._dir}/${sheet}`); + } + for (let script of mod.js?.client || []) { + script = await import(fs.localPath(`repo/${mod._dir}/${script}`)); + script.default(api, await registry.db(mod.id)); + } + return true; + }; + for (const mod of await registry.list((mod) => registry.core.includes(mod.id))) { + if (mod.js?.hook) { + let script = mod.js.hook; + script = await import(fs.localPath(`repo/${mod._dir}/${script}`)); + api[mod.name] = await script.default(api, await registry.db(mod.id)); + } + await insert(mod); + } for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { - for (const sheet of mod.css?.client || []) { - web.loadStylesheet(`repo/${mod._dir}/${sheet}`); - } - for (let script of mod.js?.client || []) { - script = await import(chrome.runtime.getURL(`repo/${mod._dir}/${script}`)); - script.default(api, await registry.db(mod.id)); - } + if (!registry.core.includes(mod.id)) await insert(mod); } const errors = await registry.errors(); if (errors.length) { diff --git a/extension/manifest.json b/extension/manifest.json index 9a687df..cb74f57 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -19,7 +19,7 @@ "page": "repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html", "open_in_tab": true }, - "web_accessible_resources": ["api/*", "dep/*", "icon/*", "repo/*"], + "web_accessible_resources": ["env.mjs", "api/*", "dep/*", "icon/*", "repo/*"], "content_scripts": [ { "matches": ["https://*.notion.so/*", "https://*.notion.site/*"], diff --git a/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/hook.mjs b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/hook.mjs new file mode 100644 index 0000000..ee8c4b8 --- /dev/null +++ b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/hook.mjs @@ -0,0 +1,38 @@ +/* + * notion-enhancer core: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2021 CloudHill (https://github.com/CloudHill) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +let _$tooltip; + +export default function (api, db) { + const { web, fmt } = api; + + return { + /** + * add a tooltip to show extra information on hover + * @param {HTMLElement} $ref - the element that will trigger the tooltip when hovered + * @param {string} text - the markdown content of the tooltip + */ + tooltip: ($ref, text) => { + if (!_$tooltip) { + _$tooltip = web.html`
`; + web.render(document.body, _$tooltip); + } + text = fmt.md.render(text); + $ref.addEventListener('mouseover', (event) => { + _$tooltip.innerHTML = text; + _$tooltip.style.display = 'block'; + }); + $ref.addEventListener('mousemove', (event) => { + _$tooltip.style.top = event.clientY - _$tooltip.clientHeight + 'px'; + _$tooltip.style.left = event.clientX - _$tooltip.clientWidth + 'px'; + }); + $ref.addEventListener('mouseout', (event) => { + _$tooltip.style.display = ''; + }); + }, + }; +} diff --git a/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/mod.json b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/mod.json new file mode 100644 index 0000000..343efc2 --- /dev/null +++ b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/mod.json @@ -0,0 +1,38 @@ +{ + "name": "components", + "id": "36a2ffc9-27ff-480e-84a7-c7700a7d232d", + "version": "0.2.0", + "description": "notion-style elements reuseable by other mods, inc. tooltips and the side panel.", + "tags": ["core"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + }, + { + "name": "CloudHill", + "email": "rh.cloudhill@gmail.com", + "homepage": "https://github.com/CloudHill", + "avatar": "https://avatars.githubusercontent.com/u/54142180" + } + ], + "js": { + "hook": "hook.mjs" + }, + "css": { + "client": ["tooltip.css", "sidebar.css"], + "menu": ["tooltip.css"], + "frame": ["tooltip.css"] + }, + "options": [ + { + "type": "hotkey", + "key": "side-panel-hotkey", + "label": "toggle side panel hotkey", + "value": "Ctrl+Alt+\\", + "tooltip": "opens the side panel in notion - will only work if a mod is making use of it." + } + ] +} diff --git a/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/sidebar.css b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/sidebar.css new file mode 100644 index 0000000..b52afd8 --- /dev/null +++ b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/sidebar.css @@ -0,0 +1,6 @@ +/* + * notion-enhancer core: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2021 CloudHill (https://github.com/CloudHill) + * (https://notion-enhancer.github.io/) under the MIT license + */ diff --git a/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/tooltip.css b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/tooltip.css new file mode 100644 index 0000000..b919963 --- /dev/null +++ b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/tooltip.css @@ -0,0 +1,25 @@ +/* + * notion-enhancer core: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +#enhancer--tooltip { + position: absolute; + background: var(--theme--ui_tooltip); + font-size: 11.5px; + 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; +} +#enhancer--tooltip p:first-child { + color: var(--theme--ui_tooltip-title); +} +#enhancer--tooltip p:not(:first-child) { + color: var(--theme--ui_tooltip-description); +} diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/components.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/blocks.mjs similarity index 93% rename from extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/components.mjs rename to extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/blocks.mjs index af203fd..472f850 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/components.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/blocks.mjs @@ -6,11 +6,11 @@ 'use strict'; -import { fmt, web } from '../../api/_.mjs'; +import { api, profileDB } from './loader.mjs'; import { notifications } from './notifications.mjs'; -import { profileDB } from './menu.mjs'; +const { fmt, web, components } = api; -export const components = { +export const blocks = { preview: (url) => web.html` { const checked = await profileDB.get([mod.id, opt.key], opt.value), - $toggle = components.toggle(opt.label, checked), + $toggle = blocks.toggle(opt.label, checked), $tooltip = web.html`${web.icon('info', { class: 'input-tooltip' })}`, $label = $toggle.children[0], $input = $toggle.children[1]; if (opt.tooltip) { $label.prepend($tooltip); - web.tooltip($tooltip, opt.tooltip); + components.tooltip($tooltip, opt.tooltip); } $input.addEventListener('change', async (event) => { await profileDB.set([mod.id, opt.key], $input.checked); @@ -89,7 +89,7 @@ export const options = { ${$options.join('')} `, $icon = web.html`${web.icon('chevron-down', { class: 'input-icon' })}`; - if (opt.tooltip) web.tooltip($tooltip, opt.tooltip); + if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); $select.addEventListener('change', async (event) => { await profileDB.set([mod.id, opt.key], $select.value); notifications.onChange(); @@ -105,7 +105,7 @@ export const options = { ), $input = web.html``, $icon = web.html`${web.icon('type', { class: 'input-icon' })}`; - if (opt.tooltip) web.tooltip($tooltip, opt.tooltip); + if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); $input.addEventListener('change', async (event) => { await profileDB.set([mod.id, opt.key], $input.value); notifications.onChange(); @@ -121,7 +121,7 @@ export const options = { ), $input = web.html``, $icon = web.html`${web.icon('hash', { class: 'input-icon' })}`; - if (opt.tooltip) web.tooltip($tooltip, opt.tooltip); + if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); $input.addEventListener('change', async (event) => { await profileDB.set([mod.id, opt.key], $input.value); notifications.onChange(); @@ -153,7 +153,7 @@ export const options = { onInput: paint, onChange: paint, }); - if (opt.tooltip) web.tooltip($tooltip, opt.tooltip); + if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); $input.addEventListener('change', async (event) => { await profileDB.set([mod.id, opt.key], $input.value); notifications.onChange(); @@ -175,7 +175,7 @@ export const options = { $icon = web.html`${web.icon('file', { class: 'input-icon' })}`, $filename = web.html`${web.escape(filename || 'none')}`, $latest = web.render(web.html``, $filename); - if (opt.tooltip) web.tooltip($tooltip, opt.tooltip); + if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); $input.addEventListener('change', (event) => { const file = event.target.files[0], reader = new FileReader(); @@ -208,7 +208,7 @@ export const options = { ), $input = web.html``, $icon = web.html`${web.icon('command', { class: 'input-icon' })}`; - if (opt.tooltip) web.tooltip($tooltip, opt.tooltip); + if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); $input.addEventListener('keydown', async (event) => { event.preventDefault(); const pressed = [], diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/loader.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/loader.mjs new file mode 100644 index 0000000..3db3711 --- /dev/null +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/loader.mjs @@ -0,0 +1,32 @@ +/* + * notion-enhancer core: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +import * as _api from '../../api/_.mjs'; +export const api = { ..._api }, + { fs, registry, web } = api; + +export const db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'), + profileName = await registry.profileName(), + profileDB = await registry.profileDB(); + +const insert = (mod) => { + for (const sheet of mod.css?.menu || []) { + web.loadStylesheet(`repo/${mod._dir}/${sheet}`); + } +}; +for (const mod of await registry.list((mod) => registry.core.includes(mod.id))) { + if (mod.js?.hook) { + let script = mod.js.hook; + script = await import(fs.localPath(`repo/${mod._dir}/${script}`)); + api[mod.name] = await script.default(api, await registry.db(mod.id)); + } + await insert(mod); +} +for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { + if (!registry.core.includes(mod.id)) await insert(mod); +} 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 1208f52..819720b 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs @@ -6,24 +6,14 @@ 'use strict'; -import { env, fs, storage, registry, web } from '../../api/_.mjs'; - -export const db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'), - profileName = await registry.profileName(), - profileDB = await registry.profileDB(); - +import { api, db, profileName, profileDB } from './loader.mjs'; import './styles.mjs'; import { notifications } from './notifications.mjs'; -import { components, options } from './components.mjs'; +import { blocks, options } from './blocks.mjs'; +const { env, fs, storage, registry, web } = api; web.addHotkeyListener(await db.get(['hotkey']), env.focusNotion); -for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { - for (const sheet of mod.css?.menu || []) { - web.loadStylesheet(`repo/${mod._dir}/${sheet}`); - } -} - const loadTheme = async () => { document.documentElement.className = (await db.get(['theme'], 'light')) === 'dark' ? 'dark' : ''; @@ -204,7 +194,7 @@ const _$modListCache = {}, }, mod: async (mod) => { const $mod = web.html`
`, - $toggle = components.toggle('', await registry.enabled(mod.id)); + $toggle = blocks.toggle('', await registry.enabled(mod.id)); $toggle.addEventListener('change', async (event) => { if (event.target.checked && mod.tags.includes('theme')) { const mode = mod.tags.includes('light') ? 'light' : 'dark', @@ -233,8 +223,8 @@ const _$modListCache = {}, } $mod.className = 'mod-selected'; const fragment = [ - web.render(components.title(mod.name), components.version(mod.version)), - components.tags(mod.tags), + web.render(blocks.title(mod.name), blocks.version(mod.version)), + blocks.tags(mod.tags), await generators.options(mod), ]; web.render(web.empty($options), ...fragment); @@ -244,7 +234,7 @@ const _$modListCache = {}, web.render( $mod, mod.preview - ? components.preview( + ? blocks.preview( mod.preview.startsWith('http') ? mod.preview : fs.localPath(`repo/${mod._dir}/${mod.preview}`) @@ -252,10 +242,10 @@ const _$modListCache = {}, : '', 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), + web.render(blocks.title(mod.name), blocks.version(mod.version)), + blocks.tags(mod.tags), + blocks.description(mod.description), + blocks.authors(mod.authors), mod.environments.includes(env.name) && !registry.core.includes(mod.id) ? $toggle : '' diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/notifications.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/notifications.mjs index 5ca52fc..305dae6 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/notifications.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/notifications.mjs @@ -6,10 +6,9 @@ 'use strict'; -import { env, fs, storage, fmt, registry, web } from '../../api/_.mjs'; -const db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'); - +import { api } from './loader.mjs'; import { tw } from './styles.mjs'; +const { env, fs, storage, fmt, registry, web } = api; export const notifications = { $container: web.html`
`, 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 8a5e70e..6ea1353 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.mjs @@ -6,7 +6,8 @@ 'use strict'; -import { web } from '../../api/_.mjs'; +import { api } from './loader.mjs'; +const { web } = api; let _defaultView = ''; const _views = new Map(); diff --git a/extension/repo/registry.json b/extension/repo/registry.json index 4aaac80..7977fb3 100644 --- a/extension/repo/registry.json +++ b/extension/repo/registry.json @@ -1,6 +1,7 @@ [ "menu@a6621988-551d-495a-97d8-3c568bca2e9e", "theming@0f0bf8b6-eae6-4273-b307-8fc43f2ee082", + "components@36a2ffc9-27ff-480e-84a7-c7700a7d232d", "tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2", "bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f", "calendar-scroll@b1c7db33-dfee-489a-a76c-0dd66f7ed29a"