diff --git a/extension/api/_.mjs b/extension/api/_.mjs index dc8ce08..c1a108d 100644 --- a/extension/api/_.mjs +++ b/extension/api/_.mjs @@ -9,18 +9,17 @@ /** @module notion-enhancer/api */ /** environment-specific methods and constants */ -import * as env from './env.mjs'; - +export * as env from './env.mjs'; /** environment-specific filesystem reading */ -const fs = env.name === 'extension' ? await import('./extension-fs.mjs') : {}; +export * as fs from './fs.mjs'; /** environment-specific data persistence */ -const storage = env.name === 'extension' ? await import('./extension-storage.mjs') : {}; +export * as storage from './storage.mjs'; /** helpers for formatting, validating and parsing values */ -import * as fmt from './fmt.mjs'; +export * as fmt from './fmt.mjs'; /** interactions with the enhancer's repository of mods */ -import * as registry from './registry.mjs'; +export * as registry from './registry.mjs'; /** helpers for manipulation of a webpage */ -import * as web from './web.mjs'; - -export { env, fs, storage, fmt, registry, web }; +export * as web from './web.mjs'; +/** notion-style elements inc. the sidebar */ +export * as components from './components/_.mjs'; diff --git a/extension/api/components/_.mjs b/extension/api/components/_.mjs new file mode 100644 index 0000000..63e8daa --- /dev/null +++ b/extension/api/components/_.mjs @@ -0,0 +1,21 @@ +/* + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * notion-style elements inc. the sidebar + * @module notion-enhancer/api/components + */ + +/** + * 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 { tooltip } from './tooltip.mjs'; + +export { side } from './tooltip.mjs'; diff --git a/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/sidebar.css b/extension/api/components/sidebar.css similarity index 100% rename from extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/sidebar.css rename to extension/api/components/sidebar.css diff --git a/extension/api/components/sidebar.mjs b/extension/api/components/sidebar.mjs new file mode 100644 index 0000000..7593a46 --- /dev/null +++ b/extension/api/components/sidebar.mjs @@ -0,0 +1,24 @@ +/* + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * notion-style elements inc. the sidebar + * @module notion-enhancer/api/components/side-panel + */ + +import { web } from '../_.mjs'; + +let _$sidebar; + +export const sidebar = (icon, name, loader = ($panel) => {}) => { + if (!_$sidebar) { + web.loadStylesheet('api/components/sidebar.css'); + _$sidebar = web.html`
`; + web.render(document.body, _$sidebar); + } +}; diff --git a/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/tooltip.css b/extension/api/components/tooltip.css similarity index 100% rename from extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/tooltip.css rename to extension/api/components/tooltip.css diff --git a/extension/api/components/tooltip.mjs b/extension/api/components/tooltip.mjs new file mode 100644 index 0000000..5d03bb8 --- /dev/null +++ b/extension/api/components/tooltip.mjs @@ -0,0 +1,41 @@ +/* + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * notion-style elements inc. the sidebar + * @module notion-enhancer/api/components/tooltip + */ + +import { fmt, web } from '../_.mjs'; + +let _$tooltip; + +/** + * 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) { + web.loadStylesheet('api/components/tooltip.css'); + _$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/api/env.mjs b/extension/api/env.mjs index 1a33040..b2bccdc 100644 --- a/extension/api/env.mjs +++ b/extension/api/env.mjs @@ -11,7 +11,7 @@ * @module notion-enhancer/api/env */ -import env from '../env.mjs'; +import * as env from '../env/env.mjs'; /** * the environment/platform name code is currently being executed in diff --git a/extension/api/fs.mjs b/extension/api/fs.mjs new file mode 100644 index 0000000..7aa05b3 --- /dev/null +++ b/extension/api/fs.mjs @@ -0,0 +1,46 @@ +/* + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * environment-specific filesystem reading + * @module notion-enhancer/api/fs + */ + +import * as fs from '../env/fs.mjs'; + +/** + * transform a path relative to the enhancer root directory into an absolute path + * @type {function} + * @param {string} path - a url or within-the-enhancer filepath + * @returns {string} an absolute filepath + */ +export const localPath = fs.localPath; + +/** + * fetch and parse a json file's contents + * @type {function} + * @param {string} path - a url or within-the-enhancer filepath + * @returns {object} the json value of the requested file as a js object + */ +export const getJSON = fs.getJSON; + +/** + * fetch a text file's contents + * @type {function} + * @param {string} path - a url or within-the-enhancer filepath + * @returns {string} the text content of the requested file + */ +export const getText = fs.getText; + +/** + * check if a file exists + * @type {function} + * @param {string} path - a url or within-the-enhancer filepath + * @returns {boolean} whether or not the file exists + */ +export const isFile = fs.isFile; diff --git a/extension/api/registry-validation.mjs b/extension/api/registry-validation.mjs index 48add10..afe7819 100644 --- a/extension/api/registry-validation.mjs +++ b/extension/api/registry-validation.mjs @@ -148,22 +148,6 @@ 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/storage.mjs b/extension/api/storage.mjs new file mode 100644 index 0000000..4a3e63b --- /dev/null +++ b/extension/api/storage.mjs @@ -0,0 +1,67 @@ +/* + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * environment-specific data persistence + * @module notion-enhancer/api/storage + */ + +import * as storage from '../env/storage.mjs'; + +/** + * get persisted data + * @type {function} + * @param {array} path - the path of keys to the value being fetched + * @param {*} [fallback] - a default value if the path is not matched + * @returns {Promise} value ?? fallback + */ +export const get = storage.get; + +/** + * persist data + * @type {function} + * @param {array} path - the path of keys to the value being set + * @param {*} value - the data to save + * @returns {Promise} resolves when data has been saved + */ +export const set = storage.set; + +/** + * create a wrapper for accessing a partition of the storage + * @type {function} + * @param {array} namespace - the path of keys to prefix all storage requests with + * @param {function} [get] - the storage get function to be wrapped + * @param {function} [set] - the storage set function to be wrapped + * @returns {object} an object with the wrapped get/set functions + */ +export const db = storage.db; + +/** + * add an event listener for changes in storage + * @type {function} + * @param {onStorageChangeCallback} callback - called whenever a change in + * storage is initiated from the current process + */ +export const addChangeListener = storage.addChangeListener; + +/** + * remove a listener added with storage.addChangeListener + * @type {function} + * @param {onStorageChangeCallback} callback + */ +export const removeChangeListener = storage.removeChangeListener; + +/** + * @callback onStorageChangeCallback + * @param {object} event + * @param {string} event.type - 'set' or 'reset' + * @param {string} event.namespace- the name of the store, e.g. a mod id + * @param {string} [event.key] - the key associated with the changed value + * @param {string} [event.new] - the new value being persisted to the store + * @param {string} [event.old] - the previous value associated with the key + */ diff --git a/extension/env.mjs b/extension/env.mjs deleted file mode 100644 index 6d75f6e..0000000 --- a/extension/env.mjs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * notion-enhancer: api - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -'use strict'; - -/** - * environment-specific methods and constants - * @module notion-enhancer/api/env - */ - -const focusMenu = () => chrome.runtime.sendMessage({ action: 'focusMenu' }), - focusNotion = () => chrome.runtime.sendMessage({ action: 'focusNotion' }), - reload = () => chrome.runtime.sendMessage({ action: 'reload' }); - -export default { - name: 'extension', - version: chrome.runtime.getManifest().version, - focusMenu, - focusNotion, - reload, -}; diff --git a/extension/env/env.mjs b/extension/env/env.mjs new file mode 100644 index 0000000..af15ca2 --- /dev/null +++ b/extension/env/env.mjs @@ -0,0 +1,44 @@ +/* + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * environment-specific methods and constants + * @module notion-enhancer/api/env + */ + +/** + * the environment/platform name code is currently being executed in + * @constant + * @type {string} + */ +export const name = 'extension'; + +/** + * the current version of the enhancer + * @constant + * @type {string} + */ +export const version = chrome.runtime.getManifest().version; + +/** + * open the enhancer's menu + * @type {function} + */ +export const focusMenu = () => chrome.runtime.sendMessage({ action: 'focusMenu' }); + +/** + * focus an active notion tab + * @type {function} + */ +export const focusNotion = () => chrome.runtime.sendMessage({ action: 'focusNotion' }); + +/** + * reload all notion and enhancer menu tabs to apply changes + * @type {function} + */ +export const reload = () => chrome.runtime.sendMessage({ action: 'reload' }); diff --git a/extension/api/extension-fs.mjs b/extension/env/fs.mjs similarity index 100% rename from extension/api/extension-fs.mjs rename to extension/env/fs.mjs diff --git a/extension/api/extension-storage.mjs b/extension/env/storage.mjs similarity index 100% rename from extension/api/extension-storage.mjs rename to extension/env/storage.mjs diff --git a/extension/launcher.js b/extension/launcher.js index cf7173c..730ba8f 100644 --- a/extension/launcher.js +++ b/extension/launcher.js @@ -13,28 +13,16 @@ page = location.pathname.split(/[/-]/g).reverse()[0].length === 32; if (site || page) { - 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); - } + import(chrome.runtime.getURL('api/_.mjs')).then(async (api) => { + const { fs, registry, web } = api; for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { - if (!registry.core.includes(mod.id)) await insert(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)); + } } const errors = await registry.errors(); if (errors.length) { diff --git a/extension/manifest.json b/extension/manifest.json index cb74f57..c0e7351 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": ["env.mjs", "api/*", "dep/*", "icon/*", "repo/*"], + "web_accessible_resources": ["env/*", "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 deleted file mode 100644 index ee8c4b8..0000000 --- a/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/hook.mjs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 index 343efc2..5b43a62 100644 --- a/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/mod.json +++ b/extension/repo/components@36a2ffc9-27ff-480e-84a7-c7700a7d232d/mod.json @@ -1,8 +1,9 @@ { + "__comment": "pseudo-mod to allow configuration of API-provided components", "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.", + "description": "notion-style elements reused by other mods, inc. the sidebar.", "tags": ["core"], "authors": [ { @@ -18,21 +19,15 @@ "avatar": "https://avatars.githubusercontent.com/u/54142180" } ], - "js": { - "hook": "hook.mjs" - }, - "css": { - "client": ["tooltip.css", "sidebar.css"], - "menu": ["tooltip.css"], - "frame": ["tooltip.css"] - }, + "js": {}, + "css": {}, "options": [ { "type": "hotkey", "key": "side-panel-hotkey", - "label": "toggle side panel hotkey", + "label": "toggle enhancer sidebar hotkey", "value": "Ctrl+Alt+\\", - "tooltip": "opens the side panel in notion - will only work if a mod is making use of it." + "tooltip": "opens/closes the extra sidebar in notion - will only work if a mod is making use of it." } ] } diff --git a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/blocks.mjs b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/blocks.mjs index 472f850..41cf1cb 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/blocks.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/blocks.mjs @@ -6,9 +6,9 @@ 'use strict'; -import { api, profileDB } from './loader.mjs'; +import { fmt, web, registry, components } from '../../api/_.mjs'; import { notifications } from './notifications.mjs'; -const { fmt, web, components } = api; +const profileDB = await registry.profileDB(); export const blocks = { preview: (url) => web.html` (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 819720b..49c2611 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.mjs @@ -6,11 +6,20 @@ 'use strict'; -import { api, db, profileName, profileDB } from './loader.mjs'; -import './styles.mjs'; +import { env, fs, storage, registry, web } from '../../api/_.mjs'; import { notifications } from './notifications.mjs'; import { blocks, options } from './blocks.mjs'; -const { env, fs, storage, registry, web } = api; +import './styles.mjs'; + +const db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'), + profileName = await registry.profileName(), + profileDB = await registry.profileDB(); + +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}`); + } +} web.addHotkeyListener(await db.get(['hotkey']), env.focusNotion); 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 305dae6..64fcf64 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/notifications.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/notifications.mjs @@ -6,9 +6,8 @@ 'use strict'; -import { api } from './loader.mjs'; +import { env, fs, storage, fmt, registry, web } from '../../api/_.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 6ea1353..8a5e70e 100644 --- a/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.mjs +++ b/extension/repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/router.mjs @@ -6,8 +6,7 @@ 'use strict'; -import { api } from './loader.mjs'; -const { web } = api; +import { web } from '../../api/_.mjs'; let _defaultView = ''; const _views = new Map();