diff --git a/.github/workflows/submodules.yml b/.github/workflows/submodules.yml deleted file mode 100644 index 463aa5e..0000000 --- a/.github/workflows/submodules.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: 'update submodules' - -on: - workflow_dispatch: - -jobs: - sync: - name: 'update submodules' - runs-on: ubuntu-latest - steps: - - name: checkout repo - uses: actions/checkout@v2 - with: - submodules: true - - name: pull updates - run: | - git pull --recurse-submodules - git submodule update --remote --recursive - - name: commit changes - uses: stefanzweifel/git-auto-commit-action@v4 diff --git a/.gitignore b/.gitignore index dbf0821..114de42 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,9 @@ -node_modules/* \ No newline at end of file +# builds +dist/ + +# dependencies +node_modules/ + +# yarn +.yarn/ +.pnp.* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index d1db488..0000000 --- a/.gitmodules +++ /dev/null @@ -1,16 +0,0 @@ -[submodule "api"] - path = insert/api - url = git@github.com:notion-enhancer/api.git - branch = dev -[submodule "repo"] - path = insert/repo - url = git@github.com:notion-enhancer/repo.git - branch = dev -[submodule "media"] - path = insert/media - url = git@github.com:notion-enhancer/media.git - branch = main -[submodule "dep"] - path = insert/dep - url = git@github.com:notion-enhancer/dep.git - branch = main diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..36e01b3 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1,4 @@ +nodeLinker: node-modules +yarnPath: .yarn/releases/yarn-3.3.0.cjs + +enableMessageNames: false diff --git a/bin.mjs b/bin.mjs index b9d17d5..b35dccd 100755 --- a/bin.mjs +++ b/bin.mjs @@ -2,143 +2,374 @@ /** * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2022 dragonwocky (https://dragonwocky.me/) * (https://notion-enhancer.github.io/) under the MIT license */ -'use strict'; +import arg from "arg"; +import chalk from "chalk-template"; +import os from "node:os"; +import { createRequire } from "node:module"; +import { + getAppPath, + getBackupPath, + getCachePath, + checkEnhancementVersion, + setNotionPath, + unpackApp, + applyEnhancements, + takeBackup, + restoreBackup, + removeCache, +} from "./scripts/enhance-desktop-app.mjs"; +import { existsSync } from "node:fs"; +const nodeRequire = createRequire(import.meta.url), + manifest = nodeRequire("./package.json"); -import os from 'os'; +let __quiet, __debug; +const print = (...args) => __quiet || process.stdout.write(chalk(...args)), + printObject = (value) => __quiet || console.dir(value, { depth: null }), + clearLine = `\r\x1b[K`, + showCursor = `\x1b[?25h`, + hideCursor = `\x1b[?25l`, + cursorUp = (n) => `\x1b[${n}A`, + cursorForward = (n) => `\x1b[${n}C`; -import { pkg, findNotion } from './pkg/helpers.mjs'; -import { line, options, log, help, args, lastSpinner } from './pkg/cli.mjs'; - -import apply from './pkg/apply.mjs'; -import remove from './pkg/remove.mjs'; -import check from './pkg/check.mjs'; - -const manifest = pkg(), - opts = options({ - y: 'yes', - n: 'no', - d: 'dev', - h: 'help', - v: 'version', - }), - promptRes = opts.get('yes') ? 'y' : opts.get('no') ? 'n' : undefined; - -const displayHelp = () => { - help({ - name: manifest.name, - version: manifest.version, - link: manifest.homepage, - commands: [ - ['apply', 'add enhancements to the notion app'], - ['remove', 'return notion to its pre-enhanced/pre-modded state'], - ['check, status', 'check the current state of the notion app'], - ], - options: [ - ['-y, --yes', 'skip prompts'], - ['-n, --no', 'skip prompts'], - ['-d, --dev', 'show detailed error messages (for debug purposes)'], - [ - '--path=', - 'provide a file location to enhance (otherwise auto-picked)', - ], - ['--no-backup', 'skip backup (faster enhancement, but disables removal)'], - ['--patch', 'overwrite inserted files (useful for quick development/testing)'], - ['-h, --help', 'display usage information'], - ['-v, --version', 'display version number'], - ], - }); -}; -if (opts.get('help')) { - displayHelp(); - process.exit(0); -} - -if (opts.get('version')) { - log( - `${manifest.name}/${manifest.version} ${ - process.platform - }-${os.arch()}/${os.release()} node/${process.version}` - ); - process.exit(0); -} - -function handleError(err) { - if (opts.get('dev')) { - const strs = [], - tags = [], - stack = err.stack.split('\n'); - for (let i = 0; i < stack.length; i++) { - const text = stack[i].replace(/^ /, ' '); - if (i === 0) { - const [type, msg] = text.split(/:((.+)|$)/); - strs.push('{bold.red '); - tags.push(type); - strs.push(':} '); - tags.push(msg); - } else { - strs.push('{grey '); - tags.push(text); - strs.push('}'); - tags.push(''); +let __confirmation; +const readStdin = () => { + return new Promise((res) => { + process.stdin.resume(); + process.stdin.setEncoding("utf8"); + process.stdin.once("data", (key) => { + process.stdin.pause(); + res(key); + }); + }); + }, + promptConfirmation = async (prompt) => { + let input; + const validInputs = ["Y", "y", "N", "n"], + promptLength = ` > ${prompt} [Y/n]: `.length; + // prevent line clear remove existing stdout + print`\n`; + do { + // clear line and repeat prompt until valid input is received + print`${cursorUp(1)}${clearLine} {inverse > ${prompt} [Y/n]:} `; + // autofill prompt response if --yes, --no or --quiet flags passed + if (validInputs.includes(__confirmation)) { + input = __confirmation; + print`${__confirmation}\n`; + } else input = (await readStdin()).trim(); + if (!input) { + // default to Y if enter is pressed w/out input + input = "Y"; + print`${cursorUp(1)}${cursorForward(promptLength)}Y\n`; } - if (i !== stack.length - 1) { - strs.push('\n'); - tags.push(''); + } while (!validInputs.includes(input)); + // move cursor to immediately after input + print`${cursorUp(1)}${cursorForward(promptLength + 1)}`; + return input; + }; + +let __spinner; +const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"], + stopSpinner = () => { + if (!__spinner) return; + clearInterval(__spinner); + // show cursor and overwrite spinner with arrow on completion + print`\b{bold.yellow →}\n${showCursor}`; + __spinner = undefined; + }, + startSpinner = () => { + // cleanup prev spinner if necessary + stopSpinner(); + // hide cursor and print first frame + print`${hideCursor}{bold.yellow ${spinnerFrames[0]}}`; + let i = 0; + __spinner = setInterval(() => { + i++; + // overwrite spinner with next frame + print`\b{bold.yellow ${spinnerFrames[i % spinnerFrames.length]}}`; + }, 80); + }; + +const compileOptsToArgSpec = (options) => { + const args = {}; + for (const [opt, [type]] of options) { + const aliases = opt.split(", ").map((alias) => alias.split("=")[0]), + param = aliases[1] ?? aliases[0]; + args[param] = type; + for (let i = 0; i < aliases.length; i++) { + if (aliases[i] === param) continue; + args[aliases[i]] = param; } } - log(strs, ...tags); - } else { - log`{bold.red Error:} ${err.message} {grey (run with -d for more information)}`; - } -} + return args; + }, + compileOptsToJsonOutput = (options) => { + // the structure used to define options above + // is convenient and compact, but requires additional + // parsing to understand. this function processes + // options into a more explicitly defined structure + return options.map(([opt, [type, description]]) => { + const option = { + aliases: opt.split(", ").map((alias) => alias.split("=")[0]), + type, + description, + }, + example = opt + .split(", ") + .map((alias) => alias.split("=")[1]) + .find((value) => value); + if (example) option.example = example; + return option; + }); + }; + +const printHelp = (commands, options) => { + const { name, version, homepage } = manifest, + usage = `${name} [options]`; + if (args["--json"]) { + printObject({ + name, + version, + homepage, + usage, + commands: Object.fromEntries(commands), + options: compileOptsToJsonOutput(options), + }); + } else { + const cmdPad = Math.max(...commands.map(([cmd]) => cmd.length)), + optPad = Math.max(...options.map((opt) => opt[0].length)), + parseCmd = (cmd) => chalk` ${cmd[0].padEnd(cmdPad)} {grey :} ${cmd[1]}`, + parseOpt = (opt) => chalk` ${opt[0].padEnd(optPad)} {grey :} ${opt[1][1]}`; + print`{bold.whiteBright ${name} v${version}}\n{grey ${homepage}} + \n{bold.whiteBright USAGE}\n${name} [options] + \n{bold.whiteBright COMMANDS}\n${commands.map(parseCmd).join("\n")} + \n{bold.whiteBright OPTIONS}\n${options.map(parseOpt).join("\n")}\n`; + } + }, + printVersion = () => { + if (args["--json"]) { + printObject({ + [manifest.name]: manifest.version, + node: process.version.slice(1), + platform: process.platform, + architecture: process.arch, + os: os.release(), + }); + } else { + const enhancerVersion = `${manifest.name}@v${manifest.version}`, + nodeVersion = `node@${process.version}`, + osVersion = `${process.platform}-${process.arch}/${os.release()}`; + print`${enhancerVersion} via ${nodeVersion} on ${osVersion}\n`; + } + }; try { - const notionPath = opts.get('path') || findNotion(); + const commands = [ + // ["command", "description"] + ["apply", "add enhancements to the notion app"], + ["remove", "return notion to its pre-enhanced/pre-modded state"], + ["check", "check the current state of the notion app"], + ], + options = [ + // ["alias, option=example", [type, "description"]] + [ + "--path=", + [String, "manually provide a notion installation location"], + ], + ["--overwrite", [Boolean, "for rapid development; unsafely overwrite sources"]], + ["--no-backup", [Boolean, "skip backup; enhancement will be faster but irreversible"]], + ["-y, --yes", [Boolean, 'skip prompts; assume "yes" and run non-interactively']], + ["-n, --no", [Boolean, 'skip prompts; assume "no" and run non-interactively']], + ["-q, --quiet", [Boolean, 'skip prompts; assume "no" unless -y and hide all output']], + ["-d, --debug", [Boolean, "show detailed error messages"]], + ["-j, --json", [Boolean, "display json output (where applicable)"]], + ["-h, --help", [Boolean, "display usage information"]], + ["-v, --version", [Boolean, "display version number"]], + ]; - switch (args()[0]) { - case 'apply': { - log`{bold.rgb(245,245,245) [NOTION-ENHANCER] APPLY}`; - const res = await apply(notionPath, { - overwritePrevious: promptRes, - patchPrevious: opts.get('patch') ? true : false, - takeBackup: opts.get('no-backup') ? false : true, - }); - if (res) { - log`{bold.rgb(245,245,245) SUCCESS} {green ✔}`; - } else log`{bold.rgb(245,245,245) CANCELLED} {red ✘}`; - break; + const args = arg(compileOptsToArgSpec(options)); + if (args["--debug"]) __debug = true; + if (args["--quiet"]) __quiet = true; + if (args["--no"] || args["--quiet"]) __confirmation = "n"; + if (args["--yes"]) __confirmation = "y"; + if (args["--help"]) printHelp(commands, options), process.exit(); + if (args["--version"]) printVersion(), process.exit(); + if (args["--path"]) setNotionPath(args["--path"]); + + const appPath = getAppPath(), + backupPath = getBackupPath(), + cachePath = getCachePath(), + insertVersion = checkEnhancementVersion(); + + const messages = { + "notion-found": `notion installation found`, + "notion-not-found": `notion installation not found (corrupted or nonexistent)`, + "notion-is-packed": `electron archive found: extracting app.asar`, + + "not-applied": `notion-enhancer not applied`, + "version-applied": `notion-enhancer v${manifest.version} applied`, + "version-mismatch": `notion-enhancer v${insertVersion} applied != v${manifest.version} current`, + "prompt-version-replace": `replace?`, + + "backup-found": `backup found`, + "backup-not-found": `backup not found`, + "creating-backup": `backing up notion before enhancement`, + "restoring-backup": `restoring`, + "inserting-enhancements": `inserting enhancements and patching notion sources`, + "manual-removal-instructions": `to remove the notion-enhancer from notion, uninstall notion and + then install a vanilla version of the app from https://www.notion.so/desktop (mac, + windows) or ${manifest.homepage}/getting-started/installation (linux)`, + + "cache-found": `cache found`, + "cache-not-found": `cache not found: nothing to remove`, + "prompt-cache-removal": `remove?`, + }; + const SUCCESS = chalk`{bold.whiteBright SUCCESS} {green ✔}`, + FAILURE = chalk`{bold.whiteBright FAILURE} {red ✘}`, + CANCELLED = chalk`{bold.whiteBright CANCELLED} {red ✘}`, + INCOMPLETE = Symbol(); + + const interactiveRestoreBackup = async () => { + if (backupPath) { + // replace enhanced app with vanilla app.bak/app.asar.bak + print` {grey * ${messages["backup-found"]}: ${messages["restoring-backup"]}} `; + startSpinner(); + await restoreBackup(); + stopSpinner(); + return INCOMPLETE; + } else { + print` {red * ${messages["backup-not-found"]}: ${messages["manual-removal-instructions"]}}\n`; + return FAILURE; } - case 'remove': { - log`{bold.rgb(245,245,245) [NOTION-ENHANCER] REMOVE}`; - const res = await remove(notionPath, { delCache: promptRes }); - if (res) { - log`{bold.rgb(245,245,245) SUCCESS} {green ✔}`; - } else log`{bold.rgb(245,245,245) CANCELLED} {red ✘}`; - break; - } - case 'check': - case 'status': { - log`{bold.rgb(245,245,245) [NOTION-ENHANCER] CHECK}`; - const status = check(notionPath); - line.prev(); - if (opts.get('dev')) { - line.forward(24); - console.log(status); - } else { - line.forward(23); - line.write(': ' + status.message + '\r\n'); + }; + + const canEnhancementsBeApplied = async () => { + if (!appPath) { + // notion not installed + print` {red * ${messages["notion-not-found"]}}\n`; + return FAILURE; + } else if (insertVersion === manifest.version) { + // same version already applied + if (args["--overwrite"]) { + print` {grey * ${messages["inserting-enhancements"]}} `; + startSpinner(); + await applyEnhancements(); + stopSpinner(); + print` {grey * ${messages["version-applied"]}}\n`; + } else print` {grey * ${messages["notion-found"]}: ${messages["version-applied"]}}\n`; + return SUCCESS; } + if (insertVersion && insertVersion !== manifest.version) { + // diff version already applied + print` {grey * ${messages["notion-found"]}: ${messages["version-mismatch"]}}\n`; + const replaceEnhancements = // + ["Y", "y"].includes(await promptConfirmation(messages["prompt-version-replace"])); + print`\n`; + return replaceEnhancements ? await interactiveRestoreBackup() : CANCELLED; + } else return INCOMPLETE; + }, + interactiveApplyEnhancements = async () => { + if (appPath.endsWith(".asar")) { + print` {grey * ${messages["notion-is-packed"]}} `; + // asar blocks thread = spinner won't actually spin + // first frame at least can serve as waiting indicator + startSpinner(); + unpackApp(); + stopSpinner(); + } + // backup is used to restore app to pre-enhanced state + // new backup should be taken every enhancement + // e.g. in case old backup was from prev. version of app + if (!args["--no-backup"]) { + print` {grey * ${messages["creating-backup"]}} `; + startSpinner(); + await takeBackup(); + stopSpinner(); + } + print` {grey * ${messages["inserting-enhancements"]}} `; + startSpinner(); + await applyEnhancements(); + stopSpinner(); + print` {grey * ${messages["version-applied"]}}\n`; + return SUCCESS; + }; + + const interactiveRemoveEnhancements = async () => { + if (!appPath) { + // notion not installed + print` {red * ${messages["notion-not-found"]}}\n`; + return FAILURE; + } else if (insertVersion) { + print` {grey * ${messages["notion-found"]}: ${messages["version-applied"]}}\n`; + return (await interactiveRestoreBackup()) === INCOMPLETE ? SUCCESS : FAILURE; + } + print` {grey * ${messages["notion-found"]}: ${messages["not-applied"]}}\n`; + return SUCCESS; + }, + promptCacheRemoval = async () => { + // optionally remove ~/.notion-enhancer + if (existsSync(cachePath)) { + print` {grey * ${messages["cache-found"]}: ${cachePath}}\n`; + if (["Y", "y"].includes(await promptConfirmation(messages["prompt-cache-removal"]))) { + print` `; + startSpinner(); + await removeCache(); + stopSpinner(); + } else print`\n`; + } else print` {grey * ${messages["cache-not-found"]}}\n`; + }; + + switch (args["_"][0]) { + case "apply": { + print`{bold.whiteBright [NOTION-ENHANCER] APPLY}\n`; + let res = await canEnhancementsBeApplied(); + if (res === INCOMPLETE) res = await interactiveApplyEnhancements(); + print`${res}\n`; break; } + case "remove": { + print`{bold.whiteBright [NOTION-ENHANCER] REMOVE}\n`; + const res = await interactiveRemoveEnhancements(); + await promptCacheRemoval(); + print`${res}\n`; + break; + } + case "check": { + if (args["--json"]) { + printObject({ + appPath, + backupPath, + cachePath, + cacheExists: existsSync(cachePath), + insertVersion, + currentVersion: manifest.version, + }); + process.exit(); + } + print`{bold.whiteBright [NOTION-ENHANCER] CHECK:} `; + if (manifest.version === insertVersion) { + print`${messages["version-applied"]}\n`; + } else if (insertVersion) { + print`${messages["version-mismatch"]}\n`; + } else if (appPath) { + print`${messages["not-applied"]}\n`; + } else print`${messages["notion-not-found"]}\n`; + break; + } + default: - displayHelp(); + printHelp(commands, options); } -} catch (err) { - if (lastSpinner) lastSpinner.stop(); - handleError(err); - process.exit(1); +} catch (error) { + const message = error.message.split("\n")[0]; + if (__debug) { + print`{bold.red ${error.name}:} ${message}\n{grey ${error.stack + .split("\n") + .splice(1) + .map((at) => at.replace(/\s{4}/g, " ")) + .join("\n")}}`; + } else print`{bold.red Error:} ${message} {grey (run with -d for more information)}\n`; } diff --git a/insert/api b/insert/api deleted file mode 160000 index 9815d73..0000000 --- a/insert/api +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9815d73b9277e96864654a8d8dd48762039c9845 diff --git a/insert/dep b/insert/dep deleted file mode 160000 index 1a47625..0000000 --- a/insert/dep +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1a4762550fe185706be26678f734b0475066c3e4 diff --git a/insert/media b/insert/media deleted file mode 160000 index 2a0a179..0000000 --- a/insert/media +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2a0a17998385f1d86148b9213451b3a5deff6bae diff --git a/insert/repo b/insert/repo deleted file mode 160000 index 3a67243..0000000 --- a/insert/repo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3a67243fd5caec24b484276e563bdb8da7a0adcd diff --git a/package.json b/package.json index b837521..4052aa9 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,25 @@ { "name": "notion-enhancer", - "version": "0.11.0", + "version": "0.11.1-dev", "author": "dragonwocky (https://dragonwocky.me/)", "description": "an enhancer/customiser for the all-in-one productivity workspace notion.so", - "homepage": "https://github.com/notion-enhancer/desktop", + "homepage": "https://notion-enhancer.github.io", + "repository": "github:notion-enhancer/desktop", + "bugs": "https://github.com/notion-enhancer/desktop/issues", + "funding": "https://github.com/sponsors/dragonwocky", "license": "MIT", - "bin": { - "notion-enhancer": "bin.mjs" - }, + "bin": "bin.mjs", "type": "module", + "scripts": { + "build-ext": "./scripts/build-browser-extension.sh" + }, "engines": { "node": ">=16.x.x" }, - "scripts": { - "test": "echo \"no test specified\"", - "preuninstall": "node bin.js remove -n" - }, "dependencies": { - "asar": "^3.1.0", - "chalk": "^4.1.2" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/notion-enhancer/desktop.git" + "@electron/asar": "^3.2.2", + "arg": "^5.0.2", + "chalk-template": "^0.4.0" }, "keywords": [ "windows", @@ -40,7 +37,5 @@ "notion", "notion-enhancer" ], - "bugs": { - "url": "https://github.com/notion-enhancer/desktop/issues" - } + "packageManager": "yarn@3.3.0" } diff --git a/pkg/apply.mjs b/pkg/apply.mjs deleted file mode 100644 index 859e399..0000000 --- a/pkg/apply.mjs +++ /dev/null @@ -1,98 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -import fs from 'fs'; -import fsp from 'fs/promises'; -import path from 'path'; -import asar from 'asar'; - -import { log, line, spinner } from './cli.mjs'; -import { __dirname, pkg, findNotion, copyDir, readDirDeep } from './helpers.mjs'; - -import check from './check.mjs'; -import remove from './remove.mjs'; - -export default async function ( - notionFolder = findNotion(), - { overwritePrevious = undefined, patchPrevious = false, takeBackup = true } = {} -) { - let status = check(notionFolder); - switch (status.code) { - case 0: // not applied - break; - case 1: // corrupted - throw Error(status.message); - case 2: // same version already applied - if (!patchPrevious) { - log` {grey * notion-enhancer v${status.version} already applied}`; - return true; - } - break; - case 3: // diff version already applied - log` * ${status.message}`; - const prompt = ['Y', 'y', 'N', 'n', ''], - res = prompt.includes(overwritePrevious) - ? overwritePrevious - : await line.read(' {inverse > overwrite? [Y/n]:} ', prompt); - if (res.toLowerCase() === 'n') { - log` * keeping previous version: exiting`; - return false; - } - await remove(notionFolder, { cache: 'n' }); - status = await check(notionFolder); - } - - let s; - if (status.executable.endsWith('.asar')) { - s = spinner(' * unpacking app files').loop(); - asar.extractAll(status.executable, status.executable.replace(/\.asar$/, '')); - s.stop(); - } - if (status.code === 0 && takeBackup) { - s = spinner(' * backing up default app').loop(); - if (status.executable.endsWith('.asar')) { - await fsp.rename(status.executable, status.executable + '.bak'); - status.executable = status.executable.replace(/\.asar$/, ''); - } else { - await copyDir(status.executable, status.executable + '.bak'); - } - s.stop(); - } - - s = spinner(' * inserting enhancements').loop(); - if (status.code === 0) { - const notionFiles = (await readDirDeep(status.executable)) - .map((file) => file.path) - .filter((file) => file.endsWith('.js') && !file.includes('node_modules')); - for (const file of notionFiles) { - const target = file.slice(status.executable.length + 1, -3).replace(/\\/g, '/'), - replacer = path.resolve(`${__dirname(import.meta)}/replacers/${target}.mjs`); - if (fs.existsSync(replacer)) { - await (await import(`./replacers/${target}.mjs`)).default(file); - } - await fsp.appendFile( - file, - `\n\n//notion-enhancer\nrequire('notion-enhancer')('${target}', exports, (js) => eval(js));` - ); - } - } - const node_modules = path.resolve(`${status.executable}/node_modules/notion-enhancer`); - await copyDir(`${__dirname(import.meta)}/../insert`, node_modules); - s.stop(); - - s = spinner(' * recording version').loop(); - await fsp.writeFile( - path.resolve(`${node_modules}/package.json`), - `{ - "name": "notion-enhancer", - "version": "${pkg().version}", - "main": "init.cjs" - }` - ); - s.stop(); - - return true; -} diff --git a/pkg/check.mjs b/pkg/check.mjs deleted file mode 100644 index 473dc4f..0000000 --- a/pkg/check.mjs +++ /dev/null @@ -1,63 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -import fs from 'fs'; -import path from 'path'; - -import { pkg, findNotion, findEnhancerCache } from './helpers.mjs'; - -export default function (notionFolder = findNotion()) { - const resolvePath = (filepath) => path.resolve(`${notionFolder}/${filepath}`), - pathExists = (filepath) => fs.existsSync(resolvePath(filepath)), - enhancerVersion = pkg().version; - - const executableApp = pathExists('app'), - executableAsar = pathExists('app.asar'), - executable = executableApp ? 'app' : executableAsar ? 'app.asar' : undefined, - backupApp = pathExists('app.bak'), - backupAsar = pathExists('app.asar.bak'), - backup = backupApp ? 'app.bak' : backupAsar ? 'app.asar.bak' : undefined, - insert = pathExists('app/node_modules/notion-enhancer'), - insertVersion = insert - ? pkg(resolvePath('app/node_modules/notion-enhancer/package.json')).version - : undefined, - insertCache = findEnhancerCache(); - - const res = { - executable: executable ? resolvePath(executable) : undefined, - backup: backup ? resolvePath(backup) : undefined, - cache: fs.existsSync(insertCache) ? insertCache : undefined, - installation: path.resolve( - resolvePath('.') - .split(path.sep) - .reduceRight((prev, val) => { - if (val.toLowerCase().includes('notion') || prev.toLowerCase().includes('notion')) - prev = `${val}/${prev}`; - return prev; - }, '') - ), - }; - if (insert) { - if (insertVersion === enhancerVersion) { - res.code = 2; - res.version = enhancerVersion; - res.message = `notion-enhancer v${enhancerVersion} applied.`; - } else { - res.code = 3; - res.version = insertVersion; - res.message = `notion-enhancer v${insertVersion} found applied != v${enhancerVersion} package.`; - } - } else { - if (executable) { - res.code = 0; - res.message = 'notion-enhancer has not been applied.'; - } else { - res.code = 1; - res.message = 'notion installation has been corrupted, no executable found.'; - } - } - return res; -} diff --git a/pkg/cli.mjs b/pkg/cli.mjs deleted file mode 100644 index 08d21a8..0000000 --- a/pkg/cli.mjs +++ /dev/null @@ -1,142 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -'use strict'; - -import chalk from 'chalk'; - -export const log = (strs, ...tags) => { - if (!Array.isArray(strs)) strs = [strs]; - if (!strs.raw) strs.raw = [...strs]; - console.log(chalk(strs, ...tags)); -}; - -export const cursor = { - hide: () => process.stdout.write('\x1b[?25l'), - show: () => process.stdout.write('\x1b[?25h'), -}; - -export const line = { - clear: () => process.stdout.write('\r\x1b[K'), - backspace: (n = 1) => process.stdout.write('\b'.repeat(n)), - write: (string) => process.stdout.write(string), - prev: (n = 1) => process.stdout.write(`\x1b[${n}A`), - next: (n = 1) => process.stdout.write(`\x1b[${n}B`), - forward: (n = 1) => process.stdout.write(`\x1b[${n}C`), - back: (n = 1) => process.stdout.write(`\x1b[${n}D`), - new: () => process.stdout.write('\n'), - async read(prompt = '', values = []) { - let input = ''; - prompt = [prompt]; - prompt.raw = [prompt[0]]; - prompt = chalk(prompt); - this.new(); - do { - this.prev(); - this.clear(); - this.write(prompt); - input = await new Promise((res, rej) => { - process.stdin.resume(); - process.stdin.setEncoding('utf8'); - process.stdin.once('data', (key) => { - process.stdin.pause(); - res(key.slice(0, -1)); - }); - }); - } while (values.length && !values.includes(input)); - return input; - }, -}; - -export let lastSpinner; - -export const spinner = ( - message, - frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'], - complete = '→' -) => { - if (lastSpinner?.interval) lastSpinner.stop(); - const spinner = { - interval: undefined, - i: 0, - step() { - this.i = (this.i + 1) % frames.length; - line.backspace(3); - line.write(chalk` {bold.yellow ${frames[this.i]}} `); - cursor.hide(); - return this; - }, - loop(ms = 80) { - if (this.interval) clearInterval(this.interval); - this.interval = setInterval(() => this.step(), ms); - return this; - }, - stop() { - if (this.interval) { - clearInterval(this.interval); - this.interval = undefined; - } - line.backspace(3); - line.write(chalk` {bold.yellow ${complete}}\n`); - cursor.show(); - return this; - }, - }; - line.write(chalk`${message} {bold.yellow ${frames[spinner.i]}} `); - lastSpinner = spinner; - return spinner; -}; - -export const args = () => process.argv.slice(2).filter((arg) => !arg.startsWith('-')); - -export const options = (aliases = {}) => { - return new Map( - process.argv - .slice(2) - .filter((arg) => arg.startsWith('-')) - .map((arg) => { - let opt, - val = true; - if (arg.startsWith('--')) { - if (arg.includes('=')) { - [opt, val] = arg.slice(2).split(/=((.+)|$)/); - } else opt = arg.slice(2); - } else { - opt = arg.slice(1); - } - if (parseInt(val).toString() === val) val = +val; - if (aliases[opt]) opt = aliases[opt]; - return [opt, val]; - }) - ); -}; - -export const help = ({ - name = process.argv[1].split('/').reverse()[0], - usage = `${name} [options]`, - version = '', - link = '', - commands = [], - options = [], -}) => { - if (version) version = ' v' + version; - const cmdPad = Math.max(...commands.map((cmd) => cmd[0].length)), - optPad = Math.max(...options.map((opt) => opt[0].length)); - commands = commands - .map((cmd) => ` ${cmd[0].padEnd(cmdPad)} ${chalk`{grey :}`} ${cmd[1]}`) - .join('\n'); - options = options - .map((opt) => ` ${opt[0].padEnd(optPad)} ${chalk`{grey :}`} ${opt[1]}`) - .join('\n'); - log`{bold.rgb(245,245,245) ${name}${version}}`; - if (link) log`{grey ${link}}`; - log`\n{bold.rgb(245,245,245) USAGE}`; - log`{yellow $} ${usage}`; - log`\n{bold.rgb(245,245,245) COMMANDS}`; - log`${commands}`; - log`\n{bold.rgb(245,245,245) OPTIONS}`; - log`${options}`; -}; diff --git a/pkg/helpers.mjs b/pkg/helpers.mjs deleted file mode 100644 index ff05f7d..0000000 --- a/pkg/helpers.mjs +++ /dev/null @@ -1,110 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -import os from 'os'; -import fs from 'fs'; -import fsp from 'fs/promises'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import { execSync } from 'child_process'; - -export const __dirname = (meta) => path.dirname(fileURLToPath(meta.url)); - -export const pkg = (filepath = `${__dirname(import.meta)}/../package.json`) => { - try { - return JSON.parse(fs.readFileSync(path.resolve(filepath))); - } catch { - return {}; - } -}; - -export const platform = - process.platform === 'linux' && os.release().toLowerCase().includes('microsoft') - ? 'wsl' - : process.platform; - -let __notion; -export const findNotion = () => { - if (__notion) return __notion; - switch (platform) { - case 'darwin': - __notion = ''; - const userInstall = `/Users/${process.env.USER}/Applications/Notion.app/Contents/Resources`, - globalInstall = '/Applications/Notion.app/Contents/Resources'; - if (fs.existsSync(userInstall)) { - __notion = userInstall; - } else if (fs.existsSync(globalInstall)) { - __notion = globalInstall; - } - break; - case 'win32': - __notion = process.env.LOCALAPPDATA + '\\Programs\\Notion\\resources'; - break; - case 'wsl': - const [drive, ...windowsPath] = execSync('cmd.exe /c echo %localappdata%', { - encoding: 'utf8', - stdio: 'pipe', - }); - __notion = `/mnt/${drive.toLowerCase()}${windowsPath - .slice(1, -2) - .join('') - .replace(/\\/g, '/')}/Programs/Notion/resources`; - break; - case 'linux': - // https://aur.archlinux.org/packages/notion-app/ - if (fs.existsSync('/opt/notion-app')) __notion = '/opt/notion-app'; - } - return __notion; -}; - -let __enhancerCache; -export const findEnhancerCache = () => { - if (__enhancerCache) return __enhancerCache; - let home = os.homedir(); - if (platform === 'wsl') { - const [drive, ...windowsPath] = execSync('cmd.exe /c echo %systemdrive%%homepath%', { - encoding: 'utf8', - stdio: 'pipe', - }); - home = `/mnt/${drive.toLowerCase()}${windowsPath - .slice(1, -2) - .join('') - .replace(/\\/g, '/')}`; - } - __enhancerCache = path.resolve(`${home}/.notion-enhancer`); - return __enhancerCache; -}; - -export const copyDir = async (src, dest) => { - src = path.resolve(src); - dest = path.resolve(dest); - if (!fs.existsSync(dest)) await fsp.mkdir(dest); - for (let file of await fsp.readdir(src)) { - const stat = await fsp.lstat(path.join(src, file)); - if (stat.isDirectory()) { - await copyDir(path.join(src, file), path.join(dest, file)); - } else if (stat.isSymbolicLink()) { - await fsp.symlink(await fsp.readlink(path.join(src, file)), path.join(dest, file)); - } else await fsp.copyFile(path.join(src, file), path.join(dest, file)); - } - return true; -}; - -export const readDirDeep = async (dir) => { - dir = path.resolve(dir); - let files = []; - for (let file of await fsp.readdir(dir)) { - if (['node_modules', '.git'].includes(file)) continue; - file = path.join(dir, file); - const stat = await fsp.lstat(file); - if (stat.isDirectory()) { - files = files.concat(await readDirDeep(file)); - } else if (stat.isSymbolicLink()) { - files.push({ type: 'symbolic', path: file }); - } else files.push({ type: 'file', path: file }); - } - return files; -}; diff --git a/pkg/remove.mjs b/pkg/remove.mjs deleted file mode 100644 index de164f8..0000000 --- a/pkg/remove.mjs +++ /dev/null @@ -1,48 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -import fsp from 'fs/promises'; - -import { log, spinner, line } from './cli.mjs'; -import { findNotion } from './helpers.mjs'; - -import check from './check.mjs'; - -export default async function (notionFolder = findNotion(), { delCache = undefined } = {}) { - const status = check(notionFolder); - - let s; - if (status.code > 1 && status.executable) { - s = spinner(' * removing enhancements').loop(); - await fsp.rm(status.executable, { recursive: true }); - s.stop(); - } else log` {grey * enhancements not found: skipping}`; - - if (status.backup) { - s = spinner(' * restoring backup').loop(); - await fsp.rename(status.backup, status.backup.replace(/\.bak$/, '')); - s.stop(); - } else log` {grey * backup not found: skipping}`; - - if (status.cache) { - log` * enhancer cache found: ${status.cache}`; - const prompt = ['Y', 'y', 'N', 'n', '']; - let res; - if (prompt.includes(delCache)) { - res = delCache; - log` {inverse > delete? [Y/n]:} ${delCache} {grey (auto-filled)}`; - } else res = await line.read(' {inverse > delete? [Y/n]:} ', prompt); - if (res.toLowerCase() === 'n') { - log` * keeping enhancer cache`; - } else { - s = spinner(' * deleting enhancer cache').loop(); - await fsp.rm(status.cache, { recursive: true }); - s.stop(); - } - } else log` {grey * enhancer cache not found: skipping}`; - - return true; -} diff --git a/pkg/replacers/main/main.mjs b/pkg/replacers/main/main.mjs deleted file mode 100644 index 617e126..0000000 --- a/pkg/replacers/main/main.mjs +++ /dev/null @@ -1,23 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -'use strict'; - -import fsp from 'fs/promises'; - -export default async function (filepath) { - // https://github.com/notion-enhancer/desktop/issues/160 - // enable the notion:// url scheme/protocol on linux - const contents = await fsp.readFile(filepath, 'utf8'); - await fsp.writeFile( - filepath, - contents.replace( - /process.platform === "win32"/g, - 'process.platform === "win32" || process.platform === "linux"' - ) - ); - return true; -} diff --git a/pkg/replacers/main/schemeHandler.mjs b/pkg/replacers/main/schemeHandler.mjs deleted file mode 100644 index 02251dd..0000000 --- a/pkg/replacers/main/schemeHandler.mjs +++ /dev/null @@ -1,45 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -'use strict'; - -import fsp from 'fs/promises'; - -export default async function (filepath) { - // https://github.com/notion-enhancer/desktop/issues/291 - // bypass csp issues by intercepting notion:// protocol - const contents = await fsp.readFile(filepath, 'utf8'); - await fsp.writeFile( - filepath, - contents.replace( - /const success = protocol\.registerStreamProtocol\(config_1.default.protocol, async \(req, callback\) => \{/, - `const success = protocol.registerStreamProtocol(config_1.default.protocol, async (req, callback) => { - { - // notion-enhancer - const schemePrefix = 'notion://www.notion.so/__notion-enhancer/'; - if (req.url.startsWith(schemePrefix)) { - const { search, hash, pathname } = new URL(req.url), - resolvePath = (path) => require('path').resolve(\`\${__dirname}/\${path}\`), - fileExt = pathname.split('.').reverse()[0], - mimeDB = Object.entries(require('notion-enhancer/dep/mime-db.json')), - mimeType = mimeDB - .filter(([mime, data]) => data.extensions) - .find(([mime, data]) => data.extensions.includes(fileExt)); - let filePath = '../node_modules/notion-enhancer/'; - filePath += req.url.slice(schemePrefix.length); - if (search) filePath = filePath.slice(0, -search.length); - if (hash) filePath = filePath.slice(0, -hash.length); - callback({ - data: require('fs').createReadStream(resolvePath(filePath)), - headers: { 'content-type': mimeType }, - }); - } - }` - ) - ); - - return true; -} diff --git a/pkg/replacers/main/systemMenu.mjs b/pkg/replacers/main/systemMenu.mjs deleted file mode 100644 index f5d18ea..0000000 --- a/pkg/replacers/main/systemMenu.mjs +++ /dev/null @@ -1,22 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -'use strict'; - -import fsp from 'fs/promises'; - -export default async function (filepath) { - // so that e.g. tabs access and modify the template - const contents = await fsp.readFile(filepath, 'utf8'); - await fsp.writeFile( - filepath, - contents.replace( - /electron_1\.Menu\.setApplicationMenu\(menu\);/g, - 'electron_1.Menu.setApplicationMenu(menu); return template;' - ) - ); - return true; -} diff --git a/scripts/build-browser-extension.sh b/scripts/build-browser-extension.sh new file mode 100755 index 0000000..0150883 --- /dev/null +++ b/scripts/build-browser-extension.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +version=$(node -p "require('./package.json').version") + +cd src +mkdir -p ../dist +rm -f "../dist/notion-enhancer-$version.zip" +zip -r9 "../dist/notion-enhancer-$version.zip" . -x electron/\* \ No newline at end of file diff --git a/scripts/enhance-desktop-app.mjs b/scripts/enhance-desktop-app.mjs new file mode 100755 index 0000000..f95bbdb --- /dev/null +++ b/scripts/enhance-desktop-app.mjs @@ -0,0 +1,189 @@ +/** + * notion-enhancer + * (c) 2022 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +import asar from "@electron/asar"; +import os from "node:os"; +import { promises as fsp, existsSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { join, resolve } from "node:path"; +import { execSync } from "node:child_process"; +import { createRequire } from "node:module"; + +import patch from "./patch-desktop-app.mjs"; + +let __notionResources, __enhancerCache; +const nodeRequire = createRequire(import.meta.url), + manifest = nodeRequire("../package.json"), + platform = + process.platform === "linux" && os.release().toLowerCase().includes("microsoft") + ? "wsl" + : process.platform, + polyfillWslEnv = (name) => { + if (platform !== "wsl" || process.env[name]) return process.env[name]; + // adds a windows environment variable to process.env + // in a wsl environment, inc. path conversion + const value = execSync(`cmd.exe /c echo %${name}%`, { + encoding: "utf8", + stdio: "pipe", + }).trim(), + isAbsolutePath = /^[a-zA-Z]:[\\\/]/.test(value), + onSystemDrive = /^[\\\/]/.test(value); + if (isAbsolutePath) { + // e.g. C:\Program Files + const drive = value[0].toLowerCase(), + path = value.slice(2).replace(/\\/g, "/"); + process.env[name] = `/mnt/${drive}${path}`; + } else if (onSystemDrive) { + // e.g. \Program Files + const drive = polyfillWslEnv("SYSTEMDRIVE")[0].toLowerCase(), + path = value.replace(/\\/g, "/"); + process.env[name] = `/mnt/${drive}${path}`; + } else process.env[name] = value; + return process.env[name]; + }, + readdirDeep = async (dir) => { + dir = resolve(dir); + let files = []; + for (let file of await fsp.readdir(dir)) { + if (["node_modules", ".git"].includes(file)) continue; + file = join(dir, file); + const stat = await fsp.lstat(file); + if (stat.isDirectory()) { + files = files.concat(await readdirDeep(file)); + } else if (stat.isSymbolicLink()) { + // + } else files.push(file); + } + return files; + }; + +const setNotionPath = (path) => { + // sets notion resource path to user provided value + // e.g. with the --path cli option + __notionResources = path; + }, + getResourcePath = (path) => { + if (__notionResources) return resolve(`${__notionResources}/${path}`); + polyfillWslEnv("LOCALAPPDATA"); + polyfillWslEnv("PROGRAMW6432"); + const potentialPaths = [ + // [["targeted", "platforms"], "/path/to/notion/resources"] + [["darwin"], `/Users/${process.env.USER}/Applications/Notion.app/Contents/Resources`], + [["darwin"], "/Applications/Notion.app/Contents/Resources"], + [["win32", "wsl"], resolve(`${process.env.LOCALAPPDATA}/Programs/Notion/resources`)], + [["win32", "wsl"], resolve(`${process.env.PROGRAMW6432}/Notion/resources`)], + // https://aur.archlinux.org/packages/notion-app/ + [["linux"], "/opt/notion-app"], + ]; + for (const [targetPlatforms, testPath] of potentialPaths) { + if (!targetPlatforms.includes(platform)) continue; + if (!existsSync(testPath)) continue; + __notionResources = testPath; + return resolve(`${__notionResources}/${path}`); + } + }, + // prefer unpacked if both exist + getAppPath = () => ["app", "app.asar"].map(getResourcePath).find(existsSync), + getBackupPath = () => ["app.bak", "app.asar.bak"].map(getResourcePath).find(existsSync), + getCachePath = () => { + if (__enhancerCache) return __enhancerCache; + const home = platform === "wsl" ? polyfillWslEnv("HOMEPATH") : os.homedir(); + __enhancerCache = resolve(`${home}/.notion-enhancer`); + return __enhancerCache; + }, + checkEnhancementVersion = () => { + const manifestPath = getResourcePath("app/node_modules/notion-enhancer/package.json"); + if (!existsSync(manifestPath)) return undefined; + const insertVersion = nodeRequire(manifestPath).version; + return insertVersion; + }; + +const unpackApp = () => { + const appPath = getAppPath(); + if (!appPath || !appPath.endsWith("asar")) return false; + // asar reads synchronously + asar.extractAll(appPath, appPath.replace(/\.asar$/, "")); + return true; + }, + applyEnhancements = async () => { + const appPath = getAppPath(); + if (!appPath || appPath.endsWith("asar")) return false; + const srcPath = fileURLToPath(new URL("../src", import.meta.url)), + insertPath = getResourcePath("app/node_modules/notion-enhancer"); + if (existsSync(insertPath)) await fsp.rm(insertPath, { recursive: true }); + // insert the notion-enhancer/src folder into notion's node_modules folder + const excludedDests = [ + getResourcePath("app/node_modules/notion-enhancer/browser"), + getResourcePath("app/node_modules/notion-enhancer/manifest.json"), + ]; + await fsp.cp(srcPath, insertPath, { + recursive: true, + // exclude browser-specific files + filter: (_, dest) => !excludedDests.includes(dest), + }); + // call patch-desktop-app.mjs on each file + const notionScripts = (await readdirDeep(appPath)).filter((file) => file.endsWith(".js")), + scriptUpdates = []; + for (const file of notionScripts) { + const scriptId = file.slice(appPath.length + 1, -3).replace(/\\/g, "/"), + scriptContent = await fsp.readFile(file, { encoding: "utf8" }), + patchedContent = await patch(scriptId, scriptContent), + changesMade = patchedContent !== scriptContent; + if (changesMade) scriptUpdates.push(fsp.writeFile(file, patchedContent)); + } + // create package.json + const manifestPath = getResourcePath("app/node_modules/notion-enhancer/package.json"), + insertManifest = { ...manifest, main: "electron/init.cjs" }; + // remove cli-specific fields + delete insertManifest.bin; + delete insertManifest.type; + delete insertManifest.engines; + delete insertManifest.dependencies; + delete insertManifest.packageManager; + scriptUpdates.push(fsp.writeFile(manifestPath, JSON.stringify(insertManifest))); + await Promise.all(scriptUpdates); + return true; + }, + takeBackup = async () => { + const appPath = getAppPath(); + if (!appPath) return false; + const backupPath = getBackupPath(); + if (backupPath) await fsp.rm(backupPath, { recursive: true }); + const destPath = `${appPath}.bak`; + if (!appPath.endsWith(".asar")) { + await fsp.cp(appPath, destPath, { recursive: true }); + } else await fsp.rename(appPath, destPath); + return true; + }, + restoreBackup = async () => { + const backupPath = getBackupPath(); + if (!backupPath) return false; + const destPath = backupPath.replace(/\.bak$/, ""); + if (existsSync(destPath)) await fsp.rm(destPath, { recursive: true }); + await fsp.rename(backupPath, destPath); + const appPath = getAppPath(); + if (destPath !== appPath) await fsp.rm(appPath, { recursive: true }); + return true; + }, + removeCache = async () => { + if (!existsSync(getCachePath())) return; + await fsp.rm(getCachePath()); + return true; + }; + +export { + getResourcePath, + getAppPath, + getBackupPath, + getCachePath, + checkEnhancementVersion, + setNotionPath, + unpackApp, + applyEnhancements, + takeBackup, + restoreBackup, + removeCache, +}; diff --git a/scripts/patch-desktop-app.mjs b/scripts/patch-desktop-app.mjs new file mode 100755 index 0000000..10ee885 --- /dev/null +++ b/scripts/patch-desktop-app.mjs @@ -0,0 +1,67 @@ +/** + * notion-enhancer + * (c) 2022 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +const patches = { + "*": async (scriptId, scriptContent) => { + const prevTriggerFound = /require\(['|"]notion-enhancer['|"]\)/.test(scriptContent); + if (prevTriggerFound) return scriptContent; + const enhancerTrigger = + '\n\n/*notion-enhancer*/require("notion-enhancer")' + + `('${scriptId}',exports,(js)=>eval(js));`; + return scriptContent + enhancerTrigger; + }, + + "main/main": async (scriptContent) => { + // https://github.com/notion-enhancer/desktop/issues/160 + // enable the notion:// url scheme/protocol on linux + const searchValue = /process.platform === "win32"/g, + replaceValue = 'process.platform === "win32" || process.platform === "linux"'; + if (scriptContent.includes(replaceValue)) return scriptContent; + return scriptContent.replace(searchValue, replaceValue); + }, + + "main/schemeHandler": async (scriptContent) => { + // https://github.com/notion-enhancer/desktop/issues/291 + // bypass csp issues by intercepting notion:// protocol + const searchValue = + "protocol.registerStreamProtocol(config_1.default.protocol, async (req, callback) => {", + replaceValue = `${searchValue} + { /* notion-enhancer */ + const schemePrefix = "notion://www.notion.so/__notion-enhancer/"; + if (req.url.startsWith(schemePrefix)) { + const { search, hash, pathname } = new URL(req.url), + fileExt = pathname.split(".").reverse()[0], + filePath = \`../node_modules/notion-enhancer/\${req.url.slice( + schemePrefix.length, + -(search.length + hash.length) + )}\`, + mimeType = Object.entries(require("notion-enhancer/dep/mime-db.json")) + .filter(([_, data]) => data.extensions) + .find(([_, data]) => data.extensions.includes(fileExt)); + callback({ + data: require("fs").createReadStream(require("path").resolve(\`\${__dirname}/\${filePath}\`)), + headers: { "content-type": mimeType }, + }); + } + }`; + if (scriptContent.includes(replaceValue)) return scriptContent; + return scriptContent.replace(searchValue, replaceValue); + }, + + "main/systemMenu": async (scriptContent) => { + // exposes template for modification + const searchValue = "electron_1.Menu.setApplicationMenu(menu);", + replaceValue = `${searchValue} return template;`; + if (scriptContent.includes(replaceValue)) return scriptContent; + return scriptContent.replace(searchValue, replaceValue); + }, +}; + +export default async (scriptId, scriptContent) => { + if (patches["*"]) scriptContent = await patches["*"](scriptId, scriptContent); + if (patches[scriptId]) scriptContent = await patches[scriptId](scriptContent); + return scriptContent; +}; diff --git a/src/browser/LICENSE b/src/browser/LICENSE new file mode 100644 index 0000000..b503961 --- /dev/null +++ b/src/browser/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 dragonwocky (https://dragonwocky.me/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/browser/README.md b/src/browser/README.md new file mode 100644 index 0000000..420700e --- /dev/null +++ b/src/browser/README.md @@ -0,0 +1,5 @@ +# notion-enhancer/extension + +an enhancer/customiser for the all-in-one productivity workspace notion.so (browser) + +[read the docs online](https://notion-enhancer.github.io/) diff --git a/src/browser/env/env.mjs b/src/browser/env/env.mjs new file mode 100644 index 0000000..17c905d --- /dev/null +++ b/src/browser/env/env.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'; + +/** environment-specific methods and constants */ + +/** + * 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/src/browser/env/fs.mjs b/src/browser/env/fs.mjs new file mode 100644 index 0000000..020a37d --- /dev/null +++ b/src/browser/env/fs.mjs @@ -0,0 +1,48 @@ +/* + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** environment-specific file reading */ + +/** + * transform a path relative to the enhancer root directory into an absolute path + * @param {string} path - a url or within-the-enhancer filepath + * @returns {string} an absolute filepath + */ +export const localPath = chrome.runtime.getURL; + +/** + * fetch and parse a json file's contents + * @param {string} path - a url or within-the-enhancer filepath + * @param {object=} opts - the second argument of a fetch() request + * @returns {object} the json value of the requested file as a js object + */ +export const getJSON = (path, opts = {}) => + fetch(path.startsWith('http') ? path : localPath(path), opts).then((res) => res.json()); + +/** + * fetch a text file's contents + * @param {string} path - a url or within-the-enhancer filepath + * @param {object=} opts - the second argument of a fetch() request + * @returns {string} the text content of the requested file + */ +export const getText = (path, opts = {}) => + fetch(path.startsWith('http') ? path : localPath(path), opts).then((res) => res.text()); + +/** + * check if a file exists + * @param {string} path - a url or within-the-enhancer filepath + * @returns {boolean} whether or not the file exists + */ +export const isFile = async (path) => { + try { + await fetch(path.startsWith('http') ? path : localPath(path)); + return true; + } catch { + return false; + } +}; diff --git a/src/browser/env/storage.mjs b/src/browser/env/storage.mjs new file mode 100644 index 0000000..0018513 --- /dev/null +++ b/src/browser/env/storage.mjs @@ -0,0 +1,116 @@ +/* + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** environment-specific data persistence */ + +const _queue = [], + _onChangeListeners = []; + +/** + * get persisted data + * @param {string[]} path - the path of keys to the value being fetched + * @param {unknown=} fallback - a default value if the path is not matched + * @returns {Promise} value ?? fallback + */ +export const get = (path, fallback = undefined) => { + if (!path.length) return fallback; + return new Promise((res, rej) => + chrome.storage.local.get(async (values) => { + let value = values; + while (path.length) { + if (value === undefined) { + value = fallback; + break; + } + value = value[path.shift()]; + } + res(value ?? fallback); + }) + ); +}; + +/** + * persist data + * @param {string[]} path - the path of keys to the value being set + * @param {unknown} value - the data to save + * @returns {Promise} resolves when data has been saved + */ +export const set = (path, value) => { + if (!path.length) return undefined; + const precursor = _queue[_queue.length - 1] || undefined, + interaction = new Promise(async (res, rej) => { + if (precursor !== undefined) { + await precursor; + _queue.shift(); + } + const pathClone = [...path], + namespace = path[0]; + chrome.storage.local.get(async (values) => { + let pointer = values, + old; + while (path.length) { + const key = path.shift(); + if (!path.length) { + old = pointer[key]; + pointer[key] = value; + break; + } + pointer[key] = pointer[key] ?? {}; + pointer = pointer[key]; + } + chrome.storage.local.set({ [namespace]: values[namespace] }, () => { + _onChangeListeners.forEach((listener) => + listener({ path: pathClone, new: value, old }) + ); + res(value); + }); + }); + }); + _queue.push(interaction); + return interaction; +}; + +/** + * create a wrapper for accessing a partition of the storage + * @param {string[]} 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 = (namespace, getFunc = get, setFunc = set) => { + if (typeof namespace === 'string') namespace = [namespace]; + return { + get: (path = [], fallback = undefined) => getFunc([...namespace, ...path], fallback), + set: (path, value) => setFunc([...namespace, ...path], value), + }; +}; + +/** + * add an event listener for changes in storage + * @param {onStorageChangeCallback} callback - called whenever a change in + * storage is initiated from the current process + */ +export const addChangeListener = (callback) => { + _onChangeListeners.push(callback); +}; + +/** + * remove a listener added with storage.addChangeListener + * @param {onStorageChangeCallback} callback + */ +export const removeChangeListener = (callback) => { + _onChangeListeners = _onChangeListeners.filter((listener) => listener !== callback); +}; + +/** + * @callback onStorageChangeCallback + * @param {object} event + * @param {string} event.path- the path of keys to 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/src/browser/init.js b/src/browser/init.js new file mode 100644 index 0000000..e8de939 --- /dev/null +++ b/src/browser/init.js @@ -0,0 +1,35 @@ +/* + * notion-enhancer + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +(async () => { + const site = location.host.endsWith('.notion.site'), + page = location.pathname.split(/[/-]/g).reverse()[0].length === 32, + whitelisted = ['/', '/onboarding'].includes(location.pathname), + signedIn = localStorage['LRU:KeyValueStore2:current-user-id']; + + if (site || page || (whitelisted && signedIn)) { + const api = await import(chrome.runtime.getURL('api/index.mjs')), + { fs, registry, web } = api; + + 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(fs.localPath(`repo/${mod._dir}/${script}`)); + script.default(api, await registry.db(mod.id)); + } + } + + const errors = await registry.errors(); + if (errors.length) { + console.log('[notion-enhancer] registry errors:'); + console.table(errors); + } + } +})(); diff --git a/src/browser/worker.js b/src/browser/worker.js new file mode 100644 index 0000000..48f5833 --- /dev/null +++ b/src/browser/worker.js @@ -0,0 +1,60 @@ +/* + * notion-enhancer + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +function focusMenu() { + chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { + const url = chrome.runtime.getURL('repo/menu/menu.html'), + menu = tabs.find((tab) => tab.url.startsWith(url)); + if (menu) { + chrome.tabs.highlight({ 'tabs': menu.index }); + } else chrome.tabs.create({ url }); + }); +} +chrome.browserAction.onClicked.addListener(focusMenu); + +function focusNotion() { + chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { + const notion = tabs.find((tab) => { + const url = new URL(tab.url), + matches = url.host.endsWith('.notion.so') || url.host.endsWith('.notion.site'); + return matches; + }); + if (notion) { + chrome.tabs.highlight({ 'tabs': notion.index }); + } else chrome.tabs.create({ url: 'https://notion.so/' }); + }); +} + +function reload() { + chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { + const menu = chrome.runtime.getURL('repo/menu/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 'focusMenu': + focusMenu(); + break; + case 'focusNotion': + focusNotion(); + break; + case 'reload': + reload(); + break; + } + return true; +}); diff --git a/src/common/.github/workflows/update-parents.yml b/src/common/.github/workflows/update-parents.yml new file mode 100644 index 0000000..ac428e4 --- /dev/null +++ b/src/common/.github/workflows/update-parents.yml @@ -0,0 +1,29 @@ +name: 'update parent repositories' + +on: + push: + branches: + - dev + +jobs: + sync: + name: update parent + runs-on: ubuntu-latest + strategy: + matrix: + repo: ['notion-enhancer/extension', 'notion-enhancer/desktop'] + steps: + - name: checkout repo + uses: actions/checkout@v2 + with: + token: ${{ secrets.CI_TOKEN }} + submodules: true + repository: ${{ matrix.repo }} + - name: pull updates + run: | + git pull --recurse-submodules + git submodule update --remote --recursive + - name: commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: '[${{ github.event.repository.name }}] ${{ github.event.head_commit.message }}' diff --git a/src/common/LICENSE b/src/common/LICENSE new file mode 100644 index 0000000..b503961 --- /dev/null +++ b/src/common/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 dragonwocky (https://dragonwocky.me/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/common/README.md b/src/common/README.md new file mode 100644 index 0000000..ab8ffc8 --- /dev/null +++ b/src/common/README.md @@ -0,0 +1,5 @@ +# notion-enhancer/api + +the standard api available within the notion-enhancer + +[read the docs online](https://notion-enhancer.github.io/documentation/api) diff --git a/src/common/components/corner-action.css b/src/common/components/corner-action.css new file mode 100644 index 0000000..fdff4ac --- /dev/null +++ b/src/common/components/corner-action.css @@ -0,0 +1,55 @@ +/** + * notion-enhancer: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2021 CloudHill (https://github.com/CloudHill) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +#enhancer--corner-actions { + position: absolute; + bottom: 26px; + right: 26px; + z-index: 101; + cursor: default; + pointer-events: none; + display: flex; + flex-direction: row-reverse; +} + +#enhancer--corner-actions > div { + position: static !important; + width: 36px; + height: 36px; + margin-left: 12px; + pointer-events: auto; + border-radius: 100%; + font-size: 20px; + + display: flex; + align-items: center; + justify-content: center; + color: var(--theme--icon); + fill: var(--theme--icon); + background: var(--theme--ui_corner_action) !important; + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.15)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.15)) 0px 2px 4px !important; + + user-select: none; + cursor: pointer; +} +#enhancer--corner-actions > div:hover { + background: var(--theme--ui_corner_action-hover) !important; +} +#enhancer--corner-actions > div:active { + background: var(--theme--ui_corner_action-active) !important; +} +#enhancer--corner-actions > div.hidden { + display: none; +} + +#enhancer--corner-actions > div > svg { + width: 22px; + height: 22px; + color: var(--theme--icon); + fill: var(--theme--icon); +} diff --git a/src/common/components/corner-action.mjs b/src/common/components/corner-action.mjs new file mode 100644 index 0000000..bf42f9b --- /dev/null +++ b/src/common/components/corner-action.mjs @@ -0,0 +1,43 @@ +/** + * notion-enhancer: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2021 CloudHill (https://github.com/CloudHill) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** shared notion-style elements */ + +import { web } from '../index.mjs'; + +let $stylesheet, $cornerButtonsContainer; + +/** + * adds a button to notion's bottom right corner + * @param {string} icon - an svg string + * @param {function} listener - the function to call when the button is clicked + * @returns {Element} the appended corner action element + */ +export const addCornerAction = async (icon, listener) => { + if (!$stylesheet) { + $stylesheet = web.loadStylesheet('api/components/corner-action.css'); + $cornerButtonsContainer = web.html`
`; + } + + await web.whenReady(['.notion-help-button']); + const $helpButton = document.querySelector('.notion-help-button'), + $onboardingButton = document.querySelector('.onboarding-checklist-button'); + if ($onboardingButton) $cornerButtonsContainer.prepend($onboardingButton); + $cornerButtonsContainer.prepend($helpButton); + web.render( + document.querySelector('.notion-app-inner > .notion-cursor-listener'), + $cornerButtonsContainer + ); + + const $actionButton = web.html`
${icon}
`; + $actionButton.addEventListener('click', listener); + web.render($cornerButtonsContainer, $actionButton); + + return $actionButton; +}; diff --git a/src/common/components/feather.mjs b/src/common/components/feather.mjs new file mode 100644 index 0000000..9bd68fd --- /dev/null +++ b/src/common/components/feather.mjs @@ -0,0 +1,33 @@ +/** + * notion-enhancer: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** shared notion-style elements */ + +import { fs, web } from '../index.mjs'; + +let _$iconSheet; + +/** + * generate an icon from the feather icons set + * @param {string} name - the name/id of the icon + * @param {object} attrs - an object of attributes to apply to the icon e.g. classes + * @returns {string} an svg string + */ +export const feather = async (name, attrs = {}) => { + if (!_$iconSheet) { + _$iconSheet = web.html`${await fs.getText('dep/feather-sprite.svg')}`; + } + attrs.style = ( + (attrs.style ? attrs.style + ';' : '') + + 'stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill:none;' + ).trim(); + attrs.viewBox = '0 0 24 24'; + return ` `${web.escape(key)}="${web.escape(val)}"`) + .join(' ')}>${_$iconSheet.getElementById(name)?.innerHTML}`; +}; diff --git a/src/common/components/index.mjs b/src/common/components/index.mjs new file mode 100644 index 0000000..28c52b5 --- /dev/null +++ b/src/common/components/index.mjs @@ -0,0 +1,55 @@ +/** + * notion-enhancer: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * shared notion-style elements + * @namespace components + */ +import * as _api from '../index.mjs'; // trick jsdoc + +/** + * add a tooltip to show extra information on hover + * @param {HTMLElement} $ref - the element that will trigger the tooltip when hovered + * @param {string|HTMLElement} $content - markdown or element content of the tooltip + * @param {object=} options - configuration of how the tooltip should be displayed + * @param {number=} options.delay - the amount of time in ms the element needs to be hovered over + * for the tooltip to be shown (default: 100) + * @param {string=} options.offsetDirection - which side of the element the tooltip + * should be shown on: 'top', 'bottom', 'left' or 'right' (default: 'bottom') + * @param {number=} options.maxLines - the max number of lines that the content may be wrapped + * to, used to position and size the tooltip correctly (default: 1) + */ +export { addTooltip } from './tooltip.mjs'; + +/** + * generate an icon from the feather icons set + * @param {string} name - the name/id of the icon + * @param {object} attrs - an object of attributes to apply to the icon e.g. classes + * @returns {string} an svg string + */ +export { feather } from './feather.mjs'; + +/** + * adds a view to the enhancer's side panel + * @param {object} panel - information used to construct and render the panel + * @param {string} panel.id - a uuid, used to restore the last open view on reload + * @param {string} panel.icon - an svg string + * @param {string} panel.title - the name of the view + * @param {Element} panel.$content - an element containing the content of the view + * @param {function} panel.onBlur - runs when the view is selected/focused + * @param {function} panel.onFocus - runs when the view is unfocused/closed + */ +export { addPanelView } from './panel.mjs'; + +/** + * adds a button to notion's bottom right corner + * @param {string} icon - an svg string + * @param {function} listener - the function to call when the button is clicked + * @returns {Element} the appended corner action element + */ +export { addCornerAction } from './corner-action.mjs'; diff --git a/src/common/components/panel.css b/src/common/components/panel.css new file mode 100644 index 0000000..547d484 --- /dev/null +++ b/src/common/components/panel.css @@ -0,0 +1,221 @@ +/** + * notion-enhancer: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2021 CloudHill (https://github.com/CloudHill) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root { + --component--panel-width: 260px; +} + +#enhancer--panel-hover-trigger { + height: 100vh; + width: 2.5rem; + max-height: 100%; + z-index: 999; + position: absolute; + top: 45px; + right: 0; + flex-grow: 0; + flex-shrink: 0; + transition: width 300ms ease-in-out; +} +#enhancer--panel-hover-trigger[data-enhancer-panel-pinned] { + /* taking up the physical space of the panel to move topbar buttons */ + top: 0; + position: relative; + width: var(--component--panel-width); +} + +.notion-frame { + transition: padding-right 300ms ease-in-out; +} +.notion-frame[data-enhancer-panel-pinned] { + padding-right: var(--component--panel-width); +} +.notion-cursor-listener > div[style*='flex-end'] { + transition: margin-right 300ms ease-in-out; +} +.notion-cursor-listener > div[style*='flex-end'][data-enhancer-panel-pinned], +#enhancer--panel[data-enhancer-panel-pinned] + div[style*='flex-end'] { + margin-right: var(--component--panel-width); +} + +#enhancer--panel { + z-index: 999; + position: absolute; + background: var(--theme--bg_secondary); + width: var(--component--panel-width); + right: calc(-1 * var(--component--panel-width)); + opacity: 0; + height: 100vh; + flex-grow: 0; + flex-shrink: 0; + display: flex; + flex-direction: column; + transition: 300ms ease-in; + + margin-top: 5rem; + max-height: calc(100vh - 10rem); +} +#enhancer--panel-hover-trigger:hover + #enhancer--panel:not([data-enhancer-panel-pinned]), +#enhancer--panel:not([data-enhancer-panel-pinned]):hover { + opacity: 1; + transform: translateX(calc(-1 * var(--component--panel-width))); + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.05)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.1)) 0px 3px 6px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.2)) 0px 9px 24px !important; +} +#enhancer--panel[data-enhancer-panel-pinned] { + opacity: 1; + max-height: 100%; + margin-top: 0; + transform: translateX(calc(-1 * var(--component--panel-width))); +} + +.enhancer--panel-view-title { + margin: 0; + height: 1em; + display: flex; + align-items: center; + font-size: 1.1rem; + font-weight: 600; +} +.enhancer--panel-view-title svg, +.enhancer--panel-view-title img { + height: 1em; + width: 1em; +} +.enhancer--panel-view-icon { + margin-bottom: -2px; +} +.enhancer--panel-view-title .enhancer--panel-view-title-text { + font-size: 0.9em; + margin: 0 0 0 0.75em; + padding-bottom: 0.3em; + white-space: nowrap; + overflow: hidden; +} + +#enhancer--panel-header { + font-size: 1.2rem; + font-weight: 600; + display: flex; + align-items: center; + cursor: pointer; + user-select: none; + padding: 0.75rem 0 0.75rem 1rem; + background: var(--theme--bg_secondary); +} +#enhancer--panel-header-title { + max-width: calc(100% - 4.25rem); +} +#enhancer--panel-header-title .enhancer--panel-view-title { + font-size: 1.2rem; +} +#enhancer--panel-header-title .enhancer--panel-view-title-text { + max-width: calc(100% - 1.75em); + position: relative; +} + +#enhancer--panel-switcher-overlay-container { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 999; + overflow: hidden; +} +#enhancer--panel-switcher { + max-width: 320px; + position: relative; + right: 14px; + border-radius: 3px; + padding: 8px 0; + background: var(--theme--bg_card); + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.05)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.1)) 0px 3px 6px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.2)) 0px 9px 24px !important; + overflow: hidden; +} +.enhancer--panel-switcher-item { + display: flex; + align-items: center; + width: 100%; + padding: 8px 14px; + user-select: none; + cursor: pointer; + overflow: hidden; + position: relative; + background: var(--theme--bg_card); +} +#enhancer--panel-header:hover, +#enhancer--panel-header:focus-within, +.enhancer--panel-switcher-item:hover, +.enhancer--panel-switcher-item:focus { + background: var(--theme--ui_interactive-hover); +} +#enhancer--panel-header:active, +.enhancer--panel-switcher-item:active { + background: var(--theme--ui_interactive-active); +} + +#enhancer--panel-content { + margin: 0.75rem 1rem; + font-size: 1rem; +} +#enhancer--panel-header-switcher { + padding: 4px; +} +#enhancer--panel-header-toggle { + margin-left: auto; + padding-right: 1rem; + height: 100%; + width: 2.5em; + opacity: 0; + display: flex; +} +#enhancer--panel-header-toggle > div { + margin: auto 0 auto auto; +} +#enhancer--panel-header-switcher, +#enhancer--panel-header-toggle > div { + color: var(--theme--icon_secondary); + height: 1em; + width: 1em; + cursor: pointer; + display: flex; + flex-direction: column; + transition: 300ms ease-in-out; +} +#enhancer--panel #enhancer--panel-header-toggle svg { + transition: 300ms ease-in-out; +} +#enhancer--panel:not([data-enhancer-panel-pinned]) #enhancer--panel-header-toggle svg { + transform: rotateZ(-180deg); +} +#enhancer--panel:hover #enhancer--panel-header-toggle { + opacity: 1; +} + +#enhancer--panel-resize { + position: absolute; + left: -5px; + height: 100%; + width: 10px; +} +#enhancer--panel[data-enhancer-panel-pinned] #enhancer--panel-resize { + cursor: col-resize; +} +#enhancer--panel-resize div { + transition: background 150ms ease-in-out; + background: transparent; + width: 2px; + margin-left: 4px; + height: 100%; +} +#enhancer--panel[data-enhancer-panel-pinned] #enhancer--panel-resize:hover div { + background: var(--theme--ui_divider); +} diff --git a/src/common/components/panel.mjs b/src/common/components/panel.mjs new file mode 100644 index 0000000..8f82a58 --- /dev/null +++ b/src/common/components/panel.mjs @@ -0,0 +1,292 @@ +/** + * notion-enhancer: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2021 CloudHill (https://github.com/CloudHill) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** shared notion-style elements */ + +import { web, components, registry } from '../index.mjs'; + +const _views = [], + svgExpand = web.raw` + + `; + +let $stylesheet, + db, + // open + close + $notionFrame, + $notionRightSidebar, + $panel, + $hoverTrigger, + // resize + $resizeHandle, + dragStartX, + dragStartWidth, + dragEventsFired, + panelWidth, + // render content + $notionApp, + $pinnedToggle, + $panelTitle, + $header, + $panelContent, + $switcher, + $switcherTrigger, + $switcherOverlayContainer; + +// open + close +const panelPinnedAttr = 'data-enhancer-panel-pinned', + isPinned = () => $panel.hasAttribute(panelPinnedAttr), + togglePanel = () => { + const $elems = [$notionFrame, $notionRightSidebar, $hoverTrigger, $panel].filter( + ($el) => $el + ); + if (isPinned()) { + closeSwitcher(); + for (const $elem of $elems) $elem.removeAttribute(panelPinnedAttr); + } else { + for (const $elem of $elems) $elem.setAttribute(panelPinnedAttr, 'true'); + } + db.set(['panel.pinned'], isPinned()); + }, + // resize + updateWidth = () => { + document.documentElement.style.setProperty('--component--panel-width', panelWidth + 'px'); + db.set(['panel.width'], panelWidth); + }, + resizeDrag = (event) => { + event.preventDefault(); + dragEventsFired = true; + panelWidth = dragStartWidth + (dragStartX - event.clientX); + if (panelWidth < 190) panelWidth = 190; + if (panelWidth > 480) panelWidth = 480; + $panel.style.width = panelWidth + 'px'; + $hoverTrigger.style.width = panelWidth + 'px'; + $notionFrame.style.paddingRight = panelWidth + 'px'; + if ($notionRightSidebar) $notionRightSidebar.style.right = panelWidth + 'px'; + }, + resizeEnd = (_event) => { + $panel.style.width = ''; + $hoverTrigger.style.width = ''; + $notionFrame.style.paddingRight = ''; + if ($notionRightSidebar) $notionRightSidebar.style.right = ''; + updateWidth(); + $resizeHandle.style.cursor = ''; + document.body.removeEventListener('mousemove', resizeDrag); + document.body.removeEventListener('mouseup', resizeEnd); + }, + resizeStart = (event) => { + dragStartX = event.clientX; + dragStartWidth = panelWidth; + $resizeHandle.style.cursor = 'auto'; + document.body.addEventListener('mousemove', resizeDrag); + document.body.addEventListener('mouseup', resizeEnd); + }, + // render content + isSwitcherOpen = () => document.body.contains($switcher), + openSwitcher = () => { + if (!isPinned()) return togglePanel(); + web.render($notionApp, $switcherOverlayContainer); + web.empty($switcher); + for (const view of _views) { + const open = $panelTitle.contains(view.$title), + $item = web.render( + web.html`
`, + web.render( + web.html``, + view.$icon.cloneNode(true), + view.$title.cloneNode(true) + ) + ); + $item.addEventListener('click', () => { + renderView(view); + db.set(['panel.open'], view.id); + }); + web.render($switcher, $item); + } + const rect = $header.getBoundingClientRect(); + web.render( + web.empty($switcherOverlayContainer), + web.render( + web.html`
`, + web.render( + web.html`
`, + $switcher + ) + ) + ); + $switcher.querySelector('[data-open]').focus(); + $switcher.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 200 }); + document.addEventListener('keydown', switcherKeyListeners); + }, + closeSwitcher = () => { + document.removeEventListener('keydown', switcherKeyListeners); + $switcher.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 200 }).onfinish = () => + $switcherOverlayContainer.remove(); + }, + switcherKeyListeners = (event) => { + if (isSwitcherOpen()) { + switch (event.key) { + case 'Escape': + closeSwitcher(); + event.stopPropagation(); + break; + case 'Enter': + document.activeElement.click(); + event.stopPropagation(); + break; + case 'ArrowUp': { + const $prev = event.target.previousElementSibling; + ($prev || event.target.parentElement.lastElementChild).focus(); + event.stopPropagation(); + break; + } + case 'ArrowDown': { + const $next = event.target.nextElementSibling; + ($next || event.target.parentElement.firstElementChild).focus(); + event.stopPropagation(); + break; + } + } + } + }, + renderView = (view) => { + const prevView = _views.find(({ $content }) => document.contains($content)); + web.render( + web.empty($panelTitle), + web.render( + web.html``, + view.$icon, + view.$title + ) + ); + view.onFocus(); + web.render(web.empty($panelContent), view.$content); + if (prevView) prevView.onBlur(); + }; + +async function createPanel() { + await web.whenReady(['.notion-frame']); + $notionFrame = document.querySelector('.notion-frame'); + + $panel = web.html`
`; + $hoverTrigger = web.html`
`; + $resizeHandle = web.html`
`; + $panelTitle = web.html`
`; + $header = web.render(web.html`
`, $panelTitle); + $panelContent = web.html`
`; + $switcher = web.html`
`; + $switcherTrigger = web.html`
+ ${svgExpand} +
`; + $switcherOverlayContainer = web.html`
`; + + const notionRightSidebarSelector = '.notion-cursor-listener > div[style*="flex-end"]', + detectRightSidebar = () => { + if (!document.contains($notionRightSidebar)) { + $notionRightSidebar = document.querySelector(notionRightSidebarSelector); + if (isPinned() && $notionRightSidebar) { + $notionRightSidebar.setAttribute(panelPinnedAttr, 'true'); + } + } + }; + $notionRightSidebar = document.querySelector(notionRightSidebarSelector); + web.addDocumentObserver(detectRightSidebar, [notionRightSidebarSelector]); + + if (await db.get(['panel.pinned'])) togglePanel(); + web.addHotkeyListener(await db.get(['panel.hotkey']), togglePanel); + $pinnedToggle.addEventListener('click', (event) => { + event.stopPropagation(); + togglePanel(); + }); + web.render( + $panel, + web.render($header, $panelTitle, $switcherTrigger, $pinnedToggle), + $panelContent, + $resizeHandle + ); + + await enablePanelResize(); + await createViews(); + + const cursorListenerSelector = + '.notion-cursor-listener > .notion-sidebar-container ~ [style^="position: absolute"]'; + await web.whenReady([cursorListenerSelector]); + document.querySelector(cursorListenerSelector).before($hoverTrigger, $panel); +} + +async function enablePanelResize() { + panelWidth = await db.get(['panel.width'], 240); + updateWidth(); + $resizeHandle.addEventListener('mousedown', resizeStart); + $resizeHandle.addEventListener('click', () => { + if (dragEventsFired) { + dragEventsFired = false; + } else togglePanel(); + }); +} + +function createViews() { + $notionApp = document.querySelector('.notion-app-inner'); + $header.addEventListener('click', openSwitcher); + $switcherTrigger.addEventListener('click', openSwitcher); + $switcherOverlayContainer.addEventListener('click', closeSwitcher); +} + +/** + * adds a view to the enhancer's side panel + * @param {object} panel - information used to construct and render the panel + * @param {string} panel.id - a uuid, used to restore the last open view on reload + * @param {string} panel.icon - an svg string + * @param {string} panel.title - the name of the view + * @param {Element} panel.$content - an element containing the content of the view + * @param {function} panel.onBlur - runs when the view is selected/focused + * @param {function} panel.onFocus - runs when the view is unfocused/closed + */ +export const addPanelView = async ({ + id, + icon, + title, + $content, + onFocus = () => {}, + onBlur = () => {}, +}) => { + if (!$stylesheet) { + $stylesheet = web.loadStylesheet('api/components/panel.css'); + } + + if (!db) db = await registry.db('36a2ffc9-27ff-480e-84a7-c7700a7d232d'); + if (!$pinnedToggle) { + $pinnedToggle = web.html`
+ ${await components.feather('chevrons-right')} +
`; + } + + const view = { + id, + $icon: web.render( + web.html``, + icon instanceof Element ? icon : web.html`${icon}` + ), + $title: web.render(web.html``, title), + $content, + onFocus, + onBlur, + }; + _views.push(view); + if (_views.length === 1) await createPanel(); + if (_views.length === 1 || (await db.get(['panel.open'])) === id) renderView(view); +}; diff --git a/src/common/components/tooltip.css b/src/common/components/tooltip.css new file mode 100644 index 0000000..32664dd --- /dev/null +++ b/src/common/components/tooltip.css @@ -0,0 +1,31 @@ +/** + * notion-enhancer: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +#enhancer--tooltip { + font-family: var(--theme--font_sans); + background: var(--theme--ui_tooltip); + border-radius: 3px; + box-shadow: var(--theme--ui_shadow) 0px 1px 4px; + color: var(--theme--ui_tooltip-description); + display: none; + font-size: 12px; + font-weight: 500; + line-height: 1.4; + max-width: 20rem; + overflow: hidden; + padding: 4px 8px; + position: absolute; + z-index: 999999999999999999; + pointer-events: none; +} +#enhancer--tooltip p { + margin: 0; +} +#enhancer--tooltip b, +#enhancer--tooltip strong { + font-weight: 500; + color: var(--theme--ui_tooltip-title); +} diff --git a/src/common/components/tooltip.mjs b/src/common/components/tooltip.mjs new file mode 100644 index 0000000..80f1d03 --- /dev/null +++ b/src/common/components/tooltip.mjs @@ -0,0 +1,118 @@ +/** + * notion-enhancer: components + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** shared notion-style elements */ + +import { fs, web } from '../index.mjs'; + +let $stylesheet, _$tooltip; + +const countLines = ($el) => + [...$el.getClientRects()].reduce( + (prev, val) => (prev.some((p) => p.y === val.y) ? prev : [...prev, val]), + [] + ).length, + position = ($ref, offsetDirection, maxLines) => { + _$tooltip.style.top = `0px`; + _$tooltip.style.left = `0px`; + const rect = $ref.getBoundingClientRect(), + { offsetWidth, offsetHeight } = _$tooltip, + pad = 6; + let x = rect.x, + y = Math.floor(rect.y); + + if (['top', 'bottom'].includes(offsetDirection)) { + if (offsetDirection === 'top') y -= offsetHeight + pad; + if (offsetDirection === 'bottom') y += rect.height + pad; + x -= offsetWidth / 2 - rect.width / 2; + _$tooltip.style.left = `${x}px`; + _$tooltip.style.top = `${y}px`; + const testLines = () => countLines(_$tooltip.firstElementChild) > maxLines, + padEdgesX = testLines(); + while (testLines()) { + _$tooltip.style.left = `${window.innerWidth - x > x ? x++ : x--}px`; + } + if (padEdgesX) { + x += window.innerWidth - x > x ? pad : -pad; + _$tooltip.style.left = `${x}px`; + } + _$tooltip.style.textAlign = 'center'; + } + + if (['left', 'right'].includes(offsetDirection)) { + y -= offsetHeight / 2 - rect.height / 2; + if (offsetDirection === 'left') x -= offsetWidth + pad; + if (offsetDirection === 'right') x += rect.width + pad; + _$tooltip.style.left = `${x}px`; + _$tooltip.style.top = `${y}px`; + _$tooltip.style.textAlign = 'start'; + } + + return true; + }; + +/** + * add a tooltip to show extra information on hover + * @param {HTMLElement} $ref - the element that will trigger the tooltip when hovered + * @param {string|HTMLElement} $content - markdown or element content of the tooltip + * @param {object=} options - configuration of how the tooltip should be displayed + * @param {number=} options.delay - the amount of time in ms the element needs to be hovered over + * for the tooltip to be shown (default: 100) + * @param {string=} options.offsetDirection - which side of the element the tooltip + * should be shown on: 'top', 'bottom', 'left' or 'right' (default: 'bottom') + * @param {number=} options.maxLines - the max number of lines that the content may be wrapped + * to, used to position and size the tooltip correctly (default: 1) + */ +export const addTooltip = async ( + $ref, + $content, + { delay = 100, offsetDirection = 'bottom', maxLines = 1 } = {} +) => { + if (!$stylesheet) { + $stylesheet = web.loadStylesheet('api/components/tooltip.css'); + _$tooltip = web.html`
`; + web.render(document.body, _$tooltip); + } + + if (!globalThis.markdownit) await import(fs.localPath('dep/markdown-it.min.js')); + const md = markdownit({ linkify: true }); + + if (!($content instanceof Element)) + $content = web.html`
+ ${$content + .split('\n') + .map((text) => md.renderInline(text)) + .join('
')} +
`; + + let displayDelay; + $ref.addEventListener('mouseover', (_event) => { + if (!displayDelay) { + displayDelay = setTimeout(async () => { + if ($ref.matches(':hover')) { + if (_$tooltip.style.display !== 'block') { + _$tooltip.style.display = 'block'; + web.render(web.empty(_$tooltip), $content); + position($ref, offsetDirection, maxLines); + await _$tooltip.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 65 }) + .finished; + } + } + displayDelay = undefined; + }, delay); + } + }); + + $ref.addEventListener('mouseout', async (_event) => { + displayDelay = undefined; + if (_$tooltip.style.display === 'block' && !$ref.matches(':hover')) { + await _$tooltip.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 65 }).finished; + _$tooltip.style.display = ''; + } + }); +}; diff --git a/src/common/electron.mjs b/src/common/electron.mjs new file mode 100644 index 0000000..1492333 --- /dev/null +++ b/src/common/electron.mjs @@ -0,0 +1,106 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * access to electron renderer apis + * @namespace electron + */ +import * as _api from './index.mjs'; // trick jsdoc + +/** + * access to the electron BrowserWindow instance for the current window + * see https://www.electronjs.org/docs/latest/api/browser-window + * @type {BrowserWindow} + * @process electron (renderer process) + */ +export const browser = globalThis.__enhancerElectronApi?.browser; + +/** + * access to the electron webFrame instance for the current page + * see https://www.electronjs.org/docs/latest/api/web-frame + * @type {webFrame} + * @process electron (renderer process) + */ +export const webFrame = globalThis.__enhancerElectronApi?.webFrame; + +/** + * send a message to the main electron process + * @param {string} channel - the message identifier + * @param {any} data - the data to pass along with the message + * @param {string=} namespace - a prefix for the message to categorise + * it as e.g. enhancer-related. this should not be changed unless replicating + * builtin ipc events. + * @process electron (renderer process) + */ +export const sendMessage = (channel, data, namespace = 'notion-enhancer') => { + if (globalThis.__enhancerElectronApi) { + globalThis.__enhancerElectronApi.ipcRenderer.sendMessage(channel, data, namespace); + } +}; + +/** + * send a message to the webview's parent renderer process + * @param {string} channel - the message identifier + * @param {any} data - the data to pass along with the message + * @param {string=} namespace - a prefix for the message to categorise + * it as e.g. enhancer-related. this should not be changed unless replicating + * builtin ipc events. + * @process electron (renderer process) + */ +export const sendMessageToHost = (channel, data, namespace = 'notion-enhancer') => { + if (globalThis.__enhancerElectronApi) { + globalThis.__enhancerElectronApi.ipcRenderer.sendMessageToHost(channel, data, namespace); + } +}; + +/** + * receive a message from either the main process or + * the webview's parent renderer process + * @param {string} channel - the message identifier to listen for + * @param {function} callback - the message handler, passed the args (event, data) + * @param {string=} namespace - a prefix for the message to categorise + * it as e.g. enhancer-related. this should not be changed unless replicating + * builtin ipc events. + * @process electron (renderer process) + */ +export const onMessage = (channel, callback, namespace = 'notion-enhancer') => { + if (globalThis.__enhancerElectronApi) { + globalThis.__enhancerElectronApi.ipcRenderer.onMessage(channel, callback, namespace); + } +}; + +/** + * require() notion app files + * @param {string} path - within notion/resources/app/ e.g. main/createWindow.js + * @process electron (main process) + */ +export const notionRequire = (path) => { + return globalThis.__enhancerElectronApi + ? globalThis.__enhancerElectronApi.notionRequire(path) + : null; +}; + +/** + * get all available app windows excluding the menu + * @process electron (main process) + */ +export const getNotionWindows = () => { + return globalThis.__enhancerElectronApi + ? globalThis.__enhancerElectronApi.getNotionWindows() + : null; +}; + +/** + * get the currently focused notion window + * @process electron (main process) + */ +export const getFocusedNotionWindow = () => { + return globalThis.__enhancerElectronApi + ? globalThis.__enhancerElectronApi.getFocusedNotionWindow() + : null; +}; diff --git a/src/common/env.mjs b/src/common/env.mjs new file mode 100644 index 0000000..27f3ac4 --- /dev/null +++ b/src/common/env.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 methods and constants + * @namespace env + */ + +import * as env from '../env/env.mjs'; + +/** + * the environment/platform name code is currently being executed in + * @constant + * @type {string} + */ +export const name = env.name; + +/** + * the current version of the enhancer + * @constant + * @type {string} + */ +export const version = env.version; + +/** + * open the enhancer's menu + * @type {function} + */ +export const focusMenu = env.focusMenu; + +/** + * focus an active notion tab + * @type {function} + */ +export const focusNotion = env.focusNotion; + +/** + * reload all notion and enhancer menu tabs to apply changes + * @type {function} + */ +export const reload = env.reload; diff --git a/src/common/fmt.mjs b/src/common/fmt.mjs new file mode 100644 index 0000000..57660cb --- /dev/null +++ b/src/common/fmt.mjs @@ -0,0 +1,137 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * helpers for formatting or parsing text + * @namespace fmt + */ + +import { fs } from './index.mjs'; + +/** + * transform a heading into a slug (a lowercase alphanumeric string separated by hyphens), + * e.g. for use as an anchor id + * @param {string} heading - the original heading to be slugified + * @param {Set=} slugs - a list of pre-generated slugs to avoid duplicates + * @returns {string} the generated slug + */ +export const slugger = (heading, slugs = new Set()) => { + heading = heading + .replace(/\s/g, '-') + .replace(/[^A-Za-z0-9-_]/g, '') + .toLowerCase(); + let i = 0, + slug = heading; + while (slugs.has(slug)) { + i++; + slug = `${heading}-${i}`; + } + return slug; +}; + +/** + * generate a reasonably random uuidv4 string. uses crypto implementation if available + * (from https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid) + * @returns {string} a uuidv4 + */ +export const uuidv4 = () => { + if (crypto?.randomUUID) return crypto.randomUUID(); + return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => + (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16) + ); +}; + +/** + * log-based shading of an rgb color, from + * https://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors + * @param {number} shade - a decimal amount to shade the color. + * 1 = white, 0 = the original color, -1 = black + * @param {string} color - the rgb color + * @returns {string} the shaded color + */ +export const rgbLogShade = (shade, color) => { + const int = parseInt, + round = Math.round, + [a, b, c, d] = color.split(','), + t = shade < 0 ? 0 : shade * 255 ** 2, + p = shade < 0 ? 1 + shade : 1 - shade; + return ( + 'rgb' + + (d ? 'a(' : '(') + + round((p * int(a[3] == 'a' ? a.slice(5) : a.slice(4)) ** 2 + t) ** 0.5) + + ',' + + round((p * int(b) ** 2 + t) ** 0.5) + + ',' + + round((p * int(c) ** 2 + t) ** 0.5) + + (d ? ',' + d : ')') + ); +}; + +/** + * pick a contrasting color e.g. for text on a variable color background + * using the hsp (perceived brightness) constants from http://alienryderflex.com/hsp.html + * @param {number} r - red (0-255) + * @param {number} g - green (0-255) + * @param {number} b - blue (0-255) + * @returns {string} the contrasting rgb color, white or black + */ +export const rgbContrast = (r, g, b) => { + return Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)) > 165.75 + ? 'rgb(0,0,0)' + : 'rgb(255,255,255)'; +}; + +const patterns = { + alphanumeric: /^[\w\.-]+$/, + uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, + semver: + /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/i, + email: + /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i, + url: /^[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,64}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/i, + color: /^(?:#|0x)(?:[a-f0-9]{3}|[a-f0-9]{6})\b|(?:rgb|hsl)a?\([^\)]*\)$/i, +}; +function test(str, pattern) { + const match = str.match(pattern); + return !!(match && match.length); +} + +/** + * test the type of a value. unifies builtin, regex, and environment/api checks + * @param {unknown} value - the value to check + * @param {string|string[]} type - the type the value should be or a list of allowed values + * @returns {boolean} whether or not the value matches the type + */ +export const is = async (value, type, { extension = '' } = {}) => { + extension = !value || !value.endsWith || value.endsWith(extension); + if (Array.isArray(type)) { + return type.includes(value); + } + switch (type) { + case 'array': + return Array.isArray(value); + case 'object': + return value && typeof value === 'object' && !Array.isArray(value); + case 'undefined': + case 'boolean': + case 'number': + return typeof value === type && extension; + case 'string': + return typeof value === type && extension; + case 'alphanumeric': + case 'uuid': + case 'semver': + case 'email': + case 'url': + case 'color': + return typeof value === 'string' && test(value, patterns[type]) && extension; + case 'file': + return typeof value === 'string' && value && (await fs.isFile(value)) && extension; + } + return false; +}; diff --git a/src/common/fs.mjs b/src/common/fs.mjs new file mode 100644 index 0000000..fa18d7b --- /dev/null +++ b/src/common/fs.mjs @@ -0,0 +1,55 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * environment-specific file reading + * @namespace fs + */ + +import * as fs from '../env/fs.mjs'; + +/** + * get an absolute path to files within notion + * @param {string} path - relative to the root notion/resources/app/ e.g. renderer/search.js + * @process electron + */ +export const notionPath = fs.notionPath; + +/** + * 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 + * @param {FetchOptions=} opts - the second argument of a fetch() request + * @returns {unknown} 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 + * @param {FetchOptions=} opts - the second argument of a fetch() request + * @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/src/common/index.cjs b/src/common/index.cjs new file mode 100644 index 0000000..c130ac1 --- /dev/null +++ b/src/common/index.cjs @@ -0,0 +1,21 @@ +var le=Object.defineProperty;var Xe=e=>le(e,"__esModule",{value:!0});var y=(e,t)=>{Xe(e);for(var n in t)le(e,n,{get:t[n],enumerable:!0})};y(exports,{components:()=>R,electron:()=>V,env:()=>j,fmt:()=>h,fs:()=>u,notion:()=>X,registry:()=>g,storage:()=>_,web:()=>o});var j={};y(j,{focusMenu:()=>Ye,focusNotion:()=>De,name:()=>Ge,reload:()=>et,version:()=>Qe});"use strict";var de=globalThis.__enhancerElectronApi.platform,pe=globalThis.__enhancerElectronApi.version,ue=globalThis.__enhancerElectronApi.focusMenu,he=globalThis.__enhancerElectronApi.focusNotion,fe=globalThis.__enhancerElectronApi.reload;"use strict";var Ge=de,Qe=pe,Ye=ue,De=he,et=fe;var u={};y(u,{getJSON:()=>rt,getText:()=>st,isFile:()=>ot,localPath:()=>nt,notionPath:()=>tt});"use strict";var M=globalThis.__enhancerElectronApi.notionPath,L=e=>`notion://www.notion.so/__notion-enhancer/${e}`,me=(e,t={})=>{if(e=e.replace(/^https:\/\/www\.notion\.so\//,"notion://www.notion.so/"),e.startsWith("http")||e.startsWith("notion://"))return fetch(e,t).then(r=>r.json());try{return globalThis.__enhancerElectronApi.nodeRequire(`notion-enhancer/${e}`)}catch{return fetch(L(e),t).then(s=>s.json())}},ge=(e,t={})=>{if(e=e.replace(/^https:\/\/www\.notion\.so\//,"notion://www.notion.so/"),e.startsWith("http")||e.startsWith("notion://"))return fetch(e,t).then(r=>r.text());try{return globalThis.__enhancerElectronApi.nodeRequire("fs").readFileSync(M(`notion-enhancer/${e}`))}catch{return fetch(L(e),t).then(s=>s.text())}},ye=async e=>{try{let t=globalThis.__enhancerElectronApi.nodeRequire("fs");if(e.startsWith("http"))await fetch(e);else try{t.existsSync(M(`notion-enhancer/${e}`))}catch{await fetch(L(e))}return!0}catch{return!1}};"use strict";var tt=M,nt=L,rt=me,st=ge,ot=ye;var _={};y(_,{addChangeListener:()=>lt,db:()=>ct,get:()=>it,removeChangeListener:()=>dt,set:()=>at});"use strict";var H=(e,t=void 0)=>globalThis.__enhancerElectronApi.db.get(e,t),I=(e,t)=>globalThis.__enhancerElectronApi.db.set(e,t),we=(e,t=H,n=I)=>(typeof e=="string"&&(e=[e]),{get:(r=[],s=void 0)=>t([...e,...r],s),set:(r,s)=>n([...e,...r],s)}),ve=e=>globalThis.__enhancerElectronApi.db.addChangeListener(e),be=e=>globalThis.__enhancerElectronApi.db.removeChangeListener(e);"use strict";var it=H,at=I,ct=we,lt=ve,dt=be;var V={};y(V,{browser:()=>pt,getFocusedNotionWindow:()=>wt,getNotionWindows:()=>yt,notionRequire:()=>gt,onMessage:()=>mt,sendMessage:()=>ht,sendMessageToHost:()=>ft,webFrame:()=>ut});"use strict";var pt=globalThis.__enhancerElectronApi?.browser,ut=globalThis.__enhancerElectronApi?.webFrame,ht=(e,t,n="notion-enhancer")=>{globalThis.__enhancerElectronApi&&globalThis.__enhancerElectronApi.ipcRenderer.sendMessage(e,t,n)},ft=(e,t,n="notion-enhancer")=>{globalThis.__enhancerElectronApi&&globalThis.__enhancerElectronApi.ipcRenderer.sendMessageToHost(e,t,n)},mt=(e,t,n="notion-enhancer")=>{globalThis.__enhancerElectronApi&&globalThis.__enhancerElectronApi.ipcRenderer.onMessage(e,t,n)},gt=e=>globalThis.__enhancerElectronApi?globalThis.__enhancerElectronApi.notionRequire(e):null,yt=()=>globalThis.__enhancerElectronApi?globalThis.__enhancerElectronApi.getNotionWindows():null,wt=()=>globalThis.__enhancerElectronApi?globalThis.__enhancerElectronApi.getFocusedNotionWindow():null;var X={};y(X,{create:()=>xt,get:()=>xe,getPageID:()=>J,getSpaceID:()=>T,getUserID:()=>_e,search:()=>vt,set:()=>bt,sign:()=>$t,upload:()=>_t});"use strict";var m=e=>(e?.length===32&&!e.includes("-")&&(e=e.replace(/([\d\w]{8})([\d\w]{4})([\d\w]{4})([\d\w]{4})([\d\w]{12})/,"$1-$2-$3-$4-$5")),e),xe=async(e,t="block")=>{e=m(e);let n=await u.getJSON("https://www.notion.so/api/v3/getRecordValues",{headers:{"Content-Type":"application/json"},body:JSON.stringify({requests:[{table:t,id:e}]}),method:"POST"});return n?.results?.[0]?.value||n},_e=()=>JSON.parse(localStorage["LRU:KeyValueStore2:current-user-id"]||{}).value,J=()=>m(o.queryParams().get("p")||location.pathname.split(/(-|\/)/g).reverse()[0]),K,T=async()=>(K||(K=(await xe(J())).space_id),K),vt=async(e="",t=20,n=T())=>(n=m(await n),await u.getJSON("https://www.notion.so/api/v3/search",{headers:{"Content-Type":"application/json"},body:JSON.stringify({type:"BlocksInSpace",query:e,spaceId:n,limit:t,filters:{isDeletedOnly:!1,excludeTemplates:!1,isNavigableOnly:!1,requireEditPermissions:!1,ancestors:[],createdBy:[],editedBy:[],lastEditedTime:{},createdTime:{}},sort:"Relevance",source:"quick_find"}),method:"POST"})),bt=async({recordID:e,recordTable:t="block",spaceID:n=T(),path:r=[]},s={})=>{n=m(await n),e=m(e);let c=await u.getJSON("https://www.notion.so/api/v3/saveTransactions",{headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:h.uuidv4(),transactions:[{id:h.uuidv4(),spaceId:n,operations:[{pointer:{table:t,id:e,spaceId:n},path:r,command:r.length?"set":"update",args:s}]}]}),method:"POST"});return c.errorId?c:!0},xt=async({recordValue:e={},recordTable:t="block"}={},{prepend:n=!1,siblingID:r=void 0,parentID:s=J(),parentTable:c="block",spaceID:a=T(),userID:i=_e()}={})=>{a=m(await a),s=m(s),r=m(r);let d=m(e?.id??h.uuidv4()),x=[],A={type:"text",id:d,version:0,created_time:new Date().getTime(),last_edited_time:new Date().getTime(),parent_id:s,parent_table:c,alive:!0,created_by_table:"notion_user",created_by_id:i,last_edited_by_table:"notion_user",last_edited_by_id:i,space_id:a,permissions:[{type:"user_permission",role:"editor",user_id:i}]};c==="space"?(s=a,A.parent_id=a,x.push("pages"),A.type="page"):c==="collection_view"?(x.push("page_sort"),A.type="page"):x.push("content");let ce=await u.getJSON("https://www.notion.so/api/v3/saveTransactions",{headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:h.uuidv4(),transactions:[{id:h.uuidv4(),spaceId:a,operations:[{pointer:{table:c,id:s,spaceId:a},path:x,command:n?"listBefore":"listAfter",args:{...r?{after:r}:{},id:d}},{pointer:{table:t,id:d,spaceId:a},path:[],command:"set",args:{...A,...e}}]}]}),method:"POST"});return ce.errorId?ce:d},_t=async(e,{pageID:t=J(),spaceID:n=T()}={})=>{n=m(await n),t=m(t);let r=await u.getJSON("https://www.notion.so/api/v3/getUploadFileUrl",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({bucket:"secure",name:e.name,contentType:e.type,record:{table:"block",id:t,spaceId:n}})});return r.errorId?r:(fetch(r.signedPutUrl,{method:"PUT",headers:{"content-type":e.type},body:e}),r.url)},$t=(e,t,n="block")=>(e.startsWith("/")&&(e=`https://notion.so${e}`),e.includes("secure.notion-static.com")&&(e=new URL(e),e=`https://www.notion.so/signed/${encodeURIComponent(e.origin+e.pathname)}?table=${n}&id=${t}`),e);var h={};y(h,{is:()=>kt,rgbContrast:()=>Lt,rgbLogShade:()=>jt,slugger:()=>At,uuidv4:()=>Et});"use strict";var At=(e,t=new Set)=>{e=e.replace(/\s/g,"-").replace(/[^A-Za-z0-9-_]/g,"").toLowerCase();let n=0,r=e;for(;t.has(r);)n++,r=`${e}-${n}`;return r},Et=()=>crypto?.randomUUID?crypto.randomUUID():([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,e=>(e^crypto.getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16)),jt=(e,t)=>{var n=parseInt,r=Math.round,[s,c,t,a]=t.split(","),i=e<0,d=i?0:e*255**2,i=i?1+e:1-e;return"rgb"+(a?"a(":"(")+r((i*n(s[3]=="a"?s.slice(5):s.slice(4))**2+d)**.5)+","+r((i*n(c)**2+d)**.5)+","+r((i*n(t)**2+d)**.5)+(a?","+a:")")},Lt=(e,t,n)=>Math.sqrt(.299*(e*e)+.587*(t*t)+.114*(n*n))>165.75?"rgb(0,0,0)":"rgb(255,255,255)",Tt={alphanumeric:/^[\w\.-]+$/,uuid:/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,semver:/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/i,email:/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i,url:/^[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,64}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/i,color:/^(?:#|0x)(?:[a-f0-9]{3}|[a-f0-9]{6})\b|(?:rgb|hsl)a?\([^\)]*\)$/i};function St(e,t){let n=e.match(t);return!!(n&&n.length)}var kt=async(e,t,{extension:n=""}={})=>{if(n=!e||!e.endsWith||e.endsWith(n),Array.isArray(t))return t.includes(e);switch(t){case"array":return Array.isArray(e);case"object":return e&&typeof e=="object"&&!Array.isArray(e);case"undefined":case"boolean":case"number":return typeof e===t&&n;case"string":return typeof e===t&&n;case"alphanumeric":case"uuid":case"semver":case"email":case"url":case"color":return typeof e=="string"&&St(e,Tt[t])&&n;case"file":return typeof e=="string"&&e&&await u.isFile(e)&&n}return!1};var g={};y(g,{core:()=>Ae,db:()=>Ut,enabled:()=>zt,errors:()=>Wt,get:()=>ee,list:()=>D,optionDefault:()=>je,optionTypes:()=>Jt,profileDB:()=>G,profileName:()=>Ee,supportedEnvs:()=>Mt});"use strict";var l=async(e,t,n,r,{extension:s="",error:c=`invalid ${t} (${s?`${s} `:""}${r}): ${JSON.stringify(n)}`,optional:a=!1}={})=>{let i;for(let d of Array.isArray(r)?[r]:r.split("|"))if(d==="file"?i=n&&!n.startsWith("http")?await h.is(`repo/${e._dir}/${n}`,d,{extension:s}):!1:i=await h.is(n,d,{extension:s}),i)break;return i||a&&await h.is(n,"undefined")?!0:(c&&e._err(c),!1)},Pt=async e=>(e.environments=e.environments??g.supportedEnvs,await l(e,"environments",e.environments,"array")?e.environments.map(n=>l(e,"environments.env",n,g.supportedEnvs)):!1),Ct=async e=>{if(!await l(e,"tags",e.tags,"array"))return!1;let n=["core","extension","theme","integration"];if(!e.tags.filter(i=>n.includes(i)).length)return e._err(`invalid tags (must contain at least one of 'core', 'extension', 'theme' or 'integration'): + ${JSON.stringify(e.tags)}`),!1;let s=e.tags.includes("theme"),c=e.tags.includes("light")||e.tags.includes("dark"),a=e.tags.includes("light")&&e.tags.includes("dark");return s&&(!c||a)?(e._err(`invalid tags (themes must be either 'light' or 'dark', not neither or both): + ${JSON.stringify(e.tags)}`),!1):e.tags.map(i=>l(e,"tags.tag",i,"string"))},Ot=async e=>await l(e,"authors",e.authors,"array")?e.authors.map(n=>[l(e,"authors.author.name",n.name,"string"),l(e,"authors.author.email",n.email,"email",{optional:!0}),l(e,"authors.author.homepage",n.homepage,"url"),l(e,"authors.author.avatar",n.avatar,"url")]):!1,Nt=async e=>{if(!await l(e,"css",e.css,"object"))return!1;let n=[];for(let r of["frame","client","menu"]){if(!e.css[r])continue;let s=await l(e,`css.${r}`,e.css[r],"array");s&&(s=e.css[r].map(c=>l(e,`css.${r}.file`,c,"file",{extension:".css"}))),n.push(s)}return n},Rt=async e=>{if(!await l(e,"js",e.js,"object"))return!1;let n=[];for(let r of["frame","client","menu"]){if(!e.js[r])continue;let s=await l(e,`js.${r}`,e.js[r],"array");s&&(s=e.js[r].map(c=>l(e,`js.${r}.file`,c,"file",{extension:".mjs"}))),n.push(s)}if(e.js.electron)if(await l(e,"js.electron",e.js.electron,"array"))for(let s of e.js.electron){if(!await l(e,"js.electron.file",s,"object")){n.push(!1);continue}n.push([l(e,"js.electron.file.source",s.source,"file",{extension:".cjs"}),l(e,"js.electron.file.target",s.target,"string",{extension:".js"})])}else n.push(!1);return n},qt=async e=>{if(!await l(e,"options",e.options,"array"))return!1;let n=[];for(let r of e.options){let s="options.option";if(!await l(e,`${s}.type`,r.type,g.optionTypes)){n.push(!1);continue}switch(r.environments=r.environments??g.supportedEnvs,n.push([l(e,`${s}.key`,r.key,"alphanumeric"),l(e,`${s}.label`,r.label,"string"),l(e,`${s}.tooltip`,r.tooltip,"string",{optional:!0}),l(e,`${s}.environments`,r.environments,"array").then(a=>a?r.environments.map(i=>l(e,`${s}.environments.env`,i,g.supportedEnvs)):!1)]),r.type){case"toggle":n.push(l(e,`${s}.value`,r.value,"boolean"));break;case"select":{let a=await l(e,`${s}.values`,r.values,"array");a&&(a=r.values.map(i=>l(e,`${s}.values.value`,i,"string"))),n.push(a);break}case"text":case"hotkey":n.push(l(e,`${s}.value`,r.value,"string"));break;case"number":case"color":n.push(l(e,`${s}.value`,r.value,r.type));break;case"file":{let a=await l(e,`${s}.extensions`,r.extensions,"array");a&&(a=r.extensions.map(i=>l(e,`${s}.extensions.extension`,i,"string"))),n.push(a);break}}}return n};async function $e(e){let t=[l(e,"name",e.name,"string"),l(e,"id",e.id,"uuid"),l(e,"version",e.version,"semver"),Pt(e),l(e,"description",e.description,"string"),l(e,"preview",e.preview,"file|url",{optional:!0}),Ct(e),Ot(e),Nt(e),Rt(e),qt(e)];do t=await Promise.all(t.flat(1/0));while(t.some(n=>Array.isArray(n)));return t.every(n=>n)}"use strict";var Ae=["a6621988-551d-495a-97d8-3c568bca2e9e","0f0bf8b6-eae6-4273-b307-8fc43f2ee082","36a2ffc9-27ff-480e-84a7-c7700a7d232d"],Mt=["linux","win32","darwin","extension"],Jt=["toggle","select","text","number","color","file","hotkey"],Ee=async()=>_.get(["currentprofile"],"default"),G=async()=>_.db(["profiles",await Ee()]),Q,Y=[],D=async(e=t=>!0)=>{Q||(Q=new Promise(async(n,r)=>{let s=[];for(let c of await u.getJSON("repo/registry.json"))try{let a={...await u.getJSON(`repo/${c}/mod.json`),_dir:c,_err:i=>Y.push({source:c,message:i})};await $e(a)&&s.push(a)}catch{Y.push({source:c,message:"invalid mod.json"})}n(s)}));let t=[];for(let n of await Q)await e(n)&&t.push(n);return t},Wt=async()=>(await D(),Y),ee=async e=>(await D(t=>t.id===e))[0],zt=async e=>(await ee(e)).environments.includes(j.name)?Ae.includes(e)?!0:(await G()).get(["_mods",e],!1):!1,je=async(e,t)=>{let n=await ee(e),r=n.options.find(s=>s.key===t);if(!!r)switch(r.type){case"toggle":case"text":case"number":case"color":case"hotkey":return r.value;case"select":return r.values[0];case"file":return}},Ut=async e=>{let t=await G();return _.db([e],async(n,r=void 0)=>(typeof n=="string"&&(n=[n]),n.length===2&&(r=await je(e,n[1])??r),t.get(n,r)),t.set)};var o={};y(o,{addDocumentObserver:()=>Gt,addHotkeyListener:()=>Kt,copyToClipboard:()=>It,empty:()=>Ft,escape:()=>Te,html:()=>ke,loadStylesheet:()=>Ht,queryParams:()=>Bt,raw:()=>Se,readFromClipboard:()=>Vt,removeDocumentObserver:()=>Qt,removeHotkeyListener:()=>Xt,render:()=>Pe,whenReady:()=>Zt});"use strict";var Le=!1,S=[],te,W=[],ne=[],Zt=(e=[])=>new Promise((t,n)=>{let r=()=>{let s=setInterval(c,100);function c(){!e.every(i=>document.querySelector(i))||(clearInterval(s),t(!0))}c()};document.readyState!=="complete"?document.addEventListener("readystatechange",s=>{document.readyState==="complete"&&r()}):r()}),Bt=()=>new URLSearchParams(window.location.search),Te=e=>e.replace(/&/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\\/g,"\"),Se=(e,...t)=>{let n=e.map(r=>r+(["string","number"].includes(typeof t[0])?t.shift():Te(JSON.stringify(t.shift(),null,2)??""))).join("");return n.includes("r.trim()).filter(r=>r.length).join(" ")},ke=(e,...t)=>{let n=document.createRange().createContextualFragment(Se(e,...t));return n.children.length===1?n.children[0]:n.children},Pe=(e,...t)=>(t=t.map(n=>n instanceof HTMLCollection?[...n]:n).flat(1/0).filter(n=>n),e.append(...t),e),Ft=e=>{for(;e.firstChild&&e.removeChild(e.firstChild););return e},Ht=e=>{let t=ke``;return Pe(document.head,t),t},It=async e=>{try{await navigator.clipboard.writeText(e)}catch{let t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.position="absolute",t.style.left="-9999px",document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t)}},Vt=()=>navigator.clipboard.readText(),Ce=(e,t)=>{if(document.activeElement.nodeName==="INPUT"&&!t.listenInInput)return;let r={metaKey:["meta","os","win","cmd","command"],ctrlKey:["ctrl","control"],shiftKey:["shift"],altKey:["alt"]};if(!!t.keys.every(c=>{c=c.toLowerCase();for(let a in r)if(r[a].includes(c)&&e[a])return r[a]=[],!0;if(c==="space"&&(c=" "),c==="plus"&&(c="+"),c===e.key.toLowerCase())return!0})){for(let c in r){let a=e[c],i=r[c].length>0;if(a&&i)return}t.callback(e)}},Kt=(e,t,{listenInInput:n=!1,keydown:r=!1}={})=>{typeof e=="string"&&(e=e.split("+")),S.push({keys:e,callback:t,listenInInput:n,keydown:r}),Le||(Le=!0,document.addEventListener("keyup",s=>{for(let c of S.filter(({keydown:a})=>!a))Ce(s,c)}),document.addEventListener("keydown",s=>{for(let c of S.filter(({keydown:a})=>a))Ce(s,c)}))},Xt=e=>{S=S.filter(t=>t.callback!==e)},Gt=(e,t=[])=>{if(!te){let n=r=>{for(;r.length;){let s=r.shift(),c=(i,d)=>i instanceof Element&&(i.matches(d)||i.matches(`${d} *`)||i.querySelector(d)),a=i=>s.target.matches(i)||s.target.matches(`${i} *`)||[...s.addedNodes].some(d=>c(d,i));for(let i of W)(!i.selectors.length||i.selectors.some(a))&&i.callback(s)}};te=new MutationObserver((r,s)=>{ne.length||requestIdleCallback(()=>n(ne)),ne.push(...r)}),te.observe(document.body,{childList:!0,subtree:!0,attributes:!0})}W.push({callback:e,selectors:t})},Qt=e=>{W=W.filter(t=>t.callback!==e)};var R={};y(R,{addCornerAction:()=>Ke,addPanelView:()=>Ie,addTooltip:()=>Ne,feather:()=>Re});"use strict";var Oe,p,Yt=e=>[...e.getClientRects()].reduce((t,n)=>t.some(r=>r.y===n.y)?t:[...t,n],[]).length,Dt=async(e,t,n)=>{p.style.top="0px",p.style.left="0px";let r=e.getBoundingClientRect(),{offsetWidth:s,offsetHeight:c}=p,a=6,i=r.x,d=Math.floor(r.y);if(["top","bottom"].includes(t)){t==="top"&&(d-=c+a),t==="bottom"&&(d+=r.height+a),i-=s/2-r.width/2,p.style.left=`${i}px`,p.style.top=`${d}px`;let x=()=>Yt(p.firstElementChild)>n,A=x();for(;x();)p.style.left=`${window.innerWidth-i>i?i++:i--}px`;A&&(i+=window.innerWidth-i>i?a:-a,p.style.left=`${i}px`),p.style.textAlign="center"}return["left","right"].includes(t)&&(d-=c/2-r.height/2,t==="left"&&(i-=s+a),t==="right"&&(i+=r.width+a),p.style.left=`${i}px`,p.style.top=`${d}px`,p.style.textAlign="start"),!0},Ne=async(e,t,{delay:n=100,offsetDirection:r="bottom",maxLines:s=1}={})=>{Oe||(Oe=o.loadStylesheet("api/components/tooltip.css"),p=o.html`
`,o.render(document.body,p)),globalThis.markdownit||await import(u.localPath("dep/markdown-it.min.js"));let c=markdownit({linkify:!0});t instanceof Element||(t=o.html`
+ ${t.split(` +`).map(i=>c.renderInline(i)).join("
")} +
`);let a;e.addEventListener("mouseover",async i=>{a||(a=setTimeout(async()=>{e.matches(":hover")&&p.style.display!=="block"&&(p.style.display="block",o.render(o.empty(p),t),Dt(e,r,s),await p.animate([{opacity:0},{opacity:1}],{duration:65}).finished),a=void 0},n))}),e.addEventListener("mouseout",async i=>{a=void 0,p.style.display==="block"&&!e.matches(":hover")&&(await p.animate([{opacity:1},{opacity:0}],{duration:65}).finished,p.style.display="")})};"use strict";var re,Re=async(e,t={})=>(re||(re=o.html`${await u.getText("dep/feather-sprite.svg")}`),t.style=((t.style?t.style+";":"")+"stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill:none;").trim(),t.viewBox="0 0 24 24",``${o.escape(n)}="${o.escape(r)}"`).join(" ")}>${re.getElementById(e)?.innerHTML}`);"use strict";var k=[],en=o.raw` + + `,qe,v,z,w,$,P,E,Me,Je,se,f,We,U,C,Z,oe,b,ie,O,B="data-enhancer-panel-pinned",F=()=>$.hasAttribute(B),N=()=>{let e=[z,w,P,$].filter(t=>t);if(F()){ae();for(let t of e)t.removeAttribute(B)}else for(let t of e)t.setAttribute(B,"true");v.set(["panel.pinned"],F())},ze=async()=>{document.documentElement.style.setProperty("--component--panel-width",f+"px"),v.set(["panel.width"],f)},Ue=e=>{e.preventDefault(),se=!0,f=Je+(Me-e.clientX),f<190&&(f=190),f>480&&(f=480),$.style.width=f+"px",P.style.width=f+"px",z.style.paddingRight=f+"px",w&&(w.style.right=f+"px")},Ze=e=>{$.style.width="",P.style.width="",z.style.paddingRight="",w&&(w.style.right=""),ze(),E.style.cursor="",document.body.removeEventListener("mousemove",Ue),document.body.removeEventListener("mouseup",Ze)},tn=e=>{Me=e.clientX,Je=f,E.style.cursor="auto",document.body.addEventListener("mousemove",Ue),document.body.addEventListener("mouseup",Ze)},nn=()=>document.body.contains(b),Be=()=>{if(!F())return N();o.render(We,O),o.empty(b);for(let t of k){let n=C.contains(t.$title),r=o.render(o.html`
`,o.render(o.html``,t.$icon.cloneNode(!0),t.$title.cloneNode(!0)));r.addEventListener("click",()=>{He(t),v.set(["panel.open"],t.id)}),o.render(b,r)}let e=Z.getBoundingClientRect();o.render(o.empty(O),o.render(o.html`
`,o.render(o.html`
`,b))),b.querySelector("[data-open]").focus(),b.animate([{opacity:0},{opacity:1}],{duration:200}),document.addEventListener("keydown",Fe)},ae=()=>{document.removeEventListener("keydown",Fe),b.animate([{opacity:1},{opacity:0}],{duration:200}).onfinish=()=>O.remove()},Fe=e=>{if(nn())switch(e.key){case"Escape":ae(),e.stopPropagation();break;case"Enter":document.activeElement.click(),e.stopPropagation();break;case"ArrowUp":(e.target.previousElementSibling||e.target.parentElement.lastElementChild).focus(),e.stopPropagation();break;case"ArrowDown":(e.target.nextElementSibling||e.target.parentElement.firstElementChild).focus(),e.stopPropagation();break}},He=e=>{let t=k.find(({$content:n})=>document.contains(n));o.render(o.empty(C),o.render(o.html``,e.$icon,e.$title)),e.onFocus(),o.render(o.empty(oe),e.$content),t&&t.onBlur()};async function rn(){await o.whenReady([".notion-frame"]),z=document.querySelector(".notion-frame"),$=o.html`
`,P=o.html`
`,E=o.html`
`,C=o.html`
`,Z=o.render(o.html`
`,C),oe=o.html`
`,b=o.html`
`,ie=o.html`
+ ${en} +
`,O=o.html`
`;let e='.notion-cursor-listener > div[style*="flex-end"]',t=()=>{document.contains(w)||(w=document.querySelector(e),F()&&w&&w.setAttribute(B,"true"))};w=document.querySelector(e),o.addDocumentObserver(t,[e]),await v.get(["panel.pinned"])&&N(),o.addHotkeyListener(await v.get(["panel.hotkey"]),N),U.addEventListener("click",r=>{r.stopPropagation(),N()}),o.render($,o.render(Z,C,ie,U),oe,E),await sn(),await on();let n='.notion-cursor-listener > .notion-sidebar-container ~ [style^="position: absolute"]';await o.whenReady([n]),document.querySelector(n).before(P,$)}async function sn(){f=await v.get(["panel.width"],240),ze(),E.addEventListener("mousedown",tn),E.addEventListener("click",()=>{se?se=!1:N()})}async function on(){We=document.querySelector(".notion-app-inner"),Z.addEventListener("click",Be),ie.addEventListener("click",Be),O.addEventListener("click",ae)}var Ie=async({id:e,icon:t,title:n,$content:r,onFocus:s=()=>{},onBlur:c=()=>{}})=>{qe||(qe=o.loadStylesheet("api/components/panel.css")),v||(v=await g.db("36a2ffc9-27ff-480e-84a7-c7700a7d232d")),U||(U=o.html`
+ ${await R.feather("chevrons-right")} +
`);let a={id:e,$icon:o.render(o.html``,t instanceof Element?t:o.html`${t}`),$title:o.render(o.html``,n),$content:r,onFocus:s,onBlur:c};k.push(a),k.length===1&&await rn(),(k.length===1||await v.get(["panel.open"])===e)&&He(a)};"use strict";var Ve,q,Ke=async(e,t)=>{Ve||(Ve=o.loadStylesheet("api/components/corner-action.css"),q=o.html`
`),await o.whenReady([".notion-help-button"]);let n=document.querySelector(".notion-help-button"),r=document.querySelector(".onboarding-checklist-button");r&&q.prepend(r),q.prepend(n),o.render(document.querySelector(".notion-app-inner > .notion-cursor-listener"),q);let s=o.html`
${e}
`;return s.addEventListener("click",t),o.render(q,s),s};"use strict";"use strict"; diff --git a/src/common/index.mjs b/src/common/index.mjs new file mode 100644 index 0000000..f3538f8 --- /dev/null +++ b/src/common/index.mjs @@ -0,0 +1,31 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +// compiles to .cjs for use in electron: +// npx -y esbuild insert/api/index.mjs --minify --bundle --format=cjs --outfile=insert/api/index.cjs + +/** environment-specific methods and constants */ +export * as env from './env.mjs'; +/** environment-specific file reading */ +export * as fs from './fs.mjs'; +/** environment-specific data persistence */ +export * as storage from './storage.mjs'; + +/** access to electron renderer apis */ +export * as electron from './electron.mjs'; + +/** a basic wrapper around notion's unofficial api */ +export * as notion from './notion.mjs'; +/** helpers for formatting, validating and parsing values */ +export * as fmt from './fmt.mjs'; +/** interactions with the enhancer's repository of mods */ +export * as registry from './registry.mjs'; +/** helpers for manipulation of a webpage */ +export * as web from './web.mjs'; +/** shared notion-style elements */ +export * as components from './components/index.mjs'; diff --git a/src/common/notion.mjs b/src/common/notion.mjs new file mode 100644 index 0000000..8757ba4 --- /dev/null +++ b/src/common/notion.mjs @@ -0,0 +1,367 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * a basic wrapper around notion's content apis + * @namespace notion + */ + +import { web, fs, fmt } from './index.mjs'; + +const standardiseUUID = (uuid) => { + if (uuid?.length === 32 && !uuid.includes('-')) { + uuid = uuid.replace( + /([\d\w]{8})([\d\w]{4})([\d\w]{4})([\d\w]{4})([\d\w]{12})/, + '$1-$2-$3-$4-$5' + ); + } + return uuid; +}; + +/** + * unofficial content api: get a block by id + * (requires user to be signed in or content to be public). + * why not use the official api? + * 1. cors blocking prevents use on the client + * 2. the majority of blocks are still 'unsupported' + * @param {string} id - uuidv4 record id + * @param {string=} table - record type (default: 'block'). + * may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment' + * @returns {Promise} record data. type definitions can be found here: + * https://github.com/NotionX/react-notion-x/tree/master/packages/notion-types/src + */ +export const get = async (id, table = 'block') => { + id = standardiseUUID(id); + const json = await fs.getJSON('https://www.notion.so/api/v3/getRecordValues', { + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ requests: [{ table, id }] }), + method: 'POST', + }); + return json?.results?.[0]?.value || json; +}; + +/** + * get the id of the current user (requires user to be signed in) + * @returns {string} uuidv4 user id + */ +export const getUserID = () => + JSON.parse(localStorage['LRU:KeyValueStore2:current-user-id'] || {}).value; + +/** + * get the id of the currently open page + * @returns {string} uuidv4 page id + */ +export const getPageID = () => + standardiseUUID( + web.queryParams().get('p') || location.pathname.split(/(-|\/)/g).reverse()[0] + ); + +let _spaceID; +/** + * get the id of the currently open workspace (requires user to be signed in) + * @returns {string} uuidv4 space id + */ +export const getSpaceID = async () => { + if (!_spaceID) _spaceID = (await get(getPageID())).space_id; + return _spaceID; +}; + +/** + * unofficial content api: search all blocks in a space + * (requires user to be signed in or content to be public). + * why not use the official api? + * 1. cors blocking prevents use on the client + * 2. the majority of blocks are still 'unsupported' + * @param {string=} query - query to search blocks in the space for + * @param {number=} limit - the max number of results to return (default: 20) + * @param {string=} spaceID - uuidv4 workspace id + * @returns {object} the number of total results, the list of matches, and related record values. + * type definitions can be found here: https://github.com/NotionX/react-notion-x/blob/master/packages/notion-types/src/api.ts + */ +export const search = async (query = '', limit = 20, spaceID = getSpaceID()) => { + spaceID = standardiseUUID(await spaceID); + const json = await fs.getJSON('https://www.notion.so/api/v3/search', { + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + type: 'BlocksInSpace', + query, + spaceId: spaceID, + limit, + filters: { + isDeletedOnly: false, + excludeTemplates: false, + isNavigableOnly: false, + requireEditPermissions: false, + ancestors: [], + createdBy: [], + editedBy: [], + lastEditedTime: {}, + createdTime: {}, + }, + sort: 'Relevance', + source: 'quick_find', + }), + method: 'POST', + }); + return json; +}; + +/** + * unofficial content api: update a property/the content of an existing record + * (requires user to be signed in or content to be public). + * TEST THIS THOROUGHLY. misuse can corrupt a record, leading the notion client + * to be unable to parse and render content properly and throw errors. + * why not use the official api? + * 1. cors blocking prevents use on the client + * 2. the majority of blocks are still 'unsupported' + * @param {object} pointer - the record being updated + * @param {object} recordValue - the new raw data values to set to the record. + * for examples, use notion.get to fetch an existing block record. + * to use this to update content, set pointer.path to ['properties', 'title] + * and recordValue to an array of rich text segments. a segment is an array + * where the first value is the displayed text and the second value + * is an array of decorations. a decoration is an array where the first value + * is a modifier and the second value specifies it. e.g. + * [ + * ['bold text', [['b']]], + * [' '], + * ['an italicised link', [['i'], ['a', 'https://github.com']]], + * [' '], + * ['highlighted text', [['h', 'pink_background']]], + * ] + * more examples can be creating a block with the desired content/formatting, + * then find the value of blockRecord.properties.title using notion.get. + * type definitions can be found here: https://github.com/NotionX/react-notion-x/blob/master/packages/notion-types/src/core.ts + * @param {string} pointer.recordID - uuidv4 record id + * @param {string=} pointer.recordTable - record type (default: 'block'). + * may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment' + * @param {string=} pointer.property - the record property to update. + * for record content, it will be the default: 'title'. + * for page properties, it will be the property id (the key used in pageRecord.properties). + * other possible values are unknown/untested + * @param {string=} pointer.spaceID - uuidv4 workspace id + * @param {string=} pointer.path - the path to the key to be set within the record + * (default: [], the root of the record's values) + * @returns {boolean|object} true if success, else an error object + */ +export const set = async ( + { recordID, recordTable = 'block', spaceID = getSpaceID(), path = [] }, + recordValue = {} +) => { + spaceID = standardiseUUID(await spaceID); + recordID = standardiseUUID(recordID); + const json = await fs.getJSON('https://www.notion.so/api/v3/saveTransactions', { + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + requestId: fmt.uuidv4(), + transactions: [ + { + id: fmt.uuidv4(), + spaceId: spaceID, + operations: [ + { + pointer: { + table: recordTable, + id: recordID, + spaceId: spaceID, + }, + path, + command: path.length ? 'set' : 'update', + args: recordValue, + }, + ], + }, + ], + }), + method: 'POST', + }); + return json.errorId ? json : true; +}; + +/** + * unofficial content api: create and add a new block to a page + * (requires user to be signed in or content to be public). + * TEST THIS THOROUGHLY. misuse can corrupt a record, leading the notion client + * to be unable to parse and render content properly and throw errors. + * why not use the official api? + * 1. cors blocking prevents use on the client + * 2. the majority of blocks are still 'unsupported' + * @param {object} insert - the new record. + * @param {object} pointer - where to insert the new block + * for examples, use notion.get to fetch an existing block record. + * type definitions can be found here: https://github.com/NotionX/react-notion-x/blob/master/packages/notion-types/src/block.ts + * may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment' + * @param {object=} insert.recordValue - the new raw data values to set to the record. + * @param {object=} insert.recordTable - record type (default: 'block'). + * may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment' + * @param {string=} pointer.prepend - insert before pointer.siblingID. if false, will be appended after + * @param {string=} pointer.siblingID - uuidv4 sibling id. if unset, the record will be + * inserted at the end of the page start (or the start if pointer.prepend is true) + * @param {string=} pointer.parentID - uuidv4 parent id + * @param {string=} pointer.parentTable - parent record type (default: 'block'). + * @param {string=} pointer.spaceID - uuidv4 space id + * @param {string=} pointer.userID - uuidv4 user id + * instead of the end + * @returns {string|object} error object or uuidv4 of the new record + */ +export const create = async ( + { recordValue = {}, recordTable = 'block' } = {}, + { + prepend = false, + siblingID = undefined, + parentID = getPageID(), + parentTable = 'block', + spaceID = getSpaceID(), + userID = getUserID(), + } = {} +) => { + spaceID = standardiseUUID(await spaceID); + parentID = standardiseUUID(parentID); + siblingID = standardiseUUID(siblingID); + const recordID = standardiseUUID(recordValue?.id ?? fmt.uuidv4()), + path = [], + args = { + type: 'text', + id: recordID, + version: 0, + created_time: new Date().getTime(), + last_edited_time: new Date().getTime(), + parent_id: parentID, + parent_table: parentTable, + alive: true, + created_by_table: 'notion_user', + created_by_id: userID, + last_edited_by_table: 'notion_user', + last_edited_by_id: userID, + space_id: spaceID, + permissions: [{ type: 'user_permission', role: 'editor', user_id: userID }], + }; + if (parentTable === 'space') { + parentID = spaceID; + args.parent_id = spaceID; + path.push('pages'); + args.type = 'page'; + } else if (parentTable === 'collection_view') { + path.push('page_sort'); + args.type = 'page'; + } else { + path.push('content'); + } + const json = await fs.getJSON('https://www.notion.so/api/v3/saveTransactions', { + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + requestId: fmt.uuidv4(), + transactions: [ + { + id: fmt.uuidv4(), + spaceId: spaceID, + operations: [ + { + pointer: { + table: parentTable, + id: parentID, + spaceId: spaceID, + }, + path, + command: prepend ? 'listBefore' : 'listAfter', + args: { + ...(siblingID ? { after: siblingID } : {}), + id: recordID, + }, + }, + { + pointer: { + table: recordTable, + id: recordID, + spaceId: spaceID, + }, + path: [], + command: 'set', + args: { + ...args, + ...recordValue, + }, + }, + ], + }, + ], + }), + method: 'POST', + }); + return json.errorId ? json : recordID; +}; + +/** + * unofficial content api: upload a file to notion's aws servers + * (requires user to be signed in or content to be public). + * TEST THIS THOROUGHLY. misuse can corrupt a record, leading the notion client + * to be unable to parse and render content properly and throw errors. + * why not use the official api? + * 1. cors blocking prevents use on the client + * 2. the majority of blocks are still 'unsupported' + * @param {File} file - the file to upload + * @param {object=} pointer - where the file should be accessible from + * @param {string=} pointer.pageID - uuidv4 page id + * @param {string=} pointer.spaceID - uuidv4 space id + * @returns {string|object} error object or the url of the uploaded file + */ +export const upload = async (file, { pageID = getPageID(), spaceID = getSpaceID() } = {}) => { + spaceID = standardiseUUID(await spaceID); + pageID = standardiseUUID(pageID); + const json = await fs.getJSON('https://www.notion.so/api/v3/getUploadFileUrl', { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify({ + bucket: 'secure', + name: file.name, + contentType: file.type, + record: { + table: 'block', + id: pageID, + spaceId: spaceID, + }, + }), + }); + if (json.errorId) return json; + fetch(json.signedPutUrl, { + method: 'PUT', + headers: { 'content-type': file.type }, + body: file, + }); + return json.url; +}; + +/** + * redirect through notion to a resource's signed aws url for display outside of notion + * (requires user to be signed in or content to be public) + * @param src source url for file + * @param {string} recordID uuidv4 record/block/file id + * @param {string=} recordTable record type (default: 'block'). + * may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment' + * @returns {string} url signed if necessary, else string as-is + */ +export const sign = (src, recordID, recordTable = 'block') => { + if (src.startsWith('/')) src = `https://notion.so${src}`; + if (src.includes('secure.notion-static.com')) { + src = new URL(src); + src = `https://www.notion.so/signed/${encodeURIComponent( + src.origin + src.pathname + )}?table=${recordTable}&id=${recordID}`; + } + return src; +}; diff --git a/src/common/registry-validation.mjs b/src/common/registry-validation.mjs new file mode 100644 index 0000000..1b032d2 --- /dev/null +++ b/src/common/registry-validation.mjs @@ -0,0 +1,224 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +import { fmt, registry } from './index.mjs'; + +const check = async ( + mod, + key, + value, + types, + { + extension = '', + error = `invalid ${key} (${extension ? `${extension} ` : ''}${types}): ${JSON.stringify( + value + )}`, + optional = false, + } = {} +) => { + let test; + for (const type of Array.isArray(types) ? [types] : types.split('|')) { + if (type === 'file') { + test = + value && !value.startsWith('http') + ? await fmt.is(`repo/${mod._dir}/${value}`, type, { extension }) + : false; + } else test = await fmt.is(value, type, { extension }); + if (test) break; + } + if (!test) { + if (optional && (await fmt.is(value, 'undefined'))) return true; + if (error) mod._err(error); + return false; + } + return true; +}; + +const validateEnvironments = async (mod) => { + mod.environments = mod.environments ?? registry.supportedEnvs; + const isArray = await check(mod, 'environments', mod.environments, 'array'); + if (!isArray) return false; + return mod.environments.map((tag) => + check(mod, 'environments.env', tag, registry.supportedEnvs) + ); + }, + validateTags = async (mod) => { + const isArray = await check(mod, 'tags', mod.tags, 'array'); + if (!isArray) return false; + const categoryTags = ['core', 'extension', 'theme', 'integration'], + containsCategory = mod.tags.filter((tag) => categoryTags.includes(tag)).length; + if (!containsCategory) { + mod._err( + `invalid tags (must contain at least one of 'core', 'extension', 'theme' or 'integration'): + ${JSON.stringify(mod.tags)}` + ); + return false; + } + const isTheme = mod.tags.includes('theme'), + hasThemeMode = mod.tags.includes('light') || mod.tags.includes('dark'), + isBothThemeModes = mod.tags.includes('light') && mod.tags.includes('dark'); + if (isTheme && (!hasThemeMode || isBothThemeModes)) { + mod._err( + `invalid tags (themes must be either 'light' or 'dark', not neither or both): + ${JSON.stringify(mod.tags)}` + ); + return false; + } + return mod.tags.map((tag) => check(mod, 'tags.tag', tag, 'string')); + }, + validateAuthors = async (mod) => { + const isArray = await check(mod, 'authors', mod.authors, 'array'); + if (!isArray) return false; + return mod.authors.map((author) => [ + check(mod, 'authors.author.name', author.name, 'string'), + check(mod, 'authors.author.email', author.email, 'email', { optional: true }), + check(mod, 'authors.author.homepage', author.homepage, 'url'), + check(mod, 'authors.author.avatar', author.avatar, 'url'), + ]); + }, + validateCSS = async (mod) => { + const isArray = await check(mod, 'css', mod.css, 'object'); + if (!isArray) return false; + const tests = []; + for (const dest of ['frame', 'client', 'menu']) { + if (!mod.css[dest]) continue; + let test = await check(mod, `css.${dest}`, mod.css[dest], 'array'); + if (test) { + test = mod.css[dest].map((file) => + check(mod, `css.${dest}.file`, file, 'file', { extension: '.css' }) + ); + } + tests.push(test); + } + return tests; + }, + validateJS = async (mod) => { + const isArray = await check(mod, 'js', mod.js, 'object'); + if (!isArray) return false; + const tests = []; + for (const dest of ['frame', 'client', 'menu']) { + if (!mod.js[dest]) continue; + let test = await check(mod, `js.${dest}`, mod.js[dest], 'array'); + if (test) { + test = mod.js[dest].map((file) => + check(mod, `js.${dest}.file`, file, 'file', { extension: '.mjs' }) + ); + } + tests.push(test); + } + if (mod.js.electron) { + const isArray = await check(mod, 'js.electron', mod.js.electron, 'array'); + if (isArray) { + for (const file of mod.js.electron) { + const isObject = await check(mod, 'js.electron.file', file, 'object'); + if (!isObject) { + tests.push(false); + continue; + } + tests.push([ + check(mod, 'js.electron.file.source', file.source, 'file', { + extension: '.cjs', + }), + // referencing the file within the electron app + // existence can't be validated, so only format is + check(mod, 'js.electron.file.target', file.target, 'string', { + extension: '.js', + }), + ]); + } + } else tests.push(false); + } + return tests; + }, + validateOptions = async (mod) => { + const isArray = await check(mod, 'options', mod.options, 'array'); + if (!isArray) return false; + const tests = []; + for (const option of mod.options) { + const key = 'options.option', + optTypeValid = await check(mod, `${key}.type`, option.type, registry.optionTypes); + if (!optTypeValid) { + tests.push(false); + continue; + } + option.environments = option.environments ?? registry.supportedEnvs; + tests.push([ + check(mod, `${key}.key`, option.key, 'alphanumeric'), + check(mod, `${key}.label`, option.label, 'string'), + check(mod, `${key}.tooltip`, option.tooltip, 'string', { + optional: true, + }), + check(mod, `${key}.environments`, option.environments, 'array').then((isArray) => { + if (!isArray) return false; + return option.environments.map((environment) => + check(mod, `${key}.environments.env`, environment, registry.supportedEnvs) + ); + }), + ]); + switch (option.type) { + case 'toggle': + tests.push(check(mod, `${key}.value`, option.value, 'boolean')); + break; + case 'select': { + let test = await check(mod, `${key}.values`, option.values, 'array'); + if (test) { + test = option.values.map((value) => + check(mod, `${key}.values.value`, value, 'string') + ); + } + tests.push(test); + break; + } + case 'text': + case 'hotkey': + tests.push(check(mod, `${key}.value`, option.value, 'string')); + break; + case 'number': + case 'color': + tests.push(check(mod, `${key}.value`, option.value, option.type)); + break; + case 'file': { + let test = await check(mod, `${key}.extensions`, option.extensions, 'array'); + if (test) { + test = option.extensions.map((ext) => + check(mod, `${key}.extensions.extension`, ext, 'string') + ); + } + tests.push(test); + break; + } + } + } + return tests; + }; + +/** + * internally used to validate mod.json files and provide helpful errors + * @private + * @param {object} mod - a mod's mod.json in object form + * @returns {boolean} whether or not the mod has passed validation + */ +export async function validate(mod) { + let conditions = [ + check(mod, 'name', mod.name, 'string'), + check(mod, 'id', mod.id, 'uuid'), + check(mod, 'version', mod.version, 'semver'), + validateEnvironments(mod), + check(mod, 'description', mod.description, 'string'), + check(mod, 'preview', mod.preview, 'file|url', { optional: true }), + validateTags(mod), + validateAuthors(mod), + validateCSS(mod), + validateJS(mod), + validateOptions(mod), + ]; + do { + conditions = await Promise.all(conditions.flat(Infinity)); + } while (conditions.some((condition) => Array.isArray(condition))); + return conditions.every((passed) => passed); +} diff --git a/src/common/registry.mjs b/src/common/registry.mjs new file mode 100644 index 0000000..4c2682b --- /dev/null +++ b/src/common/registry.mjs @@ -0,0 +1,160 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * interactions with the enhancer's repository of mods + * @namespace registry + */ + +import { env, fs, storage } from './index.mjs'; +import { validate } from './registry-validation.mjs'; + +/** + * mod ids whitelisted as part of the enhancer's core, permanently enabled + * @constant + * @type {string[]} + */ +export const core = [ + 'a6621988-551d-495a-97d8-3c568bca2e9e', + '0f0bf8b6-eae6-4273-b307-8fc43f2ee082', + '36a2ffc9-27ff-480e-84a7-c7700a7d232d', +]; + +/** + * all environments/platforms currently supported by the enhancer + * @constant + * @type {string[]} + */ +export const supportedEnvs = ['linux', 'win32', 'darwin', 'extension']; + +/** + * all available configuration types + * @constant + * @type {string[]} + */ +export const optionTypes = ['toggle', 'select', 'text', 'number', 'color', 'file', 'hotkey']; + +/** + * the name of the active configuration profile + * @returns {string} + */ +export const profileName = () => storage.get(['currentprofile'], 'default'); + +/** + * the root database for the current profile + * @returns {object} the get/set functions for the profile's storage + */ +export const profileDB = async () => storage.db(['profiles', await profileName()]); + +let _list; +const _errors = []; +/** + * list all available mods in the repo + * @param {function} filter - a function to filter out mods + * @returns {array} a validated list of mod.json objects + */ +export const list = async (filter = (mod) => true) => { + if (!_list) { + // deno-lint-ignore no-async-promise-executor + _list = new Promise(async (res, _rej) => { + const passed = []; + for (const dir of await fs.getJSON('repo/registry.json')) { + try { + const mod = { + ...(await fs.getJSON(`repo/${dir}/mod.json`)), + _dir: dir, + _err: (message) => _errors.push({ source: dir, message }), + }; + if (await validate(mod)) passed.push(mod); + } catch { + _errors.push({ source: dir, message: 'invalid mod.json' }); + } + } + res(passed); + }); + } + const filtered = []; + for (const mod of await _list) if (await filter(mod)) filtered.push(mod); + return filtered; +}; + +/** + * list validation errors encountered when loading the repo + * @returns {{ source: string, message: string }[]} error objects with an error message and a source directory + */ +export const errors = async () => { + await list(); + return _errors; +}; + +/** + * get a single mod from the repo + * @param {string} id - the uuid of the mod + * @returns {object} the mod's mod.json + */ +export const get = async (id) => { + return (await list((mod) => mod.id === id))[0]; +}; + +/** + * checks if a mod is enabled: affected by the core whitelist, + * environment and menu configuration + * @param {string} id - the uuid of the mod + * @returns {boolean} whether or not the mod is enabled + */ +export const enabled = async (id) => { + const mod = await get(id); + if (!mod.environments.includes(env.name)) return false; + if (core.includes(id)) return true; + return (await profileDB()).get(['_mods', id], false); +}; + +/** + * get a default value of a mod's option according to its mod.json + * @param {string} id - the uuid of the mod + * @param {string} key - the key of the option + * @returns {string|number|boolean|undefined} the option's default value + */ +export const optionDefault = async (id, key) => { + const mod = await get(id), + opt = mod.options.find((opt) => opt.key === key); + if (!opt) return undefined; + switch (opt.type) { + case 'toggle': + case 'text': + case 'number': + case 'color': + case 'hotkey': + return opt.value; + case 'select': + return opt.values[0]; + case 'file': + return undefined; + } +}; + +/** + * access the storage partition of a mod in the current profile + * @param {string} id - the uuid of the mod + * @returns {object} an object with the wrapped get/set functions + */ +export const db = async (id) => { + const db = await profileDB(); + return storage.db( + [id], + async (path, fallback = undefined) => { + if (typeof path === 'string') path = [path]; + if (path.length === 2) { + // profiles -> profile -> mod -> option + fallback = (await optionDefault(id, path[1])) ?? fallback; + } + return db.get(path, fallback); + }, + db.set + ); +}; diff --git a/src/common/storage.mjs b/src/common/storage.mjs new file mode 100644 index 0000000..8c7dc31 --- /dev/null +++ b/src/common/storage.mjs @@ -0,0 +1,65 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * environment-specific data persistence + * @namespace storage + */ + +import * as storage from '../env/storage.mjs'; + +/** + * get persisted data + * @type {function} + * @param {string[]} path - the path of keys to the value being fetched + * @param {unknown=} fallback - a default value if the path is not matched + * @returns {Promise} value ?? fallback + */ +export const get = storage.get; + +/** + * persist data + * @type {function} + * @param {string[]} path - the path of keys to the value being set + * @param {unknown} 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 {string[]} 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.path- the path of keys to 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/src/common/web.mjs b/src/common/web.mjs new file mode 100644 index 0000000..8b201e0 --- /dev/null +++ b/src/common/web.mjs @@ -0,0 +1,301 @@ +/** + * notion-enhancer: api + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +/** + * helpers for manipulation of a webpage + * @namespace web + */ + +import { fs } from './index.mjs'; + +let _hotkeyListenersActivated = false, + _hotkeyEventListeners = [], + _documentObserver, + _documentObserverListeners = []; +const _documentObserverEvents = []; + +/** + * wait until a page is loaded and ready for modification + * @param {array=} selectors - wait for the existence of elements that match these css selectors + * @returns {Promise} a promise that will resolve when the page is ready + */ +export const whenReady = (selectors = []) => { + return new Promise((res, _rej) => { + const onLoad = () => { + const interval = setInterval(isReady, 100); + function isReady() { + const ready = selectors.every((selector) => document.querySelector(selector)); + if (!ready) return; + clearInterval(interval); + res(true); + } + isReady(); + }; + if (document.readyState !== 'complete') { + document.addEventListener('readystatechange', (_event) => { + if (document.readyState === 'complete') onLoad(); + }); + } else onLoad(); + }); +}; + +/** + * 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 + * @returns {string} escaped string + */ +export const escape = (str) => + str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/'/g, ''') + .replace(/"/g, '"') + .replace(/\\/g, '\'); + +/** + * a tagged template processor for raw html: + * stringifies, minifies, and syntax highlights + * @example web.raw`

hello

` + * @returns {string} the processed html + */ +export const raw = (str, ...templates) => { + const html = str + .map( + (chunk) => + chunk + + (['string', 'number'].includes(typeof templates[0]) + ? templates.shift() + : escape(JSON.stringify(templates.shift(), null, 2) ?? '')) + ) + .join(''); + return html.includes(' line.trim()) + .filter((line) => line.length) + .join(' '); +}; + +/** + * create a single html element inc. attributes and children from a string + * @example web.html`

hello

` + * @returns {Element} the constructed html element + */ +export const html = (str, ...templates) => { + const $fragment = document.createRange().createContextualFragment(raw(str, ...templates)); + return $fragment.children.length === 1 ? $fragment.children[0] : $fragment.children; +}; + +/** + * appends a list of html elements to a parent + * @param $container - the parent element + * @param $elems - the elements to be appended + * @returns {Element} the updated $container + */ +export const render = ($container, ...$elems) => { + $elems = $elems + .map(($elem) => ($elem instanceof HTMLCollection ? [...$elem] : $elem)) + .flat(Infinity) + .filter(($elem) => $elem); + $container.append(...$elems); + return $container; +}; + +/** + * removes all children from an element without deleting them/their behaviours + * @param $container - the parent element + * @returns {Element} the updated $container + */ +export const empty = ($container) => { + while ($container.firstChild && $container.removeChild($container.firstChild)); + return $container; +}; + +/** + * loads/applies a css stylesheet to the page + * @param {string} path - a url or within-the-enhancer filepath + */ +export const loadStylesheet = (path) => { + const $stylesheet = html``; + render(document.head, $stylesheet); + return $stylesheet; +}; + +/** + * copy text to the clipboard + * @param {string} str - the string to copy + * @returns {Promise} + */ +export const copyToClipboard = async (str) => { + try { + await navigator.clipboard.writeText(str); + } catch { + const $el = document.createElement('textarea'); + $el.value = str; + $el.setAttribute('readonly', ''); + $el.style.position = 'absolute'; + $el.style.left = '-9999px'; + document.body.appendChild($el); + $el.select(); + document.execCommand('copy'); + document.body.removeChild($el); + } +}; + +/** + * read text from the clipboard + * @returns {Promise} + */ +export const readFromClipboard = () => { + return navigator.clipboard.readText(); +}; + +const triggerHotkeyListener = (event, hotkey) => { + const inInput = document.activeElement.nodeName === 'INPUT' && !hotkey.listenInInput; + if (inInput) return; + const modifiers = { + metaKey: ['meta', 'os', 'win', 'cmd', 'command'], + ctrlKey: ['ctrl', 'control'], + shiftKey: ['shift'], + altKey: ['alt'], + }, + pressed = hotkey.keys.every((key) => { + key = key.toLowerCase(); + for (const modifier in modifiers) { + const pressed = modifiers[modifier].includes(key) && event[modifier]; + if (pressed) { + // mark modifier as part of hotkey + modifiers[modifier] = []; + return true; + } + } + if (key === 'space') key = ' '; + if (key === 'plus') key = '+'; + if (key === event.key.toLowerCase()) return true; + }); + if (!pressed) return; + // test for modifiers not in hotkey + // e.g. to differentiate ctrl+x from ctrl+shift+x + for (const modifier in modifiers) { + const modifierPressed = event[modifier], + modifierNotInHotkey = modifiers[modifier].length > 0; + if (modifierPressed && modifierNotInHotkey) return; + } + hotkey.callback(event); +}; + +/** + * register a hotkey listener to the page + * @param {array|string} keys - the combination of keys that will trigger the hotkey. + * key codes can be tested at http://keycode.info/ and are case-insensitive. + * available modifiers are 'alt', 'ctrl', 'meta', and 'shift'. + * can be provided as a + separated string. + * @param {function} callback - called whenever the keys are pressed + * @param {object=} opts - fine-tuned control over when the hotkey should be triggered + * @param {boolean=} opts.listenInInput - whether the hotkey callback should be triggered + * when an input is focused + * @param {boolean=} opts.keydown - whether to listen for the hotkey on keydown. + * by default, hotkeys are triggered by the keyup event. + */ +export const addHotkeyListener = ( + keys, + callback, + { listenInInput = false, keydown = false } = {} +) => { + if (typeof keys === 'string') keys = keys.split('+'); + _hotkeyEventListeners.push({ keys, callback, listenInInput, keydown }); + + if (!_hotkeyListenersActivated) { + _hotkeyListenersActivated = true; + document.addEventListener('keyup', (event) => { + for (const hotkey of _hotkeyEventListeners.filter(({ keydown }) => !keydown)) { + triggerHotkeyListener(event, hotkey); + } + }); + document.addEventListener('keydown', (event) => { + for (const hotkey of _hotkeyEventListeners.filter(({ keydown }) => keydown)) { + triggerHotkeyListener(event, hotkey); + } + }); + } +}; +/** + * remove a listener added with web.addHotkeyListener + * @param {function} callback + */ +export const removeHotkeyListener = (callback) => { + _hotkeyEventListeners = _hotkeyEventListeners.filter( + (listener) => listener.callback !== callback + ); +}; + +/** + * add a listener to watch for changes to the dom + * @param {onDocumentObservedCallback} callback + * @param {string[]=} selectors + */ +export const addDocumentObserver = (callback, selectors = []) => { + if (!_documentObserver) { + const handle = (queue) => { + while (queue.length) { + const event = queue.shift(), + matchesAddedNode = ($node, selector) => + $node instanceof Element && + ($node.matches(selector) || + $node.matches(`${selector} *`) || + $node.querySelector(selector)), + matchesTarget = (selector) => + event.target.matches(selector) || + event.target.matches(`${selector} *`) || + [...event.addedNodes].some(($node) => matchesAddedNode($node, selector)); + for (const listener of _documentObserverListeners) { + if (!listener.selectors.length || listener.selectors.some(matchesTarget)) { + listener.callback(event); + } + } + } + }; + _documentObserver = new MutationObserver((list, _observer) => { + if (!_documentObserverEvents.length) + requestIdleCallback(() => handle(_documentObserverEvents)); + _documentObserverEvents.push(...list); + }); + _documentObserver.observe(document.body, { + childList: true, + subtree: true, + attributes: true, + }); + } + _documentObserverListeners.push({ callback, selectors }); +}; + +/** + * remove a listener added with web.addDocumentObserver + * @param {onDocumentObservedCallback} callback + */ +export const removeDocumentObserver = (callback) => { + _documentObserverListeners = _documentObserverListeners.filter( + (listener) => listener.callback !== callback + ); +}; + +/** + * @callback onDocumentObservedCallback + * @param {MutationRecord} event - the observed dom mutation event + */ diff --git a/src/dep/README.md b/src/dep/README.md new file mode 100644 index 0000000..db23dd7 --- /dev/null +++ b/src/dep/README.md @@ -0,0 +1,5 @@ +# notion-enhancer/dep + +libraries depended on by the notion-enhancer + +[read the docs online](https://notion-enhancer.github.io/about/credits/#dependencies) diff --git a/src/dep/feather-sprite.svg b/src/dep/feather-sprite.svg new file mode 100644 index 0000000..6e20a2c --- /dev/null +++ b/src/dep/feather-sprite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/dep/jscolor.min.js b/src/dep/jscolor.min.js new file mode 100644 index 0000000..8bb82f9 --- /dev/null +++ b/src/dep/jscolor.min.js @@ -0,0 +1,6 @@ +/** + * JSColor v2.4.6 + * @licence GNU GLP v3 + * @source https://jscolor.com/ + */ +(function (global, factory) { "use strict"; if (typeof module === "object" && typeof module.exports === "object") { module.exports = global.document ? factory(global) : function (win) { if (!win.document) { throw new Error("jscolor needs a window with document") } return factory(win) }; return } factory(global) })(typeof window !== "undefined" ? window : this, function (window) { "use strict"; var jscolor = function () { var jsc = { initialized: false, instances: [], readyQueue: [], register: function () { if (typeof window !== "undefined" && window.document) { window.document.addEventListener("DOMContentLoaded", jsc.pub.init, false) } }, installBySelector: function (selector, rootNode) { rootNode = rootNode ? jsc.node(rootNode) : window.document; if (!rootNode) { throw new Error("Missing root node") } var elms = rootNode.querySelectorAll(selector); var matchClass = new RegExp("(^|\\s)(" + jsc.pub.lookupClass + ")(\\s*(\\{[^}]*\\})|\\s|$)", "i"); for (var i = 0; i < elms.length; i += 1){ if (elms[i].jscolor && elms[i].jscolor instanceof jsc.pub) { continue } if (elms[i].type !== undefined && elms[i].type.toLowerCase() == "color" && jsc.isColorAttrSupported) { continue } var dataOpts, m; if ((dataOpts = jsc.getDataAttr(elms[i], "jscolor")) !== null || elms[i].className && (m = elms[i].className.match(matchClass))) { var targetElm = elms[i]; var optsStr = ""; if (dataOpts !== null) { optsStr = dataOpts } else if (m) { console.warn('Installation using class name is DEPRECATED. Use data-jscolor="" attribute instead.' + jsc.docsRef); if (m[4]) { optsStr = m[4] } } var opts = null; if (optsStr.trim()) { try { opts = jsc.parseOptionsStr(optsStr) } catch (e) { console.warn(e + "\n" + optsStr) } } try { new jsc.pub(targetElm, opts) } catch (e) { console.warn(e) } } } }, parseOptionsStr: function (str) { var opts = null; try { opts = JSON.parse(str) } catch (eParse) { if (!jsc.pub.looseJSON) { throw new Error("Could not parse jscolor options as JSON: " + eParse) } else { try { opts = new Function("var opts = (" + str + '); return typeof opts === "object" ? opts : {};')() } catch (eEval) { throw new Error("Could not evaluate jscolor options: " + eEval) } } } return opts }, getInstances: function () { var inst = []; for (var i = 0; i < jsc.instances.length; i += 1){ if (jsc.instances[i] && jsc.instances[i].targetElement) { inst.push(jsc.instances[i]) } } return inst }, createEl: function (tagName) { var el = window.document.createElement(tagName); jsc.setData(el, "gui", true); return el }, node: function (nodeOrSelector) { if (!nodeOrSelector) { return null } if (typeof nodeOrSelector === "string") { var sel = nodeOrSelector; var el = null; try { el = window.document.querySelector(sel) } catch (e) { console.warn(e); return null } if (!el) { console.warn("No element matches the selector: %s", sel) } return el } if (jsc.isNode(nodeOrSelector)) { return nodeOrSelector } console.warn("Invalid node of type %s: %s", typeof nodeOrSelector, nodeOrSelector); return null }, isNode: function (val) { if (typeof Node === "object") { return val instanceof Node } return val && typeof val === "object" && typeof val.nodeType === "number" && typeof val.nodeName === "string" }, nodeName: function (node) { if (node && node.nodeName) { return node.nodeName.toLowerCase() } return false }, removeChildren: function (node) { while (node.firstChild) { node.removeChild(node.firstChild) } }, isTextInput: function (el) { return el && jsc.nodeName(el) === "input" && el.type.toLowerCase() === "text" }, isButton: function (el) { if (!el) { return false } var n = jsc.nodeName(el); return n === "button" || n === "input" && ["button", "submit", "reset"].indexOf(el.type.toLowerCase()) > -1 }, isButtonEmpty: function (el) { switch (jsc.nodeName(el)) { case "input": return !el.value || el.value.trim() === ""; case "button": return el.textContent.trim() === "" }return null }, isPassiveEventSupported: function () { var supported = false; try { var opts = Object.defineProperty({}, "passive", { get: function () { supported = true } }); window.addEventListener("testPassive", null, opts); window.removeEventListener("testPassive", null, opts) } catch (e) { } return supported }(), isColorAttrSupported: function () { var elm = window.document.createElement("input"); if (elm.setAttribute) { elm.setAttribute("type", "color"); if (elm.type.toLowerCase() == "color") { return true } } return false }(), dataProp: "_data_jscolor", setData: function () { var obj = arguments[0]; if (arguments.length === 3) { var data = obj.hasOwnProperty(jsc.dataProp) ? obj[jsc.dataProp] : obj[jsc.dataProp] = {}; var prop = arguments[1]; var value = arguments[2]; data[prop] = value; return true } else if (arguments.length === 2 && typeof arguments[1] === "object") { var data = obj.hasOwnProperty(jsc.dataProp) ? obj[jsc.dataProp] : obj[jsc.dataProp] = {}; var map = arguments[1]; for (var prop in map) { if (map.hasOwnProperty(prop)) { data[prop] = map[prop] } } return true } throw new Error("Invalid arguments") }, removeData: function () { var obj = arguments[0]; if (!obj.hasOwnProperty(jsc.dataProp)) { return true } for (var i = 1; i < arguments.length; i += 1){ var prop = arguments[i]; delete obj[jsc.dataProp][prop] } return true }, getData: function (obj, prop, setDefault) { if (!obj.hasOwnProperty(jsc.dataProp)) { if (setDefault !== undefined) { obj[jsc.dataProp] = {} } else { return undefined } } var data = obj[jsc.dataProp]; if (!data.hasOwnProperty(prop) && setDefault !== undefined) { data[prop] = setDefault } return data[prop] }, getDataAttr: function (el, name) { var attrName = "data-" + name; var attrValue = el.getAttribute(attrName); return attrValue }, setDataAttr: function (el, name, value) { var attrName = "data-" + name; el.setAttribute(attrName, value) }, _attachedGroupEvents: {}, attachGroupEvent: function (groupName, el, evnt, func) { if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) { jsc._attachedGroupEvents[groupName] = [] } jsc._attachedGroupEvents[groupName].push([el, evnt, func]); el.addEventListener(evnt, func, false) }, detachGroupEvents: function (groupName) { if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) { for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1){ var evt = jsc._attachedGroupEvents[groupName][i]; evt[0].removeEventListener(evt[1], evt[2], false) } delete jsc._attachedGroupEvents[groupName] } }, preventDefault: function (e) { if (e.preventDefault) { e.preventDefault() } e.returnValue = false }, captureTarget: function (target) { if (target.setCapture) { jsc._capturedTarget = target; jsc._capturedTarget.setCapture() } }, releaseTarget: function () { if (jsc._capturedTarget) { jsc._capturedTarget.releaseCapture(); jsc._capturedTarget = null } }, triggerEvent: function (el, eventName, bubbles, cancelable) { if (!el) { return } var ev = null; if (typeof Event === "function") { ev = new Event(eventName, { bubbles: bubbles, cancelable: cancelable }) } else { ev = window.document.createEvent("Event"); ev.initEvent(eventName, bubbles, cancelable) } if (!ev) { return false } jsc.setData(ev, "internal", true); el.dispatchEvent(ev); return true }, triggerInputEvent: function (el, eventName, bubbles, cancelable) { if (!el) { return } if (jsc.isTextInput(el)) { jsc.triggerEvent(el, eventName, bubbles, cancelable) } }, eventKey: function (ev) { var keys = { 9: "Tab", 13: "Enter", 27: "Escape" }; if (typeof ev.code === "string") { return ev.code } else if (ev.keyCode !== undefined && keys.hasOwnProperty(ev.keyCode)) { return keys[ev.keyCode] } return null }, strList: function (str) { if (!str) { return [] } return str.replace(/^\s+|\s+$/g, "").split(/\s+/) }, hasClass: function (elm, className) { if (!className) { return false } if (elm.classList !== undefined) { return elm.classList.contains(className) } return -1 != (" " + elm.className.replace(/\s+/g, " ") + " ").indexOf(" " + className + " ") }, addClass: function (elm, className) { var classNames = jsc.strList(className); if (elm.classList !== undefined) { for (var i = 0; i < classNames.length; i += 1){ elm.classList.add(classNames[i]) } return } for (var i = 0; i < classNames.length; i += 1){ if (!jsc.hasClass(elm, classNames[i])) { elm.className += (elm.className ? " " : "") + classNames[i] } } }, removeClass: function (elm, className) { var classNames = jsc.strList(className); if (elm.classList !== undefined) { for (var i = 0; i < classNames.length; i += 1){ elm.classList.remove(classNames[i]) } return } for (var i = 0; i < classNames.length; i += 1){ var repl = new RegExp("^\\s*" + classNames[i] + "\\s*|" + "\\s*" + classNames[i] + "\\s*$|" + "\\s+" + classNames[i] + "(\\s+)", "g"); elm.className = elm.className.replace(repl, "$1") } }, getCompStyle: function (elm) { var compStyle = window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle; if (!compStyle) { return {} } return compStyle }, setStyle: function (elm, styles, important, reversible) { var priority = important ? "important" : ""; var origStyle = null; for (var prop in styles) { if (styles.hasOwnProperty(prop)) { var setVal = null; if (styles[prop] === null) { if (!origStyle) { origStyle = jsc.getData(elm, "origStyle") } if (origStyle && origStyle.hasOwnProperty(prop)) { setVal = origStyle[prop] } } else { if (reversible) { if (!origStyle) { origStyle = jsc.getData(elm, "origStyle", {}) } if (!origStyle.hasOwnProperty(prop)) { origStyle[prop] = elm.style[prop] } } setVal = styles[prop] } if (setVal !== null) { elm.style.setProperty(prop, setVal, priority) } } } }, hexColor: function (r, g, b) { return "#" + (("0" + Math.round(r).toString(16)).substr(-2) + ("0" + Math.round(g).toString(16)).substr(-2) + ("0" + Math.round(b).toString(16)).substr(-2)).toUpperCase() }, hexaColor: function (r, g, b, a) { return "#" + (("0" + Math.round(r).toString(16)).substr(-2) + ("0" + Math.round(g).toString(16)).substr(-2) + ("0" + Math.round(b).toString(16)).substr(-2) + ("0" + Math.round(a * 255).toString(16)).substr(-2)).toUpperCase() }, rgbColor: function (r, g, b) { return "rgb(" + Math.round(r) + "," + Math.round(g) + "," + Math.round(b) + ")" }, rgbaColor: function (r, g, b, a) { return "rgba(" + Math.round(r) + "," + Math.round(g) + "," + Math.round(b) + "," + Math.round((a === undefined || a === null ? 1 : a) * 100) / 100 + ")" }, linearGradient: function () { function getFuncName() { var stdName = "linear-gradient"; var prefixes = ["", "-webkit-", "-moz-", "-o-", "-ms-"]; var helper = window.document.createElement("div"); for (var i = 0; i < prefixes.length; i += 1){ var tryFunc = prefixes[i] + stdName; var tryVal = tryFunc + "(to right, rgba(0,0,0,0), rgba(0,0,0,0))"; helper.style.background = tryVal; if (helper.style.background) { return tryFunc } } return stdName } var funcName = getFuncName(); return function () { return funcName + "(" + Array.prototype.join.call(arguments, ", ") + ")" } }(), setBorderRadius: function (elm, value) { jsc.setStyle(elm, { "border-radius": value || "0" }) }, setBoxShadow: function (elm, value) { jsc.setStyle(elm, { "box-shadow": value || "none" }) }, getElementPos: function (e, relativeToViewport) { var x = 0, y = 0; var rect = e.getBoundingClientRect(); x = rect.left; y = rect.top; if (!relativeToViewport) { var viewPos = jsc.getViewPos(); x += viewPos[0]; y += viewPos[1] } return [x, y] }, getElementSize: function (e) { return [e.offsetWidth, e.offsetHeight] }, getAbsPointerPos: function (e) { var x = 0, y = 0; if (typeof e.changedTouches !== "undefined" && e.changedTouches.length) { x = e.changedTouches[0].clientX; y = e.changedTouches[0].clientY } else if (typeof e.clientX === "number") { x = e.clientX; y = e.clientY } return { x: x, y: y } }, getRelPointerPos: function (e) { var target = e.target || e.srcElement; var targetRect = target.getBoundingClientRect(); var x = 0, y = 0; var clientX = 0, clientY = 0; if (typeof e.changedTouches !== "undefined" && e.changedTouches.length) { clientX = e.changedTouches[0].clientX; clientY = e.changedTouches[0].clientY } else if (typeof e.clientX === "number") { clientX = e.clientX; clientY = e.clientY } x = clientX - targetRect.left; y = clientY - targetRect.top; return { x: x, y: y } }, getViewPos: function () { var doc = window.document.documentElement; return [(window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)] }, getViewSize: function () { var doc = window.document.documentElement; return [window.innerWidth || doc.clientWidth, window.innerHeight || doc.clientHeight] }, RGB_HSV: function (r, g, b) { r /= 255; g /= 255; b /= 255; var n = Math.min(Math.min(r, g), b); var v = Math.max(Math.max(r, g), b); var m = v - n; if (m === 0) { return [null, 0, 100 * v] } var h = r === n ? 3 + (b - g) / m : g === n ? 5 + (r - b) / m : 1 + (g - r) / m; return [60 * (h === 6 ? 0 : h), 100 * (m / v), 100 * v] }, HSV_RGB: function (h, s, v) { var u = 255 * (v / 100); if (h === null) { return [u, u, u] } h /= 60; s /= 100; var i = Math.floor(h); var f = i % 2 ? h - i : 1 - (h - i); var m = u * (1 - s); var n = u * (1 - s * f); switch (i) { case 6: case 0: return [u, n, m]; case 1: return [n, u, m]; case 2: return [m, u, n]; case 3: return [m, n, u]; case 4: return [n, m, u]; case 5: return [u, m, n] } }, parseColorString: function (str) { var ret = { rgba: null, format: null }; var m; if (m = str.match(/^\W*([0-9A-F]{3,8})\W*$/i)) { if (m[1].length === 8) { ret.format = "hexa"; ret.rgba = [parseInt(m[1].substr(0, 2), 16), parseInt(m[1].substr(2, 2), 16), parseInt(m[1].substr(4, 2), 16), parseInt(m[1].substr(6, 2), 16) / 255] } else if (m[1].length === 6) { ret.format = "hex"; ret.rgba = [parseInt(m[1].substr(0, 2), 16), parseInt(m[1].substr(2, 2), 16), parseInt(m[1].substr(4, 2), 16), null] } else if (m[1].length === 3) { ret.format = "hex"; ret.rgba = [parseInt(m[1].charAt(0) + m[1].charAt(0), 16), parseInt(m[1].charAt(1) + m[1].charAt(1), 16), parseInt(m[1].charAt(2) + m[1].charAt(2), 16), null] } else { return false } return ret } if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { var par = m[1].split(","); var re = /^\s*(\d+|\d*\.\d+|\d+\.\d*)\s*$/; var mR, mG, mB, mA; if (par.length >= 3 && (mR = par[0].match(re)) && (mG = par[1].match(re)) && (mB = par[2].match(re))) { ret.format = "rgb"; ret.rgba = [parseFloat(mR[1]) || 0, parseFloat(mG[1]) || 0, parseFloat(mB[1]) || 0, null]; if (par.length >= 4 && (mA = par[3].match(re))) { ret.format = "rgba"; ret.rgba[3] = parseFloat(mA[1]) || 0 } return ret } } return false }, parsePaletteValue: function (mixed) { var vals = []; if (typeof mixed === "string") { mixed.replace(/#[0-9A-F]{3}([0-9A-F]{3})?|rgba?\(([^)]*)\)/gi, function (val) { vals.push(val) }) } else if (Array.isArray(mixed)) { vals = mixed } var colors = []; for (var i = 0; i < vals.length; i++){ var color = jsc.parseColorString(vals[i]); if (color) { colors.push(color) } } return colors }, containsTranparentColor: function (colors) { for (var i = 0; i < colors.length; i++){ var a = colors[i].rgba[3]; if (a !== null && a < 1) { return true } } return false }, isAlphaFormat: function (format) { switch (format.toLowerCase()) { case "hexa": case "rgba": return true }return false }, scaleCanvasForHighDPR: function (canvas) { var dpr = window.devicePixelRatio || 1; canvas.width *= dpr; canvas.height *= dpr; var ctx = canvas.getContext("2d"); ctx.scale(dpr, dpr) }, genColorPreviewCanvas: function (color, separatorPos, specWidth, scaleForHighDPR) { var sepW = Math.round(jsc.pub.previewSeparator.length); var sqSize = jsc.pub.chessboardSize; var sqColor1 = jsc.pub.chessboardColor1; var sqColor2 = jsc.pub.chessboardColor2; var cWidth = specWidth ? specWidth : sqSize * 2; var cHeight = sqSize * 2; var canvas = jsc.createEl("canvas"); var ctx = canvas.getContext("2d"); canvas.width = cWidth; canvas.height = cHeight; if (scaleForHighDPR) { jsc.scaleCanvasForHighDPR(canvas) } ctx.fillStyle = sqColor1; ctx.fillRect(0, 0, cWidth, cHeight); ctx.fillStyle = sqColor2; for (var x = 0; x < cWidth; x += sqSize * 2){ ctx.fillRect(x, 0, sqSize, sqSize); ctx.fillRect(x + sqSize, sqSize, sqSize, sqSize) } if (color) { ctx.fillStyle = color; ctx.fillRect(0, 0, cWidth, cHeight) } var start = null; switch (separatorPos) { case "left": start = 0; ctx.clearRect(0, 0, sepW / 2, cHeight); break; case "right": start = cWidth - sepW; ctx.clearRect(cWidth - sepW / 2, 0, sepW / 2, cHeight); break }if (start !== null) { ctx.lineWidth = 1; for (var i = 0; i < jsc.pub.previewSeparator.length; i += 1){ ctx.beginPath(); ctx.strokeStyle = jsc.pub.previewSeparator[i]; ctx.moveTo(.5 + start + i, 0); ctx.lineTo(.5 + start + i, cHeight); ctx.stroke() } } return { canvas: canvas, width: cWidth, height: cHeight } }, genColorPreviewGradient: function (color, position, width) { var params = []; if (position && width) { params = ["to " + { left: "right", right: "left" }[position], color + " 0%", color + " " + width + "px", "rgba(0,0,0,0) " + (width + 1) + "px", "rgba(0,0,0,0) 100%"] } else { params = ["to right", color + " 0%", color + " 100%"] } return jsc.linearGradient.apply(this, params) }, redrawPosition: function () { if (!jsc.picker || !jsc.picker.owner) { return } var thisObj = jsc.picker.owner; var tp, vp; if (thisObj.fixed) { tp = jsc.getElementPos(thisObj.targetElement, true); vp = [0, 0] } else { tp = jsc.getElementPos(thisObj.targetElement); vp = jsc.getViewPos() } var ts = jsc.getElementSize(thisObj.targetElement); var vs = jsc.getViewSize(); var pd = jsc.getPickerDims(thisObj); var ps = [pd.borderW, pd.borderH]; var a, b, c; switch (thisObj.position.toLowerCase()) { case "left": a = 1; b = 0; c = -1; break; case "right": a = 1; b = 0; c = 1; break; case "top": a = 0; b = 1; c = -1; break; default: a = 0; b = 1; c = 1; break }var l = (ts[b] + ps[b]) / 2; if (!thisObj.smartPosition) { var pp = [tp[a], tp[b] + ts[b] - l + l * c] } else { var pp = [-vp[a] + tp[a] + ps[a] > vs[a] ? -vp[a] + tp[a] + ts[a] / 2 > vs[a] / 2 && tp[a] + ts[a] - ps[a] >= 0 ? tp[a] + ts[a] - ps[a] : tp[a] : tp[a], -vp[b] + tp[b] + ts[b] + ps[b] - l + l * c > vs[b] ? -vp[b] + tp[b] + ts[b] / 2 > vs[b] / 2 && tp[b] + ts[b] - l - l * c >= 0 ? tp[b] + ts[b] - l - l * c : tp[b] + ts[b] - l + l * c : tp[b] + ts[b] - l + l * c >= 0 ? tp[b] + ts[b] - l + l * c : tp[b] + ts[b] - l - l * c] } var x = pp[a]; var y = pp[b]; var positionValue = thisObj.fixed ? "fixed" : "absolute"; var contractShadow = (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && pp[1] + ps[1] < tp[1] + ts[1]; jsc._drawPosition(thisObj, x, y, positionValue, contractShadow) }, _drawPosition: function (thisObj, x, y, positionValue, contractShadow) { var vShadow = contractShadow ? 0 : thisObj.shadowBlur; jsc.picker.wrap.style.position = positionValue; jsc.picker.wrap.style.left = x + "px"; jsc.picker.wrap.style.top = y + "px"; jsc.setBoxShadow(jsc.picker.boxS, thisObj.shadow ? new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : null) }, getPickerDims: function (thisObj) { var w = 2 * thisObj.controlBorderWidth + thisObj.width; var h = 2 * thisObj.controlBorderWidth + thisObj.height; var sliderSpace = 2 * thisObj.controlBorderWidth + 2 * jsc.getControlPadding(thisObj) + thisObj.sliderSize; if (jsc.getSliderChannel(thisObj)) { w += sliderSpace } if (thisObj.hasAlphaChannel()) { w += sliderSpace } var pal = jsc.getPaletteDims(thisObj, w); if (pal.height) { h += pal.height + thisObj.padding } if (thisObj.closeButton) { h += 2 * thisObj.controlBorderWidth + thisObj.padding + thisObj.buttonHeight } var pW = w + 2 * thisObj.padding; var pH = h + 2 * thisObj.padding; return { contentW: w, contentH: h, paddedW: pW, paddedH: pH, borderW: pW + 2 * thisObj.borderWidth, borderH: pH + 2 * thisObj.borderWidth, palette: pal } }, getPaletteDims: function (thisObj, width) { var cols = 0, rows = 0, cellW = 0, cellH = 0, height = 0; var sampleCount = thisObj._palette ? thisObj._palette.length : 0; if (sampleCount) { cols = thisObj.paletteCols; rows = cols > 0 ? Math.ceil(sampleCount / cols) : 0; cellW = Math.max(1, Math.floor((width - (cols - 1) * thisObj.paletteSpacing) / cols)); cellH = thisObj.paletteHeight ? Math.min(thisObj.paletteHeight, cellW) : cellW } if (rows) { height = rows * cellH + (rows - 1) * thisObj.paletteSpacing } return { cols: cols, rows: rows, cellW: cellW, cellH: cellH, width: width, height: height } }, getControlPadding: function (thisObj) { return Math.max(thisObj.padding / 2, 2 * thisObj.pointerBorderWidth + thisObj.pointerThickness - thisObj.controlBorderWidth) }, getPadYChannel: function (thisObj) { switch (thisObj.mode.charAt(1).toLowerCase()) { case "v": return "v"; break }return "s" }, getSliderChannel: function (thisObj) { if (thisObj.mode.length > 2) { switch (thisObj.mode.charAt(2).toLowerCase()) { case "s": return "s"; break; case "v": return "v"; break } } return null }, triggerCallback: function (thisObj, prop) { if (!thisObj[prop]) { return } var callback = null; if (typeof thisObj[prop] === "string") { try { callback = new Function(thisObj[prop]) } catch (e) { console.error(e) } } else { callback = thisObj[prop] } if (callback) { callback.call(thisObj) } }, triggerGlobal: function (eventNames) { var inst = jsc.getInstances(); for (var i = 0; i < inst.length; i += 1){ inst[i].trigger(eventNames) } }, _pointerMoveEvent: { mouse: "mousemove", touch: "touchmove" }, _pointerEndEvent: { mouse: "mouseup", touch: "touchend" }, _pointerOrigin: null, _capturedTarget: null, onDocumentKeyUp: function (e) { if (["Tab", "Escape"].indexOf(jsc.eventKey(e)) !== -1) { if (jsc.picker && jsc.picker.owner) { jsc.picker.owner.tryHide() } } }, onWindowResize: function (e) { jsc.redrawPosition() }, onWindowScroll: function (e) { jsc.redrawPosition() }, onParentScroll: function (e) { if (jsc.picker && jsc.picker.owner) { jsc.picker.owner.tryHide() } }, onDocumentMouseDown: function (e) { var target = e.target || e.srcElement; if (target.jscolor && target.jscolor instanceof jsc.pub) { if (target.jscolor.showOnClick && !target.disabled) { target.jscolor.show() } } else if (jsc.getData(target, "gui")) { var control = jsc.getData(target, "control"); if (control) { jsc.onControlPointerStart(e, target, jsc.getData(target, "control"), "mouse") } } else { if (jsc.picker && jsc.picker.owner) { jsc.picker.owner.tryHide() } } }, onPickerTouchStart: function (e) { var target = e.target || e.srcElement; if (jsc.getData(target, "control")) { jsc.onControlPointerStart(e, target, jsc.getData(target, "control"), "touch") } }, onControlPointerStart: function (e, target, controlName, pointerType) { var thisObj = jsc.getData(target, "instance"); jsc.preventDefault(e); jsc.captureTarget(target); var registerDragEvents = function (doc, offset) { jsc.attachGroupEvent("drag", doc, jsc._pointerMoveEvent[pointerType], jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); jsc.attachGroupEvent("drag", doc, jsc._pointerEndEvent[pointerType], jsc.onDocumentPointerEnd(e, target, controlName, pointerType)) }; registerDragEvents(window.document, [0, 0]); if (window.parent && window.frameElement) { var rect = window.frameElement.getBoundingClientRect(); var ofs = [-rect.left, -rect.top]; registerDragEvents(window.parent.window.document, ofs) } var abs = jsc.getAbsPointerPos(e); var rel = jsc.getRelPointerPos(e); jsc._pointerOrigin = { x: abs.x - rel.x, y: abs.y - rel.y }; switch (controlName) { case "pad": if (jsc.getSliderChannel(thisObj) === "v" && thisObj.channels.v === 0) { thisObj.fromHSVA(null, null, 100, null) } jsc.setPad(thisObj, e, 0, 0); break; case "sld": jsc.setSld(thisObj, e, 0); break; case "asld": jsc.setASld(thisObj, e, 0); break }thisObj.trigger("input") }, onDocumentPointerMove: function (e, target, controlName, pointerType, offset) { return function (e) { var thisObj = jsc.getData(target, "instance"); switch (controlName) { case "pad": jsc.setPad(thisObj, e, offset[0], offset[1]); break; case "sld": jsc.setSld(thisObj, e, offset[1]); break; case "asld": jsc.setASld(thisObj, e, offset[1]); break }thisObj.trigger("input") } }, onDocumentPointerEnd: function (e, target, controlName, pointerType) { return function (e) { var thisObj = jsc.getData(target, "instance"); jsc.detachGroupEvents("drag"); jsc.releaseTarget(); thisObj.trigger("input"); thisObj.trigger("change") } }, onPaletteSampleClick: function (e) { var target = e.currentTarget; var thisObj = jsc.getData(target, "instance"); var color = jsc.getData(target, "color"); if (thisObj.format.toLowerCase() === "any") { thisObj._setFormat(color.format); if (!jsc.isAlphaFormat(thisObj.getFormat())) { color.rgba[3] = 1 } } if (color.rgba[3] === null) { if (thisObj.paletteSetsAlpha === true || thisObj.paletteSetsAlpha === "auto" && thisObj._paletteHasTransparency) { color.rgba[3] = 1 } } thisObj.fromRGBA.apply(thisObj, color.rgba); thisObj.trigger("input"); thisObj.trigger("change"); if (thisObj.hideOnPaletteClick) { thisObj.hide() } }, setPad: function (thisObj, e, ofsX, ofsY) { var pointerAbs = jsc.getAbsPointerPos(e); var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.controlBorderWidth; var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; var xVal = x * (360 / (thisObj.width - 1)); var yVal = 100 - y * (100 / (thisObj.height - 1)); switch (jsc.getPadYChannel(thisObj)) { case "s": thisObj.fromHSVA(xVal, yVal, null, null); break; case "v": thisObj.fromHSVA(xVal, null, yVal, null); break } }, setSld: function (thisObj, e, ofsY) { var pointerAbs = jsc.getAbsPointerPos(e); var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; var yVal = 100 - y * (100 / (thisObj.height - 1)); switch (jsc.getSliderChannel(thisObj)) { case "s": thisObj.fromHSVA(null, yVal, null, null); break; case "v": thisObj.fromHSVA(null, null, yVal, null); break } }, setASld: function (thisObj, e, ofsY) { var pointerAbs = jsc.getAbsPointerPos(e); var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; var yVal = 1 - y * (1 / (thisObj.height - 1)); if (yVal < 1) { var fmt = thisObj.getFormat(); if (thisObj.format.toLowerCase() === "any" && !jsc.isAlphaFormat(fmt)) { thisObj._setFormat(fmt === "hex" ? "hexa" : "rgba") } } thisObj.fromHSVA(null, null, null, yVal) }, createPadCanvas: function () { var ret = { elm: null, draw: null }; var canvas = jsc.createEl("canvas"); var ctx = canvas.getContext("2d"); var drawFunc = function (width, height, type) { canvas.width = width; canvas.height = height; ctx.clearRect(0, 0, canvas.width, canvas.height); var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); hGrad.addColorStop(0 / 6, "#F00"); hGrad.addColorStop(1 / 6, "#FF0"); hGrad.addColorStop(2 / 6, "#0F0"); hGrad.addColorStop(3 / 6, "#0FF"); hGrad.addColorStop(4 / 6, "#00F"); hGrad.addColorStop(5 / 6, "#F0F"); hGrad.addColorStop(6 / 6, "#F00"); ctx.fillStyle = hGrad; ctx.fillRect(0, 0, canvas.width, canvas.height); var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); switch (type.toLowerCase()) { case "s": vGrad.addColorStop(0, "rgba(255,255,255,0)"); vGrad.addColorStop(1, "rgba(255,255,255,1)"); break; case "v": vGrad.addColorStop(0, "rgba(0,0,0,0)"); vGrad.addColorStop(1, "rgba(0,0,0,1)"); break }ctx.fillStyle = vGrad; ctx.fillRect(0, 0, canvas.width, canvas.height) }; ret.elm = canvas; ret.draw = drawFunc; return ret }, createSliderGradient: function () { var ret = { elm: null, draw: null }; var canvas = jsc.createEl("canvas"); var ctx = canvas.getContext("2d"); var drawFunc = function (width, height, color1, color2) { canvas.width = width; canvas.height = height; ctx.clearRect(0, 0, canvas.width, canvas.height); var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); grad.addColorStop(0, color1); grad.addColorStop(1, color2); ctx.fillStyle = grad; ctx.fillRect(0, 0, canvas.width, canvas.height) }; ret.elm = canvas; ret.draw = drawFunc; return ret }, createASliderGradient: function () { var ret = { elm: null, draw: null }; var canvas = jsc.createEl("canvas"); var ctx = canvas.getContext("2d"); var drawFunc = function (width, height, color) { canvas.width = width; canvas.height = height; ctx.clearRect(0, 0, canvas.width, canvas.height); var sqSize = canvas.width / 2; var sqColor1 = jsc.pub.chessboardColor1; var sqColor2 = jsc.pub.chessboardColor2; ctx.fillStyle = sqColor1; ctx.fillRect(0, 0, canvas.width, canvas.height); if (sqSize > 0) { for (var y = 0; y < canvas.height; y += sqSize * 2){ ctx.fillStyle = sqColor2; ctx.fillRect(0, y, sqSize, sqSize); ctx.fillRect(sqSize, y + sqSize, sqSize, sqSize) } } var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); grad.addColorStop(0, color); grad.addColorStop(1, "rgba(0,0,0,0)"); ctx.fillStyle = grad; ctx.fillRect(0, 0, canvas.width, canvas.height) }; ret.elm = canvas; ret.draw = drawFunc; return ret }, BoxShadow: function () { var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) { this.hShadow = hShadow; this.vShadow = vShadow; this.blur = blur; this.spread = spread; this.color = color; this.inset = !!inset }; BoxShadow.prototype.toString = function () { var vals = [Math.round(this.hShadow) + "px", Math.round(this.vShadow) + "px", Math.round(this.blur) + "px", Math.round(this.spread) + "px", this.color]; if (this.inset) { vals.push("inset") } return vals.join(" ") }; return BoxShadow }(), flags: { leaveValue: 1 << 0, leaveAlpha: 1 << 1, leavePreview: 1 << 2 }, enumOpts: { format: ["auto", "any", "hex", "hexa", "rgb", "rgba"], previewPosition: ["left", "right"], mode: ["hsv", "hvs", "hs", "hv"], position: ["left", "right", "top", "bottom"], alphaChannel: ["auto", true, false], paletteSetsAlpha: ["auto", true, false] }, deprecatedOpts: { styleElement: "previewElement", onFineChange: "onInput", overwriteImportant: "forceStyle", closable: "closeButton", insetWidth: "controlBorderWidth", insetColor: "controlBorderColor", refine: null }, docsRef: " " + "See https://jscolor.com/docs/", pub: function (targetElement, opts) { var THIS = this; if (!opts) { opts = {} } this.channels = { r: 255, g: 255, b: 255, h: 0, s: 0, v: 100, a: 1 }; this.format = "auto"; this.value = undefined; this.alpha = undefined; this.random = false; this.onChange = undefined; this.onInput = undefined; this.valueElement = undefined; this.alphaElement = undefined; this.previewElement = undefined; this.previewPosition = "left"; this.previewSize = 32; this.previewPadding = 8; this.required = true; this.hash = true; this.uppercase = true; this.forceStyle = true; this.width = 181; this.height = 101; this.mode = "HSV"; this.alphaChannel = "auto"; this.position = "bottom"; this.smartPosition = true; this.showOnClick = true; this.hideOnLeave = true; this.palette = []; this.paletteCols = 10; this.paletteSetsAlpha = "auto"; this.paletteHeight = 16; this.paletteSpacing = 4; this.hideOnPaletteClick = false; this.sliderSize = 16; this.crossSize = 8; this.closeButton = false; this.closeText = "Close"; this.buttonColor = "rgba(0,0,0,1)"; this.buttonHeight = 18; this.padding = 12; this.backgroundColor = "rgba(255,255,255,1)"; this.borderWidth = 1; this.borderColor = "rgba(187,187,187,1)"; this.borderRadius = 8; this.controlBorderWidth = 1; this.controlBorderColor = "rgba(187,187,187,1)"; this.shadow = true; this.shadowBlur = 15; this.shadowColor = "rgba(0,0,0,0.2)"; this.pointerColor = "rgba(76,76,76,1)"; this.pointerBorderWidth = 1; this.pointerBorderColor = "rgba(255,255,255,1)"; this.pointerThickness = 2; this.zIndex = 5e3; this.container = undefined; this.minS = 0; this.maxS = 100; this.minV = 0; this.maxV = 100; this.minA = 0; this.maxA = 1; this.option = function () { if (!arguments.length) { throw new Error("No option specified") } if (arguments.length === 1 && typeof arguments[0] === "string") { try { return getOption(arguments[0]) } catch (e) { console.warn(e) } return false } else if (arguments.length >= 2 && typeof arguments[0] === "string") { try { if (!setOption(arguments[0], arguments[1])) { return false } } catch (e) { console.warn(e); return false } this.redraw(); this.exposeColor(); return true } else if (arguments.length === 1 && typeof arguments[0] === "object") { var opts = arguments[0]; var success = true; for (var opt in opts) { if (opts.hasOwnProperty(opt)) { try { if (!setOption(opt, opts[opt])) { success = false } } catch (e) { console.warn(e); success = false } } } this.redraw(); this.exposeColor(); return success } throw new Error("Invalid arguments") }; this.channel = function (name, value) { if (typeof name !== "string") { throw new Error("Invalid value for channel name: " + name) } if (value === undefined) { if (!this.channels.hasOwnProperty(name.toLowerCase())) { console.warn("Getting unknown channel: " + name); return false } return this.channels[name.toLowerCase()] } else { var res = false; switch (name.toLowerCase()) { case "r": res = this.fromRGBA(value, null, null, null); break; case "g": res = this.fromRGBA(null, value, null, null); break; case "b": res = this.fromRGBA(null, null, value, null); break; case "h": res = this.fromHSVA(value, null, null, null); break; case "s": res = this.fromHSVA(null, value, null, null); break; case "v": res = this.fromHSVA(null, null, value, null); break; case "a": res = this.fromHSVA(null, null, null, value); break; default: console.warn("Setting unknown channel: " + name); return false }if (res) { this.redraw(); return true } } return false }; this.trigger = function (eventNames) { var evs = jsc.strList(eventNames); for (var i = 0; i < evs.length; i += 1){ var ev = evs[i].toLowerCase(); var callbackProp = null; switch (ev) { case "input": callbackProp = "onInput"; break; case "change": callbackProp = "onChange"; break }if (callbackProp) { jsc.triggerCallback(this, callbackProp) } jsc.triggerInputEvent(this.valueElement, ev, true, true) } }; this.fromHSVA = function (h, s, v, a, flags) { if (h === undefined) { h = null } if (s === undefined) { s = null } if (v === undefined) { v = null } if (a === undefined) { a = null } if (h !== null) { if (isNaN(h)) { return false } this.channels.h = Math.max(0, Math.min(360, h)) } if (s !== null) { if (isNaN(s)) { return false } this.channels.s = Math.max(0, Math.min(100, this.maxS, s), this.minS) } if (v !== null) { if (isNaN(v)) { return false } this.channels.v = Math.max(0, Math.min(100, this.maxV, v), this.minV) } if (a !== null) { if (isNaN(a)) { return false } this.channels.a = this.hasAlphaChannel() ? Math.max(0, Math.min(1, this.maxA, a), this.minA) : 1 } var rgb = jsc.HSV_RGB(this.channels.h, this.channels.s, this.channels.v); this.channels.r = rgb[0]; this.channels.g = rgb[1]; this.channels.b = rgb[2]; this.exposeColor(flags); return true }; this.fromRGBA = function (r, g, b, a, flags) { if (r === undefined) { r = null } if (g === undefined) { g = null } if (b === undefined) { b = null } if (a === undefined) { a = null } if (r !== null) { if (isNaN(r)) { return false } r = Math.max(0, Math.min(255, r)) } if (g !== null) { if (isNaN(g)) { return false } g = Math.max(0, Math.min(255, g)) } if (b !== null) { if (isNaN(b)) { return false } b = Math.max(0, Math.min(255, b)) } if (a !== null) { if (isNaN(a)) { return false } this.channels.a = this.hasAlphaChannel() ? Math.max(0, Math.min(1, this.maxA, a), this.minA) : 1 } var hsv = jsc.RGB_HSV(r === null ? this.channels.r : r, g === null ? this.channels.g : g, b === null ? this.channels.b : b); if (hsv[0] !== null) { this.channels.h = Math.max(0, Math.min(360, hsv[0])) } if (hsv[2] !== 0) { this.channels.s = Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])) } this.channels.v = Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); var rgb = jsc.HSV_RGB(this.channels.h, this.channels.s, this.channels.v); this.channels.r = rgb[0]; this.channels.g = rgb[1]; this.channels.b = rgb[2]; this.exposeColor(flags); return true }; this.fromHSV = function (h, s, v, flags) { console.warn("fromHSV() method is DEPRECATED. Using fromHSVA() instead." + jsc.docsRef); return this.fromHSVA(h, s, v, null, flags) }; this.fromRGB = function (r, g, b, flags) { console.warn("fromRGB() method is DEPRECATED. Using fromRGBA() instead." + jsc.docsRef); return this.fromRGBA(r, g, b, null, flags) }; this.fromString = function (str, flags) { if (!this.required && str.trim() === "") { this.setPreviewElementBg(null); this.setValueElementValue(""); return true } var color = jsc.parseColorString(str); if (!color) { return false } if (this.format.toLowerCase() === "any") { this._setFormat(color.format); if (!jsc.isAlphaFormat(this.getFormat())) { color.rgba[3] = 1 } } this.fromRGBA(color.rgba[0], color.rgba[1], color.rgba[2], color.rgba[3], flags); return true }; this.randomize = function (minV, maxV, minS, maxS, minH, maxH, minA, maxA) { if (minV === undefined) { minV = 0 } if (maxV === undefined) { maxV = 100 } if (minS === undefined) { minS = 0 } if (maxS === undefined) { maxS = 100 } if (minH === undefined) { minH = 0 } if (maxH === undefined) { maxH = 359 } if (minA === undefined) { minA = 1 } if (maxA === undefined) { maxA = 1 } this.fromHSVA(minH + Math.floor(Math.random() * (maxH - minH + 1)), minS + Math.floor(Math.random() * (maxS - minS + 1)), minV + Math.floor(Math.random() * (maxV - minV + 1)), (100 * minA + Math.floor(Math.random() * (100 * (maxA - minA) + 1))) / 100) }; this.toString = function (format) { if (format === undefined) { format = this.getFormat() } switch (format.toLowerCase()) { case "hex": return this.toHEXString(); break; case "hexa": return this.toHEXAString(); break; case "rgb": return this.toRGBString(); break; case "rgba": return this.toRGBAString(); break }return false }; this.toHEXString = function () { return jsc.hexColor(this.channels.r, this.channels.g, this.channels.b) }; this.toHEXAString = function () { return jsc.hexaColor(this.channels.r, this.channels.g, this.channels.b, this.channels.a) }; this.toRGBString = function () { return jsc.rgbColor(this.channels.r, this.channels.g, this.channels.b) }; this.toRGBAString = function () { return jsc.rgbaColor(this.channels.r, this.channels.g, this.channels.b, this.channels.a) }; this.toGrayscale = function () { return .213 * this.channels.r + .715 * this.channels.g + .072 * this.channels.b }; this.toCanvas = function () { return jsc.genColorPreviewCanvas(this.toRGBAString()).canvas }; this.toDataURL = function () { return this.toCanvas().toDataURL() }; this.toBackground = function () { return jsc.pub.background(this.toRGBAString()) }; this.isLight = function () { return this.toGrayscale() > 255 / 2 }; this.hide = function () { if (isPickerOwner()) { detachPicker() } }; this.show = function () { drawPicker() }; this.redraw = function () { if (isPickerOwner()) { drawPicker() } }; this.getFormat = function () { return this._currentFormat }; this._setFormat = function (format) { this._currentFormat = format.toLowerCase() }; this.hasAlphaChannel = function () { if (this.alphaChannel === "auto") { return this.format.toLowerCase() === "any" || jsc.isAlphaFormat(this.getFormat()) || this.alpha !== undefined || this.alphaElement !== undefined } return this.alphaChannel }; this.processValueInput = function (str) { if (!this.fromString(str)) { this.exposeColor() } }; this.processAlphaInput = function (str) { if (!this.fromHSVA(null, null, null, parseFloat(str))) { this.exposeColor() } }; this.exposeColor = function (flags) { var colorStr = this.toString(); var fmt = this.getFormat(); jsc.setDataAttr(this.targetElement, "current-color", colorStr); if (!(flags & jsc.flags.leaveValue) && this.valueElement) { if (fmt === "hex" || fmt === "hexa") { if (!this.uppercase) { colorStr = colorStr.toLowerCase() } if (!this.hash) { colorStr = colorStr.replace(/^#/, "") } } this.setValueElementValue(colorStr) } if (!(flags & jsc.flags.leaveAlpha) && this.alphaElement) { var alphaVal = Math.round(this.channels.a * 100) / 100; this.setAlphaElementValue(alphaVal) } if (!(flags & jsc.flags.leavePreview) && this.previewElement) { var previewPos = null; if (jsc.isTextInput(this.previewElement) || jsc.isButton(this.previewElement) && !jsc.isButtonEmpty(this.previewElement)) { previewPos = this.previewPosition } this.setPreviewElementBg(this.toRGBAString()) } if (isPickerOwner()) { redrawPad(); redrawSld(); redrawASld() } }; this.setPreviewElementBg = function (color) { if (!this.previewElement) { return } var position = null; var width = null; if (jsc.isTextInput(this.previewElement) || jsc.isButton(this.previewElement) && !jsc.isButtonEmpty(this.previewElement)) { position = this.previewPosition; width = this.previewSize } var backgrounds = []; if (!color) { backgrounds.push({ image: "none", position: "left top", size: "auto", repeat: "no-repeat", origin: "padding-box" }) } else { backgrounds.push({ image: jsc.genColorPreviewGradient(color, position, width ? width - jsc.pub.previewSeparator.length : null), position: "left top", size: "auto", repeat: position ? "repeat-y" : "repeat", origin: "padding-box" }); var preview = jsc.genColorPreviewCanvas("rgba(0,0,0,0)", position ? { left: "right", right: "left" }[position] : null, width, true); backgrounds.push({ image: "url('" + preview.canvas.toDataURL() + "')", position: (position || "left") + " top", size: preview.width + "px " + preview.height + "px", repeat: position ? "repeat-y" : "repeat", origin: "padding-box" }) } var bg = { image: [], position: [], size: [], repeat: [], origin: [] }; for (var i = 0; i < backgrounds.length; i += 1){ bg.image.push(backgrounds[i].image); bg.position.push(backgrounds[i].position); bg.size.push(backgrounds[i].size); bg.repeat.push(backgrounds[i].repeat); bg.origin.push(backgrounds[i].origin) } var sty = { "background-image": bg.image.join(", "), "background-position": bg.position.join(", "), "background-size": bg.size.join(", "), "background-repeat": bg.repeat.join(", "), "background-origin": bg.origin.join(", ") }; jsc.setStyle(this.previewElement, sty, this.forceStyle); var padding = { left: null, right: null }; if (position) { padding[position] = this.previewSize + this.previewPadding + "px" } var sty = { "padding-left": padding.left, "padding-right": padding.right }; jsc.setStyle(this.previewElement, sty, this.forceStyle, true) }; this.setValueElementValue = function (str) { if (this.valueElement) { if (jsc.nodeName(this.valueElement) === "input") { this.valueElement.value = str } else { this.valueElement.innerHTML = str } } }; this.setAlphaElementValue = function (str) { if (this.alphaElement) { if (jsc.nodeName(this.alphaElement) === "input") { this.alphaElement.value = str } else { this.alphaElement.innerHTML = str } } }; this._processParentElementsInDOM = function () { if (this._parentElementsProcessed) { return } this._parentElementsProcessed = true; var elm = this.targetElement; do { var compStyle = jsc.getCompStyle(elm); if (compStyle.position && compStyle.position.toLowerCase() === "fixed") { this.fixed = true } if (elm !== this.targetElement) { if (!jsc.getData(elm, "hasScrollListener")) { elm.addEventListener("scroll", jsc.onParentScroll, false); jsc.setData(elm, "hasScrollListener", true) } } } while ((elm = elm.parentNode) && jsc.nodeName(elm) !== "body") }; this.tryHide = function () { if (this.hideOnLeave) { this.hide() } }; this.set__palette = function (val) { this.palette = val; this._palette = jsc.parsePaletteValue(val); this._paletteHasTransparency = jsc.containsTranparentColor(this._palette) }; function setOption(option, value) { if (typeof option !== "string") { throw new Error("Invalid value for option name: " + option) } if (jsc.enumOpts.hasOwnProperty(option)) { if (typeof value === "string") { value = value.toLowerCase() } if (jsc.enumOpts[option].indexOf(value) === -1) { throw new Error("Option '" + option + "' has invalid value: " + value) } } if (jsc.deprecatedOpts.hasOwnProperty(option)) { var oldOpt = option; var newOpt = jsc.deprecatedOpts[option]; if (newOpt) { console.warn("Option '%s' is DEPRECATED, using '%s' instead." + jsc.docsRef, oldOpt, newOpt); option = newOpt } else { throw new Error("Option '" + option + "' is DEPRECATED") } } var setter = "set__" + option; if (typeof THIS[setter] === "function") { THIS[setter](value); return true } else if (option in THIS) { THIS[option] = value; return true } throw new Error("Unrecognized configuration option: " + option) } function getOption(option) { if (typeof option !== "string") { throw new Error("Invalid value for option name: " + option) } if (jsc.deprecatedOpts.hasOwnProperty(option)) { var oldOpt = option; var newOpt = jsc.deprecatedOpts[option]; if (newOpt) { console.warn("Option '%s' is DEPRECATED, using '%s' instead." + jsc.docsRef, oldOpt, newOpt); option = newOpt } else { throw new Error("Option '" + option + "' is DEPRECATED") } } var getter = "get__" + option; if (typeof THIS[getter] === "function") { return THIS[getter](value) } else if (option in THIS) { return THIS[option] } throw new Error("Unrecognized configuration option: " + option) } function detachPicker() { jsc.removeClass(THIS.targetElement, jsc.pub.activeClassName); jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); delete jsc.picker.owner } function drawPicker() { THIS._processParentElementsInDOM(); if (!jsc.picker) { jsc.picker = { owner: null, wrap: jsc.createEl("div"), box: jsc.createEl("div"), boxS: jsc.createEl("div"), boxB: jsc.createEl("div"), pad: jsc.createEl("div"), padB: jsc.createEl("div"), padM: jsc.createEl("div"), padCanvas: jsc.createPadCanvas(), cross: jsc.createEl("div"), crossBY: jsc.createEl("div"), crossBX: jsc.createEl("div"), crossLY: jsc.createEl("div"), crossLX: jsc.createEl("div"), sld: jsc.createEl("div"), sldB: jsc.createEl("div"), sldM: jsc.createEl("div"), sldGrad: jsc.createSliderGradient(), sldPtrS: jsc.createEl("div"), sldPtrIB: jsc.createEl("div"), sldPtrMB: jsc.createEl("div"), sldPtrOB: jsc.createEl("div"), asld: jsc.createEl("div"), asldB: jsc.createEl("div"), asldM: jsc.createEl("div"), asldGrad: jsc.createASliderGradient(), asldPtrS: jsc.createEl("div"), asldPtrIB: jsc.createEl("div"), asldPtrMB: jsc.createEl("div"), asldPtrOB: jsc.createEl("div"), pal: jsc.createEl("div"), btn: jsc.createEl("div"), btnT: jsc.createEl("span") }; jsc.picker.pad.appendChild(jsc.picker.padCanvas.elm); jsc.picker.padB.appendChild(jsc.picker.pad); jsc.picker.cross.appendChild(jsc.picker.crossBY); jsc.picker.cross.appendChild(jsc.picker.crossBX); jsc.picker.cross.appendChild(jsc.picker.crossLY); jsc.picker.cross.appendChild(jsc.picker.crossLX); jsc.picker.padB.appendChild(jsc.picker.cross); jsc.picker.box.appendChild(jsc.picker.padB); jsc.picker.box.appendChild(jsc.picker.padM); jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); jsc.picker.sldB.appendChild(jsc.picker.sld); jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); jsc.picker.box.appendChild(jsc.picker.sldB); jsc.picker.box.appendChild(jsc.picker.sldM); jsc.picker.asld.appendChild(jsc.picker.asldGrad.elm); jsc.picker.asldB.appendChild(jsc.picker.asld); jsc.picker.asldB.appendChild(jsc.picker.asldPtrOB); jsc.picker.asldPtrOB.appendChild(jsc.picker.asldPtrMB); jsc.picker.asldPtrMB.appendChild(jsc.picker.asldPtrIB); jsc.picker.asldPtrIB.appendChild(jsc.picker.asldPtrS); jsc.picker.box.appendChild(jsc.picker.asldB); jsc.picker.box.appendChild(jsc.picker.asldM); jsc.picker.box.appendChild(jsc.picker.pal); jsc.picker.btn.appendChild(jsc.picker.btnT); jsc.picker.box.appendChild(jsc.picker.btn); jsc.picker.boxB.appendChild(jsc.picker.box); jsc.picker.wrap.appendChild(jsc.picker.boxS); jsc.picker.wrap.appendChild(jsc.picker.boxB); jsc.picker.wrap.addEventListener("touchstart", jsc.onPickerTouchStart, jsc.isPassiveEventSupported ? { passive: false } : false) } var p = jsc.picker; var displaySlider = !!jsc.getSliderChannel(THIS); var displayAlphaSlider = THIS.hasAlphaChannel(); var pickerDims = jsc.getPickerDims(THIS); var crossOuterSize = 2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize; var controlPadding = jsc.getControlPadding(THIS); var borderRadius = Math.min(THIS.borderRadius, Math.round(THIS.padding * Math.PI)); var padCursor = "crosshair"; p.wrap.className = "jscolor-picker-wrap"; p.wrap.style.clear = "both"; p.wrap.style.width = pickerDims.borderW + "px"; p.wrap.style.height = pickerDims.borderH + "px"; p.wrap.style.zIndex = THIS.zIndex; p.box.className = "jscolor-picker"; p.box.style.width = pickerDims.paddedW + "px"; p.box.style.height = pickerDims.paddedH + "px"; p.box.style.position = "relative"; p.boxS.className = "jscolor-picker-shadow"; p.boxS.style.position = "absolute"; p.boxS.style.left = "0"; p.boxS.style.top = "0"; p.boxS.style.width = "100%"; p.boxS.style.height = "100%"; jsc.setBorderRadius(p.boxS, borderRadius + "px"); p.boxB.className = "jscolor-picker-border"; p.boxB.style.position = "relative"; p.boxB.style.border = THIS.borderWidth + "px solid"; p.boxB.style.borderColor = THIS.borderColor; p.boxB.style.background = THIS.backgroundColor; jsc.setBorderRadius(p.boxB, borderRadius + "px"); p.padM.style.background = "rgba(255,0,0,.2)"; p.sldM.style.background = "rgba(0,255,0,.2)"; p.asldM.style.background = "rgba(0,0,255,.2)"; p.padM.style.opacity = p.sldM.style.opacity = p.asldM.style.opacity = "0"; p.pad.style.position = "relative"; p.pad.style.width = THIS.width + "px"; p.pad.style.height = THIS.height + "px"; p.padCanvas.draw(THIS.width, THIS.height, jsc.getPadYChannel(THIS)); p.padB.style.position = "absolute"; p.padB.style.left = THIS.padding + "px"; p.padB.style.top = THIS.padding + "px"; p.padB.style.border = THIS.controlBorderWidth + "px solid"; p.padB.style.borderColor = THIS.controlBorderColor; p.padM.style.position = "absolute"; p.padM.style.left = 0 + "px"; p.padM.style.top = 0 + "px"; p.padM.style.width = THIS.padding + 2 * THIS.controlBorderWidth + THIS.width + controlPadding + "px"; p.padM.style.height = 2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height + "px"; p.padM.style.cursor = padCursor; jsc.setData(p.padM, { instance: THIS, control: "pad" }); p.cross.style.position = "absolute"; p.cross.style.left = p.cross.style.top = "0"; p.cross.style.width = p.cross.style.height = crossOuterSize + "px"; p.crossBY.style.position = p.crossBX.style.position = "absolute"; p.crossBY.style.background = p.crossBX.style.background = THIS.pointerBorderColor; p.crossBY.style.width = p.crossBX.style.height = 2 * THIS.pointerBorderWidth + THIS.pointerThickness + "px"; p.crossBY.style.height = p.crossBX.style.width = crossOuterSize + "px"; p.crossBY.style.left = p.crossBX.style.top = Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth + "px"; p.crossBY.style.top = p.crossBX.style.left = "0"; p.crossLY.style.position = p.crossLX.style.position = "absolute"; p.crossLY.style.background = p.crossLX.style.background = THIS.pointerColor; p.crossLY.style.height = p.crossLX.style.width = crossOuterSize - 2 * THIS.pointerBorderWidth + "px"; p.crossLY.style.width = p.crossLX.style.height = THIS.pointerThickness + "px"; p.crossLY.style.left = p.crossLX.style.top = Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) + "px"; p.crossLY.style.top = p.crossLX.style.left = THIS.pointerBorderWidth + "px"; p.sld.style.overflow = "hidden"; p.sld.style.width = THIS.sliderSize + "px"; p.sld.style.height = THIS.height + "px"; p.sldGrad.draw(THIS.sliderSize, THIS.height, "#000", "#000"); p.sldB.style.display = displaySlider ? "block" : "none"; p.sldB.style.position = "absolute"; p.sldB.style.left = THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + 2 * controlPadding + "px"; p.sldB.style.top = THIS.padding + "px"; p.sldB.style.border = THIS.controlBorderWidth + "px solid"; p.sldB.style.borderColor = THIS.controlBorderColor; p.sldM.style.display = displaySlider ? "block" : "none"; p.sldM.style.position = "absolute"; p.sldM.style.left = THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding + "px"; p.sldM.style.top = 0 + "px"; p.sldM.style.width = THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth + (displayAlphaSlider ? 0 : Math.max(0, THIS.padding - controlPadding)) + "px"; p.sldM.style.height = 2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height + "px"; p.sldM.style.cursor = "default"; jsc.setData(p.sldM, { instance: THIS, control: "sld" }); p.sldPtrIB.style.border = p.sldPtrOB.style.border = THIS.pointerBorderWidth + "px solid " + THIS.pointerBorderColor; p.sldPtrOB.style.position = "absolute"; p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + "px"; p.sldPtrOB.style.top = "0"; p.sldPtrMB.style.border = THIS.pointerThickness + "px solid " + THIS.pointerColor; p.sldPtrS.style.width = THIS.sliderSize + "px"; p.sldPtrS.style.height = jsc.pub.sliderInnerSpace + "px"; p.asld.style.overflow = "hidden"; p.asld.style.width = THIS.sliderSize + "px"; p.asld.style.height = THIS.height + "px"; p.asldGrad.draw(THIS.sliderSize, THIS.height, "#000"); p.asldB.style.display = displayAlphaSlider ? "block" : "none"; p.asldB.style.position = "absolute"; p.asldB.style.left = THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding + (displaySlider ? THIS.sliderSize + 3 * controlPadding + 2 * THIS.controlBorderWidth : 0) + "px"; p.asldB.style.top = THIS.padding + "px"; p.asldB.style.border = THIS.controlBorderWidth + "px solid"; p.asldB.style.borderColor = THIS.controlBorderColor; p.asldM.style.display = displayAlphaSlider ? "block" : "none"; p.asldM.style.position = "absolute"; p.asldM.style.left = THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding + (displaySlider ? THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth : 0) + "px"; p.asldM.style.top = 0 + "px"; p.asldM.style.width = THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth + Math.max(0, THIS.padding - controlPadding) + "px"; p.asldM.style.height = 2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height + "px"; p.asldM.style.cursor = "default"; jsc.setData(p.asldM, { instance: THIS, control: "asld" }); p.asldPtrIB.style.border = p.asldPtrOB.style.border = THIS.pointerBorderWidth + "px solid " + THIS.pointerBorderColor; p.asldPtrOB.style.position = "absolute"; p.asldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + "px"; p.asldPtrOB.style.top = "0"; p.asldPtrMB.style.border = THIS.pointerThickness + "px solid " + THIS.pointerColor; p.asldPtrS.style.width = THIS.sliderSize + "px"; p.asldPtrS.style.height = jsc.pub.sliderInnerSpace + "px"; p.pal.className = "jscolor-palette"; p.pal.style.display = pickerDims.palette.rows ? "block" : "none"; p.pal.style.position = "absolute"; p.pal.style.left = THIS.padding + "px"; p.pal.style.top = 2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height + "px"; p.pal.innerHTML = ""; var chessboard = jsc.genColorPreviewCanvas("rgba(0,0,0,0)"); var si = 0; for (var r = 0; r < pickerDims.palette.rows; r++){ for (var c = 0; c < pickerDims.palette.cols && si < THIS._palette.length; c++, si++){ var sampleColor = THIS._palette[si]; var sampleCssColor = jsc.rgbaColor.apply(null, sampleColor.rgba); var sc = jsc.createEl("div"); sc.style.width = pickerDims.palette.cellW - 2 * THIS.controlBorderWidth + "px"; sc.style.height = pickerDims.palette.cellH - 2 * THIS.controlBorderWidth + "px"; sc.style.backgroundColor = sampleCssColor; var sw = jsc.createEl("div"); sw.className = "jscolor-palette-sample"; sw.style.display = "block"; sw.style.position = "absolute"; sw.style.left = (pickerDims.palette.cols <= 1 ? 0 : Math.round(10 * (c * ((pickerDims.contentW - pickerDims.palette.cellW) / (pickerDims.palette.cols - 1)))) / 10) + "px"; sw.style.top = r * (pickerDims.palette.cellH + THIS.paletteSpacing) + "px"; sw.style.border = THIS.controlBorderWidth + "px solid"; sw.style.borderColor = THIS.controlBorderColor; sw.style.cursor = "pointer"; if (sampleColor.rgba[3] !== null && sampleColor.rgba[3] < 1) { sw.style.backgroundImage = "url('" + chessboard.canvas.toDataURL() + "')"; sw.style.backgroundRepeat = "repeat"; sw.style.backgroundPosition = "center center" } jsc.setData(sw, { instance: THIS, control: "palette-sample", color: sampleColor }); sw.addEventListener("click", jsc.onPaletteSampleClick, false); sw.appendChild(sc); p.pal.appendChild(sw) } } function setBtnBorder() { var insetColors = THIS.controlBorderColor.split(/\s+/); var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + " " + insetColors[0] + " " + insetColors[0] + " " + insetColors[1]; p.btn.style.borderColor = outsetColor } var btnPadding = 15; p.btn.className = "jscolor-btn-close"; p.btn.style.display = THIS.closeButton ? "block" : "none"; p.btn.style.position = "absolute"; p.btn.style.left = THIS.padding + "px"; p.btn.style.bottom = THIS.padding + "px"; p.btn.style.padding = "0 " + btnPadding + "px"; p.btn.style.maxWidth = pickerDims.contentW - 2 * THIS.controlBorderWidth - 2 * btnPadding + "px"; p.btn.style.overflow = "hidden"; p.btn.style.height = THIS.buttonHeight + "px"; p.btn.style.whiteSpace = "nowrap"; p.btn.style.border = THIS.controlBorderWidth + "px solid"; setBtnBorder(); p.btn.style.color = THIS.buttonColor; p.btn.style.font = "12px sans-serif"; p.btn.style.textAlign = "center"; p.btn.style.cursor = "pointer"; p.btn.onmousedown = function () { THIS.hide() }; p.btnT.style.lineHeight = THIS.buttonHeight + "px"; p.btnT.innerHTML = ""; p.btnT.appendChild(window.document.createTextNode(THIS.closeText)); redrawPad(); redrawSld(); redrawASld(); if (jsc.picker.owner && jsc.picker.owner !== THIS) { jsc.removeClass(jsc.picker.owner.targetElement, jsc.pub.activeClassName) } jsc.picker.owner = THIS; if (THIS.container === window.document.body) { jsc.redrawPosition() } else { jsc._drawPosition(THIS, 0, 0, "relative", false) } if (p.wrap.parentNode !== THIS.container) { THIS.container.appendChild(p.wrap) } jsc.addClass(THIS.targetElement, jsc.pub.activeClassName) } function redrawPad() { var yChannel = jsc.getPadYChannel(THIS); var x = Math.round(THIS.channels.h / 360 * (THIS.width - 1)); var y = Math.round((1 - THIS.channels[yChannel] / 100) * (THIS.height - 1)); var crossOuterSize = 2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize; var ofs = -Math.floor(crossOuterSize / 2); jsc.picker.cross.style.left = x + ofs + "px"; jsc.picker.cross.style.top = y + ofs + "px"; switch (jsc.getSliderChannel(THIS)) { case "s": var rgb1 = jsc.HSV_RGB(THIS.channels.h, 100, THIS.channels.v); var rgb2 = jsc.HSV_RGB(THIS.channels.h, 0, THIS.channels.v); var color1 = "rgb(" + Math.round(rgb1[0]) + "," + Math.round(rgb1[1]) + "," + Math.round(rgb1[2]) + ")"; var color2 = "rgb(" + Math.round(rgb2[0]) + "," + Math.round(rgb2[1]) + "," + Math.round(rgb2[2]) + ")"; jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); break; case "v": var rgb = jsc.HSV_RGB(THIS.channels.h, THIS.channels.s, 100); var color1 = "rgb(" + Math.round(rgb[0]) + "," + Math.round(rgb[1]) + "," + Math.round(rgb[2]) + ")"; var color2 = "#000"; jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); break }jsc.picker.asldGrad.draw(THIS.sliderSize, THIS.height, THIS.toHEXString()) } function redrawSld() { var sldChannel = jsc.getSliderChannel(THIS); if (sldChannel) { var y = Math.round((1 - THIS.channels[sldChannel] / 100) * (THIS.height - 1)); jsc.picker.sldPtrOB.style.top = y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(jsc.pub.sliderInnerSpace / 2) + "px" } jsc.picker.asldGrad.draw(THIS.sliderSize, THIS.height, THIS.toHEXString()) } function redrawASld() { var y = Math.round((1 - THIS.channels.a) * (THIS.height - 1)); jsc.picker.asldPtrOB.style.top = y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(jsc.pub.sliderInnerSpace / 2) + "px" } function isPickerOwner() { return jsc.picker && jsc.picker.owner === THIS } function onValueKeyDown(ev) { if (jsc.eventKey(ev) === "Enter") { if (THIS.valueElement) { THIS.processValueInput(THIS.valueElement.value) } THIS.tryHide() } } function onAlphaKeyDown(ev) { if (jsc.eventKey(ev) === "Enter") { if (THIS.alphaElement) { THIS.processAlphaInput(THIS.alphaElement.value) } THIS.tryHide() } } function onValueChange(ev) { if (jsc.getData(ev, "internal")) { return } var oldVal = THIS.valueElement.value; THIS.processValueInput(THIS.valueElement.value); jsc.triggerCallback(THIS, "onChange"); if (THIS.valueElement.value !== oldVal) { jsc.triggerInputEvent(THIS.valueElement, "change", true, true) } } function onAlphaChange(ev) { if (jsc.getData(ev, "internal")) { return } var oldVal = THIS.alphaElement.value; THIS.processAlphaInput(THIS.alphaElement.value); jsc.triggerCallback(THIS, "onChange"); jsc.triggerInputEvent(THIS.valueElement, "change", true, true); if (THIS.alphaElement.value !== oldVal) { jsc.triggerInputEvent(THIS.alphaElement, "change", true, true) } } function onValueInput(ev) { if (jsc.getData(ev, "internal")) { return } if (THIS.valueElement) { THIS.fromString(THIS.valueElement.value, jsc.flags.leaveValue) } jsc.triggerCallback(THIS, "onInput") } function onAlphaInput(ev) { if (jsc.getData(ev, "internal")) { return } if (THIS.alphaElement) { THIS.fromHSVA(null, null, null, parseFloat(THIS.alphaElement.value), jsc.flags.leaveAlpha) } jsc.triggerCallback(THIS, "onInput"); jsc.triggerInputEvent(THIS.valueElement, "input", true, true) } if (jsc.pub.options) { for (var opt in jsc.pub.options) { if (jsc.pub.options.hasOwnProperty(opt)) { try { setOption(opt, jsc.pub.options[opt]) } catch (e) { console.warn(e) } } } } var presetsArr = []; if (opts.preset) { if (typeof opts.preset === "string") { presetsArr = opts.preset.split(/\s+/) } else if (Array.isArray(opts.preset)) { presetsArr = opts.preset.slice() } else { console.warn("Unrecognized preset value") } } if (presetsArr.indexOf("default") === -1) { presetsArr.push("default") } for (var i = presetsArr.length - 1; i >= 0; i -= 1){ var pres = presetsArr[i]; if (!pres) { continue } if (!jsc.pub.presets.hasOwnProperty(pres)) { console.warn("Unknown preset: %s", pres); continue } for (var opt in jsc.pub.presets[pres]) { if (jsc.pub.presets[pres].hasOwnProperty(opt)) { try { setOption(opt, jsc.pub.presets[pres][opt]) } catch (e) { console.warn(e) } } } } var nonProperties = ["preset"]; for (var opt in opts) { if (opts.hasOwnProperty(opt)) { if (nonProperties.indexOf(opt) === -1) { try { setOption(opt, opts[opt]) } catch (e) { console.warn(e) } } } } if (this.container === undefined) { this.container = window.document.body } else { this.container = jsc.node(this.container) } if (!this.container) { throw new Error("Cannot instantiate color picker without a container element") } this.targetElement = jsc.node(targetElement); if (!this.targetElement) { if (typeof targetElement === "string" && /^[a-zA-Z][\w:.-]*$/.test(targetElement)) { var possiblyId = targetElement; throw new Error("If '" + possiblyId + "' is supposed to be an ID, please use '#" + possiblyId + "' or any valid CSS selector.") } throw new Error("Cannot instantiate color picker without a target element") } if (this.targetElement.jscolor && this.targetElement.jscolor instanceof jsc.pub) { throw new Error("Color picker already installed on this element") } this.targetElement.jscolor = this; jsc.addClass(this.targetElement, jsc.pub.className); jsc.instances.push(this); if (jsc.isButton(this.targetElement)) { if (this.targetElement.type.toLowerCase() !== "button") { this.targetElement.type = "button" } if (jsc.isButtonEmpty(this.targetElement)) { jsc.removeChildren(this.targetElement); this.targetElement.appendChild(window.document.createTextNode(" ")); var compStyle = jsc.getCompStyle(this.targetElement); var currMinWidth = parseFloat(compStyle["min-width"]) || 0; if (currMinWidth < this.previewSize) { jsc.setStyle(this.targetElement, { "min-width": this.previewSize + "px" }, this.forceStyle) } } } if (this.valueElement === undefined) { if (jsc.isTextInput(this.targetElement)) { this.valueElement = this.targetElement } else { } } else if (this.valueElement === null) { } else { this.valueElement = jsc.node(this.valueElement) } if (this.alphaElement) { this.alphaElement = jsc.node(this.alphaElement) } if (this.previewElement === undefined) { this.previewElement = this.targetElement } else if (this.previewElement === null) { } else { this.previewElement = jsc.node(this.previewElement) } if (this.valueElement && jsc.isTextInput(this.valueElement)) { var valueElementOrigEvents = { onInput: this.valueElement.oninput }; this.valueElement.oninput = null; this.valueElement.addEventListener("keydown", onValueKeyDown, false); this.valueElement.addEventListener("change", onValueChange, false); this.valueElement.addEventListener("input", onValueInput, false); if (valueElementOrigEvents.onInput) { this.valueElement.addEventListener("input", valueElementOrigEvents.onInput, false) } this.valueElement.setAttribute("autocomplete", "off"); this.valueElement.setAttribute("autocorrect", "off"); this.valueElement.setAttribute("autocapitalize", "off"); this.valueElement.setAttribute("spellcheck", false) } if (this.alphaElement && jsc.isTextInput(this.alphaElement)) { this.alphaElement.addEventListener("keydown", onAlphaKeyDown, false); this.alphaElement.addEventListener("change", onAlphaChange, false); this.alphaElement.addEventListener("input", onAlphaInput, false); this.alphaElement.setAttribute("autocomplete", "off"); this.alphaElement.setAttribute("autocorrect", "off"); this.alphaElement.setAttribute("autocapitalize", "off"); this.alphaElement.setAttribute("spellcheck", false) } var initValue = "FFFFFF"; if (this.value !== undefined) { initValue = this.value } else if (this.valueElement && this.valueElement.value !== undefined) { initValue = this.valueElement.value } var initAlpha = undefined; if (this.alpha !== undefined) { initAlpha = "" + this.alpha } else if (this.alphaElement && this.alphaElement.value !== undefined) { initAlpha = this.alphaElement.value } this._currentFormat = null; if (["auto", "any"].indexOf(this.format.toLowerCase()) > -1) { var color = jsc.parseColorString(initValue); this._currentFormat = color ? color.format : "hex" } else { this._currentFormat = this.format.toLowerCase() } this.processValueInput(initValue); if (initAlpha !== undefined) { this.processAlphaInput(initAlpha) } if (this.random) { this.randomize.apply(this, Array.isArray(this.random) ? this.random : []) } } }; jsc.pub.className = "jscolor"; jsc.pub.activeClassName = "jscolor-active"; jsc.pub.looseJSON = true; jsc.pub.presets = {}; jsc.pub.presets["default"] = {}; jsc.pub.presets["light"] = { backgroundColor: "rgba(255,255,255,1)", controlBorderColor: "rgba(187,187,187,1)", buttonColor: "rgba(0,0,0,1)" }; jsc.pub.presets["dark"] = { backgroundColor: "rgba(51,51,51,1)", controlBorderColor: "rgba(153,153,153,1)", buttonColor: "rgba(240,240,240,1)" }; jsc.pub.presets["small"] = { width: 101, height: 101, padding: 10, sliderSize: 14, paletteCols: 8 }; jsc.pub.presets["medium"] = { width: 181, height: 101, padding: 12, sliderSize: 16, paletteCols: 10 }; jsc.pub.presets["large"] = { width: 271, height: 151, padding: 12, sliderSize: 24, paletteCols: 15 }; jsc.pub.presets["thin"] = { borderWidth: 1, controlBorderWidth: 1, pointerBorderWidth: 1 }; jsc.pub.presets["thick"] = { borderWidth: 2, controlBorderWidth: 2, pointerBorderWidth: 2 }; jsc.pub.sliderInnerSpace = 3; jsc.pub.chessboardSize = 8; jsc.pub.chessboardColor1 = "#666666"; jsc.pub.chessboardColor2 = "#999999"; jsc.pub.previewSeparator = ["rgba(255,255,255,.65)", "rgba(128,128,128,.65)"]; jsc.pub.init = function () { if (jsc.initialized) { return } window.document.addEventListener("mousedown", jsc.onDocumentMouseDown, false); window.document.addEventListener("keyup", jsc.onDocumentKeyUp, false); window.addEventListener("resize", jsc.onWindowResize, false); window.addEventListener("scroll", jsc.onWindowScroll, false); jsc.pub.install(); jsc.initialized = true; while (jsc.readyQueue.length) { var func = jsc.readyQueue.shift(); func() } }; jsc.pub.install = function (rootNode) { var success = true; try { jsc.installBySelector("[data-jscolor]", rootNode) } catch (e) { success = false; console.warn(e) } if (jsc.pub.lookupClass) { try { jsc.installBySelector("input." + jsc.pub.lookupClass + ", " + "button." + jsc.pub.lookupClass, rootNode) } catch (e) { } } return success }; jsc.pub.ready = function (func) { if (typeof func !== "function") { console.warn("Passed value is not a function"); return false } if (jsc.initialized) { func() } else { jsc.readyQueue.push(func) } return true }; jsc.pub.trigger = function (eventNames) { var triggerNow = function () { jsc.triggerGlobal(eventNames) }; if (jsc.initialized) { triggerNow() } else { jsc.pub.ready(triggerNow) } }; jsc.pub.hide = function () { if (jsc.picker && jsc.picker.owner) { jsc.picker.owner.hide() } }; jsc.pub.chessboard = function (color) { if (!color) { color = "rgba(0,0,0,0)" } var preview = jsc.genColorPreviewCanvas(color); return preview.canvas.toDataURL() }; jsc.pub.background = function (color) { var backgrounds = []; backgrounds.push(jsc.genColorPreviewGradient(color)); var preview = jsc.genColorPreviewCanvas(); backgrounds.push(["url('" + preview.canvas.toDataURL() + "')", "left top", "repeat"].join(" ")); return backgrounds.join(", ") }; jsc.pub.options = {}; jsc.pub.lookupClass = "jscolor"; jsc.pub.installByClassName = function () { console.error('jscolor.installByClassName() is DEPRECATED. Use data-jscolor="" attribute instead of a class name.' + jsc.docsRef); return false }; jsc.register(); return jsc.pub }(); if (typeof window.jscolor === "undefined") { window.jscolor = window.JSColor = jscolor } return jscolor }); diff --git a/src/dep/markdown-it.min.js b/src/dep/markdown-it.min.js new file mode 100644 index 0000000..943e1f9 --- /dev/null +++ b/src/dep/markdown-it.min.js @@ -0,0 +1,12 @@ +/** + * markdown-it 12.2.0 + * @licence MIT + * @source https://github.com/markdown-it/markdown-it + */ +!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(e="undefined"!=typeof globalThis?globalThis:e||self).markdownit=r()}(this,(function(){"use strict";function e(e){if(e.__esModule)return e;var r=Object.defineProperty({},"__esModule",{value:!0});return Object.keys(e).forEach((function(t){var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:function(){return e[t]}})})),r}var r={Aacute:"\xc1",aacute:"\xe1",Abreve:"\u0102",abreve:"\u0103",ac:"\u223e",acd:"\u223f",acE:"\u223e\u0333",Acirc:"\xc2",acirc:"\xe2",acute:"\xb4",Acy:"\u0410",acy:"\u0430",AElig:"\xc6",aelig:"\xe6",af:"\u2061",Afr:"\ud835\udd04",afr:"\ud835\udd1e",Agrave:"\xc0",agrave:"\xe0",alefsym:"\u2135",aleph:"\u2135",Alpha:"\u0391",alpha:"\u03b1",Amacr:"\u0100",amacr:"\u0101",amalg:"\u2a3f",amp:"&",AMP:"&",andand:"\u2a55",And:"\u2a53",and:"\u2227",andd:"\u2a5c",andslope:"\u2a58",andv:"\u2a5a",ang:"\u2220",ange:"\u29a4",angle:"\u2220",angmsdaa:"\u29a8",angmsdab:"\u29a9",angmsdac:"\u29aa",angmsdad:"\u29ab",angmsdae:"\u29ac",angmsdaf:"\u29ad",angmsdag:"\u29ae",angmsdah:"\u29af",angmsd:"\u2221",angrt:"\u221f",angrtvb:"\u22be",angrtvbd:"\u299d",angsph:"\u2222",angst:"\xc5",angzarr:"\u237c",Aogon:"\u0104",aogon:"\u0105",Aopf:"\ud835\udd38",aopf:"\ud835\udd52",apacir:"\u2a6f",ap:"\u2248",apE:"\u2a70",ape:"\u224a",apid:"\u224b",apos:"'",ApplyFunction:"\u2061",approx:"\u2248",approxeq:"\u224a",Aring:"\xc5",aring:"\xe5",Ascr:"\ud835\udc9c",ascr:"\ud835\udcb6",Assign:"\u2254",ast:"*",asymp:"\u2248",asympeq:"\u224d",Atilde:"\xc3",atilde:"\xe3",Auml:"\xc4",auml:"\xe4",awconint:"\u2233",awint:"\u2a11",backcong:"\u224c",backepsilon:"\u03f6",backprime:"\u2035",backsim:"\u223d",backsimeq:"\u22cd",Backslash:"\u2216",Barv:"\u2ae7",barvee:"\u22bd",barwed:"\u2305",Barwed:"\u2306",barwedge:"\u2305",bbrk:"\u23b5",bbrktbrk:"\u23b6",bcong:"\u224c",Bcy:"\u0411",bcy:"\u0431",bdquo:"\u201e",becaus:"\u2235",because:"\u2235",Because:"\u2235",bemptyv:"\u29b0",bepsi:"\u03f6",bernou:"\u212c",Bernoullis:"\u212c",Beta:"\u0392",beta:"\u03b2",beth:"\u2136",between:"\u226c",Bfr:"\ud835\udd05",bfr:"\ud835\udd1f",bigcap:"\u22c2",bigcirc:"\u25ef",bigcup:"\u22c3",bigodot:"\u2a00",bigoplus:"\u2a01",bigotimes:"\u2a02",bigsqcup:"\u2a06",bigstar:"\u2605",bigtriangledown:"\u25bd",bigtriangleup:"\u25b3",biguplus:"\u2a04",bigvee:"\u22c1",bigwedge:"\u22c0",bkarow:"\u290d",blacklozenge:"\u29eb",blacksquare:"\u25aa",blacktriangle:"\u25b4",blacktriangledown:"\u25be",blacktriangleleft:"\u25c2",blacktriangleright:"\u25b8",blank:"\u2423",blk12:"\u2592",blk14:"\u2591",blk34:"\u2593",block:"\u2588",bne:"=\u20e5",bnequiv:"\u2261\u20e5",bNot:"\u2aed",bnot:"\u2310",Bopf:"\ud835\udd39",bopf:"\ud835\udd53",bot:"\u22a5",bottom:"\u22a5",bowtie:"\u22c8",boxbox:"\u29c9",boxdl:"\u2510",boxdL:"\u2555",boxDl:"\u2556",boxDL:"\u2557",boxdr:"\u250c",boxdR:"\u2552",boxDr:"\u2553",boxDR:"\u2554",boxh:"\u2500",boxH:"\u2550",boxhd:"\u252c",boxHd:"\u2564",boxhD:"\u2565",boxHD:"\u2566",boxhu:"\u2534",boxHu:"\u2567",boxhU:"\u2568",boxHU:"\u2569",boxminus:"\u229f",boxplus:"\u229e",boxtimes:"\u22a0",boxul:"\u2518",boxuL:"\u255b",boxUl:"\u255c",boxUL:"\u255d",boxur:"\u2514",boxuR:"\u2558",boxUr:"\u2559",boxUR:"\u255a",boxv:"\u2502",boxV:"\u2551",boxvh:"\u253c",boxvH:"\u256a",boxVh:"\u256b",boxVH:"\u256c",boxvl:"\u2524",boxvL:"\u2561",boxVl:"\u2562",boxVL:"\u2563",boxvr:"\u251c",boxvR:"\u255e",boxVr:"\u255f",boxVR:"\u2560",bprime:"\u2035",breve:"\u02d8",Breve:"\u02d8",brvbar:"\xa6",bscr:"\ud835\udcb7",Bscr:"\u212c",bsemi:"\u204f",bsim:"\u223d",bsime:"\u22cd",bsolb:"\u29c5",bsol:"\\",bsolhsub:"\u27c8",bull:"\u2022",bullet:"\u2022",bump:"\u224e",bumpE:"\u2aae",bumpe:"\u224f",Bumpeq:"\u224e",bumpeq:"\u224f",Cacute:"\u0106",cacute:"\u0107",capand:"\u2a44",capbrcup:"\u2a49",capcap:"\u2a4b",cap:"\u2229",Cap:"\u22d2",capcup:"\u2a47",capdot:"\u2a40",CapitalDifferentialD:"\u2145",caps:"\u2229\ufe00",caret:"\u2041",caron:"\u02c7",Cayleys:"\u212d",ccaps:"\u2a4d",Ccaron:"\u010c",ccaron:"\u010d",Ccedil:"\xc7",ccedil:"\xe7",Ccirc:"\u0108",ccirc:"\u0109",Cconint:"\u2230",ccups:"\u2a4c",ccupssm:"\u2a50",Cdot:"\u010a",cdot:"\u010b",cedil:"\xb8",Cedilla:"\xb8",cemptyv:"\u29b2",cent:"\xa2",centerdot:"\xb7",CenterDot:"\xb7",cfr:"\ud835\udd20",Cfr:"\u212d",CHcy:"\u0427",chcy:"\u0447",check:"\u2713",checkmark:"\u2713",Chi:"\u03a7",chi:"\u03c7",circ:"\u02c6",circeq:"\u2257",circlearrowleft:"\u21ba",circlearrowright:"\u21bb",circledast:"\u229b",circledcirc:"\u229a",circleddash:"\u229d",CircleDot:"\u2299",circledR:"\xae",circledS:"\u24c8",CircleMinus:"\u2296",CirclePlus:"\u2295",CircleTimes:"\u2297",cir:"\u25cb",cirE:"\u29c3",cire:"\u2257",cirfnint:"\u2a10",cirmid:"\u2aef",cirscir:"\u29c2",ClockwiseContourIntegral:"\u2232",CloseCurlyDoubleQuote:"\u201d",CloseCurlyQuote:"\u2019",clubs:"\u2663",clubsuit:"\u2663",colon:":",Colon:"\u2237",Colone:"\u2a74",colone:"\u2254",coloneq:"\u2254",comma:",",commat:"@",comp:"\u2201",compfn:"\u2218",complement:"\u2201",complexes:"\u2102",cong:"\u2245",congdot:"\u2a6d",Congruent:"\u2261",conint:"\u222e",Conint:"\u222f",ContourIntegral:"\u222e",copf:"\ud835\udd54",Copf:"\u2102",coprod:"\u2210",Coproduct:"\u2210",copy:"\xa9",COPY:"\xa9",copysr:"\u2117",CounterClockwiseContourIntegral:"\u2233",crarr:"\u21b5",cross:"\u2717",Cross:"\u2a2f",Cscr:"\ud835\udc9e",cscr:"\ud835\udcb8",csub:"\u2acf",csube:"\u2ad1",csup:"\u2ad0",csupe:"\u2ad2",ctdot:"\u22ef",cudarrl:"\u2938",cudarrr:"\u2935",cuepr:"\u22de",cuesc:"\u22df",cularr:"\u21b6",cularrp:"\u293d",cupbrcap:"\u2a48",cupcap:"\u2a46",CupCap:"\u224d",cup:"\u222a",Cup:"\u22d3",cupcup:"\u2a4a",cupdot:"\u228d",cupor:"\u2a45",cups:"\u222a\ufe00",curarr:"\u21b7",curarrm:"\u293c",curlyeqprec:"\u22de",curlyeqsucc:"\u22df",curlyvee:"\u22ce",curlywedge:"\u22cf",curren:"\xa4",curvearrowleft:"\u21b6",curvearrowright:"\u21b7",cuvee:"\u22ce",cuwed:"\u22cf",cwconint:"\u2232",cwint:"\u2231",cylcty:"\u232d",dagger:"\u2020",Dagger:"\u2021",daleth:"\u2138",darr:"\u2193",Darr:"\u21a1",dArr:"\u21d3",dash:"\u2010",Dashv:"\u2ae4",dashv:"\u22a3",dbkarow:"\u290f",dblac:"\u02dd",Dcaron:"\u010e",dcaron:"\u010f",Dcy:"\u0414",dcy:"\u0434",ddagger:"\u2021",ddarr:"\u21ca",DD:"\u2145",dd:"\u2146",DDotrahd:"\u2911",ddotseq:"\u2a77",deg:"\xb0",Del:"\u2207",Delta:"\u0394",delta:"\u03b4",demptyv:"\u29b1",dfisht:"\u297f",Dfr:"\ud835\udd07",dfr:"\ud835\udd21",dHar:"\u2965",dharl:"\u21c3",dharr:"\u21c2",DiacriticalAcute:"\xb4",DiacriticalDot:"\u02d9",DiacriticalDoubleAcute:"\u02dd",DiacriticalGrave:"`",DiacriticalTilde:"\u02dc",diam:"\u22c4",diamond:"\u22c4",Diamond:"\u22c4",diamondsuit:"\u2666",diams:"\u2666",die:"\xa8",DifferentialD:"\u2146",digamma:"\u03dd",disin:"\u22f2",div:"\xf7",divide:"\xf7",divideontimes:"\u22c7",divonx:"\u22c7",DJcy:"\u0402",djcy:"\u0452",dlcorn:"\u231e",dlcrop:"\u230d",dollar:"$",Dopf:"\ud835\udd3b",dopf:"\ud835\udd55",Dot:"\xa8",dot:"\u02d9",DotDot:"\u20dc",doteq:"\u2250",doteqdot:"\u2251",DotEqual:"\u2250",dotminus:"\u2238",dotplus:"\u2214",dotsquare:"\u22a1",doublebarwedge:"\u2306",DoubleContourIntegral:"\u222f",DoubleDot:"\xa8",DoubleDownArrow:"\u21d3",DoubleLeftArrow:"\u21d0",DoubleLeftRightArrow:"\u21d4",DoubleLeftTee:"\u2ae4",DoubleLongLeftArrow:"\u27f8",DoubleLongLeftRightArrow:"\u27fa",DoubleLongRightArrow:"\u27f9",DoubleRightArrow:"\u21d2",DoubleRightTee:"\u22a8",DoubleUpArrow:"\u21d1",DoubleUpDownArrow:"\u21d5",DoubleVerticalBar:"\u2225",DownArrowBar:"\u2913",downarrow:"\u2193",DownArrow:"\u2193",Downarrow:"\u21d3",DownArrowUpArrow:"\u21f5",DownBreve:"\u0311",downdownarrows:"\u21ca",downharpoonleft:"\u21c3",downharpoonright:"\u21c2",DownLeftRightVector:"\u2950",DownLeftTeeVector:"\u295e",DownLeftVectorBar:"\u2956",DownLeftVector:"\u21bd",DownRightTeeVector:"\u295f",DownRightVectorBar:"\u2957",DownRightVector:"\u21c1",DownTeeArrow:"\u21a7",DownTee:"\u22a4",drbkarow:"\u2910",drcorn:"\u231f",drcrop:"\u230c",Dscr:"\ud835\udc9f",dscr:"\ud835\udcb9",DScy:"\u0405",dscy:"\u0455",dsol:"\u29f6",Dstrok:"\u0110",dstrok:"\u0111",dtdot:"\u22f1",dtri:"\u25bf",dtrif:"\u25be",duarr:"\u21f5",duhar:"\u296f",dwangle:"\u29a6",DZcy:"\u040f",dzcy:"\u045f",dzigrarr:"\u27ff",Eacute:"\xc9",eacute:"\xe9",easter:"\u2a6e",Ecaron:"\u011a",ecaron:"\u011b",Ecirc:"\xca",ecirc:"\xea",ecir:"\u2256",ecolon:"\u2255",Ecy:"\u042d",ecy:"\u044d",eDDot:"\u2a77",Edot:"\u0116",edot:"\u0117",eDot:"\u2251",ee:"\u2147",efDot:"\u2252",Efr:"\ud835\udd08",efr:"\ud835\udd22",eg:"\u2a9a",Egrave:"\xc8",egrave:"\xe8",egs:"\u2a96",egsdot:"\u2a98",el:"\u2a99",Element:"\u2208",elinters:"\u23e7",ell:"\u2113",els:"\u2a95",elsdot:"\u2a97",Emacr:"\u0112",emacr:"\u0113",empty:"\u2205",emptyset:"\u2205",EmptySmallSquare:"\u25fb",emptyv:"\u2205",EmptyVerySmallSquare:"\u25ab",emsp13:"\u2004",emsp14:"\u2005",emsp:"\u2003",ENG:"\u014a",eng:"\u014b",ensp:"\u2002",Eogon:"\u0118",eogon:"\u0119",Eopf:"\ud835\udd3c",eopf:"\ud835\udd56",epar:"\u22d5",eparsl:"\u29e3",eplus:"\u2a71",epsi:"\u03b5",Epsilon:"\u0395",epsilon:"\u03b5",epsiv:"\u03f5",eqcirc:"\u2256",eqcolon:"\u2255",eqsim:"\u2242",eqslantgtr:"\u2a96",eqslantless:"\u2a95",Equal:"\u2a75",equals:"=",EqualTilde:"\u2242",equest:"\u225f",Equilibrium:"\u21cc",equiv:"\u2261",equivDD:"\u2a78",eqvparsl:"\u29e5",erarr:"\u2971",erDot:"\u2253",escr:"\u212f",Escr:"\u2130",esdot:"\u2250",Esim:"\u2a73",esim:"\u2242",Eta:"\u0397",eta:"\u03b7",ETH:"\xd0",eth:"\xf0",Euml:"\xcb",euml:"\xeb",euro:"\u20ac",excl:"!",exist:"\u2203",Exists:"\u2203",expectation:"\u2130",exponentiale:"\u2147",ExponentialE:"\u2147",fallingdotseq:"\u2252",Fcy:"\u0424",fcy:"\u0444",female:"\u2640",ffilig:"\ufb03",fflig:"\ufb00",ffllig:"\ufb04",Ffr:"\ud835\udd09",ffr:"\ud835\udd23",filig:"\ufb01",FilledSmallSquare:"\u25fc",FilledVerySmallSquare:"\u25aa",fjlig:"fj",flat:"\u266d",fllig:"\ufb02",fltns:"\u25b1",fnof:"\u0192",Fopf:"\ud835\udd3d",fopf:"\ud835\udd57",forall:"\u2200",ForAll:"\u2200",fork:"\u22d4",forkv:"\u2ad9",Fouriertrf:"\u2131",fpartint:"\u2a0d",frac12:"\xbd",frac13:"\u2153",frac14:"\xbc",frac15:"\u2155",frac16:"\u2159",frac18:"\u215b",frac23:"\u2154",frac25:"\u2156",frac34:"\xbe",frac35:"\u2157",frac38:"\u215c",frac45:"\u2158",frac56:"\u215a",frac58:"\u215d",frac78:"\u215e",frasl:"\u2044",frown:"\u2322",fscr:"\ud835\udcbb",Fscr:"\u2131",gacute:"\u01f5",Gamma:"\u0393",gamma:"\u03b3",Gammad:"\u03dc",gammad:"\u03dd",gap:"\u2a86",Gbreve:"\u011e",gbreve:"\u011f",Gcedil:"\u0122",Gcirc:"\u011c",gcirc:"\u011d",Gcy:"\u0413",gcy:"\u0433",Gdot:"\u0120",gdot:"\u0121",ge:"\u2265",gE:"\u2267",gEl:"\u2a8c",gel:"\u22db",geq:"\u2265",geqq:"\u2267",geqslant:"\u2a7e",gescc:"\u2aa9",ges:"\u2a7e",gesdot:"\u2a80",gesdoto:"\u2a82",gesdotol:"\u2a84",gesl:"\u22db\ufe00",gesles:"\u2a94",Gfr:"\ud835\udd0a",gfr:"\ud835\udd24",gg:"\u226b",Gg:"\u22d9",ggg:"\u22d9",gimel:"\u2137",GJcy:"\u0403",gjcy:"\u0453",gla:"\u2aa5",gl:"\u2277",glE:"\u2a92",glj:"\u2aa4",gnap:"\u2a8a",gnapprox:"\u2a8a",gne:"\u2a88",gnE:"\u2269",gneq:"\u2a88",gneqq:"\u2269",gnsim:"\u22e7",Gopf:"\ud835\udd3e",gopf:"\ud835\udd58",grave:"`",GreaterEqual:"\u2265",GreaterEqualLess:"\u22db",GreaterFullEqual:"\u2267",GreaterGreater:"\u2aa2",GreaterLess:"\u2277",GreaterSlantEqual:"\u2a7e",GreaterTilde:"\u2273",Gscr:"\ud835\udca2",gscr:"\u210a",gsim:"\u2273",gsime:"\u2a8e",gsiml:"\u2a90",gtcc:"\u2aa7",gtcir:"\u2a7a",gt:">",GT:">",Gt:"\u226b",gtdot:"\u22d7",gtlPar:"\u2995",gtquest:"\u2a7c",gtrapprox:"\u2a86",gtrarr:"\u2978",gtrdot:"\u22d7",gtreqless:"\u22db",gtreqqless:"\u2a8c",gtrless:"\u2277",gtrsim:"\u2273",gvertneqq:"\u2269\ufe00",gvnE:"\u2269\ufe00",Hacek:"\u02c7",hairsp:"\u200a",half:"\xbd",hamilt:"\u210b",HARDcy:"\u042a",hardcy:"\u044a",harrcir:"\u2948",harr:"\u2194",hArr:"\u21d4",harrw:"\u21ad",Hat:"^",hbar:"\u210f",Hcirc:"\u0124",hcirc:"\u0125",hearts:"\u2665",heartsuit:"\u2665",hellip:"\u2026",hercon:"\u22b9",hfr:"\ud835\udd25",Hfr:"\u210c",HilbertSpace:"\u210b",hksearow:"\u2925",hkswarow:"\u2926",hoarr:"\u21ff",homtht:"\u223b",hookleftarrow:"\u21a9",hookrightarrow:"\u21aa",hopf:"\ud835\udd59",Hopf:"\u210d",horbar:"\u2015",HorizontalLine:"\u2500",hscr:"\ud835\udcbd",Hscr:"\u210b",hslash:"\u210f",Hstrok:"\u0126",hstrok:"\u0127",HumpDownHump:"\u224e",HumpEqual:"\u224f",hybull:"\u2043",hyphen:"\u2010",Iacute:"\xcd",iacute:"\xed",ic:"\u2063",Icirc:"\xce",icirc:"\xee",Icy:"\u0418",icy:"\u0438",Idot:"\u0130",IEcy:"\u0415",iecy:"\u0435",iexcl:"\xa1",iff:"\u21d4",ifr:"\ud835\udd26",Ifr:"\u2111",Igrave:"\xcc",igrave:"\xec",ii:"\u2148",iiiint:"\u2a0c",iiint:"\u222d",iinfin:"\u29dc",iiota:"\u2129",IJlig:"\u0132",ijlig:"\u0133",Imacr:"\u012a",imacr:"\u012b",image:"\u2111",ImaginaryI:"\u2148",imagline:"\u2110",imagpart:"\u2111",imath:"\u0131",Im:"\u2111",imof:"\u22b7",imped:"\u01b5",Implies:"\u21d2",incare:"\u2105",in:"\u2208",infin:"\u221e",infintie:"\u29dd",inodot:"\u0131",intcal:"\u22ba",int:"\u222b",Int:"\u222c",integers:"\u2124",Integral:"\u222b",intercal:"\u22ba",Intersection:"\u22c2",intlarhk:"\u2a17",intprod:"\u2a3c",InvisibleComma:"\u2063",InvisibleTimes:"\u2062",IOcy:"\u0401",iocy:"\u0451",Iogon:"\u012e",iogon:"\u012f",Iopf:"\ud835\udd40",iopf:"\ud835\udd5a",Iota:"\u0399",iota:"\u03b9",iprod:"\u2a3c",iquest:"\xbf",iscr:"\ud835\udcbe",Iscr:"\u2110",isin:"\u2208",isindot:"\u22f5",isinE:"\u22f9",isins:"\u22f4",isinsv:"\u22f3",isinv:"\u2208",it:"\u2062",Itilde:"\u0128",itilde:"\u0129",Iukcy:"\u0406",iukcy:"\u0456",Iuml:"\xcf",iuml:"\xef",Jcirc:"\u0134",jcirc:"\u0135",Jcy:"\u0419",jcy:"\u0439",Jfr:"\ud835\udd0d",jfr:"\ud835\udd27",jmath:"\u0237",Jopf:"\ud835\udd41",jopf:"\ud835\udd5b",Jscr:"\ud835\udca5",jscr:"\ud835\udcbf",Jsercy:"\u0408",jsercy:"\u0458",Jukcy:"\u0404",jukcy:"\u0454",Kappa:"\u039a",kappa:"\u03ba",kappav:"\u03f0",Kcedil:"\u0136",kcedil:"\u0137",Kcy:"\u041a",kcy:"\u043a",Kfr:"\ud835\udd0e",kfr:"\ud835\udd28",kgreen:"\u0138",KHcy:"\u0425",khcy:"\u0445",KJcy:"\u040c",kjcy:"\u045c",Kopf:"\ud835\udd42",kopf:"\ud835\udd5c",Kscr:"\ud835\udca6",kscr:"\ud835\udcc0",lAarr:"\u21da",Lacute:"\u0139",lacute:"\u013a",laemptyv:"\u29b4",lagran:"\u2112",Lambda:"\u039b",lambda:"\u03bb",lang:"\u27e8",Lang:"\u27ea",langd:"\u2991",langle:"\u27e8",lap:"\u2a85",Laplacetrf:"\u2112",laquo:"\xab",larrb:"\u21e4",larrbfs:"\u291f",larr:"\u2190",Larr:"\u219e",lArr:"\u21d0",larrfs:"\u291d",larrhk:"\u21a9",larrlp:"\u21ab",larrpl:"\u2939",larrsim:"\u2973",larrtl:"\u21a2",latail:"\u2919",lAtail:"\u291b",lat:"\u2aab",late:"\u2aad",lates:"\u2aad\ufe00",lbarr:"\u290c",lBarr:"\u290e",lbbrk:"\u2772",lbrace:"{",lbrack:"[",lbrke:"\u298b",lbrksld:"\u298f",lbrkslu:"\u298d",Lcaron:"\u013d",lcaron:"\u013e",Lcedil:"\u013b",lcedil:"\u013c",lceil:"\u2308",lcub:"{",Lcy:"\u041b",lcy:"\u043b",ldca:"\u2936",ldquo:"\u201c",ldquor:"\u201e",ldrdhar:"\u2967",ldrushar:"\u294b",ldsh:"\u21b2",le:"\u2264",lE:"\u2266",LeftAngleBracket:"\u27e8",LeftArrowBar:"\u21e4",leftarrow:"\u2190",LeftArrow:"\u2190",Leftarrow:"\u21d0",LeftArrowRightArrow:"\u21c6",leftarrowtail:"\u21a2",LeftCeiling:"\u2308",LeftDoubleBracket:"\u27e6",LeftDownTeeVector:"\u2961",LeftDownVectorBar:"\u2959",LeftDownVector:"\u21c3",LeftFloor:"\u230a",leftharpoondown:"\u21bd",leftharpoonup:"\u21bc",leftleftarrows:"\u21c7",leftrightarrow:"\u2194",LeftRightArrow:"\u2194",Leftrightarrow:"\u21d4",leftrightarrows:"\u21c6",leftrightharpoons:"\u21cb",leftrightsquigarrow:"\u21ad",LeftRightVector:"\u294e",LeftTeeArrow:"\u21a4",LeftTee:"\u22a3",LeftTeeVector:"\u295a",leftthreetimes:"\u22cb",LeftTriangleBar:"\u29cf",LeftTriangle:"\u22b2",LeftTriangleEqual:"\u22b4",LeftUpDownVector:"\u2951",LeftUpTeeVector:"\u2960",LeftUpVectorBar:"\u2958",LeftUpVector:"\u21bf",LeftVectorBar:"\u2952",LeftVector:"\u21bc",lEg:"\u2a8b",leg:"\u22da",leq:"\u2264",leqq:"\u2266",leqslant:"\u2a7d",lescc:"\u2aa8",les:"\u2a7d",lesdot:"\u2a7f",lesdoto:"\u2a81",lesdotor:"\u2a83",lesg:"\u22da\ufe00",lesges:"\u2a93",lessapprox:"\u2a85",lessdot:"\u22d6",lesseqgtr:"\u22da",lesseqqgtr:"\u2a8b",LessEqualGreater:"\u22da",LessFullEqual:"\u2266",LessGreater:"\u2276",lessgtr:"\u2276",LessLess:"\u2aa1",lesssim:"\u2272",LessSlantEqual:"\u2a7d",LessTilde:"\u2272",lfisht:"\u297c",lfloor:"\u230a",Lfr:"\ud835\udd0f",lfr:"\ud835\udd29",lg:"\u2276",lgE:"\u2a91",lHar:"\u2962",lhard:"\u21bd",lharu:"\u21bc",lharul:"\u296a",lhblk:"\u2584",LJcy:"\u0409",ljcy:"\u0459",llarr:"\u21c7",ll:"\u226a",Ll:"\u22d8",llcorner:"\u231e",Lleftarrow:"\u21da",llhard:"\u296b",lltri:"\u25fa",Lmidot:"\u013f",lmidot:"\u0140",lmoustache:"\u23b0",lmoust:"\u23b0",lnap:"\u2a89",lnapprox:"\u2a89",lne:"\u2a87",lnE:"\u2268",lneq:"\u2a87",lneqq:"\u2268",lnsim:"\u22e6",loang:"\u27ec",loarr:"\u21fd",lobrk:"\u27e6",longleftarrow:"\u27f5",LongLeftArrow:"\u27f5",Longleftarrow:"\u27f8",longleftrightarrow:"\u27f7",LongLeftRightArrow:"\u27f7",Longleftrightarrow:"\u27fa",longmapsto:"\u27fc",longrightarrow:"\u27f6",LongRightArrow:"\u27f6",Longrightarrow:"\u27f9",looparrowleft:"\u21ab",looparrowright:"\u21ac",lopar:"\u2985",Lopf:"\ud835\udd43",lopf:"\ud835\udd5d",loplus:"\u2a2d",lotimes:"\u2a34",lowast:"\u2217",lowbar:"_",LowerLeftArrow:"\u2199",LowerRightArrow:"\u2198",loz:"\u25ca",lozenge:"\u25ca",lozf:"\u29eb",lpar:"(",lparlt:"\u2993",lrarr:"\u21c6",lrcorner:"\u231f",lrhar:"\u21cb",lrhard:"\u296d",lrm:"\u200e",lrtri:"\u22bf",lsaquo:"\u2039",lscr:"\ud835\udcc1",Lscr:"\u2112",lsh:"\u21b0",Lsh:"\u21b0",lsim:"\u2272",lsime:"\u2a8d",lsimg:"\u2a8f",lsqb:"[",lsquo:"\u2018",lsquor:"\u201a",Lstrok:"\u0141",lstrok:"\u0142",ltcc:"\u2aa6",ltcir:"\u2a79",lt:"<",LT:"<",Lt:"\u226a",ltdot:"\u22d6",lthree:"\u22cb",ltimes:"\u22c9",ltlarr:"\u2976",ltquest:"\u2a7b",ltri:"\u25c3",ltrie:"\u22b4",ltrif:"\u25c2",ltrPar:"\u2996",lurdshar:"\u294a",luruhar:"\u2966",lvertneqq:"\u2268\ufe00",lvnE:"\u2268\ufe00",macr:"\xaf",male:"\u2642",malt:"\u2720",maltese:"\u2720",Map:"\u2905",map:"\u21a6",mapsto:"\u21a6",mapstodown:"\u21a7",mapstoleft:"\u21a4",mapstoup:"\u21a5",marker:"\u25ae",mcomma:"\u2a29",Mcy:"\u041c",mcy:"\u043c",mdash:"\u2014",mDDot:"\u223a",measuredangle:"\u2221",MediumSpace:"\u205f",Mellintrf:"\u2133",Mfr:"\ud835\udd10",mfr:"\ud835\udd2a",mho:"\u2127",micro:"\xb5",midast:"*",midcir:"\u2af0",mid:"\u2223",middot:"\xb7",minusb:"\u229f",minus:"\u2212",minusd:"\u2238",minusdu:"\u2a2a",MinusPlus:"\u2213",mlcp:"\u2adb",mldr:"\u2026",mnplus:"\u2213",models:"\u22a7",Mopf:"\ud835\udd44",mopf:"\ud835\udd5e",mp:"\u2213",mscr:"\ud835\udcc2",Mscr:"\u2133",mstpos:"\u223e",Mu:"\u039c",mu:"\u03bc",multimap:"\u22b8",mumap:"\u22b8",nabla:"\u2207",Nacute:"\u0143",nacute:"\u0144",nang:"\u2220\u20d2",nap:"\u2249",napE:"\u2a70\u0338",napid:"\u224b\u0338",napos:"\u0149",napprox:"\u2249",natural:"\u266e",naturals:"\u2115",natur:"\u266e",nbsp:"\xa0",nbump:"\u224e\u0338",nbumpe:"\u224f\u0338",ncap:"\u2a43",Ncaron:"\u0147",ncaron:"\u0148",Ncedil:"\u0145",ncedil:"\u0146",ncong:"\u2247",ncongdot:"\u2a6d\u0338",ncup:"\u2a42",Ncy:"\u041d",ncy:"\u043d",ndash:"\u2013",nearhk:"\u2924",nearr:"\u2197",neArr:"\u21d7",nearrow:"\u2197",ne:"\u2260",nedot:"\u2250\u0338",NegativeMediumSpace:"\u200b",NegativeThickSpace:"\u200b",NegativeThinSpace:"\u200b",NegativeVeryThinSpace:"\u200b",nequiv:"\u2262",nesear:"\u2928",nesim:"\u2242\u0338",NestedGreaterGreater:"\u226b",NestedLessLess:"\u226a",NewLine:"\n",nexist:"\u2204",nexists:"\u2204",Nfr:"\ud835\udd11",nfr:"\ud835\udd2b",ngE:"\u2267\u0338",nge:"\u2271",ngeq:"\u2271",ngeqq:"\u2267\u0338",ngeqslant:"\u2a7e\u0338",nges:"\u2a7e\u0338",nGg:"\u22d9\u0338",ngsim:"\u2275",nGt:"\u226b\u20d2",ngt:"\u226f",ngtr:"\u226f",nGtv:"\u226b\u0338",nharr:"\u21ae",nhArr:"\u21ce",nhpar:"\u2af2",ni:"\u220b",nis:"\u22fc",nisd:"\u22fa",niv:"\u220b",NJcy:"\u040a",njcy:"\u045a",nlarr:"\u219a",nlArr:"\u21cd",nldr:"\u2025",nlE:"\u2266\u0338",nle:"\u2270",nleftarrow:"\u219a",nLeftarrow:"\u21cd",nleftrightarrow:"\u21ae",nLeftrightarrow:"\u21ce",nleq:"\u2270",nleqq:"\u2266\u0338",nleqslant:"\u2a7d\u0338",nles:"\u2a7d\u0338",nless:"\u226e",nLl:"\u22d8\u0338",nlsim:"\u2274",nLt:"\u226a\u20d2",nlt:"\u226e",nltri:"\u22ea",nltrie:"\u22ec",nLtv:"\u226a\u0338",nmid:"\u2224",NoBreak:"\u2060",NonBreakingSpace:"\xa0",nopf:"\ud835\udd5f",Nopf:"\u2115",Not:"\u2aec",not:"\xac",NotCongruent:"\u2262",NotCupCap:"\u226d",NotDoubleVerticalBar:"\u2226",NotElement:"\u2209",NotEqual:"\u2260",NotEqualTilde:"\u2242\u0338",NotExists:"\u2204",NotGreater:"\u226f",NotGreaterEqual:"\u2271",NotGreaterFullEqual:"\u2267\u0338",NotGreaterGreater:"\u226b\u0338",NotGreaterLess:"\u2279",NotGreaterSlantEqual:"\u2a7e\u0338",NotGreaterTilde:"\u2275",NotHumpDownHump:"\u224e\u0338",NotHumpEqual:"\u224f\u0338",notin:"\u2209",notindot:"\u22f5\u0338",notinE:"\u22f9\u0338",notinva:"\u2209",notinvb:"\u22f7",notinvc:"\u22f6",NotLeftTriangleBar:"\u29cf\u0338",NotLeftTriangle:"\u22ea",NotLeftTriangleEqual:"\u22ec",NotLess:"\u226e",NotLessEqual:"\u2270",NotLessGreater:"\u2278",NotLessLess:"\u226a\u0338",NotLessSlantEqual:"\u2a7d\u0338",NotLessTilde:"\u2274",NotNestedGreaterGreater:"\u2aa2\u0338",NotNestedLessLess:"\u2aa1\u0338",notni:"\u220c",notniva:"\u220c",notnivb:"\u22fe",notnivc:"\u22fd",NotPrecedes:"\u2280",NotPrecedesEqual:"\u2aaf\u0338",NotPrecedesSlantEqual:"\u22e0",NotReverseElement:"\u220c",NotRightTriangleBar:"\u29d0\u0338",NotRightTriangle:"\u22eb",NotRightTriangleEqual:"\u22ed",NotSquareSubset:"\u228f\u0338",NotSquareSubsetEqual:"\u22e2",NotSquareSuperset:"\u2290\u0338",NotSquareSupersetEqual:"\u22e3",NotSubset:"\u2282\u20d2",NotSubsetEqual:"\u2288",NotSucceeds:"\u2281",NotSucceedsEqual:"\u2ab0\u0338",NotSucceedsSlantEqual:"\u22e1",NotSucceedsTilde:"\u227f\u0338",NotSuperset:"\u2283\u20d2",NotSupersetEqual:"\u2289",NotTilde:"\u2241",NotTildeEqual:"\u2244",NotTildeFullEqual:"\u2247",NotTildeTilde:"\u2249",NotVerticalBar:"\u2224",nparallel:"\u2226",npar:"\u2226",nparsl:"\u2afd\u20e5",npart:"\u2202\u0338",npolint:"\u2a14",npr:"\u2280",nprcue:"\u22e0",nprec:"\u2280",npreceq:"\u2aaf\u0338",npre:"\u2aaf\u0338",nrarrc:"\u2933\u0338",nrarr:"\u219b",nrArr:"\u21cf",nrarrw:"\u219d\u0338",nrightarrow:"\u219b",nRightarrow:"\u21cf",nrtri:"\u22eb",nrtrie:"\u22ed",nsc:"\u2281",nsccue:"\u22e1",nsce:"\u2ab0\u0338",Nscr:"\ud835\udca9",nscr:"\ud835\udcc3",nshortmid:"\u2224",nshortparallel:"\u2226",nsim:"\u2241",nsime:"\u2244",nsimeq:"\u2244",nsmid:"\u2224",nspar:"\u2226",nsqsube:"\u22e2",nsqsupe:"\u22e3",nsub:"\u2284",nsubE:"\u2ac5\u0338",nsube:"\u2288",nsubset:"\u2282\u20d2",nsubseteq:"\u2288",nsubseteqq:"\u2ac5\u0338",nsucc:"\u2281",nsucceq:"\u2ab0\u0338",nsup:"\u2285",nsupE:"\u2ac6\u0338",nsupe:"\u2289",nsupset:"\u2283\u20d2",nsupseteq:"\u2289",nsupseteqq:"\u2ac6\u0338",ntgl:"\u2279",Ntilde:"\xd1",ntilde:"\xf1",ntlg:"\u2278",ntriangleleft:"\u22ea",ntrianglelefteq:"\u22ec",ntriangleright:"\u22eb",ntrianglerighteq:"\u22ed",Nu:"\u039d",nu:"\u03bd",num:"#",numero:"\u2116",numsp:"\u2007",nvap:"\u224d\u20d2",nvdash:"\u22ac",nvDash:"\u22ad",nVdash:"\u22ae",nVDash:"\u22af",nvge:"\u2265\u20d2",nvgt:">\u20d2",nvHarr:"\u2904",nvinfin:"\u29de",nvlArr:"\u2902",nvle:"\u2264\u20d2",nvlt:"<\u20d2",nvltrie:"\u22b4\u20d2",nvrArr:"\u2903",nvrtrie:"\u22b5\u20d2",nvsim:"\u223c\u20d2",nwarhk:"\u2923",nwarr:"\u2196",nwArr:"\u21d6",nwarrow:"\u2196",nwnear:"\u2927",Oacute:"\xd3",oacute:"\xf3",oast:"\u229b",Ocirc:"\xd4",ocirc:"\xf4",ocir:"\u229a",Ocy:"\u041e",ocy:"\u043e",odash:"\u229d",Odblac:"\u0150",odblac:"\u0151",odiv:"\u2a38",odot:"\u2299",odsold:"\u29bc",OElig:"\u0152",oelig:"\u0153",ofcir:"\u29bf",Ofr:"\ud835\udd12",ofr:"\ud835\udd2c",ogon:"\u02db",Ograve:"\xd2",ograve:"\xf2",ogt:"\u29c1",ohbar:"\u29b5",ohm:"\u03a9",oint:"\u222e",olarr:"\u21ba",olcir:"\u29be",olcross:"\u29bb",oline:"\u203e",olt:"\u29c0",Omacr:"\u014c",omacr:"\u014d",Omega:"\u03a9",omega:"\u03c9",Omicron:"\u039f",omicron:"\u03bf",omid:"\u29b6",ominus:"\u2296",Oopf:"\ud835\udd46",oopf:"\ud835\udd60",opar:"\u29b7",OpenCurlyDoubleQuote:"\u201c",OpenCurlyQuote:"\u2018",operp:"\u29b9",oplus:"\u2295",orarr:"\u21bb",Or:"\u2a54",or:"\u2228",ord:"\u2a5d",order:"\u2134",orderof:"\u2134",ordf:"\xaa",ordm:"\xba",origof:"\u22b6",oror:"\u2a56",orslope:"\u2a57",orv:"\u2a5b",oS:"\u24c8",Oscr:"\ud835\udcaa",oscr:"\u2134",Oslash:"\xd8",oslash:"\xf8",osol:"\u2298",Otilde:"\xd5",otilde:"\xf5",otimesas:"\u2a36",Otimes:"\u2a37",otimes:"\u2297",Ouml:"\xd6",ouml:"\xf6",ovbar:"\u233d",OverBar:"\u203e",OverBrace:"\u23de",OverBracket:"\u23b4",OverParenthesis:"\u23dc",para:"\xb6",parallel:"\u2225",par:"\u2225",parsim:"\u2af3",parsl:"\u2afd",part:"\u2202",PartialD:"\u2202",Pcy:"\u041f",pcy:"\u043f",percnt:"%",period:".",permil:"\u2030",perp:"\u22a5",pertenk:"\u2031",Pfr:"\ud835\udd13",pfr:"\ud835\udd2d",Phi:"\u03a6",phi:"\u03c6",phiv:"\u03d5",phmmat:"\u2133",phone:"\u260e",Pi:"\u03a0",pi:"\u03c0",pitchfork:"\u22d4",piv:"\u03d6",planck:"\u210f",planckh:"\u210e",plankv:"\u210f",plusacir:"\u2a23",plusb:"\u229e",pluscir:"\u2a22",plus:"+",plusdo:"\u2214",plusdu:"\u2a25",pluse:"\u2a72",PlusMinus:"\xb1",plusmn:"\xb1",plussim:"\u2a26",plustwo:"\u2a27",pm:"\xb1",Poincareplane:"\u210c",pointint:"\u2a15",popf:"\ud835\udd61",Popf:"\u2119",pound:"\xa3",prap:"\u2ab7",Pr:"\u2abb",pr:"\u227a",prcue:"\u227c",precapprox:"\u2ab7",prec:"\u227a",preccurlyeq:"\u227c",Precedes:"\u227a",PrecedesEqual:"\u2aaf",PrecedesSlantEqual:"\u227c",PrecedesTilde:"\u227e",preceq:"\u2aaf",precnapprox:"\u2ab9",precneqq:"\u2ab5",precnsim:"\u22e8",pre:"\u2aaf",prE:"\u2ab3",precsim:"\u227e",prime:"\u2032",Prime:"\u2033",primes:"\u2119",prnap:"\u2ab9",prnE:"\u2ab5",prnsim:"\u22e8",prod:"\u220f",Product:"\u220f",profalar:"\u232e",profline:"\u2312",profsurf:"\u2313",prop:"\u221d",Proportional:"\u221d",Proportion:"\u2237",propto:"\u221d",prsim:"\u227e",prurel:"\u22b0",Pscr:"\ud835\udcab",pscr:"\ud835\udcc5",Psi:"\u03a8",psi:"\u03c8",puncsp:"\u2008",Qfr:"\ud835\udd14",qfr:"\ud835\udd2e",qint:"\u2a0c",qopf:"\ud835\udd62",Qopf:"\u211a",qprime:"\u2057",Qscr:"\ud835\udcac",qscr:"\ud835\udcc6",quaternions:"\u210d",quatint:"\u2a16",quest:"?",questeq:"\u225f",quot:'"',QUOT:'"',rAarr:"\u21db",race:"\u223d\u0331",Racute:"\u0154",racute:"\u0155",radic:"\u221a",raemptyv:"\u29b3",rang:"\u27e9",Rang:"\u27eb",rangd:"\u2992",range:"\u29a5",rangle:"\u27e9",raquo:"\xbb",rarrap:"\u2975",rarrb:"\u21e5",rarrbfs:"\u2920",rarrc:"\u2933",rarr:"\u2192",Rarr:"\u21a0",rArr:"\u21d2",rarrfs:"\u291e",rarrhk:"\u21aa",rarrlp:"\u21ac",rarrpl:"\u2945",rarrsim:"\u2974",Rarrtl:"\u2916",rarrtl:"\u21a3",rarrw:"\u219d",ratail:"\u291a",rAtail:"\u291c",ratio:"\u2236",rationals:"\u211a",rbarr:"\u290d",rBarr:"\u290f",RBarr:"\u2910",rbbrk:"\u2773",rbrace:"}",rbrack:"]",rbrke:"\u298c",rbrksld:"\u298e",rbrkslu:"\u2990",Rcaron:"\u0158",rcaron:"\u0159",Rcedil:"\u0156",rcedil:"\u0157",rceil:"\u2309",rcub:"}",Rcy:"\u0420",rcy:"\u0440",rdca:"\u2937",rdldhar:"\u2969",rdquo:"\u201d",rdquor:"\u201d",rdsh:"\u21b3",real:"\u211c",realine:"\u211b",realpart:"\u211c",reals:"\u211d",Re:"\u211c",rect:"\u25ad",reg:"\xae",REG:"\xae",ReverseElement:"\u220b",ReverseEquilibrium:"\u21cb",ReverseUpEquilibrium:"\u296f",rfisht:"\u297d",rfloor:"\u230b",rfr:"\ud835\udd2f",Rfr:"\u211c",rHar:"\u2964",rhard:"\u21c1",rharu:"\u21c0",rharul:"\u296c",Rho:"\u03a1",rho:"\u03c1",rhov:"\u03f1",RightAngleBracket:"\u27e9",RightArrowBar:"\u21e5",rightarrow:"\u2192",RightArrow:"\u2192",Rightarrow:"\u21d2",RightArrowLeftArrow:"\u21c4",rightarrowtail:"\u21a3",RightCeiling:"\u2309",RightDoubleBracket:"\u27e7",RightDownTeeVector:"\u295d",RightDownVectorBar:"\u2955",RightDownVector:"\u21c2",RightFloor:"\u230b",rightharpoondown:"\u21c1",rightharpoonup:"\u21c0",rightleftarrows:"\u21c4",rightleftharpoons:"\u21cc",rightrightarrows:"\u21c9",rightsquigarrow:"\u219d",RightTeeArrow:"\u21a6",RightTee:"\u22a2",RightTeeVector:"\u295b",rightthreetimes:"\u22cc",RightTriangleBar:"\u29d0",RightTriangle:"\u22b3",RightTriangleEqual:"\u22b5",RightUpDownVector:"\u294f",RightUpTeeVector:"\u295c",RightUpVectorBar:"\u2954",RightUpVector:"\u21be",RightVectorBar:"\u2953",RightVector:"\u21c0",ring:"\u02da",risingdotseq:"\u2253",rlarr:"\u21c4",rlhar:"\u21cc",rlm:"\u200f",rmoustache:"\u23b1",rmoust:"\u23b1",rnmid:"\u2aee",roang:"\u27ed",roarr:"\u21fe",robrk:"\u27e7",ropar:"\u2986",ropf:"\ud835\udd63",Ropf:"\u211d",roplus:"\u2a2e",rotimes:"\u2a35",RoundImplies:"\u2970",rpar:")",rpargt:"\u2994",rppolint:"\u2a12",rrarr:"\u21c9",Rrightarrow:"\u21db",rsaquo:"\u203a",rscr:"\ud835\udcc7",Rscr:"\u211b",rsh:"\u21b1",Rsh:"\u21b1",rsqb:"]",rsquo:"\u2019",rsquor:"\u2019",rthree:"\u22cc",rtimes:"\u22ca",rtri:"\u25b9",rtrie:"\u22b5",rtrif:"\u25b8",rtriltri:"\u29ce",RuleDelayed:"\u29f4",ruluhar:"\u2968",rx:"\u211e",Sacute:"\u015a",sacute:"\u015b",sbquo:"\u201a",scap:"\u2ab8",Scaron:"\u0160",scaron:"\u0161",Sc:"\u2abc",sc:"\u227b",sccue:"\u227d",sce:"\u2ab0",scE:"\u2ab4",Scedil:"\u015e",scedil:"\u015f",Scirc:"\u015c",scirc:"\u015d",scnap:"\u2aba",scnE:"\u2ab6",scnsim:"\u22e9",scpolint:"\u2a13",scsim:"\u227f",Scy:"\u0421",scy:"\u0441",sdotb:"\u22a1",sdot:"\u22c5",sdote:"\u2a66",searhk:"\u2925",searr:"\u2198",seArr:"\u21d8",searrow:"\u2198",sect:"\xa7",semi:";",seswar:"\u2929",setminus:"\u2216",setmn:"\u2216",sext:"\u2736",Sfr:"\ud835\udd16",sfr:"\ud835\udd30",sfrown:"\u2322",sharp:"\u266f",SHCHcy:"\u0429",shchcy:"\u0449",SHcy:"\u0428",shcy:"\u0448",ShortDownArrow:"\u2193",ShortLeftArrow:"\u2190",shortmid:"\u2223",shortparallel:"\u2225",ShortRightArrow:"\u2192",ShortUpArrow:"\u2191",shy:"\xad",Sigma:"\u03a3",sigma:"\u03c3",sigmaf:"\u03c2",sigmav:"\u03c2",sim:"\u223c",simdot:"\u2a6a",sime:"\u2243",simeq:"\u2243",simg:"\u2a9e",simgE:"\u2aa0",siml:"\u2a9d",simlE:"\u2a9f",simne:"\u2246",simplus:"\u2a24",simrarr:"\u2972",slarr:"\u2190",SmallCircle:"\u2218",smallsetminus:"\u2216",smashp:"\u2a33",smeparsl:"\u29e4",smid:"\u2223",smile:"\u2323",smt:"\u2aaa",smte:"\u2aac",smtes:"\u2aac\ufe00",SOFTcy:"\u042c",softcy:"\u044c",solbar:"\u233f",solb:"\u29c4",sol:"/",Sopf:"\ud835\udd4a",sopf:"\ud835\udd64",spades:"\u2660",spadesuit:"\u2660",spar:"\u2225",sqcap:"\u2293",sqcaps:"\u2293\ufe00",sqcup:"\u2294",sqcups:"\u2294\ufe00",Sqrt:"\u221a",sqsub:"\u228f",sqsube:"\u2291",sqsubset:"\u228f",sqsubseteq:"\u2291",sqsup:"\u2290",sqsupe:"\u2292",sqsupset:"\u2290",sqsupseteq:"\u2292",square:"\u25a1",Square:"\u25a1",SquareIntersection:"\u2293",SquareSubset:"\u228f",SquareSubsetEqual:"\u2291",SquareSuperset:"\u2290",SquareSupersetEqual:"\u2292",SquareUnion:"\u2294",squarf:"\u25aa",squ:"\u25a1",squf:"\u25aa",srarr:"\u2192",Sscr:"\ud835\udcae",sscr:"\ud835\udcc8",ssetmn:"\u2216",ssmile:"\u2323",sstarf:"\u22c6",Star:"\u22c6",star:"\u2606",starf:"\u2605",straightepsilon:"\u03f5",straightphi:"\u03d5",strns:"\xaf",sub:"\u2282",Sub:"\u22d0",subdot:"\u2abd",subE:"\u2ac5",sube:"\u2286",subedot:"\u2ac3",submult:"\u2ac1",subnE:"\u2acb",subne:"\u228a",subplus:"\u2abf",subrarr:"\u2979",subset:"\u2282",Subset:"\u22d0",subseteq:"\u2286",subseteqq:"\u2ac5",SubsetEqual:"\u2286",subsetneq:"\u228a",subsetneqq:"\u2acb",subsim:"\u2ac7",subsub:"\u2ad5",subsup:"\u2ad3",succapprox:"\u2ab8",succ:"\u227b",succcurlyeq:"\u227d",Succeeds:"\u227b",SucceedsEqual:"\u2ab0",SucceedsSlantEqual:"\u227d",SucceedsTilde:"\u227f",succeq:"\u2ab0",succnapprox:"\u2aba",succneqq:"\u2ab6",succnsim:"\u22e9",succsim:"\u227f",SuchThat:"\u220b",sum:"\u2211",Sum:"\u2211",sung:"\u266a",sup1:"\xb9",sup2:"\xb2",sup3:"\xb3",sup:"\u2283",Sup:"\u22d1",supdot:"\u2abe",supdsub:"\u2ad8",supE:"\u2ac6",supe:"\u2287",supedot:"\u2ac4",Superset:"\u2283",SupersetEqual:"\u2287",suphsol:"\u27c9",suphsub:"\u2ad7",suplarr:"\u297b",supmult:"\u2ac2",supnE:"\u2acc",supne:"\u228b",supplus:"\u2ac0",supset:"\u2283",Supset:"\u22d1",supseteq:"\u2287",supseteqq:"\u2ac6",supsetneq:"\u228b",supsetneqq:"\u2acc",supsim:"\u2ac8",supsub:"\u2ad4",supsup:"\u2ad6",swarhk:"\u2926",swarr:"\u2199",swArr:"\u21d9",swarrow:"\u2199",swnwar:"\u292a",szlig:"\xdf",Tab:"\t",target:"\u2316",Tau:"\u03a4",tau:"\u03c4",tbrk:"\u23b4",Tcaron:"\u0164",tcaron:"\u0165",Tcedil:"\u0162",tcedil:"\u0163",Tcy:"\u0422",tcy:"\u0442",tdot:"\u20db",telrec:"\u2315",Tfr:"\ud835\udd17",tfr:"\ud835\udd31",there4:"\u2234",therefore:"\u2234",Therefore:"\u2234",Theta:"\u0398",theta:"\u03b8",thetasym:"\u03d1",thetav:"\u03d1",thickapprox:"\u2248",thicksim:"\u223c",ThickSpace:"\u205f\u200a",ThinSpace:"\u2009",thinsp:"\u2009",thkap:"\u2248",thksim:"\u223c",THORN:"\xde",thorn:"\xfe",tilde:"\u02dc",Tilde:"\u223c",TildeEqual:"\u2243",TildeFullEqual:"\u2245",TildeTilde:"\u2248",timesbar:"\u2a31",timesb:"\u22a0",times:"\xd7",timesd:"\u2a30",tint:"\u222d",toea:"\u2928",topbot:"\u2336",topcir:"\u2af1",top:"\u22a4",Topf:"\ud835\udd4b",topf:"\ud835\udd65",topfork:"\u2ada",tosa:"\u2929",tprime:"\u2034",trade:"\u2122",TRADE:"\u2122",triangle:"\u25b5",triangledown:"\u25bf",triangleleft:"\u25c3",trianglelefteq:"\u22b4",triangleq:"\u225c",triangleright:"\u25b9",trianglerighteq:"\u22b5",tridot:"\u25ec",trie:"\u225c",triminus:"\u2a3a",TripleDot:"\u20db",triplus:"\u2a39",trisb:"\u29cd",tritime:"\u2a3b",trpezium:"\u23e2",Tscr:"\ud835\udcaf",tscr:"\ud835\udcc9",TScy:"\u0426",tscy:"\u0446",TSHcy:"\u040b",tshcy:"\u045b",Tstrok:"\u0166",tstrok:"\u0167",twixt:"\u226c",twoheadleftarrow:"\u219e",twoheadrightarrow:"\u21a0",Uacute:"\xda",uacute:"\xfa",uarr:"\u2191",Uarr:"\u219f",uArr:"\u21d1",Uarrocir:"\u2949",Ubrcy:"\u040e",ubrcy:"\u045e",Ubreve:"\u016c",ubreve:"\u016d",Ucirc:"\xdb",ucirc:"\xfb",Ucy:"\u0423",ucy:"\u0443",udarr:"\u21c5",Udblac:"\u0170",udblac:"\u0171",udhar:"\u296e",ufisht:"\u297e",Ufr:"\ud835\udd18",ufr:"\ud835\udd32",Ugrave:"\xd9",ugrave:"\xf9",uHar:"\u2963",uharl:"\u21bf",uharr:"\u21be",uhblk:"\u2580",ulcorn:"\u231c",ulcorner:"\u231c",ulcrop:"\u230f",ultri:"\u25f8",Umacr:"\u016a",umacr:"\u016b",uml:"\xa8",UnderBar:"_",UnderBrace:"\u23df",UnderBracket:"\u23b5",UnderParenthesis:"\u23dd",Union:"\u22c3",UnionPlus:"\u228e",Uogon:"\u0172",uogon:"\u0173",Uopf:"\ud835\udd4c",uopf:"\ud835\udd66",UpArrowBar:"\u2912",uparrow:"\u2191",UpArrow:"\u2191",Uparrow:"\u21d1",UpArrowDownArrow:"\u21c5",updownarrow:"\u2195",UpDownArrow:"\u2195",Updownarrow:"\u21d5",UpEquilibrium:"\u296e",upharpoonleft:"\u21bf",upharpoonright:"\u21be",uplus:"\u228e",UpperLeftArrow:"\u2196",UpperRightArrow:"\u2197",upsi:"\u03c5",Upsi:"\u03d2",upsih:"\u03d2",Upsilon:"\u03a5",upsilon:"\u03c5",UpTeeArrow:"\u21a5",UpTee:"\u22a5",upuparrows:"\u21c8",urcorn:"\u231d",urcorner:"\u231d",urcrop:"\u230e",Uring:"\u016e",uring:"\u016f",urtri:"\u25f9",Uscr:"\ud835\udcb0",uscr:"\ud835\udcca",utdot:"\u22f0",Utilde:"\u0168",utilde:"\u0169",utri:"\u25b5",utrif:"\u25b4",uuarr:"\u21c8",Uuml:"\xdc",uuml:"\xfc",uwangle:"\u29a7",vangrt:"\u299c",varepsilon:"\u03f5",varkappa:"\u03f0",varnothing:"\u2205",varphi:"\u03d5",varpi:"\u03d6",varpropto:"\u221d",varr:"\u2195",vArr:"\u21d5",varrho:"\u03f1",varsigma:"\u03c2",varsubsetneq:"\u228a\ufe00",varsubsetneqq:"\u2acb\ufe00",varsupsetneq:"\u228b\ufe00",varsupsetneqq:"\u2acc\ufe00",vartheta:"\u03d1",vartriangleleft:"\u22b2",vartriangleright:"\u22b3",vBar:"\u2ae8",Vbar:"\u2aeb",vBarv:"\u2ae9",Vcy:"\u0412",vcy:"\u0432",vdash:"\u22a2",vDash:"\u22a8",Vdash:"\u22a9",VDash:"\u22ab",Vdashl:"\u2ae6",veebar:"\u22bb",vee:"\u2228",Vee:"\u22c1",veeeq:"\u225a",vellip:"\u22ee",verbar:"|",Verbar:"\u2016",vert:"|",Vert:"\u2016",VerticalBar:"\u2223",VerticalLine:"|",VerticalSeparator:"\u2758",VerticalTilde:"\u2240",VeryThinSpace:"\u200a",Vfr:"\ud835\udd19",vfr:"\ud835\udd33",vltri:"\u22b2",vnsub:"\u2282\u20d2",vnsup:"\u2283\u20d2",Vopf:"\ud835\udd4d",vopf:"\ud835\udd67",vprop:"\u221d",vrtri:"\u22b3",Vscr:"\ud835\udcb1",vscr:"\ud835\udccb",vsubnE:"\u2acb\ufe00",vsubne:"\u228a\ufe00",vsupnE:"\u2acc\ufe00",vsupne:"\u228b\ufe00",Vvdash:"\u22aa",vzigzag:"\u299a",Wcirc:"\u0174",wcirc:"\u0175",wedbar:"\u2a5f",wedge:"\u2227",Wedge:"\u22c0",wedgeq:"\u2259",weierp:"\u2118",Wfr:"\ud835\udd1a",wfr:"\ud835\udd34",Wopf:"\ud835\udd4e",wopf:"\ud835\udd68",wp:"\u2118",wr:"\u2240",wreath:"\u2240",Wscr:"\ud835\udcb2",wscr:"\ud835\udccc",xcap:"\u22c2",xcirc:"\u25ef",xcup:"\u22c3",xdtri:"\u25bd",Xfr:"\ud835\udd1b",xfr:"\ud835\udd35",xharr:"\u27f7",xhArr:"\u27fa",Xi:"\u039e",xi:"\u03be",xlarr:"\u27f5",xlArr:"\u27f8",xmap:"\u27fc",xnis:"\u22fb",xodot:"\u2a00",Xopf:"\ud835\udd4f",xopf:"\ud835\udd69",xoplus:"\u2a01",xotime:"\u2a02",xrarr:"\u27f6",xrArr:"\u27f9",Xscr:"\ud835\udcb3",xscr:"\ud835\udccd",xsqcup:"\u2a06",xuplus:"\u2a04",xutri:"\u25b3",xvee:"\u22c1",xwedge:"\u22c0",Yacute:"\xdd",yacute:"\xfd",YAcy:"\u042f",yacy:"\u044f",Ycirc:"\u0176",ycirc:"\u0177",Ycy:"\u042b",ycy:"\u044b",yen:"\xa5",Yfr:"\ud835\udd1c",yfr:"\ud835\udd36",YIcy:"\u0407",yicy:"\u0457",Yopf:"\ud835\udd50",yopf:"\ud835\udd6a",Yscr:"\ud835\udcb4",yscr:"\ud835\udcce",YUcy:"\u042e",yucy:"\u044e",yuml:"\xff",Yuml:"\u0178",Zacute:"\u0179",zacute:"\u017a",Zcaron:"\u017d",zcaron:"\u017e",Zcy:"\u0417",zcy:"\u0437",Zdot:"\u017b",zdot:"\u017c",zeetrf:"\u2128",ZeroWidthSpace:"\u200b",Zeta:"\u0396",zeta:"\u03b6",zfr:"\ud835\udd37",Zfr:"\u2128",ZHcy:"\u0416",zhcy:"\u0436",zigrarr:"\u21dd",zopf:"\ud835\udd6b",Zopf:"\u2124",Zscr:"\ud835\udcb5",zscr:"\ud835\udccf",zwj:"\u200d",zwnj:"\u200c"},t=/[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4E\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDF55-\uDF59]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDC3B\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDE97-\uDE9A]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/,n={};function s(e,r,t){var o,i,a,c,l,u="";for("string"!=typeof r&&(t=r,r=s.defaultChars),void 0===t&&(t=!0),l=function(e){var r,t,s=n[e];if(s)return s;for(s=n[e]=[],r=0;r<128;r++)t=String.fromCharCode(r),/^[0-9a-z]$/i.test(t)?s.push(t):s.push("%"+("0"+r.toString(16).toUpperCase()).slice(-2));for(r=0;r=55296&&a<=57343){if(a>=55296&&a<=56319&&o+1=56320&&c<=57343){u+=encodeURIComponent(e[o]+e[o+1]),o++;continue}u+="%EF%BF%BD"}else u+=encodeURIComponent(e[o]);return u}s.defaultChars=";/?:@&=+$,-_.!~*'()#",s.componentChars="-_.!~*'()";var o=s,i={};function a(e,r){var t;return"string"!=typeof r&&(r=a.defaultChars),t=function(e){var r,t,n=i[e];if(n)return n;for(n=i[e]=[],r=0;r<128;r++)t=String.fromCharCode(r),n.push(t);for(r=0;r=55296&&c<=57343?"\ufffd\ufffd\ufffd":String.fromCharCode(c),r+=6):240==(248&s)&&r+91114111?l+="\ufffd\ufffd\ufffd\ufffd":(c-=65536,l+=String.fromCharCode(55296+(c>>10),56320+(1023&c))),r+=9):l+="\ufffd";return l}))}a.defaultChars=";/?:@&=+$,#",a.componentChars="";var c=a;function l(){this.protocol=null,this.slashes=null,this.auth=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.pathname=null}var u=/^([a-z0-9.+-]+:)/i,p=/:[0-9]*$/,h=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,f=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),d=["'"].concat(f),m=["%","/","?",";","#"].concat(d),g=["/","?","#"],_=/^[+a-z0-9A-Z_-]{0,63}$/,b=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,k={javascript:!0,"javascript:":!0},v={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0};l.prototype.parse=function(e,r){var t,n,s,o,i,a=e;if(a=a.trim(),!r&&1===e.split("#").length){var c=h.exec(a);if(c)return this.pathname=c[1],c[2]&&(this.search=c[2]),this}var l=u.exec(a);if(l&&(s=(l=l[0]).toLowerCase(),this.protocol=l,a=a.substr(l.length)),(r||l||a.match(/^\/\/[^@\/]+@[^@\/]+/))&&(!(i="//"===a.substr(0,2))||l&&k[l]||(a=a.substr(2),this.slashes=!0)),!k[l]&&(i||l&&!v[l])){var p,f,d=-1;for(t=0;t127?D+="x":D+=x[w];if(!D.match(_)){var q=A.slice(0,t),S=A.slice(t+1),F=x.match(b);F&&(q.push(F[1]),S.unshift(F[2])),S.length&&(a=S.join(".")+a),this.hostname=q.join(".");break}}}}this.hostname.length>255&&(this.hostname=""),y&&(this.hostname=this.hostname.substr(1,this.hostname.length-2))}var L=a.indexOf("#");-1!==L&&(this.hash=a.substr(L),a=a.slice(0,L));var z=a.indexOf("?");return-1!==z&&(this.search=a.substr(z),a=a.slice(0,z)),a&&(this.pathname=a),v[s]&&this.hostname&&!this.pathname&&(this.pathname=""),this},l.prototype.parseHost=function(e){var r=p.exec(e);r&&(":"!==(r=r[0])&&(this.port=r.substr(1)),e=e.substr(0,e.length-r.length)),e&&(this.hostname=e)};var C={encode:o,decode:c,format:function(e){var r="";return r+=e.protocol||"",r+=e.slashes?"//":"",r+=e.auth?e.auth+"@":"",e.hostname&&-1!==e.hostname.indexOf(":")?r+="["+e.hostname+"]":r+=e.hostname||"",r+=e.port?":"+e.port:"",r+=e.pathname||"",r+=e.search||"",r+=e.hash||""},parse:function(e,r){if(e&&e instanceof l)return e;var t=new l;return t.parse(e,r),t}},y=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,A=/[\0-\x1F\x7F-\x9F]/,x=/[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/,D={Any:y,Cc:A,Cf:/[\xAD\u0600-\u0605\u061C\u06DD\u070F\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804[\uDCBD\uDCCD]|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/,P:t,Z:x},w=function(e,r,t){return e(t={path:r,exports:{},require:function(e,r){return function(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}(null==r&&t.path)}},t.exports),t.exports}((function(e,n){var s=Object.prototype.hasOwnProperty;function o(e,r){return s.call(e,r)}function i(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function a(e){if(e>65535){var r=55296+((e-=65536)>>10),t=56320+(1023&e);return String.fromCharCode(r,t)}return String.fromCharCode(e)}var c=/\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g,l=new RegExp(c.source+"|"+/&([a-z#][a-z0-9]{1,31});/gi.source,"gi"),u=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i;var p=/[&<>"]/,h=/[&<>"]/g,f={"&":"&","<":"<",">":">",'"':"""};function d(e){return f[e]}var m=/[.?*+^$[\]\\(){}|-]/g;n.lib={},n.lib.mdurl=C,n.lib.ucmicro=D,n.assign=function(e){var r=Array.prototype.slice.call(arguments,1);return r.forEach((function(r){if(r){if("object"!=typeof r)throw new TypeError(r+"must be object");Object.keys(r).forEach((function(t){e[t]=r[t]}))}})),e},n.isString=function(e){return"[object String]"===function(e){return Object.prototype.toString.call(e)}(e)},n.has=o,n.unescapeMd=function(e){return e.indexOf("\\")<0?e:e.replace(c,"$1")},n.unescapeAll=function(e){return e.indexOf("\\")<0&&e.indexOf("&")<0?e:e.replace(l,(function(e,t,n){return t||function(e,t){var n=0;return o(r,t)?r[t]:35===t.charCodeAt(0)&&u.test(t)&&i(n="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?a(n):e}(e,n)}))},n.isValidEntityCode=i,n.fromCodePoint=a,n.escapeHtml=function(e){return p.test(e)?e.replace(h,d):e},n.arrayReplaceAt=function(e,r,t){return[].concat(e.slice(0,r),t,e.slice(r+1))},n.isSpace=function(e){switch(e){case 9:case 32:return!0}return!1},n.isWhiteSpace=function(e){if(e>=8192&&e<=8202)return!0;switch(e){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1},n.isMdAsciiPunct=function(e){switch(e){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}},n.isPunctChar=function(e){return t.test(e)},n.escapeRE=function(e){return e.replace(m,"\\$&")},n.normalizeReference=function(e){return e=e.trim().replace(/\s+/g," "),"\u1e7e"==="\u1e9e".toLowerCase()&&(e=e.replace(/\u1e9e/g,"\xdf")),e.toLowerCase().toUpperCase()}})),E=w.unescapeAll,q=w.unescapeAll,S={parseLinkLabel:function(e,r,t){var n,s,o,i,a=-1,c=e.posMax,l=e.pos;for(e.pos=r+1,n=1;e.pos32)return i;if(41===n){if(0===s)break;s--}r++}return o===r||0!==s||(i.str=E(e.slice(o,r)),i.lines=0,i.pos=r,i.ok=!0),i},parseLinkTitle:function(e,r,t){var n,s,o=0,i=r,a={ok:!1,pos:0,lines:0,str:""};if(r>=t)return a;if(34!==(s=e.charCodeAt(r))&&39!==s&&40!==s)return a;for(r++,40===s&&(s=41);r"+z(e[r].content)+""},T.code_block=function(e,r,t,n,s){var o=e[r];return""+z(e[r].content)+"\n"},T.fence=function(e,r,t,n,s){var o,i,a,c,l,u=e[r],p=u.info?L(u.info).trim():"",h="",f="";return p&&(h=(a=p.split(/(\s+)/g))[0],f=a.slice(2).join("")),0===(o=t.highlight&&t.highlight(u.content,h,f)||z(u.content)).indexOf(""+o+"\n"):"
"+o+"
\n"},T.image=function(e,r,t,n,s){var o=e[r];return o.attrs[o.attrIndex("alt")][1]=s.renderInlineAsText(o.children,t,n),s.renderToken(e,r,t)},T.hardbreak=function(e,r,t){return t.xhtmlOut?"
\n":"
\n"},T.softbreak=function(e,r,t){return t.breaks?t.xhtmlOut?"
\n":"
\n":"\n"},T.text=function(e,r){return z(e[r].content)},T.html_block=function(e,r){return e[r].content},T.html_inline=function(e,r){return e[r].content},I.prototype.renderAttrs=function(e){var r,t,n;if(!e.attrs)return"";for(n="",r=0,t=e.attrs.length;r\n":">")},I.prototype.renderInline=function(e,r,t){for(var n,s="",o=this.rules,i=0,a=e.length;i/i.test(e)}var U=/\+-|\.\.|\?\?\?\?|!!!!|,,|--/,V=/\((c|tm|r|p)\)/i,Z=/\((c|tm|r|p)\)/gi,G={c:"\xa9",r:"\xae",p:"\xa7",tm:"\u2122"};function $(e,r){return G[r.toLowerCase()]}function H(e){var r,t,n=0;for(r=e.length-1;r>=0;r--)"text"!==(t=e[r]).type||n||(t.content=t.content.replace(Z,$)),"link_open"===t.type&&"auto"===t.info&&n--,"link_close"===t.type&&"auto"===t.info&&n++}function J(e){var r,t,n=0;for(r=e.length-1;r>=0;r--)"text"!==(t=e[r]).type||n||U.test(t.content)&&(t.content=t.content.replace(/\+-/g,"\xb1").replace(/\.{2,}/g,"\u2026").replace(/([?!])\u2026/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---(?=[^-]|$)/gm,"$1\u2014").replace(/(^|\s)--(?=\s|$)/gm,"$1\u2013").replace(/(^|[^-\s])--(?=[^-\s]|$)/gm,"$1\u2013")),"link_open"===t.type&&"auto"===t.info&&n--,"link_close"===t.type&&"auto"===t.info&&n++}var W=w.isWhiteSpace,Y=w.isPunctChar,K=w.isMdAsciiPunct,Q=/['"]/,X=/['"]/g;function ee(e,r,t){return e.substr(0,r)+t+e.substr(r+1)}function re(e,r){var t,n,s,o,i,a,c,l,u,p,h,f,d,m,g,_,b,k,v,C,y;for(v=[],t=0;t=0&&!(v[b].level<=c);b--);if(v.length=b+1,"text"===n.type){i=0,a=(s=n.content).length;e:for(;i=0)u=s.charCodeAt(o.index-1);else for(b=t-1;b>=0&&("softbreak"!==e[b].type&&"hardbreak"!==e[b].type);b--)if(e[b].content){u=e[b].content.charCodeAt(e[b].content.length-1);break}if(p=32,i=48&&u<=57&&(_=g=!1),g&&_&&(g=h,_=f),g||_){if(_)for(b=v.length-1;b>=0&&(l=v[b],!(v[b].level=0&&(t=this.attrs[r][1]),t},te.prototype.attrJoin=function(e,r){var t=this.attrIndex(e);t<0?this.attrPush([e,r]):this.attrs[t][1]=this.attrs[t][1]+" "+r};var ne=te;function se(e,r,t){this.src=e,this.env=t,this.tokens=[],this.inlineMode=!1,this.md=r}se.prototype.Token=ne;var oe=se,ie=[["normalize",function(e){var r;r=(r=e.src.replace(N,"\n")).replace(O,"\ufffd"),e.src=r}],["block",function(e){var r;e.inlineMode?((r=new e.Token("inline","",0)).content=e.src,r.map=[0,1],r.children=[],e.tokens.push(r)):e.md.block.parse(e.src,e.md,e.env,e.tokens)}],["inline",function(e){var r,t,n,s=e.tokens;for(t=0,n=s.length;t=0;r--)if("link_close"!==(i=s[r]).type){if("html_inline"===i.type&&(b=i.content,/^\s]/i.test(b)&&f>0&&f--,j(i.content)&&f++),!(f>0)&&"text"===i.type&&e.md.linkify.test(i.content)){for(l=i.content,_=e.md.linkify.match(l),a=[],h=i.level,p=0,c=0;c<_.length;c++)d=_[c].url,m=e.md.normalizeLink(d),e.md.validateLink(m)&&(g=_[c].text,g=_[c].schema?"mailto:"!==_[c].schema||/^mailto:/i.test(g)?e.md.normalizeLinkText(g):e.md.normalizeLinkText("mailto:"+g).replace(/^mailto:/,""):e.md.normalizeLinkText("http://"+g).replace(/^http:\/\//,""),(u=_[c].index)>p&&((o=new e.Token("text","",0)).content=l.slice(p,u),o.level=h,a.push(o)),(o=new e.Token("link_open","a",1)).attrs=[["href",m]],o.level=h++,o.markup="linkify",o.info="auto",a.push(o),(o=new e.Token("text","",0)).content=g,o.level=h,a.push(o),(o=new e.Token("link_close","a",-1)).level=--h,o.markup="linkify",o.info="auto",a.push(o),p=_[c].lastIndex);p=0;r--)"inline"===e.tokens[r].type&&(V.test(e.tokens[r].content)&&H(e.tokens[r].children),U.test(e.tokens[r].content)&&J(e.tokens[r].children))}],["smartquotes",function(e){var r;if(e.md.options.typographer)for(r=e.tokens.length-1;r>=0;r--)"inline"===e.tokens[r].type&&Q.test(e.tokens[r].content)&&re(e.tokens[r].children,e)}]];function ae(){this.ruler=new B;for(var e=0;e=o)return-1;if((t=e.src.charCodeAt(s++))<48||t>57)return-1;for(;;){if(s>=o)return-1;if(!((t=e.src.charCodeAt(s++))>=48&&t<=57)){if(41===t||46===t)break;return-1}if(s-n>=10)return-1}return s`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*\\/?>",ve="<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>",Ce={HTML_TAG_RE:new RegExp("^(?:"+ke+"|"+ve+"|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|<[?][\\s\\S]*?[?]>|]*>|)"),HTML_OPEN_CLOSE_TAG_RE:new RegExp("^(?:"+ke+"|"+ve+")")},ye=Ce.HTML_OPEN_CLOSE_TAG_RE,Ae=[[/^<(script|pre|style|textarea)(?=(\s|>|$))/i,/<\/(script|pre|style|textarea)>/i,!0],[/^/,!0],[/^<\?/,/\?>/,!0],[/^/,!0],[/^/,!0],[new RegExp("^|$))","i"),/^$/,!0],[new RegExp(ye.source+"\\s*$"),/^$/,!1]],xe=w.isSpace,De=w.isSpace;function we(e,r,t,n){var s,o,i,a,c,l,u,p;for(this.src=e,this.md=r,this.env=t,this.tokens=n,this.bMarks=[],this.eMarks=[],this.tShift=[],this.sCount=[],this.bsCount=[],this.blkIndent=0,this.line=0,this.lineMax=0,this.tight=!1,this.ddIndent=-1,this.listIndent=-1,this.parentType="root",this.level=0,this.result="",p=!1,i=a=l=u=0,c=(o=this.src).length;a0&&this.level++,this.tokens.push(n),n},we.prototype.isEmpty=function(e){return this.bMarks[e]+this.tShift[e]>=this.eMarks[e]},we.prototype.skipEmptyLines=function(e){for(var r=this.lineMax;er;)if(!De(this.src.charCodeAt(--e)))return e+1;return e},we.prototype.skipChars=function(e,r){for(var t=this.src.length;et;)if(r!==this.src.charCodeAt(--e))return e+1;return e},we.prototype.getLines=function(e,r,t,n){var s,o,i,a,c,l,u,p=e;if(e>=r)return"";for(l=new Array(r-e),s=0;pt?new Array(o-t+1).join(" ")+this.src.slice(a,c):this.src.slice(a,c)}return l.join("")},we.prototype.Token=ne;var Ee=we,qe=[["table",function(e,r,t,n){var s,o,i,a,c,l,u,p,h,f,d,m,g,_,b,k,v,C;if(r+2>t)return!1;if(l=r+1,e.sCount[l]=4)return!1;if((i=e.bMarks[l]+e.tShift[l])>=e.eMarks[l])return!1;if(124!==(v=e.src.charCodeAt(i++))&&45!==v&&58!==v)return!1;if(i>=e.eMarks[l])return!1;if(124!==(C=e.src.charCodeAt(i++))&&45!==C&&58!==C&&!le(C))return!1;if(45===v&&le(C))return!1;for(;i=4)return!1;if((u=pe(o)).length&&""===u[0]&&u.shift(),u.length&&""===u[u.length-1]&&u.pop(),0===(p=u.length)||p!==f.length)return!1;if(n)return!0;for(_=e.parentType,e.parentType="table",k=e.md.block.ruler.getRules("blockquote"),(h=e.push("table_open","table",1)).map=m=[r,0],(h=e.push("thead_open","thead",1)).map=[r,r+1],(h=e.push("tr_open","tr",1)).map=[r,r+1],a=0;a=4)break;for((u=pe(o)).length&&""===u[0]&&u.shift(),u.length&&""===u[u.length-1]&&u.pop(),l===r+2&&((h=e.push("tbody_open","tbody",1)).map=g=[r+2,0]),(h=e.push("tr_open","tr",1)).map=[l,l+1],a=0;a=4))break;s=++n}return e.line=s,(o=e.push("code_block","code",0)).content=e.getLines(r,s,4+e.blkIndent,!1)+"\n",o.map=[r,e.line],!0}],["fence",function(e,r,t,n){var s,o,i,a,c,l,u,p=!1,h=e.bMarks[r]+e.tShift[r],f=e.eMarks[r];if(e.sCount[r]-e.blkIndent>=4)return!1;if(h+3>f)return!1;if(126!==(s=e.src.charCodeAt(h))&&96!==s)return!1;if(c=h,(o=(h=e.skipChars(h,s))-c)<3)return!1;if(u=e.src.slice(c,h),i=e.src.slice(h,f),96===s&&i.indexOf(String.fromCharCode(s))>=0)return!1;if(n)return!0;for(a=r;!(++a>=t)&&!((h=c=e.bMarks[a]+e.tShift[a])<(f=e.eMarks[a])&&e.sCount[a]=4||(h=e.skipChars(h,s))-c=4)return!1;if(62!==e.src.charCodeAt(D++))return!1;if(n)return!0;for(a=h=e.sCount[r]+1,32===e.src.charCodeAt(D)?(D++,a++,h++,s=!1,k=!0):9===e.src.charCodeAt(D)?(k=!0,(e.bsCount[r]+h)%4==3?(D++,a++,h++,s=!1):s=!0):k=!1,f=[e.bMarks[r]],e.bMarks[r]=D;D=w,_=[e.sCount[r]],e.sCount[r]=h-a,b=[e.tShift[r]],e.tShift[r]=D-e.bMarks[r],C=e.md.block.ruler.getRules("blockquote"),g=e.parentType,e.parentType="blockquote",p=r+1;p=(w=e.eMarks[p])));p++)if(62!==e.src.charCodeAt(D++)||A){if(l)break;for(v=!1,i=0,c=C.length;i=w,d.push(e.bsCount[p]),e.bsCount[p]=e.sCount[p]+1+(k?1:0),_.push(e.sCount[p]),e.sCount[p]=h-a,b.push(e.tShift[p]),e.tShift[p]=D-e.bMarks[p]}for(m=e.blkIndent,e.blkIndent=0,(y=e.push("blockquote_open","blockquote",1)).markup=">",y.map=u=[r,0],e.md.block.tokenize(e,r,p),(y=e.push("blockquote_close","blockquote",-1)).markup=">",e.lineMax=x,e.parentType=g,u[1]=e.line,i=0;i=4)return!1;if(42!==(s=e.src.charCodeAt(c++))&&45!==s&&95!==s)return!1;for(o=1;c=4)return!1;if(e.listIndent>=0&&e.sCount[r]-e.listIndent>=4&&e.sCount[r]=e.blkIndent&&(z=!0),(w=ge(e,r))>=0){if(u=!0,q=e.bMarks[r]+e.tShift[r],g=Number(e.src.slice(q,w-1)),z&&1!==g)return!1}else{if(!((w=me(e,r))>=0))return!1;u=!1}if(z&&e.skipSpaces(w)>=e.eMarks[r])return!1;if(m=e.src.charCodeAt(w-1),n)return!0;for(d=e.tokens.length,u?(L=e.push("ordered_list_open","ol",1),1!==g&&(L.attrs=[["start",g]])):L=e.push("bullet_list_open","ul",1),L.map=f=[r,0],L.markup=String.fromCharCode(m),b=r,E=!1,F=e.md.block.ruler.getRules("list"),C=e.parentType,e.parentType="list";b=_?1:k-l)>4&&(c=1),a=l+c,(L=e.push("list_item_open","li",1)).markup=String.fromCharCode(m),L.map=p=[r,0],u&&(L.info=e.src.slice(q,w-1)),x=e.tight,A=e.tShift[r],y=e.sCount[r],v=e.listIndent,e.listIndent=e.blkIndent,e.blkIndent=a,e.tight=!0,e.tShift[r]=o-e.bMarks[r],e.sCount[r]=k,o>=_&&e.isEmpty(r+1)?e.line=Math.min(e.line+2,t):e.md.block.tokenize(e,r,t,!0),e.tight&&!E||(T=!1),E=e.line-r>1&&e.isEmpty(e.line-1),e.blkIndent=e.listIndent,e.listIndent=v,e.tShift[r]=A,e.sCount[r]=y,e.tight=x,(L=e.push("list_item_close","li",-1)).markup=String.fromCharCode(m),b=r=e.line,p[1]=b,o=e.bMarks[r],b>=t)break;if(e.sCount[b]=4)break;for(S=!1,i=0,h=F.length;i=4)return!1;if(91!==e.src.charCodeAt(C))return!1;for(;++C3||e.sCount[A]<0)){for(_=!1,l=0,u=b.length;l=4)return!1;if(!e.md.options.html)return!1;if(60!==e.src.charCodeAt(c))return!1;for(a=e.src.slice(c,l),s=0;s=4)return!1;if(35!==(s=e.src.charCodeAt(c))||c>=l)return!1;for(o=1,s=e.src.charCodeAt(++c);35===s&&c6||cc&&xe(e.src.charCodeAt(i-1))&&(l=i),e.line=r+1,(a=e.push("heading_open","h"+String(o),1)).markup="########".slice(0,o),a.map=[r,e.line],(a=e.push("inline","",0)).content=e.src.slice(c,l).trim(),a.map=[r,e.line],a.children=[],(a=e.push("heading_close","h"+String(o),-1)).markup="########".slice(0,o)),!0)},["paragraph","reference","blockquote"]],["lheading",function(e,r,t){var n,s,o,i,a,c,l,u,p,h,f=r+1,d=e.md.block.ruler.getRules("paragraph");if(e.sCount[r]-e.blkIndent>=4)return!1;for(h=e.parentType,e.parentType="paragraph";f3)){if(e.sCount[f]>=e.blkIndent&&(c=e.bMarks[f]+e.tShift[f])<(l=e.eMarks[f])&&(45===(p=e.src.charCodeAt(c))||61===p)&&(c=e.skipChars(c,p),(c=e.skipSpaces(c))>=l)){u=61===p?1:2;break}if(!(e.sCount[f]<0)){for(s=!1,o=0,i=d.length;o3||e.sCount[c]<0)){for(n=!1,s=0,o=l.length;s=t))&&!(e.sCount[i]=c){e.line=t;break}for(n=0;n?@[]^_`{|}~-".split("").forEach((function(e){Ie[e.charCodeAt(0)]=1}));function Re(e,r){var t,n,s,o,i,a=[],c=r.length;for(t=0;t=0;t--)95!==(n=r[t]).marker&&42!==n.marker||-1!==n.end&&(s=r[n.end],a=t>0&&r[t-1].end===n.end+1&&r[t-1].token===n.token-1&&r[n.end+1].token===s.token+1&&r[t-1].marker===n.marker,i=String.fromCharCode(n.marker),(o=e.tokens[n.token]).type=a?"strong_open":"em_open",o.tag=a?"strong":"em",o.nesting=1,o.markup=a?i+i:i,o.content="",(o=e.tokens[s.token]).type=a?"strong_close":"em_close",o.tag=a?"strong":"em",o.nesting=-1,o.markup=a?i+i:i,o.content="",a&&(e.tokens[r[t-1].token].content="",e.tokens[r[n.end+1].token].content="",t--))}var Oe={tokenize:function(e,r){var t,n,s=e.pos,o=e.src.charCodeAt(s);if(r)return!1;if(95!==o&&42!==o)return!1;for(n=e.scanDelims(e.pos,42===o),t=0;t\x00-\x20]*)$/,$e=Ce.HTML_TAG_RE;var He=w.has,Je=w.isValidEntityCode,We=w.fromCodePoint,Ye=/^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i,Ke=/^&([a-z][a-z0-9]{1,31});/i;function Qe(e,r){var t,n,s,o,i,a,c,l,u={},p=r.length;for(t=0;ti;n-=o.jump+1)if((o=r[n]).marker===s.marker&&o.open&&o.end<0&&(c=!1,(o.close||s.open)&&(o.length+s.length)%3==0&&(o.length%3==0&&s.length%3==0||(c=!0)),!c)){l=n>0&&!r[n-1].open?r[n-1].jump+1:0,s.jump=t-n+l,s.open=!1,o.end=t,o.jump=l,o.close=!1,a=-1;break}-1!==a&&(u[s.marker][(s.open?3:0)+(s.length||0)%3]=a)}}var Xe=w.isWhiteSpace,er=w.isPunctChar,rr=w.isMdAsciiPunct;function tr(e,r,t,n){this.src=e,this.env=t,this.md=r,this.tokens=n,this.tokens_meta=Array(n.length),this.pos=0,this.posMax=this.src.length,this.level=0,this.pending="",this.pendingLevel=0,this.cache={},this.delimiters=[],this._prev_delimiters=[],this.backticks={},this.backticksScanned=!1}tr.prototype.pushPending=function(){var e=new ne("text","",0);return e.content=this.pending,e.level=this.pendingLevel,this.tokens.push(e),this.pending="",e},tr.prototype.push=function(e,r,t){this.pending&&this.pushPending();var n=new ne(e,r,t),s=null;return t<0&&(this.level--,this.delimiters=this._prev_delimiters.pop()),n.level=this.level,t>0&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],s={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(n),this.tokens_meta.push(s),n},tr.prototype.scanDelims=function(e,r){var t,n,s,o,i,a,c,l,u,p=e,h=!0,f=!0,d=this.posMax,m=this.src.charCodeAt(e);for(t=e>0?this.src.charCodeAt(e-1):32;p=0&&32===e.pending.charCodeAt(t)?t>=1&&32===e.pending.charCodeAt(t-1)?(e.pending=e.pending.replace(/ +$/,""),e.push("hardbreak","br",0)):(e.pending=e.pending.slice(0,-1),e.push("softbreak","br",0)):e.push("softbreak","br",0)),s++;s=f)return!1;if(d=a,(c=e.md.helpers.parseLinkDestination(e.src,a,e.posMax)).ok){for(u=e.md.normalizeLink(c.str),e.md.validateLink(u)?a=c.pos:u="",d=a;a=f||41!==e.src.charCodeAt(a))&&(m=!0),a++}if(m){if(void 0===e.env.references)return!1;if(a=0?s=e.src.slice(d,a++):a=o+1):a=o+1,s||(s=e.src.slice(i,o)),!(l=e.env.references[Pe(s)]))return e.pos=h,!1;u=l.href,p=l.title}return r||(e.pos=i,e.posMax=o,e.push("link_open","a",1).attrs=t=[["href",u]],p&&t.push(["title",p]),e.md.inline.tokenize(e),e.push("link_close","a",-1)),e.pos=a,e.posMax=f,!0}],["image",function(e,r){var t,n,s,o,i,a,c,l,u,p,h,f,d,m="",g=e.pos,_=e.posMax;if(33!==e.src.charCodeAt(e.pos))return!1;if(91!==e.src.charCodeAt(e.pos+1))return!1;if(a=e.pos+2,(i=e.md.helpers.parseLinkLabel(e,e.pos+1,!1))<0)return!1;if((c=i+1)<_&&40===e.src.charCodeAt(c)){for(c++;c<_&&(n=e.src.charCodeAt(c),Ve(n)||10===n);c++);if(c>=_)return!1;for(d=c,(u=e.md.helpers.parseLinkDestination(e.src,c,e.posMax)).ok&&(m=e.md.normalizeLink(u.str),e.md.validateLink(m)?c=u.pos:m=""),d=c;c<_&&(n=e.src.charCodeAt(c),Ve(n)||10===n);c++);if(u=e.md.helpers.parseLinkTitle(e.src,c,e.posMax),c<_&&d!==c&&u.ok)for(p=u.str,c=u.pos;c<_&&(n=e.src.charCodeAt(c),Ve(n)||10===n);c++);else p="";if(c>=_||41!==e.src.charCodeAt(c))return e.pos=g,!1;c++}else{if(void 0===e.env.references)return!1;if(c<_&&91===e.src.charCodeAt(c)?(d=c+1,(c=e.md.helpers.parseLinkLabel(e,c))>=0?o=e.src.slice(d,c++):c=i+1):c=i+1,o||(o=e.src.slice(a,i)),!(l=e.env.references[Ue(o)]))return e.pos=g,!1;m=l.href,p=l.title}return r||(s=e.src.slice(a,i),e.md.inline.parse(s,e.md,e.env,f=[]),(h=e.push("image","img",0)).attrs=t=[["src",m],["alt",""]],h.children=f,h.content=s,p&&t.push(["title",p])),e.pos=c,e.posMax=_,!0}],["autolink",function(e,r){var t,n,s,o,i,a,c=e.pos;if(60!==e.src.charCodeAt(c))return!1;for(i=e.pos,a=e.posMax;;){if(++c>=a)return!1;if(60===(o=e.src.charCodeAt(c)))return!1;if(62===o)break}return t=e.src.slice(i+1,c),Ge.test(t)?(n=e.md.normalizeLink(t),!!e.md.validateLink(n)&&(r||((s=e.push("link_open","a",1)).attrs=[["href",n]],s.markup="autolink",s.info="auto",(s=e.push("text","",0)).content=e.md.normalizeLinkText(t),(s=e.push("link_close","a",-1)).markup="autolink",s.info="auto"),e.pos+=t.length+2,!0)):!!Ze.test(t)&&(n=e.md.normalizeLink("mailto:"+t),!!e.md.validateLink(n)&&(r||((s=e.push("link_open","a",1)).attrs=[["href",n]],s.markup="autolink",s.info="auto",(s=e.push("text","",0)).content=e.md.normalizeLinkText(t),(s=e.push("link_close","a",-1)).markup="autolink",s.info="auto"),e.pos+=t.length+2,!0))}],["html_inline",function(e,r){var t,n,s,o=e.pos;return!!e.md.options.html&&(s=e.posMax,!(60!==e.src.charCodeAt(o)||o+2>=s)&&(!(33!==(t=e.src.charCodeAt(o+1))&&63!==t&&47!==t&&!function(e){var r=32|e;return r>=97&&r<=122}(t))&&(!!(n=e.src.slice(o).match($e))&&(r||(e.push("html_inline","",0).content=e.src.slice(o,o+n[0].length)),e.pos+=n[0].length,!0))))}],["entity",function(e,t){var n,s,o=e.pos,i=e.posMax;if(38!==e.src.charCodeAt(o))return!1;if(o+10&&n++,"text"===s[r].type&&r+1=o)break}else e.pending+=e.src[e.pos++]}e.pending&&e.pushPending()},ir.prototype.parse=function(e,r,t,n){var s,o,i,a=new this.State(e,r,t,n);for(this.tokenize(a),i=(o=this.ruler2.getRules("")).length,s=0;s=3&&":"===e[r-3]||r>=3&&"/"===e[r-3]?0:n.match(t.re.no_http)[0].length:0}},"mailto:":{validate:function(e,r,t){var n=e.slice(r);return t.re.mailto||(t.re.mailto=new RegExp("^"+t.re.src_email_name+"@"+t.re.src_host_strict,"i")),t.re.mailto.test(n)?n.match(t.re.mailto)[0].length:0}}},dr="biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|\u0440\u0444".split("|");function mr(e){var r=e.re=function(e){var r={};return r.src_Any=y.source,r.src_Cc=A.source,r.src_Z=x.source,r.src_P=t.source,r.src_ZPCc=[r.src_Z,r.src_P,r.src_Cc].join("|"),r.src_ZCc=[r.src_Z,r.src_Cc].join("|"),r.src_pseudo_letter="(?:(?![><\uff5c]|"+r.src_ZPCc+")"+r.src_Any+")",r.src_ip4="(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",r.src_auth="(?:(?:(?!"+r.src_ZCc+"|[@/\\[\\]()]).)+@)?",r.src_port="(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?",r.src_host_terminator="(?=$|[><\uff5c]|"+r.src_ZPCc+")(?!-|_|:\\d|\\.-|\\.(?!$|"+r.src_ZPCc+"))",r.src_path="(?:[/?#](?:(?!"+r.src_ZCc+"|[><\uff5c]|[()[\\]{}.,\"'?!\\-]).|\\[(?:(?!"+r.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+r.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+r.src_ZCc+'|[}]).)*\\}|\\"(?:(?!'+r.src_ZCc+'|["]).)+\\"|\\\'(?:(?!'+r.src_ZCc+"|[']).)+\\'|\\'(?="+r.src_pseudo_letter+"|[-]).|\\.{2,}[a-zA-Z0-9%/&]|\\.(?!"+r.src_ZCc+"|[.]).|"+(e&&e["---"]?"\\-(?!--(?:[^-]|$))(?:-*)|":"\\-+|")+"\\,(?!"+r.src_ZCc+").|\\!+(?!"+r.src_ZCc+"|[!]).|\\?(?!"+r.src_ZCc+"|[?]).)+|\\/)?",r.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',r.src_xn="xn--[a-z0-9\\-]{1,59}",r.src_domain_root="(?:"+r.src_xn+"|"+r.src_pseudo_letter+"{1,63})",r.src_domain="(?:"+r.src_xn+"|(?:"+r.src_pseudo_letter+")|(?:"+r.src_pseudo_letter+"(?:-|"+r.src_pseudo_letter+"){0,61}"+r.src_pseudo_letter+"))",r.src_host="(?:(?:(?:(?:"+r.src_domain+")\\.)*"+r.src_domain+"))",r.tpl_host_fuzzy="(?:"+r.src_ip4+"|(?:(?:(?:"+r.src_domain+")\\.)+(?:%TLDS%)))",r.tpl_host_no_ip_fuzzy="(?:(?:(?:"+r.src_domain+")\\.)+(?:%TLDS%))",r.src_host_strict=r.src_host+r.src_host_terminator,r.tpl_host_fuzzy_strict=r.tpl_host_fuzzy+r.src_host_terminator,r.src_host_port_strict=r.src_host+r.src_port+r.src_host_terminator,r.tpl_host_port_fuzzy_strict=r.tpl_host_fuzzy+r.src_port+r.src_host_terminator,r.tpl_host_port_no_ip_fuzzy_strict=r.tpl_host_no_ip_fuzzy+r.src_port+r.src_host_terminator,r.tpl_host_fuzzy_test="localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:"+r.src_ZPCc+"|>|$))",r.tpl_email_fuzzy='(^|[><\uff5c]|"|\\(|'+r.src_ZCc+")("+r.src_email_name+"@"+r.tpl_host_fuzzy_strict+")",r.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+r.src_ZPCc+"))((?![$+<=>^`|\uff5c])"+r.tpl_host_port_fuzzy_strict+r.src_path+")",r.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+r.src_ZPCc+"))((?![$+<=>^`|\uff5c])"+r.tpl_host_port_no_ip_fuzzy_strict+r.src_path+")",r}(e.__opts__),n=e.__tlds__.slice();function s(e){return e.replace("%TLDS%",r.src_tlds)}e.onCompile(),e.__tlds_replaced__||n.push("a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]"),n.push(r.src_xn),r.src_tlds=n.join("|"),r.email_fuzzy=RegExp(s(r.tpl_email_fuzzy),"i"),r.link_fuzzy=RegExp(s(r.tpl_link_fuzzy),"i"),r.link_no_ip_fuzzy=RegExp(s(r.tpl_link_no_ip_fuzzy),"i"),r.host_fuzzy_test=RegExp(s(r.tpl_host_fuzzy_test),"i");var o=[];function i(e,r){throw new Error('(LinkifyIt) Invalid schema "'+e+'": '+r)}e.__compiled__={},Object.keys(e.__schemas__).forEach((function(r){var t=e.__schemas__[r];if(null!==t){var n={validate:null,link:null};if(e.__compiled__[r]=n,"[object Object]"===lr(t))return!function(e){return"[object RegExp]"===lr(e)}(t.validate)?ur(t.validate)?n.validate=t.validate:i(r,t):n.validate=function(e){return function(r,t){var n=r.slice(t);return e.test(n)?n.match(e)[0].length:0}}(t.validate),void(ur(t.normalize)?n.normalize=t.normalize:t.normalize?i(r,t):n.normalize=function(e,r){r.normalize(e)});!function(e){return"[object String]"===lr(e)}(t)?i(r,t):o.push(r)}})),o.forEach((function(r){e.__compiled__[e.__schemas__[r]]&&(e.__compiled__[r].validate=e.__compiled__[e.__schemas__[r]].validate,e.__compiled__[r].normalize=e.__compiled__[e.__schemas__[r]].normalize)})),e.__compiled__[""]={validate:null,normalize:function(e,r){r.normalize(e)}};var a=Object.keys(e.__compiled__).filter((function(r){return r.length>0&&e.__compiled__[r]})).map(pr).join("|");e.re.schema_test=RegExp("(^|(?!_)(?:[><\uff5c]|"+r.src_ZPCc+"))("+a+")","i"),e.re.schema_search=RegExp("(^|(?!_)(?:[><\uff5c]|"+r.src_ZPCc+"))("+a+")","ig"),e.re.pretest=RegExp("("+e.re.schema_test.source+")|("+e.re.host_fuzzy_test.source+")|@","i"),function(e){e.__index__=-1,e.__text_cache__=""}(e)}function gr(e,r){var t=e.__index__,n=e.__last_index__,s=e.__text_cache__.slice(t,n);this.schema=e.__schema__.toLowerCase(),this.index=t+r,this.lastIndex=n+r,this.raw=s,this.text=s,this.url=s}function _r(e,r){var t=new gr(e,r);return e.__compiled__[t.schema].normalize(t,e),t}function br(e,r){if(!(this instanceof br))return new br(e,r);var t;r||(t=e,Object.keys(t||{}).reduce((function(e,r){return e||hr.hasOwnProperty(r)}),!1)&&(r=e,e={})),this.__opts__=cr({},hr,r),this.__index__=-1,this.__last_index__=-1,this.__schema__="",this.__text_cache__="",this.__schemas__=cr({},fr,e),this.__compiled__={},this.__tlds__=dr,this.__tlds_replaced__=!1,this.re={},mr(this)}br.prototype.add=function(e,r){return this.__schemas__[e]=r,mr(this),this},br.prototype.set=function(e){return this.__opts__=cr(this.__opts__,e),this},br.prototype.test=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return!1;var r,t,n,s,o,i,a,c;if(this.re.schema_test.test(e))for((a=this.re.schema_search).lastIndex=0;null!==(r=a.exec(e));)if(s=this.testSchemaAt(e,r[2],a.lastIndex)){this.__schema__=r[2],this.__index__=r.index+r[1].length,this.__last_index__=r.index+r[0].length+s;break}return this.__opts__.fuzzyLink&&this.__compiled__["http:"]&&(c=e.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||c=0&&null!==(n=e.match(this.re.email_fuzzy))&&(o=n.index+n[1].length,i=n.index+n[0].length,(this.__index__<0||othis.__last_index__)&&(this.__schema__="mailto:",this.__index__=o,this.__last_index__=i)),this.__index__>=0},br.prototype.pretest=function(e){return this.re.pretest.test(e)},br.prototype.testSchemaAt=function(e,r,t){return this.__compiled__[r.toLowerCase()]?this.__compiled__[r.toLowerCase()].validate(e,t,this):0},br.prototype.match=function(e){var r=0,t=[];this.__index__>=0&&this.__text_cache__===e&&(t.push(_r(this,r)),r=this.__last_index__);for(var n=r?e.slice(r):e;this.test(n);)t.push(_r(this,r)),n=n.slice(this.__last_index__),r+=this.__last_index__;return t.length?t:null},br.prototype.tlds=function(e,r){return e=Array.isArray(e)?e:[e],r?(this.__tlds__=this.__tlds__.concat(e).sort().filter((function(e,r,t){return e!==t[r-1]})).reverse(),mr(this),this):(this.__tlds__=e.slice(),this.__tlds_replaced__=!0,mr(this),this)},br.prototype.normalize=function(e){e.schema||(e.url="http://"+e.url),"mailto:"!==e.schema||/^mailto:/i.test(e.url)||(e.url="mailto:"+e.url)},br.prototype.onCompile=function(){};var kr=br,vr=2147483647,Cr=36,yr=/^xn--/,Ar=/[^\x20-\x7E]/,xr=/[\x2E\u3002\uFF0E\uFF61]/g,Dr={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},wr=Math.floor,Er=String.fromCharCode; +/** + * Punycode v1.4.1 + * @license MIT + * @source https://mths.be/punycode + */ +function qr(e){throw new RangeError(Dr[e])}function Sr(e,r){for(var t=e.length,n=[];t--;)n[t]=r(e[t]);return n}function Fr(e,r){var t=e.split("@"),n="";return t.length>1&&(n=t[0]+"@",e=t[1]),n+Sr((e=e.replace(xr,".")).split("."),r).join(".")}function Lr(e){for(var r,t,n=[],s=0,o=e.length;s=55296&&r<=56319&&s65535&&(r+=Er((e-=65536)>>>10&1023|55296),e=56320|1023&e),r+=Er(e)})).join("")}function Tr(e,r){return e+22+75*(e<26)-((0!=r)<<5)}function Ir(e,r,t){var n=0;for(e=t?wr(e/700):e>>1,e+=wr(e/r);e>455;n+=Cr)e=wr(e/35);return wr(n+36*e/(e+38))}function Mr(e){var r,t,n,s,o,i,a,c,l,u,p,h=[],f=e.length,d=0,m=128,g=72;for((t=e.lastIndexOf("-"))<0&&(t=0),n=0;n=128&&qr("not-basic"),h.push(e.charCodeAt(n));for(s=t>0?t+1:0;s=f&&qr("invalid-input"),((c=(p=e.charCodeAt(s++))-48<10?p-22:p-65<26?p-65:p-97<26?p-97:Cr)>=Cr||c>wr((vr-d)/i))&&qr("overflow"),d+=c*i,!(c<(l=a<=g?1:a>=g+26?26:a-g));a+=Cr)i>wr(vr/(u=Cr-l))&&qr("overflow"),i*=u;g=Ir(d-o,r=h.length+1,0==o),wr(d/r)>vr-m&&qr("overflow"),m+=wr(d/r),d%=r,h.splice(d++,0,m)}return zr(h)}function Rr(e){var r,t,n,s,o,i,a,c,l,u,p,h,f,d,m,g=[];for(h=(e=Lr(e)).length,r=128,t=0,o=72,i=0;i=r&&pwr((vr-t)/(f=n+1))&&qr("overflow"),t+=(a-r)*f,r=a,i=0;ivr&&qr("overflow"),p==r){for(c=t,l=Cr;!(c<(u=l<=o?1:l>=o+26?26:l-o));l+=Cr)m=c-u,d=Cr-u,g.push(Er(Tr(u+m%d,0))),c=wr(m/d);g.push(Er(Tr(c,0))),o=Ir(t,f,n==s),t=0,++n}++t,++r}return g.join("")}function Br(e){return Fr(e,(function(e){return yr.test(e)?Mr(e.slice(4).toLowerCase()):e}))}function Nr(e){return Fr(e,(function(e){return Ar.test(e)?"xn--"+Rr(e):e}))}var Or="1.4.1",Pr={decode:Lr,encode:zr},jr={version:Or,ucs2:Pr,toASCII:Nr,toUnicode:Br,encode:Rr,decode:Mr},Ur=e(Object.freeze({__proto__:null,decode:Mr,encode:Rr,toUnicode:Br,toASCII:Nr,version:Or,ucs2:Pr,default:jr})),Vr={default:{options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"\u201c\u201d\u2018\u2019",highlight:null,maxNesting:100},components:{core:{},block:{},inline:{}}},zero:{options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"\u201c\u201d\u2018\u2019",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline"]},block:{rules:["paragraph"]},inline:{rules:["text"],rules2:["balance_pairs","text_collapse"]}}},commonmark:{options:{html:!0,xhtmlOut:!0,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"\u201c\u201d\u2018\u2019",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline"]},block:{rules:["blockquote","code","fence","heading","hr","html_block","lheading","list","reference","paragraph"]},inline:{rules:["autolink","backticks","emphasis","entity","escape","html_inline","image","link","newline","text"],rules2:["balance_pairs","emphasis","text_collapse"]}}}},Zr=/^(vbscript|javascript|file|data):/,Gr=/^data:image\/(gif|png|jpeg|webp);/;function $r(e){var r=e.trim().toLowerCase();return!Zr.test(r)||!!Gr.test(r)}var Hr=["http:","https:","mailto:"];function Jr(e){var r=C.parse(e,!0);if(r.hostname&&(!r.protocol||Hr.indexOf(r.protocol)>=0))try{r.hostname=Ur.toASCII(r.hostname)}catch(e){}return C.encode(C.format(r))}function Wr(e){var r=C.parse(e,!0);if(r.hostname&&(!r.protocol||Hr.indexOf(r.protocol)>=0))try{r.hostname=Ur.toUnicode(r.hostname)}catch(e){}return C.decode(C.format(r),C.decode.defaultChars+"%")}function Yr(e,r){if(!(this instanceof Yr))return new Yr(e,r);r||w.isString(e)||(r=e||{},e="default"),this.inline=new ar,this.block=new Fe,this.core=new ce,this.renderer=new M,this.linkify=new kr,this.validateLink=$r,this.normalizeLink=Jr,this.normalizeLinkText=Wr,this.utils=w,this.helpers=w.assign({},S),this.options={},this.configure(e),r&&this.set(r)}return Yr.prototype.set=function(e){return w.assign(this.options,e),this},Yr.prototype.configure=function(e){var r,t=this;if(w.isString(e)&&!(e=Vr[r=e]))throw new Error('Wrong `markdown-it` preset "'+r+'", check name');if(!e)throw new Error("Wrong `markdown-it` preset, can't be empty");return e.options&&t.set(e.options),e.components&&Object.keys(e.components).forEach((function(r){e.components[r].rules&&t[r].ruler.enableOnly(e.components[r].rules),e.components[r].rules2&&t[r].ruler2.enableOnly(e.components[r].rules2)})),this},Yr.prototype.enable=function(e,r){var t=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(r){t=t.concat(this[r].ruler.enable(e,!0))}),this),t=t.concat(this.inline.ruler2.enable(e,!0));var n=e.filter((function(e){return t.indexOf(e)<0}));if(n.length&&!r)throw new Error("MarkdownIt. Failed to enable unknown rule(s): "+n);return this},Yr.prototype.disable=function(e,r){var t=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(r){t=t.concat(this[r].ruler.disable(e,!0))}),this),t=t.concat(this.inline.ruler2.disable(e,!0));var n=e.filter((function(e){return t.indexOf(e)<0}));if(n.length&&!r)throw new Error("MarkdownIt. Failed to disable unknown rule(s): "+n);return this},Yr.prototype.use=function(e){var r=[this].concat(Array.prototype.slice.call(arguments,1));return e.apply(e,r),this},Yr.prototype.parse=function(e,r){if("string"!=typeof e)throw new Error("Input data should be a String");var t=new this.core.State(e,this,r);return this.core.process(t),t.tokens},Yr.prototype.render=function(e,r){return r=r||{},this.renderer.render(this.parse(e,r),this.options,r)},Yr.prototype.parseInline=function(e,r){var t=new this.core.State(e,this,r);return t.inlineMode=!0,this.core.process(t),t.tokens},Yr.prototype.renderInline=function(e,r){return r=r||{},this.renderer.render(this.parseInline(e,r),this.options,r)},Yr})); \ No newline at end of file diff --git a/src/dep/mime-db.json b/src/dep/mime-db.json new file mode 100644 index 0000000..29786a7 --- /dev/null +++ b/src/dep/mime-db.json @@ -0,0 +1,8453 @@ +{ + "application/1d-interleaved-parityfec": { + "source": "iana" + }, + "application/3gpdash-qoe-report+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/3gpp-ims+xml": { + "source": "iana", + "compressible": true + }, + "application/3gpphal+json": { + "source": "iana", + "compressible": true + }, + "application/3gpphalforms+json": { + "source": "iana", + "compressible": true + }, + "application/a2l": { + "source": "iana" + }, + "application/ace+cbor": { + "source": "iana" + }, + "application/activemessage": { + "source": "iana" + }, + "application/activity+json": { + "source": "iana", + "compressible": true + }, + "application/alto-costmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-costmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/alto-directory+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcost+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointcostparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointprop+json": { + "source": "iana", + "compressible": true + }, + "application/alto-endpointpropparams+json": { + "source": "iana", + "compressible": true + }, + "application/alto-error+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmap+json": { + "source": "iana", + "compressible": true + }, + "application/alto-networkmapfilter+json": { + "source": "iana", + "compressible": true + }, + "application/alto-updatestreamcontrol+json": { + "source": "iana", + "compressible": true + }, + "application/alto-updatestreamparams+json": { + "source": "iana", + "compressible": true + }, + "application/aml": { + "source": "iana" + }, + "application/andrew-inset": { + "source": "iana", + "extensions": ["ez"] + }, + "application/applefile": { + "source": "iana" + }, + "application/applixware": { + "source": "apache", + "extensions": ["aw"] + }, + "application/at+jwt": { + "source": "iana" + }, + "application/atf": { + "source": "iana" + }, + "application/atfx": { + "source": "iana" + }, + "application/atom+xml": { + "source": "iana", + "compressible": true, + "extensions": ["atom"] + }, + "application/atomcat+xml": { + "source": "iana", + "compressible": true, + "extensions": ["atomcat"] + }, + "application/atomdeleted+xml": { + "source": "iana", + "compressible": true, + "extensions": ["atomdeleted"] + }, + "application/atomicmail": { + "source": "iana" + }, + "application/atomsvc+xml": { + "source": "iana", + "compressible": true, + "extensions": ["atomsvc"] + }, + "application/atsc-dwd+xml": { + "source": "iana", + "compressible": true, + "extensions": ["dwd"] + }, + "application/atsc-dynamic-event-message": { + "source": "iana" + }, + "application/atsc-held+xml": { + "source": "iana", + "compressible": true, + "extensions": ["held"] + }, + "application/atsc-rdt+json": { + "source": "iana", + "compressible": true + }, + "application/atsc-rsat+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rsat"] + }, + "application/atxml": { + "source": "iana" + }, + "application/auth-policy+xml": { + "source": "iana", + "compressible": true + }, + "application/bacnet-xdd+zip": { + "source": "iana", + "compressible": false + }, + "application/batch-smtp": { + "source": "iana" + }, + "application/bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/beep+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/calendar+json": { + "source": "iana", + "compressible": true + }, + "application/calendar+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xcs"] + }, + "application/call-completion": { + "source": "iana" + }, + "application/cals-1840": { + "source": "iana" + }, + "application/captive+json": { + "source": "iana", + "compressible": true + }, + "application/cbor": { + "source": "iana" + }, + "application/cbor-seq": { + "source": "iana" + }, + "application/cccex": { + "source": "iana" + }, + "application/ccmp+xml": { + "source": "iana", + "compressible": true + }, + "application/ccxml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["ccxml"] + }, + "application/cdfx+xml": { + "source": "iana", + "compressible": true, + "extensions": ["cdfx"] + }, + "application/cdmi-capability": { + "source": "iana", + "extensions": ["cdmia"] + }, + "application/cdmi-container": { + "source": "iana", + "extensions": ["cdmic"] + }, + "application/cdmi-domain": { + "source": "iana", + "extensions": ["cdmid"] + }, + "application/cdmi-object": { + "source": "iana", + "extensions": ["cdmio"] + }, + "application/cdmi-queue": { + "source": "iana", + "extensions": ["cdmiq"] + }, + "application/cdni": { + "source": "iana" + }, + "application/cea": { + "source": "iana" + }, + "application/cea-2018+xml": { + "source": "iana", + "compressible": true + }, + "application/cellml+xml": { + "source": "iana", + "compressible": true + }, + "application/cfw": { + "source": "iana" + }, + "application/clr": { + "source": "iana" + }, + "application/clue+xml": { + "source": "iana", + "compressible": true + }, + "application/clue_info+xml": { + "source": "iana", + "compressible": true + }, + "application/cms": { + "source": "iana" + }, + "application/cnrp+xml": { + "source": "iana", + "compressible": true + }, + "application/coap-group+json": { + "source": "iana", + "compressible": true + }, + "application/coap-payload": { + "source": "iana" + }, + "application/commonground": { + "source": "iana" + }, + "application/conference-info+xml": { + "source": "iana", + "compressible": true + }, + "application/cose": { + "source": "iana" + }, + "application/cose-key": { + "source": "iana" + }, + "application/cose-key-set": { + "source": "iana" + }, + "application/cpl+xml": { + "source": "iana", + "compressible": true + }, + "application/csrattrs": { + "source": "iana" + }, + "application/csta+xml": { + "source": "iana", + "compressible": true + }, + "application/cstadata+xml": { + "source": "iana", + "compressible": true + }, + "application/csvm+json": { + "source": "iana", + "compressible": true + }, + "application/cu-seeme": { + "source": "apache", + "extensions": ["cu"] + }, + "application/cwt": { + "source": "iana" + }, + "application/cybercash": { + "source": "iana" + }, + "application/dart": { + "compressible": true + }, + "application/dash+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mpd"] + }, + "application/dashdelta": { + "source": "iana" + }, + "application/davmount+xml": { + "source": "iana", + "compressible": true, + "extensions": ["davmount"] + }, + "application/dca-rft": { + "source": "iana" + }, + "application/dcd": { + "source": "iana" + }, + "application/dec-dx": { + "source": "iana" + }, + "application/dialog-info+xml": { + "source": "iana", + "compressible": true + }, + "application/dicom": { + "source": "iana" + }, + "application/dicom+json": { + "source": "iana", + "compressible": true + }, + "application/dicom+xml": { + "source": "iana", + "compressible": true + }, + "application/dii": { + "source": "iana" + }, + "application/dit": { + "source": "iana" + }, + "application/dns": { + "source": "iana" + }, + "application/dns+json": { + "source": "iana", + "compressible": true + }, + "application/dns-message": { + "source": "iana" + }, + "application/docbook+xml": { + "source": "apache", + "compressible": true, + "extensions": ["dbk"] + }, + "application/dots+cbor": { + "source": "iana" + }, + "application/dskpp+xml": { + "source": "iana", + "compressible": true + }, + "application/dssc+der": { + "source": "iana", + "extensions": ["dssc"] + }, + "application/dssc+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xdssc"] + }, + "application/dvcs": { + "source": "iana" + }, + "application/ecmascript": { + "source": "iana", + "compressible": true, + "extensions": ["es","ecma"] + }, + "application/edi-consent": { + "source": "iana" + }, + "application/edi-x12": { + "source": "iana", + "compressible": false + }, + "application/edifact": { + "source": "iana", + "compressible": false + }, + "application/efi": { + "source": "iana" + }, + "application/elm+json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/elm+xml": { + "source": "iana", + "compressible": true + }, + "application/emergencycalldata.cap+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/emergencycalldata.comment+xml": { + "source": "iana", + "compressible": true + }, + "application/emergencycalldata.control+xml": { + "source": "iana", + "compressible": true + }, + "application/emergencycalldata.deviceinfo+xml": { + "source": "iana", + "compressible": true + }, + "application/emergencycalldata.ecall.msd": { + "source": "iana" + }, + "application/emergencycalldata.providerinfo+xml": { + "source": "iana", + "compressible": true + }, + "application/emergencycalldata.serviceinfo+xml": { + "source": "iana", + "compressible": true + }, + "application/emergencycalldata.subscriberinfo+xml": { + "source": "iana", + "compressible": true + }, + "application/emergencycalldata.veds+xml": { + "source": "iana", + "compressible": true + }, + "application/emma+xml": { + "source": "iana", + "compressible": true, + "extensions": ["emma"] + }, + "application/emotionml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["emotionml"] + }, + "application/encaprtp": { + "source": "iana" + }, + "application/epp+xml": { + "source": "iana", + "compressible": true + }, + "application/epub+zip": { + "source": "iana", + "compressible": false, + "extensions": ["epub"] + }, + "application/eshop": { + "source": "iana" + }, + "application/exi": { + "source": "iana", + "extensions": ["exi"] + }, + "application/expect-ct-report+json": { + "source": "iana", + "compressible": true + }, + "application/express": { + "source": "iana", + "extensions": ["exp"] + }, + "application/fastinfoset": { + "source": "iana" + }, + "application/fastsoap": { + "source": "iana" + }, + "application/fdt+xml": { + "source": "iana", + "compressible": true, + "extensions": ["fdt"] + }, + "application/fhir+json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/fhir+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/fido.trusted-apps+json": { + "compressible": true + }, + "application/fits": { + "source": "iana" + }, + "application/flexfec": { + "source": "iana" + }, + "application/font-sfnt": { + "source": "iana" + }, + "application/font-tdpfr": { + "source": "iana", + "extensions": ["pfr"] + }, + "application/font-woff": { + "source": "iana", + "compressible": false + }, + "application/framework-attributes+xml": { + "source": "iana", + "compressible": true + }, + "application/geo+json": { + "source": "iana", + "compressible": true, + "extensions": ["geojson"] + }, + "application/geo+json-seq": { + "source": "iana" + }, + "application/geopackage+sqlite3": { + "source": "iana" + }, + "application/geoxacml+xml": { + "source": "iana", + "compressible": true + }, + "application/gltf-buffer": { + "source": "iana" + }, + "application/gml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["gml"] + }, + "application/gpx+xml": { + "source": "apache", + "compressible": true, + "extensions": ["gpx"] + }, + "application/gxf": { + "source": "apache", + "extensions": ["gxf"] + }, + "application/gzip": { + "source": "iana", + "compressible": false, + "extensions": ["gz"] + }, + "application/h224": { + "source": "iana" + }, + "application/held+xml": { + "source": "iana", + "compressible": true + }, + "application/hjson": { + "extensions": ["hjson"] + }, + "application/http": { + "source": "iana" + }, + "application/hyperstudio": { + "source": "iana", + "extensions": ["stk"] + }, + "application/ibe-key-request+xml": { + "source": "iana", + "compressible": true + }, + "application/ibe-pkg-reply+xml": { + "source": "iana", + "compressible": true + }, + "application/ibe-pp-data": { + "source": "iana" + }, + "application/iges": { + "source": "iana" + }, + "application/im-iscomposing+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/index": { + "source": "iana" + }, + "application/index.cmd": { + "source": "iana" + }, + "application/index.obj": { + "source": "iana" + }, + "application/index.response": { + "source": "iana" + }, + "application/index.vnd": { + "source": "iana" + }, + "application/inkml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["ink","inkml"] + }, + "application/iotp": { + "source": "iana" + }, + "application/ipfix": { + "source": "iana", + "extensions": ["ipfix"] + }, + "application/ipp": { + "source": "iana" + }, + "application/isup": { + "source": "iana" + }, + "application/its+xml": { + "source": "iana", + "compressible": true, + "extensions": ["its"] + }, + "application/java-archive": { + "source": "apache", + "compressible": false, + "extensions": ["jar","war","ear"] + }, + "application/java-serialized-object": { + "source": "apache", + "compressible": false, + "extensions": ["ser"] + }, + "application/java-vm": { + "source": "apache", + "compressible": false, + "extensions": ["class"] + }, + "application/javascript": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["js","mjs"] + }, + "application/jf2feed+json": { + "source": "iana", + "compressible": true + }, + "application/jose": { + "source": "iana" + }, + "application/jose+json": { + "source": "iana", + "compressible": true + }, + "application/jrd+json": { + "source": "iana", + "compressible": true + }, + "application/jscalendar+json": { + "source": "iana", + "compressible": true + }, + "application/json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["json","map"] + }, + "application/json-patch+json": { + "source": "iana", + "compressible": true + }, + "application/json-seq": { + "source": "iana" + }, + "application/json5": { + "extensions": ["json5"] + }, + "application/jsonml+json": { + "source": "apache", + "compressible": true, + "extensions": ["jsonml"] + }, + "application/jwk+json": { + "source": "iana", + "compressible": true + }, + "application/jwk-set+json": { + "source": "iana", + "compressible": true + }, + "application/jwt": { + "source": "iana" + }, + "application/kpml-request+xml": { + "source": "iana", + "compressible": true + }, + "application/kpml-response+xml": { + "source": "iana", + "compressible": true + }, + "application/ld+json": { + "source": "iana", + "compressible": true, + "extensions": ["jsonld"] + }, + "application/lgr+xml": { + "source": "iana", + "compressible": true, + "extensions": ["lgr"] + }, + "application/link-format": { + "source": "iana" + }, + "application/load-control+xml": { + "source": "iana", + "compressible": true + }, + "application/lost+xml": { + "source": "iana", + "compressible": true, + "extensions": ["lostxml"] + }, + "application/lostsync+xml": { + "source": "iana", + "compressible": true + }, + "application/lpf+zip": { + "source": "iana", + "compressible": false + }, + "application/lxf": { + "source": "iana" + }, + "application/mac-binhex40": { + "source": "iana", + "extensions": ["hqx"] + }, + "application/mac-compactpro": { + "source": "apache", + "extensions": ["cpt"] + }, + "application/macwriteii": { + "source": "iana" + }, + "application/mads+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mads"] + }, + "application/manifest+json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["webmanifest"] + }, + "application/marc": { + "source": "iana", + "extensions": ["mrc"] + }, + "application/marcxml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mrcx"] + }, + "application/mathematica": { + "source": "iana", + "extensions": ["ma","nb","mb"] + }, + "application/mathml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mathml"] + }, + "application/mathml-content+xml": { + "source": "iana", + "compressible": true + }, + "application/mathml-presentation+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-associated-procedure-description+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-deregister+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-envelope+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-msk+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-msk-response+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-protection-description+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-reception-report+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-register+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-register-response+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-schedule+xml": { + "source": "iana", + "compressible": true + }, + "application/mbms-user-service-description+xml": { + "source": "iana", + "compressible": true + }, + "application/mbox": { + "source": "iana", + "extensions": ["mbox"] + }, + "application/media-policy-dataset+xml": { + "source": "iana", + "compressible": true + }, + "application/media_control+xml": { + "source": "iana", + "compressible": true + }, + "application/mediaservercontrol+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mscml"] + }, + "application/merge-patch+json": { + "source": "iana", + "compressible": true + }, + "application/metalink+xml": { + "source": "apache", + "compressible": true, + "extensions": ["metalink"] + }, + "application/metalink4+xml": { + "source": "iana", + "compressible": true, + "extensions": ["meta4"] + }, + "application/mets+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mets"] + }, + "application/mf4": { + "source": "iana" + }, + "application/mikey": { + "source": "iana" + }, + "application/mipc": { + "source": "iana" + }, + "application/missing-blocks+cbor-seq": { + "source": "iana" + }, + "application/mmt-aei+xml": { + "source": "iana", + "compressible": true, + "extensions": ["maei"] + }, + "application/mmt-usd+xml": { + "source": "iana", + "compressible": true, + "extensions": ["musd"] + }, + "application/mods+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mods"] + }, + "application/moss-keys": { + "source": "iana" + }, + "application/moss-signature": { + "source": "iana" + }, + "application/mosskey-data": { + "source": "iana" + }, + "application/mosskey-request": { + "source": "iana" + }, + "application/mp21": { + "source": "iana", + "extensions": ["m21","mp21"] + }, + "application/mp4": { + "source": "iana", + "extensions": ["mp4s","m4p"] + }, + "application/mpeg4-generic": { + "source": "iana" + }, + "application/mpeg4-iod": { + "source": "iana" + }, + "application/mpeg4-iod-xmt": { + "source": "iana" + }, + "application/mrb-consumer+xml": { + "source": "iana", + "compressible": true + }, + "application/mrb-publish+xml": { + "source": "iana", + "compressible": true + }, + "application/msc-ivr+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/msc-mixer+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/msword": { + "source": "iana", + "compressible": false, + "extensions": ["doc","dot"] + }, + "application/mud+json": { + "source": "iana", + "compressible": true + }, + "application/multipart-core": { + "source": "iana" + }, + "application/mxf": { + "source": "iana", + "extensions": ["mxf"] + }, + "application/n-quads": { + "source": "iana", + "extensions": ["nq"] + }, + "application/n-triples": { + "source": "iana", + "extensions": ["nt"] + }, + "application/nasdata": { + "source": "iana" + }, + "application/news-checkgroups": { + "source": "iana", + "charset": "US-ASCII" + }, + "application/news-groupinfo": { + "source": "iana", + "charset": "US-ASCII" + }, + "application/news-transmission": { + "source": "iana" + }, + "application/nlsml+xml": { + "source": "iana", + "compressible": true + }, + "application/node": { + "source": "iana", + "extensions": ["cjs"] + }, + "application/nss": { + "source": "iana" + }, + "application/oauth-authz-req+jwt": { + "source": "iana" + }, + "application/ocsp-request": { + "source": "iana" + }, + "application/ocsp-response": { + "source": "iana" + }, + "application/octet-stream": { + "source": "iana", + "compressible": false, + "extensions": ["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"] + }, + "application/oda": { + "source": "iana", + "extensions": ["oda"] + }, + "application/odm+xml": { + "source": "iana", + "compressible": true + }, + "application/odx": { + "source": "iana" + }, + "application/oebps-package+xml": { + "source": "iana", + "compressible": true, + "extensions": ["opf"] + }, + "application/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["ogx"] + }, + "application/omdoc+xml": { + "source": "apache", + "compressible": true, + "extensions": ["omdoc"] + }, + "application/onenote": { + "source": "apache", + "extensions": ["onetoc","onetoc2","onetmp","onepkg"] + }, + "application/opc-nodeset+xml": { + "source": "iana", + "compressible": true + }, + "application/oscore": { + "source": "iana" + }, + "application/oxps": { + "source": "iana", + "extensions": ["oxps"] + }, + "application/p21": { + "source": "iana" + }, + "application/p21+zip": { + "source": "iana", + "compressible": false + }, + "application/p2p-overlay+xml": { + "source": "iana", + "compressible": true, + "extensions": ["relo"] + }, + "application/parityfec": { + "source": "iana" + }, + "application/passport": { + "source": "iana" + }, + "application/patch-ops-error+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xer"] + }, + "application/pdf": { + "source": "iana", + "compressible": false, + "extensions": ["pdf"] + }, + "application/pdx": { + "source": "iana" + }, + "application/pem-certificate-chain": { + "source": "iana" + }, + "application/pgp-encrypted": { + "source": "iana", + "compressible": false, + "extensions": ["pgp"] + }, + "application/pgp-keys": { + "source": "iana" + }, + "application/pgp-signature": { + "source": "iana", + "extensions": ["asc","sig"] + }, + "application/pics-rules": { + "source": "apache", + "extensions": ["prf"] + }, + "application/pidf+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/pidf-diff+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/pkcs10": { + "source": "iana", + "extensions": ["p10"] + }, + "application/pkcs12": { + "source": "iana" + }, + "application/pkcs7-mime": { + "source": "iana", + "extensions": ["p7m","p7c"] + }, + "application/pkcs7-signature": { + "source": "iana", + "extensions": ["p7s"] + }, + "application/pkcs8": { + "source": "iana", + "extensions": ["p8"] + }, + "application/pkcs8-encrypted": { + "source": "iana" + }, + "application/pkix-attr-cert": { + "source": "iana", + "extensions": ["ac"] + }, + "application/pkix-cert": { + "source": "iana", + "extensions": ["cer"] + }, + "application/pkix-crl": { + "source": "iana", + "extensions": ["crl"] + }, + "application/pkix-pkipath": { + "source": "iana", + "extensions": ["pkipath"] + }, + "application/pkixcmp": { + "source": "iana", + "extensions": ["pki"] + }, + "application/pls+xml": { + "source": "iana", + "compressible": true, + "extensions": ["pls"] + }, + "application/poc-settings+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/postscript": { + "source": "iana", + "compressible": true, + "extensions": ["ai","eps","ps"] + }, + "application/ppsp-tracker+json": { + "source": "iana", + "compressible": true + }, + "application/problem+json": { + "source": "iana", + "compressible": true + }, + "application/problem+xml": { + "source": "iana", + "compressible": true + }, + "application/provenance+xml": { + "source": "iana", + "compressible": true, + "extensions": ["provx"] + }, + "application/prs.alvestrand.titrax-sheet": { + "source": "iana" + }, + "application/prs.cww": { + "source": "iana", + "extensions": ["cww"] + }, + "application/prs.cyn": { + "source": "iana", + "charset": "7-BIT" + }, + "application/prs.hpub+zip": { + "source": "iana", + "compressible": false + }, + "application/prs.nprend": { + "source": "iana" + }, + "application/prs.plucker": { + "source": "iana" + }, + "application/prs.rdf-xml-crypt": { + "source": "iana" + }, + "application/prs.xsf+xml": { + "source": "iana", + "compressible": true + }, + "application/pskc+xml": { + "source": "iana", + "compressible": true, + "extensions": ["pskcxml"] + }, + "application/pvd+json": { + "source": "iana", + "compressible": true + }, + "application/qsig": { + "source": "iana" + }, + "application/raml+yaml": { + "compressible": true, + "extensions": ["raml"] + }, + "application/raptorfec": { + "source": "iana" + }, + "application/rdap+json": { + "source": "iana", + "compressible": true + }, + "application/rdf+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rdf","owl"] + }, + "application/reginfo+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rif"] + }, + "application/relax-ng-compact-syntax": { + "source": "iana", + "extensions": ["rnc"] + }, + "application/remote-printing": { + "source": "iana" + }, + "application/reputon+json": { + "source": "iana", + "compressible": true + }, + "application/resource-lists+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rl"] + }, + "application/resource-lists-diff+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rld"] + }, + "application/rfc+xml": { + "source": "iana", + "compressible": true + }, + "application/riscos": { + "source": "iana" + }, + "application/rlmi+xml": { + "source": "iana", + "compressible": true + }, + "application/rls-services+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rs"] + }, + "application/route-apd+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rapd"] + }, + "application/route-s-tsid+xml": { + "source": "iana", + "compressible": true, + "extensions": ["sls"] + }, + "application/route-usd+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rusd"] + }, + "application/rpki-ghostbusters": { + "source": "iana", + "extensions": ["gbr"] + }, + "application/rpki-manifest": { + "source": "iana", + "extensions": ["mft"] + }, + "application/rpki-publication": { + "source": "iana" + }, + "application/rpki-roa": { + "source": "iana", + "extensions": ["roa"] + }, + "application/rpki-updown": { + "source": "iana" + }, + "application/rsd+xml": { + "source": "apache", + "compressible": true, + "extensions": ["rsd"] + }, + "application/rss+xml": { + "source": "apache", + "compressible": true, + "extensions": ["rss"] + }, + "application/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "application/rtploopback": { + "source": "iana" + }, + "application/rtx": { + "source": "iana" + }, + "application/samlassertion+xml": { + "source": "iana", + "compressible": true + }, + "application/samlmetadata+xml": { + "source": "iana", + "compressible": true + }, + "application/sarif+json": { + "source": "iana", + "compressible": true + }, + "application/sarif-external-properties+json": { + "source": "iana", + "compressible": true + }, + "application/sbe": { + "source": "iana" + }, + "application/sbml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["sbml"] + }, + "application/scaip+xml": { + "source": "iana", + "compressible": true + }, + "application/scim+json": { + "source": "iana", + "compressible": true + }, + "application/scvp-cv-request": { + "source": "iana", + "extensions": ["scq"] + }, + "application/scvp-cv-response": { + "source": "iana", + "extensions": ["scs"] + }, + "application/scvp-vp-request": { + "source": "iana", + "extensions": ["spq"] + }, + "application/scvp-vp-response": { + "source": "iana", + "extensions": ["spp"] + }, + "application/sdp": { + "source": "iana", + "extensions": ["sdp"] + }, + "application/secevent+jwt": { + "source": "iana" + }, + "application/senml+cbor": { + "source": "iana" + }, + "application/senml+json": { + "source": "iana", + "compressible": true + }, + "application/senml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["senmlx"] + }, + "application/senml-etch+cbor": { + "source": "iana" + }, + "application/senml-etch+json": { + "source": "iana", + "compressible": true + }, + "application/senml-exi": { + "source": "iana" + }, + "application/sensml+cbor": { + "source": "iana" + }, + "application/sensml+json": { + "source": "iana", + "compressible": true + }, + "application/sensml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["sensmlx"] + }, + "application/sensml-exi": { + "source": "iana" + }, + "application/sep+xml": { + "source": "iana", + "compressible": true + }, + "application/sep-exi": { + "source": "iana" + }, + "application/session-info": { + "source": "iana" + }, + "application/set-payment": { + "source": "iana" + }, + "application/set-payment-initiation": { + "source": "iana", + "extensions": ["setpay"] + }, + "application/set-registration": { + "source": "iana" + }, + "application/set-registration-initiation": { + "source": "iana", + "extensions": ["setreg"] + }, + "application/sgml": { + "source": "iana" + }, + "application/sgml-open-catalog": { + "source": "iana" + }, + "application/shf+xml": { + "source": "iana", + "compressible": true, + "extensions": ["shf"] + }, + "application/sieve": { + "source": "iana", + "extensions": ["siv","sieve"] + }, + "application/simple-filter+xml": { + "source": "iana", + "compressible": true + }, + "application/simple-message-summary": { + "source": "iana" + }, + "application/simplesymbolcontainer": { + "source": "iana" + }, + "application/sipc": { + "source": "iana" + }, + "application/slate": { + "source": "iana" + }, + "application/smil": { + "source": "iana" + }, + "application/smil+xml": { + "source": "iana", + "compressible": true, + "extensions": ["smi","smil"] + }, + "application/smpte336m": { + "source": "iana" + }, + "application/soap+fastinfoset": { + "source": "iana" + }, + "application/soap+xml": { + "source": "iana", + "compressible": true + }, + "application/sparql-query": { + "source": "iana", + "extensions": ["rq"] + }, + "application/sparql-results+xml": { + "source": "iana", + "compressible": true, + "extensions": ["srx"] + }, + "application/spirits-event+xml": { + "source": "iana", + "compressible": true + }, + "application/sql": { + "source": "iana" + }, + "application/srgs": { + "source": "iana", + "extensions": ["gram"] + }, + "application/srgs+xml": { + "source": "iana", + "compressible": true, + "extensions": ["grxml"] + }, + "application/sru+xml": { + "source": "iana", + "compressible": true, + "extensions": ["sru"] + }, + "application/ssdl+xml": { + "source": "apache", + "compressible": true, + "extensions": ["ssdl"] + }, + "application/ssml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["ssml"] + }, + "application/stix+json": { + "source": "iana", + "compressible": true + }, + "application/swid+xml": { + "source": "iana", + "compressible": true, + "extensions": ["swidtag"] + }, + "application/tamp-apex-update": { + "source": "iana" + }, + "application/tamp-apex-update-confirm": { + "source": "iana" + }, + "application/tamp-community-update": { + "source": "iana" + }, + "application/tamp-community-update-confirm": { + "source": "iana" + }, + "application/tamp-error": { + "source": "iana" + }, + "application/tamp-sequence-adjust": { + "source": "iana" + }, + "application/tamp-sequence-adjust-confirm": { + "source": "iana" + }, + "application/tamp-status-query": { + "source": "iana" + }, + "application/tamp-status-response": { + "source": "iana" + }, + "application/tamp-update": { + "source": "iana" + }, + "application/tamp-update-confirm": { + "source": "iana" + }, + "application/tar": { + "compressible": true + }, + "application/taxii+json": { + "source": "iana", + "compressible": true + }, + "application/td+json": { + "source": "iana", + "compressible": true + }, + "application/tei+xml": { + "source": "iana", + "compressible": true, + "extensions": ["tei","teicorpus"] + }, + "application/tetra_isi": { + "source": "iana" + }, + "application/thraud+xml": { + "source": "iana", + "compressible": true, + "extensions": ["tfi"] + }, + "application/timestamp-query": { + "source": "iana" + }, + "application/timestamp-reply": { + "source": "iana" + }, + "application/timestamped-data": { + "source": "iana", + "extensions": ["tsd"] + }, + "application/tlsrpt+gzip": { + "source": "iana" + }, + "application/tlsrpt+json": { + "source": "iana", + "compressible": true + }, + "application/tnauthlist": { + "source": "iana" + }, + "application/token-introspection+jwt": { + "source": "iana" + }, + "application/toml": { + "compressible": true, + "extensions": ["toml"] + }, + "application/trickle-ice-sdpfrag": { + "source": "iana" + }, + "application/trig": { + "source": "iana", + "extensions": ["trig"] + }, + "application/ttml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["ttml"] + }, + "application/tve-trigger": { + "source": "iana" + }, + "application/tzif": { + "source": "iana" + }, + "application/tzif-leap": { + "source": "iana" + }, + "application/ubjson": { + "compressible": false, + "extensions": ["ubj"] + }, + "application/ulpfec": { + "source": "iana" + }, + "application/urc-grpsheet+xml": { + "source": "iana", + "compressible": true + }, + "application/urc-ressheet+xml": { + "source": "iana", + "compressible": true, + "extensions": ["rsheet"] + }, + "application/urc-targetdesc+xml": { + "source": "iana", + "compressible": true, + "extensions": ["td"] + }, + "application/urc-uisocketdesc+xml": { + "source": "iana", + "compressible": true + }, + "application/vcard+json": { + "source": "iana", + "compressible": true + }, + "application/vcard+xml": { + "source": "iana", + "compressible": true + }, + "application/vemmi": { + "source": "iana" + }, + "application/vividence.scriptfile": { + "source": "apache" + }, + "application/vnd.1000minds.decision-model+xml": { + "source": "iana", + "compressible": true, + "extensions": ["1km"] + }, + "application/vnd.3gpp-prose+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp-prose-pc3ch+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp-v2x-local-service-information": { + "source": "iana" + }, + "application/vnd.3gpp.5gnas": { + "source": "iana" + }, + "application/vnd.3gpp.access-transfer-events+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.bsf+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.gmop+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.gtpc": { + "source": "iana" + }, + "application/vnd.3gpp.interworking-data": { + "source": "iana" + }, + "application/vnd.3gpp.lpp": { + "source": "iana" + }, + "application/vnd.3gpp.mc-signalling-ear": { + "source": "iana" + }, + "application/vnd.3gpp.mcdata-affiliation-command+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcdata-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcdata-payload": { + "source": "iana" + }, + "application/vnd.3gpp.mcdata-service-config+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcdata-signalling": { + "source": "iana" + }, + "application/vnd.3gpp.mcdata-ue-config+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcdata-user-profile+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-affiliation-command+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-floor-request+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-location-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-mbms-usage-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-service-config+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-signed+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-ue-config+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-ue-init-config+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcptt-user-profile+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-affiliation-command+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-affiliation-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-location-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-mbms-usage-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-service-config+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-transmission-request+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-ue-config+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mcvideo-user-profile+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.mid-call+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.ngap": { + "source": "iana" + }, + "application/vnd.3gpp.pfcp": { + "source": "iana" + }, + "application/vnd.3gpp.pic-bw-large": { + "source": "iana", + "extensions": ["plb"] + }, + "application/vnd.3gpp.pic-bw-small": { + "source": "iana", + "extensions": ["psb"] + }, + "application/vnd.3gpp.pic-bw-var": { + "source": "iana", + "extensions": ["pvb"] + }, + "application/vnd.3gpp.s1ap": { + "source": "iana" + }, + "application/vnd.3gpp.sms": { + "source": "iana" + }, + "application/vnd.3gpp.sms+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.srvcc-ext+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.srvcc-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.state-and-event-info+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp.ussd+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp2.bcmcsinfo+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.3gpp2.sms": { + "source": "iana" + }, + "application/vnd.3gpp2.tcap": { + "source": "iana", + "extensions": ["tcap"] + }, + "application/vnd.3lightssoftware.imagescal": { + "source": "iana" + }, + "application/vnd.3m.post-it-notes": { + "source": "iana", + "extensions": ["pwn"] + }, + "application/vnd.accpac.simply.aso": { + "source": "iana", + "extensions": ["aso"] + }, + "application/vnd.accpac.simply.imp": { + "source": "iana", + "extensions": ["imp"] + }, + "application/vnd.acucobol": { + "source": "iana", + "extensions": ["acu"] + }, + "application/vnd.acucorp": { + "source": "iana", + "extensions": ["atc","acutc"] + }, + "application/vnd.adobe.air-application-installer-package+zip": { + "source": "apache", + "compressible": false, + "extensions": ["air"] + }, + "application/vnd.adobe.flash.movie": { + "source": "iana" + }, + "application/vnd.adobe.formscentral.fcdt": { + "source": "iana", + "extensions": ["fcdt"] + }, + "application/vnd.adobe.fxp": { + "source": "iana", + "extensions": ["fxp","fxpl"] + }, + "application/vnd.adobe.partial-upload": { + "source": "iana" + }, + "application/vnd.adobe.xdp+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xdp"] + }, + "application/vnd.adobe.xfdf": { + "source": "iana", + "extensions": ["xfdf"] + }, + "application/vnd.aether.imp": { + "source": "iana" + }, + "application/vnd.afpc.afplinedata": { + "source": "iana" + }, + "application/vnd.afpc.afplinedata-pagedef": { + "source": "iana" + }, + "application/vnd.afpc.cmoca-cmresource": { + "source": "iana" + }, + "application/vnd.afpc.foca-charset": { + "source": "iana" + }, + "application/vnd.afpc.foca-codedfont": { + "source": "iana" + }, + "application/vnd.afpc.foca-codepage": { + "source": "iana" + }, + "application/vnd.afpc.modca": { + "source": "iana" + }, + "application/vnd.afpc.modca-cmtable": { + "source": "iana" + }, + "application/vnd.afpc.modca-formdef": { + "source": "iana" + }, + "application/vnd.afpc.modca-mediummap": { + "source": "iana" + }, + "application/vnd.afpc.modca-objectcontainer": { + "source": "iana" + }, + "application/vnd.afpc.modca-overlay": { + "source": "iana" + }, + "application/vnd.afpc.modca-pagesegment": { + "source": "iana" + }, + "application/vnd.ah-barcode": { + "source": "iana" + }, + "application/vnd.ahead.space": { + "source": "iana", + "extensions": ["ahead"] + }, + "application/vnd.airzip.filesecure.azf": { + "source": "iana", + "extensions": ["azf"] + }, + "application/vnd.airzip.filesecure.azs": { + "source": "iana", + "extensions": ["azs"] + }, + "application/vnd.amadeus+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.amazon.ebook": { + "source": "apache", + "extensions": ["azw"] + }, + "application/vnd.amazon.mobi8-ebook": { + "source": "iana" + }, + "application/vnd.americandynamics.acc": { + "source": "iana", + "extensions": ["acc"] + }, + "application/vnd.amiga.ami": { + "source": "iana", + "extensions": ["ami"] + }, + "application/vnd.amundsen.maze+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.android.ota": { + "source": "iana" + }, + "application/vnd.android.package-archive": { + "source": "apache", + "compressible": false, + "extensions": ["apk"] + }, + "application/vnd.anki": { + "source": "iana" + }, + "application/vnd.anser-web-certificate-issue-initiation": { + "source": "iana", + "extensions": ["cii"] + }, + "application/vnd.anser-web-funds-transfer-initiation": { + "source": "apache", + "extensions": ["fti"] + }, + "application/vnd.antix.game-component": { + "source": "iana", + "extensions": ["atx"] + }, + "application/vnd.apache.arrow.file": { + "source": "iana" + }, + "application/vnd.apache.arrow.stream": { + "source": "iana" + }, + "application/vnd.apache.thrift.binary": { + "source": "iana" + }, + "application/vnd.apache.thrift.compact": { + "source": "iana" + }, + "application/vnd.apache.thrift.json": { + "source": "iana" + }, + "application/vnd.api+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.aplextor.warrp+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.apothekende.reservation+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.apple.installer+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mpkg"] + }, + "application/vnd.apple.keynote": { + "source": "iana", + "extensions": ["key"] + }, + "application/vnd.apple.mpegurl": { + "source": "iana", + "extensions": ["m3u8"] + }, + "application/vnd.apple.numbers": { + "source": "iana", + "extensions": ["numbers"] + }, + "application/vnd.apple.pages": { + "source": "iana", + "extensions": ["pages"] + }, + "application/vnd.apple.pkpass": { + "compressible": false, + "extensions": ["pkpass"] + }, + "application/vnd.arastra.swi": { + "source": "iana" + }, + "application/vnd.aristanetworks.swi": { + "source": "iana", + "extensions": ["swi"] + }, + "application/vnd.artisan+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.artsquare": { + "source": "iana" + }, + "application/vnd.astraea-software.iota": { + "source": "iana", + "extensions": ["iota"] + }, + "application/vnd.audiograph": { + "source": "iana", + "extensions": ["aep"] + }, + "application/vnd.autopackage": { + "source": "iana" + }, + "application/vnd.avalon+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.avistar+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.balsamiq.bmml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["bmml"] + }, + "application/vnd.balsamiq.bmpr": { + "source": "iana" + }, + "application/vnd.banana-accounting": { + "source": "iana" + }, + "application/vnd.bbf.usp.error": { + "source": "iana" + }, + "application/vnd.bbf.usp.msg": { + "source": "iana" + }, + "application/vnd.bbf.usp.msg+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.bekitzur-stech+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.bint.med-content": { + "source": "iana" + }, + "application/vnd.biopax.rdf+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.blink-idb-value-wrapper": { + "source": "iana" + }, + "application/vnd.blueice.multipass": { + "source": "iana", + "extensions": ["mpm"] + }, + "application/vnd.bluetooth.ep.oob": { + "source": "iana" + }, + "application/vnd.bluetooth.le.oob": { + "source": "iana" + }, + "application/vnd.bmi": { + "source": "iana", + "extensions": ["bmi"] + }, + "application/vnd.bpf": { + "source": "iana" + }, + "application/vnd.bpf3": { + "source": "iana" + }, + "application/vnd.businessobjects": { + "source": "iana", + "extensions": ["rep"] + }, + "application/vnd.byu.uapi+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.cab-jscript": { + "source": "iana" + }, + "application/vnd.canon-cpdl": { + "source": "iana" + }, + "application/vnd.canon-lips": { + "source": "iana" + }, + "application/vnd.capasystems-pg+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.cendio.thinlinc.clientconf": { + "source": "iana" + }, + "application/vnd.century-systems.tcp_stream": { + "source": "iana" + }, + "application/vnd.chemdraw+xml": { + "source": "iana", + "compressible": true, + "extensions": ["cdxml"] + }, + "application/vnd.chess-pgn": { + "source": "iana" + }, + "application/vnd.chipnuts.karaoke-mmd": { + "source": "iana", + "extensions": ["mmd"] + }, + "application/vnd.ciedi": { + "source": "iana" + }, + "application/vnd.cinderella": { + "source": "iana", + "extensions": ["cdy"] + }, + "application/vnd.cirpack.isdn-ext": { + "source": "iana" + }, + "application/vnd.citationstyles.style+xml": { + "source": "iana", + "compressible": true, + "extensions": ["csl"] + }, + "application/vnd.claymore": { + "source": "iana", + "extensions": ["cla"] + }, + "application/vnd.cloanto.rp9": { + "source": "iana", + "extensions": ["rp9"] + }, + "application/vnd.clonk.c4group": { + "source": "iana", + "extensions": ["c4g","c4d","c4f","c4p","c4u"] + }, + "application/vnd.cluetrust.cartomobile-config": { + "source": "iana", + "extensions": ["c11amc"] + }, + "application/vnd.cluetrust.cartomobile-config-pkg": { + "source": "iana", + "extensions": ["c11amz"] + }, + "application/vnd.coffeescript": { + "source": "iana" + }, + "application/vnd.collabio.xodocuments.document": { + "source": "iana" + }, + "application/vnd.collabio.xodocuments.document-template": { + "source": "iana" + }, + "application/vnd.collabio.xodocuments.presentation": { + "source": "iana" + }, + "application/vnd.collabio.xodocuments.presentation-template": { + "source": "iana" + }, + "application/vnd.collabio.xodocuments.spreadsheet": { + "source": "iana" + }, + "application/vnd.collabio.xodocuments.spreadsheet-template": { + "source": "iana" + }, + "application/vnd.collection+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.doc+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.collection.next+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.comicbook+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.comicbook-rar": { + "source": "iana" + }, + "application/vnd.commerce-battelle": { + "source": "iana" + }, + "application/vnd.commonspace": { + "source": "iana", + "extensions": ["csp"] + }, + "application/vnd.contact.cmsg": { + "source": "iana", + "extensions": ["cdbcmsg"] + }, + "application/vnd.coreos.ignition+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.cosmocaller": { + "source": "iana", + "extensions": ["cmc"] + }, + "application/vnd.crick.clicker": { + "source": "iana", + "extensions": ["clkx"] + }, + "application/vnd.crick.clicker.keyboard": { + "source": "iana", + "extensions": ["clkk"] + }, + "application/vnd.crick.clicker.palette": { + "source": "iana", + "extensions": ["clkp"] + }, + "application/vnd.crick.clicker.template": { + "source": "iana", + "extensions": ["clkt"] + }, + "application/vnd.crick.clicker.wordbank": { + "source": "iana", + "extensions": ["clkw"] + }, + "application/vnd.criticaltools.wbs+xml": { + "source": "iana", + "compressible": true, + "extensions": ["wbs"] + }, + "application/vnd.cryptii.pipe+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.crypto-shade-file": { + "source": "iana" + }, + "application/vnd.cryptomator.encrypted": { + "source": "iana" + }, + "application/vnd.cryptomator.vault": { + "source": "iana" + }, + "application/vnd.ctc-posml": { + "source": "iana", + "extensions": ["pml"] + }, + "application/vnd.ctct.ws+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.cups-pdf": { + "source": "iana" + }, + "application/vnd.cups-postscript": { + "source": "iana" + }, + "application/vnd.cups-ppd": { + "source": "iana", + "extensions": ["ppd"] + }, + "application/vnd.cups-raster": { + "source": "iana" + }, + "application/vnd.cups-raw": { + "source": "iana" + }, + "application/vnd.curl": { + "source": "iana" + }, + "application/vnd.curl.car": { + "source": "apache", + "extensions": ["car"] + }, + "application/vnd.curl.pcurl": { + "source": "apache", + "extensions": ["pcurl"] + }, + "application/vnd.cyan.dean.root+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.cybank": { + "source": "iana" + }, + "application/vnd.cyclonedx+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.cyclonedx+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.d2l.coursepackage1p0+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.d3m-dataset": { + "source": "iana" + }, + "application/vnd.d3m-problem": { + "source": "iana" + }, + "application/vnd.dart": { + "source": "iana", + "compressible": true, + "extensions": ["dart"] + }, + "application/vnd.data-vision.rdz": { + "source": "iana", + "extensions": ["rdz"] + }, + "application/vnd.datapackage+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.dataresource+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.dbf": { + "source": "iana", + "extensions": ["dbf"] + }, + "application/vnd.debian.binary-package": { + "source": "iana" + }, + "application/vnd.dece.data": { + "source": "iana", + "extensions": ["uvf","uvvf","uvd","uvvd"] + }, + "application/vnd.dece.ttml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["uvt","uvvt"] + }, + "application/vnd.dece.unspecified": { + "source": "iana", + "extensions": ["uvx","uvvx"] + }, + "application/vnd.dece.zip": { + "source": "iana", + "extensions": ["uvz","uvvz"] + }, + "application/vnd.denovo.fcselayout-link": { + "source": "iana", + "extensions": ["fe_launch"] + }, + "application/vnd.desmume.movie": { + "source": "iana" + }, + "application/vnd.dir-bi.plate-dl-nosuffix": { + "source": "iana" + }, + "application/vnd.dm.delegation+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dna": { + "source": "iana", + "extensions": ["dna"] + }, + "application/vnd.document+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.dolby.mlp": { + "source": "apache", + "extensions": ["mlp"] + }, + "application/vnd.dolby.mobile.1": { + "source": "iana" + }, + "application/vnd.dolby.mobile.2": { + "source": "iana" + }, + "application/vnd.doremir.scorecloud-binary-document": { + "source": "iana" + }, + "application/vnd.dpgraph": { + "source": "iana", + "extensions": ["dpg"] + }, + "application/vnd.dreamfactory": { + "source": "iana", + "extensions": ["dfac"] + }, + "application/vnd.drive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ds-keypoint": { + "source": "apache", + "extensions": ["kpxx"] + }, + "application/vnd.dtg.local": { + "source": "iana" + }, + "application/vnd.dtg.local.flash": { + "source": "iana" + }, + "application/vnd.dtg.local.html": { + "source": "iana" + }, + "application/vnd.dvb.ait": { + "source": "iana", + "extensions": ["ait"] + }, + "application/vnd.dvb.dvbisl+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dvb.dvbj": { + "source": "iana" + }, + "application/vnd.dvb.esgcontainer": { + "source": "iana" + }, + "application/vnd.dvb.ipdcdftnotifaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgaccess2": { + "source": "iana" + }, + "application/vnd.dvb.ipdcesgpdd": { + "source": "iana" + }, + "application/vnd.dvb.ipdcroaming": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-base": { + "source": "iana" + }, + "application/vnd.dvb.iptv.alfec-enhancement": { + "source": "iana" + }, + "application/vnd.dvb.notif-aggregate-root+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dvb.notif-container+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dvb.notif-generic+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dvb.notif-ia-msglist+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dvb.notif-ia-registration-request+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dvb.notif-ia-registration-response+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dvb.notif-init+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.dvb.pfr": { + "source": "iana" + }, + "application/vnd.dvb.service": { + "source": "iana", + "extensions": ["svc"] + }, + "application/vnd.dxr": { + "source": "iana" + }, + "application/vnd.dynageo": { + "source": "iana", + "extensions": ["geo"] + }, + "application/vnd.dzr": { + "source": "iana" + }, + "application/vnd.easykaraoke.cdgdownload": { + "source": "iana" + }, + "application/vnd.ecdis-update": { + "source": "iana" + }, + "application/vnd.ecip.rlp": { + "source": "iana" + }, + "application/vnd.ecowin.chart": { + "source": "iana", + "extensions": ["mag"] + }, + "application/vnd.ecowin.filerequest": { + "source": "iana" + }, + "application/vnd.ecowin.fileupdate": { + "source": "iana" + }, + "application/vnd.ecowin.series": { + "source": "iana" + }, + "application/vnd.ecowin.seriesrequest": { + "source": "iana" + }, + "application/vnd.ecowin.seriesupdate": { + "source": "iana" + }, + "application/vnd.efi.img": { + "source": "iana" + }, + "application/vnd.efi.iso": { + "source": "iana" + }, + "application/vnd.emclient.accessrequest+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.enliven": { + "source": "iana", + "extensions": ["nml"] + }, + "application/vnd.enphase.envoy": { + "source": "iana" + }, + "application/vnd.eprints.data+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.epson.esf": { + "source": "iana", + "extensions": ["esf"] + }, + "application/vnd.epson.msf": { + "source": "iana", + "extensions": ["msf"] + }, + "application/vnd.epson.quickanime": { + "source": "iana", + "extensions": ["qam"] + }, + "application/vnd.epson.salt": { + "source": "iana", + "extensions": ["slt"] + }, + "application/vnd.epson.ssf": { + "source": "iana", + "extensions": ["ssf"] + }, + "application/vnd.ericsson.quickcall": { + "source": "iana" + }, + "application/vnd.espass-espass+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.eszigno3+xml": { + "source": "iana", + "compressible": true, + "extensions": ["es3","et3"] + }, + "application/vnd.etsi.aoc+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.asic-e+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.etsi.asic-s+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.etsi.cug+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvcommand+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvdiscovery+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvprofile+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvsad-bc+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvsad-cod+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvsad-npvr+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvservice+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvsync+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.iptvueprofile+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.mcid+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.mheg5": { + "source": "iana" + }, + "application/vnd.etsi.overload-control-policy-dataset+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.pstn+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.sci+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.simservs+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.timestamp-token": { + "source": "iana" + }, + "application/vnd.etsi.tsl+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.etsi.tsl.der": { + "source": "iana" + }, + "application/vnd.eudora.data": { + "source": "iana" + }, + "application/vnd.evolv.ecig.profile": { + "source": "iana" + }, + "application/vnd.evolv.ecig.settings": { + "source": "iana" + }, + "application/vnd.evolv.ecig.theme": { + "source": "iana" + }, + "application/vnd.exstream-empower+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.exstream-package": { + "source": "iana" + }, + "application/vnd.ezpix-album": { + "source": "iana", + "extensions": ["ez2"] + }, + "application/vnd.ezpix-package": { + "source": "iana", + "extensions": ["ez3"] + }, + "application/vnd.f-secure.mobile": { + "source": "iana" + }, + "application/vnd.fastcopy-disk-image": { + "source": "iana" + }, + "application/vnd.fdf": { + "source": "iana", + "extensions": ["fdf"] + }, + "application/vnd.fdsn.mseed": { + "source": "iana", + "extensions": ["mseed"] + }, + "application/vnd.fdsn.seed": { + "source": "iana", + "extensions": ["seed","dataless"] + }, + "application/vnd.ffsns": { + "source": "iana" + }, + "application/vnd.ficlab.flb+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.filmit.zfc": { + "source": "iana" + }, + "application/vnd.fints": { + "source": "iana" + }, + "application/vnd.firemonkeys.cloudcell": { + "source": "iana" + }, + "application/vnd.flographit": { + "source": "iana", + "extensions": ["gph"] + }, + "application/vnd.fluxtime.clip": { + "source": "iana", + "extensions": ["ftc"] + }, + "application/vnd.font-fontforge-sfd": { + "source": "iana" + }, + "application/vnd.framemaker": { + "source": "iana", + "extensions": ["fm","frame","maker","book"] + }, + "application/vnd.frogans.fnc": { + "source": "iana", + "extensions": ["fnc"] + }, + "application/vnd.frogans.ltf": { + "source": "iana", + "extensions": ["ltf"] + }, + "application/vnd.fsc.weblaunch": { + "source": "iana", + "extensions": ["fsc"] + }, + "application/vnd.fujifilm.fb.docuworks": { + "source": "iana" + }, + "application/vnd.fujifilm.fb.docuworks.binder": { + "source": "iana" + }, + "application/vnd.fujifilm.fb.docuworks.container": { + "source": "iana" + }, + "application/vnd.fujifilm.fb.jfi+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.fujitsu.oasys": { + "source": "iana", + "extensions": ["oas"] + }, + "application/vnd.fujitsu.oasys2": { + "source": "iana", + "extensions": ["oa2"] + }, + "application/vnd.fujitsu.oasys3": { + "source": "iana", + "extensions": ["oa3"] + }, + "application/vnd.fujitsu.oasysgp": { + "source": "iana", + "extensions": ["fg5"] + }, + "application/vnd.fujitsu.oasysprs": { + "source": "iana", + "extensions": ["bh2"] + }, + "application/vnd.fujixerox.art-ex": { + "source": "iana" + }, + "application/vnd.fujixerox.art4": { + "source": "iana" + }, + "application/vnd.fujixerox.ddd": { + "source": "iana", + "extensions": ["ddd"] + }, + "application/vnd.fujixerox.docuworks": { + "source": "iana", + "extensions": ["xdw"] + }, + "application/vnd.fujixerox.docuworks.binder": { + "source": "iana", + "extensions": ["xbd"] + }, + "application/vnd.fujixerox.docuworks.container": { + "source": "iana" + }, + "application/vnd.fujixerox.hbpl": { + "source": "iana" + }, + "application/vnd.fut-misnet": { + "source": "iana" + }, + "application/vnd.futoin+cbor": { + "source": "iana" + }, + "application/vnd.futoin+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.fuzzysheet": { + "source": "iana", + "extensions": ["fzs"] + }, + "application/vnd.genomatix.tuxedo": { + "source": "iana", + "extensions": ["txd"] + }, + "application/vnd.gentics.grd+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.geo+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.geocube+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.geogebra.file": { + "source": "iana", + "extensions": ["ggb"] + }, + "application/vnd.geogebra.slides": { + "source": "iana" + }, + "application/vnd.geogebra.tool": { + "source": "iana", + "extensions": ["ggt"] + }, + "application/vnd.geometry-explorer": { + "source": "iana", + "extensions": ["gex","gre"] + }, + "application/vnd.geonext": { + "source": "iana", + "extensions": ["gxt"] + }, + "application/vnd.geoplan": { + "source": "iana", + "extensions": ["g2w"] + }, + "application/vnd.geospace": { + "source": "iana", + "extensions": ["g3w"] + }, + "application/vnd.gerber": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt": { + "source": "iana" + }, + "application/vnd.globalplatform.card-content-mgt-response": { + "source": "iana" + }, + "application/vnd.gmx": { + "source": "iana", + "extensions": ["gmx"] + }, + "application/vnd.google-apps.document": { + "compressible": false, + "extensions": ["gdoc"] + }, + "application/vnd.google-apps.presentation": { + "compressible": false, + "extensions": ["gslides"] + }, + "application/vnd.google-apps.spreadsheet": { + "compressible": false, + "extensions": ["gsheet"] + }, + "application/vnd.google-earth.kml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["kml"] + }, + "application/vnd.google-earth.kmz": { + "source": "iana", + "compressible": false, + "extensions": ["kmz"] + }, + "application/vnd.gov.sk.e-form+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.gov.sk.e-form+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.gov.sk.xmldatacontainer+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.grafeq": { + "source": "iana", + "extensions": ["gqf","gqs"] + }, + "application/vnd.gridmp": { + "source": "iana" + }, + "application/vnd.groove-account": { + "source": "iana", + "extensions": ["gac"] + }, + "application/vnd.groove-help": { + "source": "iana", + "extensions": ["ghf"] + }, + "application/vnd.groove-identity-message": { + "source": "iana", + "extensions": ["gim"] + }, + "application/vnd.groove-injector": { + "source": "iana", + "extensions": ["grv"] + }, + "application/vnd.groove-tool-message": { + "source": "iana", + "extensions": ["gtm"] + }, + "application/vnd.groove-tool-template": { + "source": "iana", + "extensions": ["tpl"] + }, + "application/vnd.groove-vcard": { + "source": "iana", + "extensions": ["vcg"] + }, + "application/vnd.hal+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hal+xml": { + "source": "iana", + "compressible": true, + "extensions": ["hal"] + }, + "application/vnd.handheld-entertainment+xml": { + "source": "iana", + "compressible": true, + "extensions": ["zmm"] + }, + "application/vnd.hbci": { + "source": "iana", + "extensions": ["hbci"] + }, + "application/vnd.hc+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hcl-bireports": { + "source": "iana" + }, + "application/vnd.hdt": { + "source": "iana" + }, + "application/vnd.heroku+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hhe.lesson-player": { + "source": "iana", + "extensions": ["les"] + }, + "application/vnd.hp-hpgl": { + "source": "iana", + "extensions": ["hpgl"] + }, + "application/vnd.hp-hpid": { + "source": "iana", + "extensions": ["hpid"] + }, + "application/vnd.hp-hps": { + "source": "iana", + "extensions": ["hps"] + }, + "application/vnd.hp-jlyt": { + "source": "iana", + "extensions": ["jlt"] + }, + "application/vnd.hp-pcl": { + "source": "iana", + "extensions": ["pcl"] + }, + "application/vnd.hp-pclxl": { + "source": "iana", + "extensions": ["pclxl"] + }, + "application/vnd.httphone": { + "source": "iana" + }, + "application/vnd.hydrostatix.sof-data": { + "source": "iana", + "extensions": ["sfd-hdstx"] + }, + "application/vnd.hyper+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hyper-item+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hyperdrive+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.hzn-3d-crossword": { + "source": "iana" + }, + "application/vnd.ibm.afplinedata": { + "source": "iana" + }, + "application/vnd.ibm.electronic-media": { + "source": "iana" + }, + "application/vnd.ibm.minipay": { + "source": "iana", + "extensions": ["mpy"] + }, + "application/vnd.ibm.modcap": { + "source": "iana", + "extensions": ["afp","listafp","list3820"] + }, + "application/vnd.ibm.rights-management": { + "source": "iana", + "extensions": ["irm"] + }, + "application/vnd.ibm.secure-container": { + "source": "iana", + "extensions": ["sc"] + }, + "application/vnd.iccprofile": { + "source": "iana", + "extensions": ["icc","icm"] + }, + "application/vnd.ieee.1905": { + "source": "iana" + }, + "application/vnd.igloader": { + "source": "iana", + "extensions": ["igl"] + }, + "application/vnd.imagemeter.folder+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.imagemeter.image+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.immervision-ivp": { + "source": "iana", + "extensions": ["ivp"] + }, + "application/vnd.immervision-ivu": { + "source": "iana", + "extensions": ["ivu"] + }, + "application/vnd.ims.imsccv1p1": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p2": { + "source": "iana" + }, + "application/vnd.ims.imsccv1p3": { + "source": "iana" + }, + "application/vnd.ims.lis.v2.result+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolconsumerprofile+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolproxy.id+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.ims.lti.v2.toolsettings.simple+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.informedcontrol.rms+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.informix-visionary": { + "source": "iana" + }, + "application/vnd.infotech.project": { + "source": "iana" + }, + "application/vnd.infotech.project+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.innopath.wamp.notification": { + "source": "iana" + }, + "application/vnd.insors.igm": { + "source": "iana", + "extensions": ["igm"] + }, + "application/vnd.intercon.formnet": { + "source": "iana", + "extensions": ["xpw","xpx"] + }, + "application/vnd.intergeo": { + "source": "iana", + "extensions": ["i2g"] + }, + "application/vnd.intertrust.digibox": { + "source": "iana" + }, + "application/vnd.intertrust.nncp": { + "source": "iana" + }, + "application/vnd.intu.qbo": { + "source": "iana", + "extensions": ["qbo"] + }, + "application/vnd.intu.qfx": { + "source": "iana", + "extensions": ["qfx"] + }, + "application/vnd.iptc.g2.catalogitem+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.iptc.g2.conceptitem+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.iptc.g2.knowledgeitem+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.iptc.g2.newsitem+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.iptc.g2.newsmessage+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.iptc.g2.packageitem+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.iptc.g2.planningitem+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.ipunplugged.rcprofile": { + "source": "iana", + "extensions": ["rcprofile"] + }, + "application/vnd.irepository.package+xml": { + "source": "iana", + "compressible": true, + "extensions": ["irp"] + }, + "application/vnd.is-xpr": { + "source": "iana", + "extensions": ["xpr"] + }, + "application/vnd.isac.fcs": { + "source": "iana", + "extensions": ["fcs"] + }, + "application/vnd.iso11783-10+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.jam": { + "source": "iana", + "extensions": ["jam"] + }, + "application/vnd.japannet-directory-service": { + "source": "iana" + }, + "application/vnd.japannet-jpnstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-payment-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-registration": { + "source": "iana" + }, + "application/vnd.japannet-registration-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-setstore-wakeup": { + "source": "iana" + }, + "application/vnd.japannet-verification": { + "source": "iana" + }, + "application/vnd.japannet-verification-wakeup": { + "source": "iana" + }, + "application/vnd.jcp.javame.midlet-rms": { + "source": "iana", + "extensions": ["rms"] + }, + "application/vnd.jisp": { + "source": "iana", + "extensions": ["jisp"] + }, + "application/vnd.joost.joda-archive": { + "source": "iana", + "extensions": ["joda"] + }, + "application/vnd.jsk.isdn-ngn": { + "source": "iana" + }, + "application/vnd.kahootz": { + "source": "iana", + "extensions": ["ktz","ktr"] + }, + "application/vnd.kde.karbon": { + "source": "iana", + "extensions": ["karbon"] + }, + "application/vnd.kde.kchart": { + "source": "iana", + "extensions": ["chrt"] + }, + "application/vnd.kde.kformula": { + "source": "iana", + "extensions": ["kfo"] + }, + "application/vnd.kde.kivio": { + "source": "iana", + "extensions": ["flw"] + }, + "application/vnd.kde.kontour": { + "source": "iana", + "extensions": ["kon"] + }, + "application/vnd.kde.kpresenter": { + "source": "iana", + "extensions": ["kpr","kpt"] + }, + "application/vnd.kde.kspread": { + "source": "iana", + "extensions": ["ksp"] + }, + "application/vnd.kde.kword": { + "source": "iana", + "extensions": ["kwd","kwt"] + }, + "application/vnd.kenameaapp": { + "source": "iana", + "extensions": ["htke"] + }, + "application/vnd.kidspiration": { + "source": "iana", + "extensions": ["kia"] + }, + "application/vnd.kinar": { + "source": "iana", + "extensions": ["kne","knp"] + }, + "application/vnd.koan": { + "source": "iana", + "extensions": ["skp","skd","skt","skm"] + }, + "application/vnd.kodak-descriptor": { + "source": "iana", + "extensions": ["sse"] + }, + "application/vnd.las": { + "source": "iana" + }, + "application/vnd.las.las+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.las.las+xml": { + "source": "iana", + "compressible": true, + "extensions": ["lasxml"] + }, + "application/vnd.laszip": { + "source": "iana" + }, + "application/vnd.leap+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.liberty-request+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.llamagraphics.life-balance.desktop": { + "source": "iana", + "extensions": ["lbd"] + }, + "application/vnd.llamagraphics.life-balance.exchange+xml": { + "source": "iana", + "compressible": true, + "extensions": ["lbe"] + }, + "application/vnd.logipipe.circuit+zip": { + "source": "iana", + "compressible": false + }, + "application/vnd.loom": { + "source": "iana" + }, + "application/vnd.lotus-1-2-3": { + "source": "iana", + "extensions": ["123"] + }, + "application/vnd.lotus-approach": { + "source": "iana", + "extensions": ["apr"] + }, + "application/vnd.lotus-freelance": { + "source": "iana", + "extensions": ["pre"] + }, + "application/vnd.lotus-notes": { + "source": "iana", + "extensions": ["nsf"] + }, + "application/vnd.lotus-organizer": { + "source": "iana", + "extensions": ["org"] + }, + "application/vnd.lotus-screencam": { + "source": "iana", + "extensions": ["scm"] + }, + "application/vnd.lotus-wordpro": { + "source": "iana", + "extensions": ["lwp"] + }, + "application/vnd.macports.portpkg": { + "source": "iana", + "extensions": ["portpkg"] + }, + "application/vnd.mapbox-vector-tile": { + "source": "iana", + "extensions": ["mvt"] + }, + "application/vnd.marlin.drm.actiontoken+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.marlin.drm.conftoken+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.marlin.drm.license+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.marlin.drm.mdcf": { + "source": "iana" + }, + "application/vnd.mason+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.maxmind.maxmind-db": { + "source": "iana" + }, + "application/vnd.mcd": { + "source": "iana", + "extensions": ["mcd"] + }, + "application/vnd.medcalcdata": { + "source": "iana", + "extensions": ["mc1"] + }, + "application/vnd.mediastation.cdkey": { + "source": "iana", + "extensions": ["cdkey"] + }, + "application/vnd.meridian-slingshot": { + "source": "iana" + }, + "application/vnd.mfer": { + "source": "iana", + "extensions": ["mwf"] + }, + "application/vnd.mfmp": { + "source": "iana", + "extensions": ["mfm"] + }, + "application/vnd.micro+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.micrografx.flo": { + "source": "iana", + "extensions": ["flo"] + }, + "application/vnd.micrografx.igx": { + "source": "iana", + "extensions": ["igx"] + }, + "application/vnd.microsoft.portable-executable": { + "source": "iana" + }, + "application/vnd.microsoft.windows.thumbnail-cache": { + "source": "iana" + }, + "application/vnd.miele+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.mif": { + "source": "iana", + "extensions": ["mif"] + }, + "application/vnd.minisoft-hp3000-save": { + "source": "iana" + }, + "application/vnd.mitsubishi.misty-guard.trustweb": { + "source": "iana" + }, + "application/vnd.mobius.daf": { + "source": "iana", + "extensions": ["daf"] + }, + "application/vnd.mobius.dis": { + "source": "iana", + "extensions": ["dis"] + }, + "application/vnd.mobius.mbk": { + "source": "iana", + "extensions": ["mbk"] + }, + "application/vnd.mobius.mqy": { + "source": "iana", + "extensions": ["mqy"] + }, + "application/vnd.mobius.msl": { + "source": "iana", + "extensions": ["msl"] + }, + "application/vnd.mobius.plc": { + "source": "iana", + "extensions": ["plc"] + }, + "application/vnd.mobius.txf": { + "source": "iana", + "extensions": ["txf"] + }, + "application/vnd.mophun.application": { + "source": "iana", + "extensions": ["mpn"] + }, + "application/vnd.mophun.certificate": { + "source": "iana", + "extensions": ["mpc"] + }, + "application/vnd.motorola.flexsuite": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.adsi": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.fis": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.gotap": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.kmr": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.ttc": { + "source": "iana" + }, + "application/vnd.motorola.flexsuite.wem": { + "source": "iana" + }, + "application/vnd.motorola.iprm": { + "source": "iana" + }, + "application/vnd.mozilla.xul+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xul"] + }, + "application/vnd.ms-3mfdocument": { + "source": "iana" + }, + "application/vnd.ms-artgalry": { + "source": "iana", + "extensions": ["cil"] + }, + "application/vnd.ms-asf": { + "source": "iana" + }, + "application/vnd.ms-cab-compressed": { + "source": "iana", + "extensions": ["cab"] + }, + "application/vnd.ms-color.iccprofile": { + "source": "apache" + }, + "application/vnd.ms-excel": { + "source": "iana", + "compressible": false, + "extensions": ["xls","xlm","xla","xlc","xlt","xlw"] + }, + "application/vnd.ms-excel.addin.macroenabled.12": { + "source": "iana", + "extensions": ["xlam"] + }, + "application/vnd.ms-excel.sheet.binary.macroenabled.12": { + "source": "iana", + "extensions": ["xlsb"] + }, + "application/vnd.ms-excel.sheet.macroenabled.12": { + "source": "iana", + "extensions": ["xlsm"] + }, + "application/vnd.ms-excel.template.macroenabled.12": { + "source": "iana", + "extensions": ["xltm"] + }, + "application/vnd.ms-fontobject": { + "source": "iana", + "compressible": true, + "extensions": ["eot"] + }, + "application/vnd.ms-htmlhelp": { + "source": "iana", + "extensions": ["chm"] + }, + "application/vnd.ms-ims": { + "source": "iana", + "extensions": ["ims"] + }, + "application/vnd.ms-lrm": { + "source": "iana", + "extensions": ["lrm"] + }, + "application/vnd.ms-office.activex+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.ms-officetheme": { + "source": "iana", + "extensions": ["thmx"] + }, + "application/vnd.ms-opentype": { + "source": "apache", + "compressible": true + }, + "application/vnd.ms-outlook": { + "compressible": false, + "extensions": ["msg"] + }, + "application/vnd.ms-package.obfuscated-opentype": { + "source": "apache" + }, + "application/vnd.ms-pki.seccat": { + "source": "apache", + "extensions": ["cat"] + }, + "application/vnd.ms-pki.stl": { + "source": "apache", + "extensions": ["stl"] + }, + "application/vnd.ms-playready.initiator+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.ms-powerpoint": { + "source": "iana", + "compressible": false, + "extensions": ["ppt","pps","pot"] + }, + "application/vnd.ms-powerpoint.addin.macroenabled.12": { + "source": "iana", + "extensions": ["ppam"] + }, + "application/vnd.ms-powerpoint.presentation.macroenabled.12": { + "source": "iana", + "extensions": ["pptm"] + }, + "application/vnd.ms-powerpoint.slide.macroenabled.12": { + "source": "iana", + "extensions": ["sldm"] + }, + "application/vnd.ms-powerpoint.slideshow.macroenabled.12": { + "source": "iana", + "extensions": ["ppsm"] + }, + "application/vnd.ms-powerpoint.template.macroenabled.12": { + "source": "iana", + "extensions": ["potm"] + }, + "application/vnd.ms-printdevicecapabilities+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.ms-printing.printticket+xml": { + "source": "apache", + "compressible": true + }, + "application/vnd.ms-printschematicket+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.ms-project": { + "source": "iana", + "extensions": ["mpp","mpt"] + }, + "application/vnd.ms-tnef": { + "source": "iana" + }, + "application/vnd.ms-windows.devicepairing": { + "source": "iana" + }, + "application/vnd.ms-windows.nwprinting.oob": { + "source": "iana" + }, + "application/vnd.ms-windows.printerpairing": { + "source": "iana" + }, + "application/vnd.ms-windows.wsd.oob": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.lic-resp": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-chlg-req": { + "source": "iana" + }, + "application/vnd.ms-wmdrm.meter-resp": { + "source": "iana" + }, + "application/vnd.ms-word.document.macroenabled.12": { + "source": "iana", + "extensions": ["docm"] + }, + "application/vnd.ms-word.template.macroenabled.12": { + "source": "iana", + "extensions": ["dotm"] + }, + "application/vnd.ms-works": { + "source": "iana", + "extensions": ["wps","wks","wcm","wdb"] + }, + "application/vnd.ms-wpl": { + "source": "iana", + "extensions": ["wpl"] + }, + "application/vnd.ms-xpsdocument": { + "source": "iana", + "compressible": false, + "extensions": ["xps"] + }, + "application/vnd.msa-disk-image": { + "source": "iana" + }, + "application/vnd.mseq": { + "source": "iana", + "extensions": ["mseq"] + }, + "application/vnd.msign": { + "source": "iana" + }, + "application/vnd.multiad.creator": { + "source": "iana" + }, + "application/vnd.multiad.creator.cif": { + "source": "iana" + }, + "application/vnd.music-niff": { + "source": "iana" + }, + "application/vnd.musician": { + "source": "iana", + "extensions": ["mus"] + }, + "application/vnd.muvee.style": { + "source": "iana", + "extensions": ["msty"] + }, + "application/vnd.mynfc": { + "source": "iana", + "extensions": ["taglet"] + }, + "application/vnd.ncd.control": { + "source": "iana" + }, + "application/vnd.ncd.reference": { + "source": "iana" + }, + "application/vnd.nearst.inv+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.nebumind.line": { + "source": "iana" + }, + "application/vnd.nervana": { + "source": "iana" + }, + "application/vnd.netfpx": { + "source": "iana" + }, + "application/vnd.neurolanguage.nlu": { + "source": "iana", + "extensions": ["nlu"] + }, + "application/vnd.nimn": { + "source": "iana" + }, + "application/vnd.nintendo.nitro.rom": { + "source": "iana" + }, + "application/vnd.nintendo.snes.rom": { + "source": "iana" + }, + "application/vnd.nitf": { + "source": "iana", + "extensions": ["ntf","nitf"] + }, + "application/vnd.noblenet-directory": { + "source": "iana", + "extensions": ["nnd"] + }, + "application/vnd.noblenet-sealer": { + "source": "iana", + "extensions": ["nns"] + }, + "application/vnd.noblenet-web": { + "source": "iana", + "extensions": ["nnw"] + }, + "application/vnd.nokia.catalogs": { + "source": "iana" + }, + "application/vnd.nokia.conml+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.conml+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.nokia.iptv.config+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.nokia.isds-radio-presets": { + "source": "iana" + }, + "application/vnd.nokia.landmark+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.landmark+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.nokia.landmarkcollection+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.nokia.n-gage.ac+xml": { + "source": "iana", + "compressible": true, + "extensions": ["ac"] + }, + "application/vnd.nokia.n-gage.data": { + "source": "iana", + "extensions": ["ngdat"] + }, + "application/vnd.nokia.n-gage.symbian.install": { + "source": "iana", + "extensions": ["n-gage"] + }, + "application/vnd.nokia.ncd": { + "source": "iana" + }, + "application/vnd.nokia.pcd+wbxml": { + "source": "iana" + }, + "application/vnd.nokia.pcd+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.nokia.radio-preset": { + "source": "iana", + "extensions": ["rpst"] + }, + "application/vnd.nokia.radio-presets": { + "source": "iana", + "extensions": ["rpss"] + }, + "application/vnd.novadigm.edm": { + "source": "iana", + "extensions": ["edm"] + }, + "application/vnd.novadigm.edx": { + "source": "iana", + "extensions": ["edx"] + }, + "application/vnd.novadigm.ext": { + "source": "iana", + "extensions": ["ext"] + }, + "application/vnd.ntt-local.content-share": { + "source": "iana" + }, + "application/vnd.ntt-local.file-transfer": { + "source": "iana" + }, + "application/vnd.ntt-local.ogw_remote-access": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_remote": { + "source": "iana" + }, + "application/vnd.ntt-local.sip-ta_tcp_stream": { + "source": "iana" + }, + "application/vnd.oasis.opendocument.chart": { + "source": "iana", + "extensions": ["odc"] + }, + "application/vnd.oasis.opendocument.chart-template": { + "source": "iana", + "extensions": ["otc"] + }, + "application/vnd.oasis.opendocument.database": { + "source": "iana", + "extensions": ["odb"] + }, + "application/vnd.oasis.opendocument.formula": { + "source": "iana", + "extensions": ["odf"] + }, + "application/vnd.oasis.opendocument.formula-template": { + "source": "iana", + "extensions": ["odft"] + }, + "application/vnd.oasis.opendocument.graphics": { + "source": "iana", + "compressible": false, + "extensions": ["odg"] + }, + "application/vnd.oasis.opendocument.graphics-template": { + "source": "iana", + "extensions": ["otg"] + }, + "application/vnd.oasis.opendocument.image": { + "source": "iana", + "extensions": ["odi"] + }, + "application/vnd.oasis.opendocument.image-template": { + "source": "iana", + "extensions": ["oti"] + }, + "application/vnd.oasis.opendocument.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["odp"] + }, + "application/vnd.oasis.opendocument.presentation-template": { + "source": "iana", + "extensions": ["otp"] + }, + "application/vnd.oasis.opendocument.spreadsheet": { + "source": "iana", + "compressible": false, + "extensions": ["ods"] + }, + "application/vnd.oasis.opendocument.spreadsheet-template": { + "source": "iana", + "extensions": ["ots"] + }, + "application/vnd.oasis.opendocument.text": { + "source": "iana", + "compressible": false, + "extensions": ["odt"] + }, + "application/vnd.oasis.opendocument.text-master": { + "source": "iana", + "extensions": ["odm"] + }, + "application/vnd.oasis.opendocument.text-template": { + "source": "iana", + "extensions": ["ott"] + }, + "application/vnd.oasis.opendocument.text-web": { + "source": "iana", + "extensions": ["oth"] + }, + "application/vnd.obn": { + "source": "iana" + }, + "application/vnd.ocf+cbor": { + "source": "iana" + }, + "application/vnd.oci.image.manifest.v1+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.oftn.l10n+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.contentaccessdownload+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.contentaccessstreaming+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.cspg-hexbinary": { + "source": "iana" + }, + "application/vnd.oipf.dae.svg+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.dae.xhtml+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.mippvcontrolmessage+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.pae.gem": { + "source": "iana" + }, + "application/vnd.oipf.spdiscovery+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.spdlist+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.ueprofile+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oipf.userprofile+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.olpc-sugar": { + "source": "iana", + "extensions": ["xo"] + }, + "application/vnd.oma-scws-config": { + "source": "iana" + }, + "application/vnd.oma-scws-http-request": { + "source": "iana" + }, + "application/vnd.oma-scws-http-response": { + "source": "iana" + }, + "application/vnd.oma.bcast.associated-procedure-parameter+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.bcast.drm-trigger+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.bcast.imd+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.bcast.ltkm": { + "source": "iana" + }, + "application/vnd.oma.bcast.notification+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.bcast.provisioningtrigger": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgboot": { + "source": "iana" + }, + "application/vnd.oma.bcast.sgdd+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.bcast.sgdu": { + "source": "iana" + }, + "application/vnd.oma.bcast.simple-symbol-container": { + "source": "iana" + }, + "application/vnd.oma.bcast.smartcard-trigger+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.bcast.sprov+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.bcast.stkm": { + "source": "iana" + }, + "application/vnd.oma.cab-address-book+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.cab-feature-handler+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.cab-pcc+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.cab-subs-invite+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.cab-user-prefs+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.dcd": { + "source": "iana" + }, + "application/vnd.oma.dcdc": { + "source": "iana" + }, + "application/vnd.oma.dd2+xml": { + "source": "iana", + "compressible": true, + "extensions": ["dd2"] + }, + "application/vnd.oma.drm.risd+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.group-usage-list+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.lwm2m+cbor": { + "source": "iana" + }, + "application/vnd.oma.lwm2m+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.lwm2m+tlv": { + "source": "iana" + }, + "application/vnd.oma.pal+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.poc.detailed-progress-report+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.poc.final-report+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.poc.groups+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.poc.invocation-descriptor+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.poc.optimized-progress-report+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.push": { + "source": "iana" + }, + "application/vnd.oma.scidm.messages+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oma.xcap-directory+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.omads-email+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/vnd.omads-file+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/vnd.omads-folder+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/vnd.omaloc-supl-init": { + "source": "iana" + }, + "application/vnd.onepager": { + "source": "iana" + }, + "application/vnd.onepagertamp": { + "source": "iana" + }, + "application/vnd.onepagertamx": { + "source": "iana" + }, + "application/vnd.onepagertat": { + "source": "iana" + }, + "application/vnd.onepagertatp": { + "source": "iana" + }, + "application/vnd.onepagertatx": { + "source": "iana" + }, + "application/vnd.openblox.game+xml": { + "source": "iana", + "compressible": true, + "extensions": ["obgx"] + }, + "application/vnd.openblox.game-binary": { + "source": "iana" + }, + "application/vnd.openeye.oeb": { + "source": "iana" + }, + "application/vnd.openofficeorg.extension": { + "source": "apache", + "extensions": ["oxt"] + }, + "application/vnd.openstreetmap.data+xml": { + "source": "iana", + "compressible": true, + "extensions": ["osm"] + }, + "application/vnd.opentimestamps.ots": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.custom-properties+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.customxmlproperties+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.drawing+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.drawingml.chart+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.extended-properties+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation": { + "source": "iana", + "compressible": false, + "extensions": ["pptx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.presprops+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide": { + "source": "iana", + "extensions": ["sldx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": { + "source": "iana", + "extensions": ["ppsx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.tags+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.template": { + "source": "iana", + "extensions": ["potx"] + }, + "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "source": "iana", + "compressible": false, + "extensions": ["xlsx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": { + "source": "iana", + "extensions": ["xltx"] + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.theme+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.themeoverride+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.vmldrawing": { + "source": "iana" + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { + "source": "iana", + "compressible": false, + "extensions": ["docx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": { + "source": "iana", + "extensions": ["dotx"] + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-package.core-properties+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.openxmlformats-package.relationships+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oracle.resource+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.orange.indata": { + "source": "iana" + }, + "application/vnd.osa.netdeploy": { + "source": "iana" + }, + "application/vnd.osgeo.mapguide.package": { + "source": "iana", + "extensions": ["mgp"] + }, + "application/vnd.osgi.bundle": { + "source": "iana" + }, + "application/vnd.osgi.dp": { + "source": "iana", + "extensions": ["dp"] + }, + "application/vnd.osgi.subsystem": { + "source": "iana", + "extensions": ["esa"] + }, + "application/vnd.otps.ct-kip+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.oxli.countgraph": { + "source": "iana" + }, + "application/vnd.pagerduty+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.palm": { + "source": "iana", + "extensions": ["pdb","pqa","oprc"] + }, + "application/vnd.panoply": { + "source": "iana" + }, + "application/vnd.paos.xml": { + "source": "iana" + }, + "application/vnd.patentdive": { + "source": "iana" + }, + "application/vnd.patientecommsdoc": { + "source": "iana" + }, + "application/vnd.pawaafile": { + "source": "iana", + "extensions": ["paw"] + }, + "application/vnd.pcos": { + "source": "iana" + }, + "application/vnd.pg.format": { + "source": "iana", + "extensions": ["str"] + }, + "application/vnd.pg.osasli": { + "source": "iana", + "extensions": ["ei6"] + }, + "application/vnd.piaccess.application-licence": { + "source": "iana" + }, + "application/vnd.picsel": { + "source": "iana", + "extensions": ["efif"] + }, + "application/vnd.pmi.widget": { + "source": "iana", + "extensions": ["wg"] + }, + "application/vnd.poc.group-advertisement+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.pocketlearn": { + "source": "iana", + "extensions": ["plf"] + }, + "application/vnd.powerbuilder6": { + "source": "iana", + "extensions": ["pbd"] + }, + "application/vnd.powerbuilder6-s": { + "source": "iana" + }, + "application/vnd.powerbuilder7": { + "source": "iana" + }, + "application/vnd.powerbuilder7-s": { + "source": "iana" + }, + "application/vnd.powerbuilder75": { + "source": "iana" + }, + "application/vnd.powerbuilder75-s": { + "source": "iana" + }, + "application/vnd.preminet": { + "source": "iana" + }, + "application/vnd.previewsystems.box": { + "source": "iana", + "extensions": ["box"] + }, + "application/vnd.proteus.magazine": { + "source": "iana", + "extensions": ["mgz"] + }, + "application/vnd.psfs": { + "source": "iana" + }, + "application/vnd.publishare-delta-tree": { + "source": "iana", + "extensions": ["qps"] + }, + "application/vnd.pvi.ptid1": { + "source": "iana", + "extensions": ["ptid"] + }, + "application/vnd.pwg-multiplexed": { + "source": "iana" + }, + "application/vnd.pwg-xhtml-print+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.qualcomm.brew-app-res": { + "source": "iana" + }, + "application/vnd.quarantainenet": { + "source": "iana" + }, + "application/vnd.quark.quarkxpress": { + "source": "iana", + "extensions": ["qxd","qxt","qwd","qwt","qxl","qxb"] + }, + "application/vnd.quobject-quoxdocument": { + "source": "iana" + }, + "application/vnd.radisys.moml+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-audit+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-audit-conf+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-audit-conn+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-audit-dialog+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-audit-stream+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-conf+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-dialog+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-dialog-base+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-dialog-fax-detect+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-dialog-fax-sendrecv+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-dialog-group+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-dialog-speech+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.radisys.msml-dialog-transform+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.rainstor.data": { + "source": "iana" + }, + "application/vnd.rapid": { + "source": "iana" + }, + "application/vnd.rar": { + "source": "iana", + "extensions": ["rar"] + }, + "application/vnd.realvnc.bed": { + "source": "iana", + "extensions": ["bed"] + }, + "application/vnd.recordare.musicxml": { + "source": "iana", + "extensions": ["mxl"] + }, + "application/vnd.recordare.musicxml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["musicxml"] + }, + "application/vnd.renlearn.rlprint": { + "source": "iana" + }, + "application/vnd.resilient.logic": { + "source": "iana" + }, + "application/vnd.restful+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.rig.cryptonote": { + "source": "iana", + "extensions": ["cryptonote"] + }, + "application/vnd.rim.cod": { + "source": "apache", + "extensions": ["cod"] + }, + "application/vnd.rn-realmedia": { + "source": "apache", + "extensions": ["rm"] + }, + "application/vnd.rn-realmedia-vbr": { + "source": "apache", + "extensions": ["rmvb"] + }, + "application/vnd.route66.link66+xml": { + "source": "iana", + "compressible": true, + "extensions": ["link66"] + }, + "application/vnd.rs-274x": { + "source": "iana" + }, + "application/vnd.ruckus.download": { + "source": "iana" + }, + "application/vnd.s3sms": { + "source": "iana" + }, + "application/vnd.sailingtracker.track": { + "source": "iana", + "extensions": ["st"] + }, + "application/vnd.sar": { + "source": "iana" + }, + "application/vnd.sbm.cid": { + "source": "iana" + }, + "application/vnd.sbm.mid2": { + "source": "iana" + }, + "application/vnd.scribus": { + "source": "iana" + }, + "application/vnd.sealed.3df": { + "source": "iana" + }, + "application/vnd.sealed.csf": { + "source": "iana" + }, + "application/vnd.sealed.doc": { + "source": "iana" + }, + "application/vnd.sealed.eml": { + "source": "iana" + }, + "application/vnd.sealed.mht": { + "source": "iana" + }, + "application/vnd.sealed.net": { + "source": "iana" + }, + "application/vnd.sealed.ppt": { + "source": "iana" + }, + "application/vnd.sealed.tiff": { + "source": "iana" + }, + "application/vnd.sealed.xls": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.html": { + "source": "iana" + }, + "application/vnd.sealedmedia.softseal.pdf": { + "source": "iana" + }, + "application/vnd.seemail": { + "source": "iana", + "extensions": ["see"] + }, + "application/vnd.seis+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.sema": { + "source": "iana", + "extensions": ["sema"] + }, + "application/vnd.semd": { + "source": "iana", + "extensions": ["semd"] + }, + "application/vnd.semf": { + "source": "iana", + "extensions": ["semf"] + }, + "application/vnd.shade-save-file": { + "source": "iana" + }, + "application/vnd.shana.informed.formdata": { + "source": "iana", + "extensions": ["ifm"] + }, + "application/vnd.shana.informed.formtemplate": { + "source": "iana", + "extensions": ["itp"] + }, + "application/vnd.shana.informed.interchange": { + "source": "iana", + "extensions": ["iif"] + }, + "application/vnd.shana.informed.package": { + "source": "iana", + "extensions": ["ipk"] + }, + "application/vnd.shootproof+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.shopkick+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.shp": { + "source": "iana" + }, + "application/vnd.shx": { + "source": "iana" + }, + "application/vnd.sigrok.session": { + "source": "iana" + }, + "application/vnd.simtech-mindmapper": { + "source": "iana", + "extensions": ["twd","twds"] + }, + "application/vnd.siren+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.smaf": { + "source": "iana", + "extensions": ["mmf"] + }, + "application/vnd.smart.notebook": { + "source": "iana" + }, + "application/vnd.smart.teacher": { + "source": "iana", + "extensions": ["teacher"] + }, + "application/vnd.snesdev-page-table": { + "source": "iana" + }, + "application/vnd.software602.filler.form+xml": { + "source": "iana", + "compressible": true, + "extensions": ["fo"] + }, + "application/vnd.software602.filler.form-xml-zip": { + "source": "iana" + }, + "application/vnd.solent.sdkm+xml": { + "source": "iana", + "compressible": true, + "extensions": ["sdkm","sdkd"] + }, + "application/vnd.spotfire.dxp": { + "source": "iana", + "extensions": ["dxp"] + }, + "application/vnd.spotfire.sfs": { + "source": "iana", + "extensions": ["sfs"] + }, + "application/vnd.sqlite3": { + "source": "iana" + }, + "application/vnd.sss-cod": { + "source": "iana" + }, + "application/vnd.sss-dtf": { + "source": "iana" + }, + "application/vnd.sss-ntf": { + "source": "iana" + }, + "application/vnd.stardivision.calc": { + "source": "apache", + "extensions": ["sdc"] + }, + "application/vnd.stardivision.draw": { + "source": "apache", + "extensions": ["sda"] + }, + "application/vnd.stardivision.impress": { + "source": "apache", + "extensions": ["sdd"] + }, + "application/vnd.stardivision.math": { + "source": "apache", + "extensions": ["smf"] + }, + "application/vnd.stardivision.writer": { + "source": "apache", + "extensions": ["sdw","vor"] + }, + "application/vnd.stardivision.writer-global": { + "source": "apache", + "extensions": ["sgl"] + }, + "application/vnd.stepmania.package": { + "source": "iana", + "extensions": ["smzip"] + }, + "application/vnd.stepmania.stepchart": { + "source": "iana", + "extensions": ["sm"] + }, + "application/vnd.street-stream": { + "source": "iana" + }, + "application/vnd.sun.wadl+xml": { + "source": "iana", + "compressible": true, + "extensions": ["wadl"] + }, + "application/vnd.sun.xml.calc": { + "source": "apache", + "extensions": ["sxc"] + }, + "application/vnd.sun.xml.calc.template": { + "source": "apache", + "extensions": ["stc"] + }, + "application/vnd.sun.xml.draw": { + "source": "apache", + "extensions": ["sxd"] + }, + "application/vnd.sun.xml.draw.template": { + "source": "apache", + "extensions": ["std"] + }, + "application/vnd.sun.xml.impress": { + "source": "apache", + "extensions": ["sxi"] + }, + "application/vnd.sun.xml.impress.template": { + "source": "apache", + "extensions": ["sti"] + }, + "application/vnd.sun.xml.math": { + "source": "apache", + "extensions": ["sxm"] + }, + "application/vnd.sun.xml.writer": { + "source": "apache", + "extensions": ["sxw"] + }, + "application/vnd.sun.xml.writer.global": { + "source": "apache", + "extensions": ["sxg"] + }, + "application/vnd.sun.xml.writer.template": { + "source": "apache", + "extensions": ["stw"] + }, + "application/vnd.sus-calendar": { + "source": "iana", + "extensions": ["sus","susp"] + }, + "application/vnd.svd": { + "source": "iana", + "extensions": ["svd"] + }, + "application/vnd.swiftview-ics": { + "source": "iana" + }, + "application/vnd.sycle+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.symbian.install": { + "source": "apache", + "extensions": ["sis","sisx"] + }, + "application/vnd.syncml+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["xsm"] + }, + "application/vnd.syncml.dm+wbxml": { + "source": "iana", + "charset": "UTF-8", + "extensions": ["bdm"] + }, + "application/vnd.syncml.dm+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["xdm"] + }, + "application/vnd.syncml.dm.notification": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmddf+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["ddf"] + }, + "application/vnd.syncml.dmtnds+wbxml": { + "source": "iana" + }, + "application/vnd.syncml.dmtnds+xml": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/vnd.syncml.ds.notification": { + "source": "iana" + }, + "application/vnd.tableschema+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.tao.intent-module-archive": { + "source": "iana", + "extensions": ["tao"] + }, + "application/vnd.tcpdump.pcap": { + "source": "iana", + "extensions": ["pcap","cap","dmp"] + }, + "application/vnd.think-cell.ppttc+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.tmd.mediaflex.api+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.tml": { + "source": "iana" + }, + "application/vnd.tmobile-livetv": { + "source": "iana", + "extensions": ["tmo"] + }, + "application/vnd.tri.onesource": { + "source": "iana" + }, + "application/vnd.trid.tpt": { + "source": "iana", + "extensions": ["tpt"] + }, + "application/vnd.triscape.mxs": { + "source": "iana", + "extensions": ["mxs"] + }, + "application/vnd.trueapp": { + "source": "iana", + "extensions": ["tra"] + }, + "application/vnd.truedoc": { + "source": "iana" + }, + "application/vnd.ubisoft.webplayer": { + "source": "iana" + }, + "application/vnd.ufdl": { + "source": "iana", + "extensions": ["ufd","ufdl"] + }, + "application/vnd.uiq.theme": { + "source": "iana", + "extensions": ["utz"] + }, + "application/vnd.umajin": { + "source": "iana", + "extensions": ["umj"] + }, + "application/vnd.unity": { + "source": "iana", + "extensions": ["unityweb"] + }, + "application/vnd.uoml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["uoml"] + }, + "application/vnd.uplanet.alert": { + "source": "iana" + }, + "application/vnd.uplanet.alert-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice": { + "source": "iana" + }, + "application/vnd.uplanet.bearer-choice-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop": { + "source": "iana" + }, + "application/vnd.uplanet.cacheop-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.channel": { + "source": "iana" + }, + "application/vnd.uplanet.channel-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.list": { + "source": "iana" + }, + "application/vnd.uplanet.list-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd": { + "source": "iana" + }, + "application/vnd.uplanet.listcmd-wbxml": { + "source": "iana" + }, + "application/vnd.uplanet.signal": { + "source": "iana" + }, + "application/vnd.uri-map": { + "source": "iana" + }, + "application/vnd.valve.source.material": { + "source": "iana" + }, + "application/vnd.vcx": { + "source": "iana", + "extensions": ["vcx"] + }, + "application/vnd.vd-study": { + "source": "iana" + }, + "application/vnd.vectorworks": { + "source": "iana" + }, + "application/vnd.vel+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.verimatrix.vcas": { + "source": "iana" + }, + "application/vnd.veritone.aion+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.veryant.thin": { + "source": "iana" + }, + "application/vnd.ves.encrypted": { + "source": "iana" + }, + "application/vnd.vidsoft.vidconference": { + "source": "iana" + }, + "application/vnd.visio": { + "source": "iana", + "extensions": ["vsd","vst","vss","vsw"] + }, + "application/vnd.visionary": { + "source": "iana", + "extensions": ["vis"] + }, + "application/vnd.vividence.scriptfile": { + "source": "iana" + }, + "application/vnd.vsf": { + "source": "iana", + "extensions": ["vsf"] + }, + "application/vnd.wap.sic": { + "source": "iana" + }, + "application/vnd.wap.slc": { + "source": "iana" + }, + "application/vnd.wap.wbxml": { + "source": "iana", + "charset": "UTF-8", + "extensions": ["wbxml"] + }, + "application/vnd.wap.wmlc": { + "source": "iana", + "extensions": ["wmlc"] + }, + "application/vnd.wap.wmlscriptc": { + "source": "iana", + "extensions": ["wmlsc"] + }, + "application/vnd.webturbo": { + "source": "iana", + "extensions": ["wtb"] + }, + "application/vnd.wfa.dpp": { + "source": "iana" + }, + "application/vnd.wfa.p2p": { + "source": "iana" + }, + "application/vnd.wfa.wsc": { + "source": "iana" + }, + "application/vnd.windows.devicepairing": { + "source": "iana" + }, + "application/vnd.wmc": { + "source": "iana" + }, + "application/vnd.wmf.bootstrap": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica": { + "source": "iana" + }, + "application/vnd.wolfram.mathematica.package": { + "source": "iana" + }, + "application/vnd.wolfram.player": { + "source": "iana", + "extensions": ["nbp"] + }, + "application/vnd.wordperfect": { + "source": "iana", + "extensions": ["wpd"] + }, + "application/vnd.wqd": { + "source": "iana", + "extensions": ["wqd"] + }, + "application/vnd.wrq-hp3000-labelled": { + "source": "iana" + }, + "application/vnd.wt.stf": { + "source": "iana", + "extensions": ["stf"] + }, + "application/vnd.wv.csp+wbxml": { + "source": "iana" + }, + "application/vnd.wv.csp+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.wv.ssp+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.xacml+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.xara": { + "source": "iana", + "extensions": ["xar"] + }, + "application/vnd.xfdl": { + "source": "iana", + "extensions": ["xfdl"] + }, + "application/vnd.xfdl.webform": { + "source": "iana" + }, + "application/vnd.xmi+xml": { + "source": "iana", + "compressible": true + }, + "application/vnd.xmpie.cpkg": { + "source": "iana" + }, + "application/vnd.xmpie.dpkg": { + "source": "iana" + }, + "application/vnd.xmpie.plan": { + "source": "iana" + }, + "application/vnd.xmpie.ppkg": { + "source": "iana" + }, + "application/vnd.xmpie.xlim": { + "source": "iana" + }, + "application/vnd.yamaha.hv-dic": { + "source": "iana", + "extensions": ["hvd"] + }, + "application/vnd.yamaha.hv-script": { + "source": "iana", + "extensions": ["hvs"] + }, + "application/vnd.yamaha.hv-voice": { + "source": "iana", + "extensions": ["hvp"] + }, + "application/vnd.yamaha.openscoreformat": { + "source": "iana", + "extensions": ["osf"] + }, + "application/vnd.yamaha.openscoreformat.osfpvg+xml": { + "source": "iana", + "compressible": true, + "extensions": ["osfpvg"] + }, + "application/vnd.yamaha.remote-setup": { + "source": "iana" + }, + "application/vnd.yamaha.smaf-audio": { + "source": "iana", + "extensions": ["saf"] + }, + "application/vnd.yamaha.smaf-phrase": { + "source": "iana", + "extensions": ["spf"] + }, + "application/vnd.yamaha.through-ngn": { + "source": "iana" + }, + "application/vnd.yamaha.tunnel-udpencap": { + "source": "iana" + }, + "application/vnd.yaoweme": { + "source": "iana" + }, + "application/vnd.yellowriver-custom-menu": { + "source": "iana", + "extensions": ["cmp"] + }, + "application/vnd.youtube.yt": { + "source": "iana" + }, + "application/vnd.zul": { + "source": "iana", + "extensions": ["zir","zirz"] + }, + "application/vnd.zzazz.deck+xml": { + "source": "iana", + "compressible": true, + "extensions": ["zaz"] + }, + "application/voicexml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["vxml"] + }, + "application/voucher-cms+json": { + "source": "iana", + "compressible": true + }, + "application/vq-rtcpxr": { + "source": "iana" + }, + "application/wasm": { + "source": "iana", + "compressible": true, + "extensions": ["wasm"] + }, + "application/watcherinfo+xml": { + "source": "iana", + "compressible": true + }, + "application/webpush-options+json": { + "source": "iana", + "compressible": true + }, + "application/whoispp-query": { + "source": "iana" + }, + "application/whoispp-response": { + "source": "iana" + }, + "application/widget": { + "source": "iana", + "extensions": ["wgt"] + }, + "application/winhlp": { + "source": "apache", + "extensions": ["hlp"] + }, + "application/wita": { + "source": "iana" + }, + "application/wordperfect5.1": { + "source": "iana" + }, + "application/wsdl+xml": { + "source": "iana", + "compressible": true, + "extensions": ["wsdl"] + }, + "application/wspolicy+xml": { + "source": "iana", + "compressible": true, + "extensions": ["wspolicy"] + }, + "application/x-7z-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["7z"] + }, + "application/x-abiword": { + "source": "apache", + "extensions": ["abw"] + }, + "application/x-ace-compressed": { + "source": "apache", + "extensions": ["ace"] + }, + "application/x-amf": { + "source": "apache" + }, + "application/x-apple-diskimage": { + "source": "apache", + "extensions": ["dmg"] + }, + "application/x-arj": { + "compressible": false, + "extensions": ["arj"] + }, + "application/x-authorware-bin": { + "source": "apache", + "extensions": ["aab","x32","u32","vox"] + }, + "application/x-authorware-map": { + "source": "apache", + "extensions": ["aam"] + }, + "application/x-authorware-seg": { + "source": "apache", + "extensions": ["aas"] + }, + "application/x-bcpio": { + "source": "apache", + "extensions": ["bcpio"] + }, + "application/x-bdoc": { + "compressible": false, + "extensions": ["bdoc"] + }, + "application/x-bittorrent": { + "source": "apache", + "extensions": ["torrent"] + }, + "application/x-blorb": { + "source": "apache", + "extensions": ["blb","blorb"] + }, + "application/x-bzip": { + "source": "apache", + "compressible": false, + "extensions": ["bz"] + }, + "application/x-bzip2": { + "source": "apache", + "compressible": false, + "extensions": ["bz2","boz"] + }, + "application/x-cbr": { + "source": "apache", + "extensions": ["cbr","cba","cbt","cbz","cb7"] + }, + "application/x-cdlink": { + "source": "apache", + "extensions": ["vcd"] + }, + "application/x-cfs-compressed": { + "source": "apache", + "extensions": ["cfs"] + }, + "application/x-chat": { + "source": "apache", + "extensions": ["chat"] + }, + "application/x-chess-pgn": { + "source": "apache", + "extensions": ["pgn"] + }, + "application/x-chrome-extension": { + "extensions": ["crx"] + }, + "application/x-cocoa": { + "source": "nginx", + "extensions": ["cco"] + }, + "application/x-compress": { + "source": "apache" + }, + "application/x-conference": { + "source": "apache", + "extensions": ["nsc"] + }, + "application/x-cpio": { + "source": "apache", + "extensions": ["cpio"] + }, + "application/x-csh": { + "source": "apache", + "extensions": ["csh"] + }, + "application/x-deb": { + "compressible": false + }, + "application/x-debian-package": { + "source": "apache", + "extensions": ["deb","udeb"] + }, + "application/x-dgc-compressed": { + "source": "apache", + "extensions": ["dgc"] + }, + "application/x-director": { + "source": "apache", + "extensions": ["dir","dcr","dxr","cst","cct","cxt","w3d","fgd","swa"] + }, + "application/x-doom": { + "source": "apache", + "extensions": ["wad"] + }, + "application/x-dtbncx+xml": { + "source": "apache", + "compressible": true, + "extensions": ["ncx"] + }, + "application/x-dtbook+xml": { + "source": "apache", + "compressible": true, + "extensions": ["dtb"] + }, + "application/x-dtbresource+xml": { + "source": "apache", + "compressible": true, + "extensions": ["res"] + }, + "application/x-dvi": { + "source": "apache", + "compressible": false, + "extensions": ["dvi"] + }, + "application/x-envoy": { + "source": "apache", + "extensions": ["evy"] + }, + "application/x-eva": { + "source": "apache", + "extensions": ["eva"] + }, + "application/x-font-bdf": { + "source": "apache", + "extensions": ["bdf"] + }, + "application/x-font-dos": { + "source": "apache" + }, + "application/x-font-framemaker": { + "source": "apache" + }, + "application/x-font-ghostscript": { + "source": "apache", + "extensions": ["gsf"] + }, + "application/x-font-libgrx": { + "source": "apache" + }, + "application/x-font-linux-psf": { + "source": "apache", + "extensions": ["psf"] + }, + "application/x-font-pcf": { + "source": "apache", + "extensions": ["pcf"] + }, + "application/x-font-snf": { + "source": "apache", + "extensions": ["snf"] + }, + "application/x-font-speedo": { + "source": "apache" + }, + "application/x-font-sunos-news": { + "source": "apache" + }, + "application/x-font-type1": { + "source": "apache", + "extensions": ["pfa","pfb","pfm","afm"] + }, + "application/x-font-vfont": { + "source": "apache" + }, + "application/x-freearc": { + "source": "apache", + "extensions": ["arc"] + }, + "application/x-futuresplash": { + "source": "apache", + "extensions": ["spl"] + }, + "application/x-gca-compressed": { + "source": "apache", + "extensions": ["gca"] + }, + "application/x-glulx": { + "source": "apache", + "extensions": ["ulx"] + }, + "application/x-gnumeric": { + "source": "apache", + "extensions": ["gnumeric"] + }, + "application/x-gramps-xml": { + "source": "apache", + "extensions": ["gramps"] + }, + "application/x-gtar": { + "source": "apache", + "extensions": ["gtar"] + }, + "application/x-gzip": { + "source": "apache" + }, + "application/x-hdf": { + "source": "apache", + "extensions": ["hdf"] + }, + "application/x-httpd-php": { + "compressible": true, + "extensions": ["php"] + }, + "application/x-install-instructions": { + "source": "apache", + "extensions": ["install"] + }, + "application/x-iso9660-image": { + "source": "apache", + "extensions": ["iso"] + }, + "application/x-iwork-keynote-sffkey": { + "extensions": ["key"] + }, + "application/x-iwork-numbers-sffnumbers": { + "extensions": ["numbers"] + }, + "application/x-iwork-pages-sffpages": { + "extensions": ["pages"] + }, + "application/x-java-archive-diff": { + "source": "nginx", + "extensions": ["jardiff"] + }, + "application/x-java-jnlp-file": { + "source": "apache", + "compressible": false, + "extensions": ["jnlp"] + }, + "application/x-javascript": { + "compressible": true + }, + "application/x-keepass2": { + "extensions": ["kdbx"] + }, + "application/x-latex": { + "source": "apache", + "compressible": false, + "extensions": ["latex"] + }, + "application/x-lua-bytecode": { + "extensions": ["luac"] + }, + "application/x-lzh-compressed": { + "source": "apache", + "extensions": ["lzh","lha"] + }, + "application/x-makeself": { + "source": "nginx", + "extensions": ["run"] + }, + "application/x-mie": { + "source": "apache", + "extensions": ["mie"] + }, + "application/x-mobipocket-ebook": { + "source": "apache", + "extensions": ["prc","mobi"] + }, + "application/x-mpegurl": { + "compressible": false + }, + "application/x-ms-application": { + "source": "apache", + "extensions": ["application"] + }, + "application/x-ms-shortcut": { + "source": "apache", + "extensions": ["lnk"] + }, + "application/x-ms-wmd": { + "source": "apache", + "extensions": ["wmd"] + }, + "application/x-ms-wmz": { + "source": "apache", + "extensions": ["wmz"] + }, + "application/x-ms-xbap": { + "source": "apache", + "extensions": ["xbap"] + }, + "application/x-msaccess": { + "source": "apache", + "extensions": ["mdb"] + }, + "application/x-msbinder": { + "source": "apache", + "extensions": ["obd"] + }, + "application/x-mscardfile": { + "source": "apache", + "extensions": ["crd"] + }, + "application/x-msclip": { + "source": "apache", + "extensions": ["clp"] + }, + "application/x-msdos-program": { + "extensions": ["exe"] + }, + "application/x-msdownload": { + "source": "apache", + "extensions": ["exe","dll","com","bat","msi"] + }, + "application/x-msmediaview": { + "source": "apache", + "extensions": ["mvb","m13","m14"] + }, + "application/x-msmetafile": { + "source": "apache", + "extensions": ["wmf","wmz","emf","emz"] + }, + "application/x-msmoney": { + "source": "apache", + "extensions": ["mny"] + }, + "application/x-mspublisher": { + "source": "apache", + "extensions": ["pub"] + }, + "application/x-msschedule": { + "source": "apache", + "extensions": ["scd"] + }, + "application/x-msterminal": { + "source": "apache", + "extensions": ["trm"] + }, + "application/x-mswrite": { + "source": "apache", + "extensions": ["wri"] + }, + "application/x-netcdf": { + "source": "apache", + "extensions": ["nc","cdf"] + }, + "application/x-ns-proxy-autoconfig": { + "compressible": true, + "extensions": ["pac"] + }, + "application/x-nzb": { + "source": "apache", + "extensions": ["nzb"] + }, + "application/x-perl": { + "source": "nginx", + "extensions": ["pl","pm"] + }, + "application/x-pilot": { + "source": "nginx", + "extensions": ["prc","pdb"] + }, + "application/x-pkcs12": { + "source": "apache", + "compressible": false, + "extensions": ["p12","pfx"] + }, + "application/x-pkcs7-certificates": { + "source": "apache", + "extensions": ["p7b","spc"] + }, + "application/x-pkcs7-certreqresp": { + "source": "apache", + "extensions": ["p7r"] + }, + "application/x-pki-message": { + "source": "iana" + }, + "application/x-rar-compressed": { + "source": "apache", + "compressible": false, + "extensions": ["rar"] + }, + "application/x-redhat-package-manager": { + "source": "nginx", + "extensions": ["rpm"] + }, + "application/x-research-info-systems": { + "source": "apache", + "extensions": ["ris"] + }, + "application/x-sea": { + "source": "nginx", + "extensions": ["sea"] + }, + "application/x-sh": { + "source": "apache", + "compressible": true, + "extensions": ["sh"] + }, + "application/x-shar": { + "source": "apache", + "extensions": ["shar"] + }, + "application/x-shockwave-flash": { + "source": "apache", + "compressible": false, + "extensions": ["swf"] + }, + "application/x-silverlight-app": { + "source": "apache", + "extensions": ["xap"] + }, + "application/x-sql": { + "source": "apache", + "extensions": ["sql"] + }, + "application/x-stuffit": { + "source": "apache", + "compressible": false, + "extensions": ["sit"] + }, + "application/x-stuffitx": { + "source": "apache", + "extensions": ["sitx"] + }, + "application/x-subrip": { + "source": "apache", + "extensions": ["srt"] + }, + "application/x-sv4cpio": { + "source": "apache", + "extensions": ["sv4cpio"] + }, + "application/x-sv4crc": { + "source": "apache", + "extensions": ["sv4crc"] + }, + "application/x-t3vm-image": { + "source": "apache", + "extensions": ["t3"] + }, + "application/x-tads": { + "source": "apache", + "extensions": ["gam"] + }, + "application/x-tar": { + "source": "apache", + "compressible": true, + "extensions": ["tar"] + }, + "application/x-tcl": { + "source": "apache", + "extensions": ["tcl","tk"] + }, + "application/x-tex": { + "source": "apache", + "extensions": ["tex"] + }, + "application/x-tex-tfm": { + "source": "apache", + "extensions": ["tfm"] + }, + "application/x-texinfo": { + "source": "apache", + "extensions": ["texinfo","texi"] + }, + "application/x-tgif": { + "source": "apache", + "extensions": ["obj"] + }, + "application/x-ustar": { + "source": "apache", + "extensions": ["ustar"] + }, + "application/x-virtualbox-hdd": { + "compressible": true, + "extensions": ["hdd"] + }, + "application/x-virtualbox-ova": { + "compressible": true, + "extensions": ["ova"] + }, + "application/x-virtualbox-ovf": { + "compressible": true, + "extensions": ["ovf"] + }, + "application/x-virtualbox-vbox": { + "compressible": true, + "extensions": ["vbox"] + }, + "application/x-virtualbox-vbox-extpack": { + "compressible": false, + "extensions": ["vbox-extpack"] + }, + "application/x-virtualbox-vdi": { + "compressible": true, + "extensions": ["vdi"] + }, + "application/x-virtualbox-vhd": { + "compressible": true, + "extensions": ["vhd"] + }, + "application/x-virtualbox-vmdk": { + "compressible": true, + "extensions": ["vmdk"] + }, + "application/x-wais-source": { + "source": "apache", + "extensions": ["src"] + }, + "application/x-web-app-manifest+json": { + "compressible": true, + "extensions": ["webapp"] + }, + "application/x-www-form-urlencoded": { + "source": "iana", + "compressible": true + }, + "application/x-x509-ca-cert": { + "source": "iana", + "extensions": ["der","crt","pem"] + }, + "application/x-x509-ca-ra-cert": { + "source": "iana" + }, + "application/x-x509-next-ca-cert": { + "source": "iana" + }, + "application/x-xfig": { + "source": "apache", + "extensions": ["fig"] + }, + "application/x-xliff+xml": { + "source": "apache", + "compressible": true, + "extensions": ["xlf"] + }, + "application/x-xpinstall": { + "source": "apache", + "compressible": false, + "extensions": ["xpi"] + }, + "application/x-xz": { + "source": "apache", + "extensions": ["xz"] + }, + "application/x-zmachine": { + "source": "apache", + "extensions": ["z1","z2","z3","z4","z5","z6","z7","z8"] + }, + "application/x400-bp": { + "source": "iana" + }, + "application/xacml+xml": { + "source": "iana", + "compressible": true + }, + "application/xaml+xml": { + "source": "apache", + "compressible": true, + "extensions": ["xaml"] + }, + "application/xcap-att+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xav"] + }, + "application/xcap-caps+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xca"] + }, + "application/xcap-diff+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xdf"] + }, + "application/xcap-el+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xel"] + }, + "application/xcap-error+xml": { + "source": "iana", + "compressible": true + }, + "application/xcap-ns+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xns"] + }, + "application/xcon-conference-info+xml": { + "source": "iana", + "compressible": true + }, + "application/xcon-conference-info-diff+xml": { + "source": "iana", + "compressible": true + }, + "application/xenc+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xenc"] + }, + "application/xhtml+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xhtml","xht"] + }, + "application/xhtml-voice+xml": { + "source": "apache", + "compressible": true + }, + "application/xliff+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xlf"] + }, + "application/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml","xsl","xsd","rng"] + }, + "application/xml-dtd": { + "source": "iana", + "compressible": true, + "extensions": ["dtd"] + }, + "application/xml-external-parsed-entity": { + "source": "iana" + }, + "application/xml-patch+xml": { + "source": "iana", + "compressible": true + }, + "application/xmpp+xml": { + "source": "iana", + "compressible": true + }, + "application/xop+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xop"] + }, + "application/xproc+xml": { + "source": "apache", + "compressible": true, + "extensions": ["xpl"] + }, + "application/xslt+xml": { + "source": "iana", + "compressible": true, + "extensions": ["xsl","xslt"] + }, + "application/xspf+xml": { + "source": "apache", + "compressible": true, + "extensions": ["xspf"] + }, + "application/xv+xml": { + "source": "iana", + "compressible": true, + "extensions": ["mxml","xhvml","xvml","xvm"] + }, + "application/yang": { + "source": "iana", + "extensions": ["yang"] + }, + "application/yang-data+json": { + "source": "iana", + "compressible": true + }, + "application/yang-data+xml": { + "source": "iana", + "compressible": true + }, + "application/yang-patch+json": { + "source": "iana", + "compressible": true + }, + "application/yang-patch+xml": { + "source": "iana", + "compressible": true + }, + "application/yin+xml": { + "source": "iana", + "compressible": true, + "extensions": ["yin"] + }, + "application/zip": { + "source": "iana", + "compressible": false, + "extensions": ["zip"] + }, + "application/zlib": { + "source": "iana" + }, + "application/zstd": { + "source": "iana" + }, + "audio/1d-interleaved-parityfec": { + "source": "iana" + }, + "audio/32kadpcm": { + "source": "iana" + }, + "audio/3gpp": { + "source": "iana", + "compressible": false, + "extensions": ["3gpp"] + }, + "audio/3gpp2": { + "source": "iana" + }, + "audio/aac": { + "source": "iana" + }, + "audio/ac3": { + "source": "iana" + }, + "audio/adpcm": { + "source": "apache", + "extensions": ["adp"] + }, + "audio/amr": { + "source": "iana", + "extensions": ["amr"] + }, + "audio/amr-wb": { + "source": "iana" + }, + "audio/amr-wb+": { + "source": "iana" + }, + "audio/aptx": { + "source": "iana" + }, + "audio/asc": { + "source": "iana" + }, + "audio/atrac-advanced-lossless": { + "source": "iana" + }, + "audio/atrac-x": { + "source": "iana" + }, + "audio/atrac3": { + "source": "iana" + }, + "audio/basic": { + "source": "iana", + "compressible": false, + "extensions": ["au","snd"] + }, + "audio/bv16": { + "source": "iana" + }, + "audio/bv32": { + "source": "iana" + }, + "audio/clearmode": { + "source": "iana" + }, + "audio/cn": { + "source": "iana" + }, + "audio/dat12": { + "source": "iana" + }, + "audio/dls": { + "source": "iana" + }, + "audio/dsr-es201108": { + "source": "iana" + }, + "audio/dsr-es202050": { + "source": "iana" + }, + "audio/dsr-es202211": { + "source": "iana" + }, + "audio/dsr-es202212": { + "source": "iana" + }, + "audio/dv": { + "source": "iana" + }, + "audio/dvi4": { + "source": "iana" + }, + "audio/eac3": { + "source": "iana" + }, + "audio/encaprtp": { + "source": "iana" + }, + "audio/evrc": { + "source": "iana" + }, + "audio/evrc-qcp": { + "source": "iana" + }, + "audio/evrc0": { + "source": "iana" + }, + "audio/evrc1": { + "source": "iana" + }, + "audio/evrcb": { + "source": "iana" + }, + "audio/evrcb0": { + "source": "iana" + }, + "audio/evrcb1": { + "source": "iana" + }, + "audio/evrcnw": { + "source": "iana" + }, + "audio/evrcnw0": { + "source": "iana" + }, + "audio/evrcnw1": { + "source": "iana" + }, + "audio/evrcwb": { + "source": "iana" + }, + "audio/evrcwb0": { + "source": "iana" + }, + "audio/evrcwb1": { + "source": "iana" + }, + "audio/evs": { + "source": "iana" + }, + "audio/flexfec": { + "source": "iana" + }, + "audio/fwdred": { + "source": "iana" + }, + "audio/g711-0": { + "source": "iana" + }, + "audio/g719": { + "source": "iana" + }, + "audio/g722": { + "source": "iana" + }, + "audio/g7221": { + "source": "iana" + }, + "audio/g723": { + "source": "iana" + }, + "audio/g726-16": { + "source": "iana" + }, + "audio/g726-24": { + "source": "iana" + }, + "audio/g726-32": { + "source": "iana" + }, + "audio/g726-40": { + "source": "iana" + }, + "audio/g728": { + "source": "iana" + }, + "audio/g729": { + "source": "iana" + }, + "audio/g7291": { + "source": "iana" + }, + "audio/g729d": { + "source": "iana" + }, + "audio/g729e": { + "source": "iana" + }, + "audio/gsm": { + "source": "iana" + }, + "audio/gsm-efr": { + "source": "iana" + }, + "audio/gsm-hr-08": { + "source": "iana" + }, + "audio/ilbc": { + "source": "iana" + }, + "audio/ip-mr_v2.5": { + "source": "iana" + }, + "audio/isac": { + "source": "apache" + }, + "audio/l16": { + "source": "iana" + }, + "audio/l20": { + "source": "iana" + }, + "audio/l24": { + "source": "iana", + "compressible": false + }, + "audio/l8": { + "source": "iana" + }, + "audio/lpc": { + "source": "iana" + }, + "audio/melp": { + "source": "iana" + }, + "audio/melp1200": { + "source": "iana" + }, + "audio/melp2400": { + "source": "iana" + }, + "audio/melp600": { + "source": "iana" + }, + "audio/mhas": { + "source": "iana" + }, + "audio/midi": { + "source": "apache", + "extensions": ["mid","midi","kar","rmi"] + }, + "audio/mobile-xmf": { + "source": "iana", + "extensions": ["mxmf"] + }, + "audio/mp3": { + "compressible": false, + "extensions": ["mp3"] + }, + "audio/mp4": { + "source": "iana", + "compressible": false, + "extensions": ["m4a","mp4a"] + }, + "audio/mp4a-latm": { + "source": "iana" + }, + "audio/mpa": { + "source": "iana" + }, + "audio/mpa-robust": { + "source": "iana" + }, + "audio/mpeg": { + "source": "iana", + "compressible": false, + "extensions": ["mpga","mp2","mp2a","mp3","m2a","m3a"] + }, + "audio/mpeg4-generic": { + "source": "iana" + }, + "audio/musepack": { + "source": "apache" + }, + "audio/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["oga","ogg","spx","opus"] + }, + "audio/opus": { + "source": "iana" + }, + "audio/parityfec": { + "source": "iana" + }, + "audio/pcma": { + "source": "iana" + }, + "audio/pcma-wb": { + "source": "iana" + }, + "audio/pcmu": { + "source": "iana" + }, + "audio/pcmu-wb": { + "source": "iana" + }, + "audio/prs.sid": { + "source": "iana" + }, + "audio/qcelp": { + "source": "iana" + }, + "audio/raptorfec": { + "source": "iana" + }, + "audio/red": { + "source": "iana" + }, + "audio/rtp-enc-aescm128": { + "source": "iana" + }, + "audio/rtp-midi": { + "source": "iana" + }, + "audio/rtploopback": { + "source": "iana" + }, + "audio/rtx": { + "source": "iana" + }, + "audio/s3m": { + "source": "apache", + "extensions": ["s3m"] + }, + "audio/scip": { + "source": "iana" + }, + "audio/silk": { + "source": "apache", + "extensions": ["sil"] + }, + "audio/smv": { + "source": "iana" + }, + "audio/smv-qcp": { + "source": "iana" + }, + "audio/smv0": { + "source": "iana" + }, + "audio/sofa": { + "source": "iana" + }, + "audio/sp-midi": { + "source": "iana" + }, + "audio/speex": { + "source": "iana" + }, + "audio/t140c": { + "source": "iana" + }, + "audio/t38": { + "source": "iana" + }, + "audio/telephone-event": { + "source": "iana" + }, + "audio/tetra_acelp": { + "source": "iana" + }, + "audio/tetra_acelp_bb": { + "source": "iana" + }, + "audio/tone": { + "source": "iana" + }, + "audio/tsvcis": { + "source": "iana" + }, + "audio/uemclip": { + "source": "iana" + }, + "audio/ulpfec": { + "source": "iana" + }, + "audio/usac": { + "source": "iana" + }, + "audio/vdvi": { + "source": "iana" + }, + "audio/vmr-wb": { + "source": "iana" + }, + "audio/vnd.3gpp.iufp": { + "source": "iana" + }, + "audio/vnd.4sb": { + "source": "iana" + }, + "audio/vnd.audiokoz": { + "source": "iana" + }, + "audio/vnd.celp": { + "source": "iana" + }, + "audio/vnd.cisco.nse": { + "source": "iana" + }, + "audio/vnd.cmles.radio-events": { + "source": "iana" + }, + "audio/vnd.cns.anp1": { + "source": "iana" + }, + "audio/vnd.cns.inf1": { + "source": "iana" + }, + "audio/vnd.dece.audio": { + "source": "iana", + "extensions": ["uva","uvva"] + }, + "audio/vnd.digital-winds": { + "source": "iana", + "extensions": ["eol"] + }, + "audio/vnd.dlna.adts": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.1": { + "source": "iana" + }, + "audio/vnd.dolby.heaac.2": { + "source": "iana" + }, + "audio/vnd.dolby.mlp": { + "source": "iana" + }, + "audio/vnd.dolby.mps": { + "source": "iana" + }, + "audio/vnd.dolby.pl2": { + "source": "iana" + }, + "audio/vnd.dolby.pl2x": { + "source": "iana" + }, + "audio/vnd.dolby.pl2z": { + "source": "iana" + }, + "audio/vnd.dolby.pulse.1": { + "source": "iana" + }, + "audio/vnd.dra": { + "source": "iana", + "extensions": ["dra"] + }, + "audio/vnd.dts": { + "source": "iana", + "extensions": ["dts"] + }, + "audio/vnd.dts.hd": { + "source": "iana", + "extensions": ["dtshd"] + }, + "audio/vnd.dts.uhd": { + "source": "iana" + }, + "audio/vnd.dvb.file": { + "source": "iana" + }, + "audio/vnd.everad.plj": { + "source": "iana" + }, + "audio/vnd.hns.audio": { + "source": "iana" + }, + "audio/vnd.lucent.voice": { + "source": "iana", + "extensions": ["lvp"] + }, + "audio/vnd.ms-playready.media.pya": { + "source": "iana", + "extensions": ["pya"] + }, + "audio/vnd.nokia.mobile-xmf": { + "source": "iana" + }, + "audio/vnd.nortel.vbk": { + "source": "iana" + }, + "audio/vnd.nuera.ecelp4800": { + "source": "iana", + "extensions": ["ecelp4800"] + }, + "audio/vnd.nuera.ecelp7470": { + "source": "iana", + "extensions": ["ecelp7470"] + }, + "audio/vnd.nuera.ecelp9600": { + "source": "iana", + "extensions": ["ecelp9600"] + }, + "audio/vnd.octel.sbc": { + "source": "iana" + }, + "audio/vnd.presonus.multitrack": { + "source": "iana" + }, + "audio/vnd.qcelp": { + "source": "iana" + }, + "audio/vnd.rhetorex.32kadpcm": { + "source": "iana" + }, + "audio/vnd.rip": { + "source": "iana", + "extensions": ["rip"] + }, + "audio/vnd.rn-realaudio": { + "compressible": false + }, + "audio/vnd.sealedmedia.softseal.mpeg": { + "source": "iana" + }, + "audio/vnd.vmx.cvsd": { + "source": "iana" + }, + "audio/vnd.wave": { + "compressible": false + }, + "audio/vorbis": { + "source": "iana", + "compressible": false + }, + "audio/vorbis-config": { + "source": "iana" + }, + "audio/wav": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/wave": { + "compressible": false, + "extensions": ["wav"] + }, + "audio/webm": { + "source": "apache", + "compressible": false, + "extensions": ["weba"] + }, + "audio/x-aac": { + "source": "apache", + "compressible": false, + "extensions": ["aac"] + }, + "audio/x-aiff": { + "source": "apache", + "extensions": ["aif","aiff","aifc"] + }, + "audio/x-caf": { + "source": "apache", + "compressible": false, + "extensions": ["caf"] + }, + "audio/x-flac": { + "source": "apache", + "extensions": ["flac"] + }, + "audio/x-m4a": { + "source": "nginx", + "extensions": ["m4a"] + }, + "audio/x-matroska": { + "source": "apache", + "extensions": ["mka"] + }, + "audio/x-mpegurl": { + "source": "apache", + "extensions": ["m3u"] + }, + "audio/x-ms-wax": { + "source": "apache", + "extensions": ["wax"] + }, + "audio/x-ms-wma": { + "source": "apache", + "extensions": ["wma"] + }, + "audio/x-pn-realaudio": { + "source": "apache", + "extensions": ["ram","ra"] + }, + "audio/x-pn-realaudio-plugin": { + "source": "apache", + "extensions": ["rmp"] + }, + "audio/x-realaudio": { + "source": "nginx", + "extensions": ["ra"] + }, + "audio/x-tta": { + "source": "apache" + }, + "audio/x-wav": { + "source": "apache", + "extensions": ["wav"] + }, + "audio/xm": { + "source": "apache", + "extensions": ["xm"] + }, + "chemical/x-cdx": { + "source": "apache", + "extensions": ["cdx"] + }, + "chemical/x-cif": { + "source": "apache", + "extensions": ["cif"] + }, + "chemical/x-cmdf": { + "source": "apache", + "extensions": ["cmdf"] + }, + "chemical/x-cml": { + "source": "apache", + "extensions": ["cml"] + }, + "chemical/x-csml": { + "source": "apache", + "extensions": ["csml"] + }, + "chemical/x-pdb": { + "source": "apache" + }, + "chemical/x-xyz": { + "source": "apache", + "extensions": ["xyz"] + }, + "font/collection": { + "source": "iana", + "extensions": ["ttc"] + }, + "font/otf": { + "source": "iana", + "compressible": true, + "extensions": ["otf"] + }, + "font/sfnt": { + "source": "iana" + }, + "font/ttf": { + "source": "iana", + "compressible": true, + "extensions": ["ttf"] + }, + "font/woff": { + "source": "iana", + "extensions": ["woff"] + }, + "font/woff2": { + "source": "iana", + "extensions": ["woff2"] + }, + "image/aces": { + "source": "iana", + "extensions": ["exr"] + }, + "image/apng": { + "compressible": false, + "extensions": ["apng"] + }, + "image/avci": { + "source": "iana" + }, + "image/avcs": { + "source": "iana" + }, + "image/avif": { + "source": "iana", + "compressible": false, + "extensions": ["avif"] + }, + "image/bmp": { + "source": "iana", + "compressible": true, + "extensions": ["bmp"] + }, + "image/cgm": { + "source": "iana", + "extensions": ["cgm"] + }, + "image/dicom-rle": { + "source": "iana", + "extensions": ["drle"] + }, + "image/emf": { + "source": "iana", + "extensions": ["emf"] + }, + "image/fits": { + "source": "iana", + "extensions": ["fits"] + }, + "image/g3fax": { + "source": "iana", + "extensions": ["g3"] + }, + "image/gif": { + "source": "iana", + "compressible": false, + "extensions": ["gif"] + }, + "image/heic": { + "source": "iana", + "extensions": ["heic"] + }, + "image/heic-sequence": { + "source": "iana", + "extensions": ["heics"] + }, + "image/heif": { + "source": "iana", + "extensions": ["heif"] + }, + "image/heif-sequence": { + "source": "iana", + "extensions": ["heifs"] + }, + "image/hej2k": { + "source": "iana", + "extensions": ["hej2"] + }, + "image/hsj2": { + "source": "iana", + "extensions": ["hsj2"] + }, + "image/ief": { + "source": "iana", + "extensions": ["ief"] + }, + "image/jls": { + "source": "iana", + "extensions": ["jls"] + }, + "image/jp2": { + "source": "iana", + "compressible": false, + "extensions": ["jp2","jpg2"] + }, + "image/jpeg": { + "source": "iana", + "compressible": false, + "extensions": ["jpeg","jpg","jpe"] + }, + "image/jph": { + "source": "iana", + "extensions": ["jph"] + }, + "image/jphc": { + "source": "iana", + "extensions": ["jhc"] + }, + "image/jpm": { + "source": "iana", + "compressible": false, + "extensions": ["jpm"] + }, + "image/jpx": { + "source": "iana", + "compressible": false, + "extensions": ["jpx","jpf"] + }, + "image/jxr": { + "source": "iana", + "extensions": ["jxr"] + }, + "image/jxra": { + "source": "iana", + "extensions": ["jxra"] + }, + "image/jxrs": { + "source": "iana", + "extensions": ["jxrs"] + }, + "image/jxs": { + "source": "iana", + "extensions": ["jxs"] + }, + "image/jxsc": { + "source": "iana", + "extensions": ["jxsc"] + }, + "image/jxsi": { + "source": "iana", + "extensions": ["jxsi"] + }, + "image/jxss": { + "source": "iana", + "extensions": ["jxss"] + }, + "image/ktx": { + "source": "iana", + "extensions": ["ktx"] + }, + "image/ktx2": { + "source": "iana", + "extensions": ["ktx2"] + }, + "image/naplps": { + "source": "iana" + }, + "image/pjpeg": { + "compressible": false + }, + "image/png": { + "source": "iana", + "compressible": false, + "extensions": ["png"] + }, + "image/prs.btif": { + "source": "iana", + "extensions": ["btif"] + }, + "image/prs.pti": { + "source": "iana", + "extensions": ["pti"] + }, + "image/pwg-raster": { + "source": "iana" + }, + "image/sgi": { + "source": "apache", + "extensions": ["sgi"] + }, + "image/svg+xml": { + "source": "iana", + "compressible": true, + "extensions": ["svg","svgz"] + }, + "image/t38": { + "source": "iana", + "extensions": ["t38"] + }, + "image/tiff": { + "source": "iana", + "compressible": false, + "extensions": ["tif","tiff"] + }, + "image/tiff-fx": { + "source": "iana", + "extensions": ["tfx"] + }, + "image/vnd.adobe.photoshop": { + "source": "iana", + "compressible": true, + "extensions": ["psd"] + }, + "image/vnd.airzip.accelerator.azv": { + "source": "iana", + "extensions": ["azv"] + }, + "image/vnd.cns.inf2": { + "source": "iana" + }, + "image/vnd.dece.graphic": { + "source": "iana", + "extensions": ["uvi","uvvi","uvg","uvvg"] + }, + "image/vnd.djvu": { + "source": "iana", + "extensions": ["djvu","djv"] + }, + "image/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "image/vnd.dwg": { + "source": "iana", + "extensions": ["dwg"] + }, + "image/vnd.dxf": { + "source": "iana", + "extensions": ["dxf"] + }, + "image/vnd.fastbidsheet": { + "source": "iana", + "extensions": ["fbs"] + }, + "image/vnd.fpx": { + "source": "iana", + "extensions": ["fpx"] + }, + "image/vnd.fst": { + "source": "iana", + "extensions": ["fst"] + }, + "image/vnd.fujixerox.edmics-mmr": { + "source": "iana", + "extensions": ["mmr"] + }, + "image/vnd.fujixerox.edmics-rlc": { + "source": "iana", + "extensions": ["rlc"] + }, + "image/vnd.globalgraphics.pgb": { + "source": "iana" + }, + "image/vnd.microsoft.icon": { + "source": "iana", + "extensions": ["ico"] + }, + "image/vnd.mix": { + "source": "iana" + }, + "image/vnd.mozilla.apng": { + "source": "iana" + }, + "image/vnd.ms-dds": { + "extensions": ["dds"] + }, + "image/vnd.ms-modi": { + "source": "iana", + "extensions": ["mdi"] + }, + "image/vnd.ms-photo": { + "source": "apache", + "extensions": ["wdp"] + }, + "image/vnd.net-fpx": { + "source": "iana", + "extensions": ["npx"] + }, + "image/vnd.pco.b16": { + "source": "iana", + "extensions": ["b16"] + }, + "image/vnd.radiance": { + "source": "iana" + }, + "image/vnd.sealed.png": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.gif": { + "source": "iana" + }, + "image/vnd.sealedmedia.softseal.jpg": { + "source": "iana" + }, + "image/vnd.svf": { + "source": "iana" + }, + "image/vnd.tencent.tap": { + "source": "iana", + "extensions": ["tap"] + }, + "image/vnd.valve.source.texture": { + "source": "iana", + "extensions": ["vtf"] + }, + "image/vnd.wap.wbmp": { + "source": "iana", + "extensions": ["wbmp"] + }, + "image/vnd.xiff": { + "source": "iana", + "extensions": ["xif"] + }, + "image/vnd.zbrush.pcx": { + "source": "iana", + "extensions": ["pcx"] + }, + "image/webp": { + "source": "apache", + "extensions": ["webp"] + }, + "image/wmf": { + "source": "iana", + "extensions": ["wmf"] + }, + "image/x-3ds": { + "source": "apache", + "extensions": ["3ds"] + }, + "image/x-cmu-raster": { + "source": "apache", + "extensions": ["ras"] + }, + "image/x-cmx": { + "source": "apache", + "extensions": ["cmx"] + }, + "image/x-freehand": { + "source": "apache", + "extensions": ["fh","fhc","fh4","fh5","fh7"] + }, + "image/x-icon": { + "source": "apache", + "compressible": true, + "extensions": ["ico"] + }, + "image/x-jng": { + "source": "nginx", + "extensions": ["jng"] + }, + "image/x-mrsid-image": { + "source": "apache", + "extensions": ["sid"] + }, + "image/x-ms-bmp": { + "source": "nginx", + "compressible": true, + "extensions": ["bmp"] + }, + "image/x-pcx": { + "source": "apache", + "extensions": ["pcx"] + }, + "image/x-pict": { + "source": "apache", + "extensions": ["pic","pct"] + }, + "image/x-portable-anymap": { + "source": "apache", + "extensions": ["pnm"] + }, + "image/x-portable-bitmap": { + "source": "apache", + "extensions": ["pbm"] + }, + "image/x-portable-graymap": { + "source": "apache", + "extensions": ["pgm"] + }, + "image/x-portable-pixmap": { + "source": "apache", + "extensions": ["ppm"] + }, + "image/x-rgb": { + "source": "apache", + "extensions": ["rgb"] + }, + "image/x-tga": { + "source": "apache", + "extensions": ["tga"] + }, + "image/x-xbitmap": { + "source": "apache", + "extensions": ["xbm"] + }, + "image/x-xcf": { + "compressible": false + }, + "image/x-xpixmap": { + "source": "apache", + "extensions": ["xpm"] + }, + "image/x-xwindowdump": { + "source": "apache", + "extensions": ["xwd"] + }, + "message/cpim": { + "source": "iana" + }, + "message/delivery-status": { + "source": "iana" + }, + "message/disposition-notification": { + "source": "iana", + "extensions": [ + "disposition-notification" + ] + }, + "message/external-body": { + "source": "iana" + }, + "message/feedback-report": { + "source": "iana" + }, + "message/global": { + "source": "iana", + "extensions": ["u8msg"] + }, + "message/global-delivery-status": { + "source": "iana", + "extensions": ["u8dsn"] + }, + "message/global-disposition-notification": { + "source": "iana", + "extensions": ["u8mdn"] + }, + "message/global-headers": { + "source": "iana", + "extensions": ["u8hdr"] + }, + "message/http": { + "source": "iana", + "compressible": false + }, + "message/imdn+xml": { + "source": "iana", + "compressible": true + }, + "message/news": { + "source": "iana" + }, + "message/partial": { + "source": "iana", + "compressible": false + }, + "message/rfc822": { + "source": "iana", + "compressible": true, + "extensions": ["eml","mime"] + }, + "message/s-http": { + "source": "iana" + }, + "message/sip": { + "source": "iana" + }, + "message/sipfrag": { + "source": "iana" + }, + "message/tracking-status": { + "source": "iana" + }, + "message/vnd.si.simp": { + "source": "iana" + }, + "message/vnd.wfa.wsc": { + "source": "iana", + "extensions": ["wsc"] + }, + "model/3mf": { + "source": "iana", + "extensions": ["3mf"] + }, + "model/e57": { + "source": "iana" + }, + "model/gltf+json": { + "source": "iana", + "compressible": true, + "extensions": ["gltf"] + }, + "model/gltf-binary": { + "source": "iana", + "compressible": true, + "extensions": ["glb"] + }, + "model/iges": { + "source": "iana", + "compressible": false, + "extensions": ["igs","iges"] + }, + "model/mesh": { + "source": "iana", + "compressible": false, + "extensions": ["msh","mesh","silo"] + }, + "model/mtl": { + "source": "iana", + "extensions": ["mtl"] + }, + "model/obj": { + "source": "iana", + "extensions": ["obj"] + }, + "model/step": { + "source": "iana" + }, + "model/step+xml": { + "source": "iana", + "compressible": true, + "extensions": ["stpx"] + }, + "model/step+zip": { + "source": "iana", + "compressible": false, + "extensions": ["stpz"] + }, + "model/step-xml+zip": { + "source": "iana", + "compressible": false, + "extensions": ["stpxz"] + }, + "model/stl": { + "source": "iana", + "extensions": ["stl"] + }, + "model/vnd.collada+xml": { + "source": "iana", + "compressible": true, + "extensions": ["dae"] + }, + "model/vnd.dwf": { + "source": "iana", + "extensions": ["dwf"] + }, + "model/vnd.flatland.3dml": { + "source": "iana" + }, + "model/vnd.gdl": { + "source": "iana", + "extensions": ["gdl"] + }, + "model/vnd.gs-gdl": { + "source": "apache" + }, + "model/vnd.gs.gdl": { + "source": "iana" + }, + "model/vnd.gtw": { + "source": "iana", + "extensions": ["gtw"] + }, + "model/vnd.moml+xml": { + "source": "iana", + "compressible": true + }, + "model/vnd.mts": { + "source": "iana", + "extensions": ["mts"] + }, + "model/vnd.opengex": { + "source": "iana", + "extensions": ["ogex"] + }, + "model/vnd.parasolid.transmit.binary": { + "source": "iana", + "extensions": ["x_b"] + }, + "model/vnd.parasolid.transmit.text": { + "source": "iana", + "extensions": ["x_t"] + }, + "model/vnd.pytha.pyox": { + "source": "iana" + }, + "model/vnd.rosette.annotated-data-model": { + "source": "iana" + }, + "model/vnd.sap.vds": { + "source": "iana", + "extensions": ["vds"] + }, + "model/vnd.usdz+zip": { + "source": "iana", + "compressible": false, + "extensions": ["usdz"] + }, + "model/vnd.valve.source.compiled-map": { + "source": "iana", + "extensions": ["bsp"] + }, + "model/vnd.vtu": { + "source": "iana", + "extensions": ["vtu"] + }, + "model/vrml": { + "source": "iana", + "compressible": false, + "extensions": ["wrl","vrml"] + }, + "model/x3d+binary": { + "source": "apache", + "compressible": false, + "extensions": ["x3db","x3dbz"] + }, + "model/x3d+fastinfoset": { + "source": "iana", + "extensions": ["x3db"] + }, + "model/x3d+vrml": { + "source": "apache", + "compressible": false, + "extensions": ["x3dv","x3dvz"] + }, + "model/x3d+xml": { + "source": "iana", + "compressible": true, + "extensions": ["x3d","x3dz"] + }, + "model/x3d-vrml": { + "source": "iana", + "extensions": ["x3dv"] + }, + "multipart/alternative": { + "source": "iana", + "compressible": false + }, + "multipart/appledouble": { + "source": "iana" + }, + "multipart/byteranges": { + "source": "iana" + }, + "multipart/digest": { + "source": "iana" + }, + "multipart/encrypted": { + "source": "iana", + "compressible": false + }, + "multipart/form-data": { + "source": "iana", + "compressible": false + }, + "multipart/header-set": { + "source": "iana" + }, + "multipart/mixed": { + "source": "iana" + }, + "multipart/multilingual": { + "source": "iana" + }, + "multipart/parallel": { + "source": "iana" + }, + "multipart/related": { + "source": "iana", + "compressible": false + }, + "multipart/report": { + "source": "iana" + }, + "multipart/signed": { + "source": "iana", + "compressible": false + }, + "multipart/vnd.bint.med-plus": { + "source": "iana" + }, + "multipart/voice-message": { + "source": "iana" + }, + "multipart/x-mixed-replace": { + "source": "iana" + }, + "text/1d-interleaved-parityfec": { + "source": "iana" + }, + "text/cache-manifest": { + "source": "iana", + "compressible": true, + "extensions": ["appcache","manifest"] + }, + "text/calendar": { + "source": "iana", + "extensions": ["ics","ifb"] + }, + "text/calender": { + "compressible": true + }, + "text/cmd": { + "compressible": true + }, + "text/coffeescript": { + "extensions": ["coffee","litcoffee"] + }, + "text/cql": { + "source": "iana" + }, + "text/cql-expression": { + "source": "iana" + }, + "text/cql-identifier": { + "source": "iana" + }, + "text/css": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["css"] + }, + "text/csv": { + "source": "iana", + "compressible": true, + "extensions": ["csv"] + }, + "text/csv-schema": { + "source": "iana" + }, + "text/directory": { + "source": "iana" + }, + "text/dns": { + "source": "iana" + }, + "text/ecmascript": { + "source": "iana" + }, + "text/encaprtp": { + "source": "iana" + }, + "text/enriched": { + "source": "iana" + }, + "text/fhirpath": { + "source": "iana" + }, + "text/flexfec": { + "source": "iana" + }, + "text/fwdred": { + "source": "iana" + }, + "text/gff3": { + "source": "iana" + }, + "text/grammar-ref-list": { + "source": "iana" + }, + "text/html": { + "source": "iana", + "compressible": true, + "extensions": ["html","htm","shtml"] + }, + "text/jade": { + "extensions": ["jade"] + }, + "text/javascript": { + "source": "iana", + "compressible": true + }, + "text/jcr-cnd": { + "source": "iana" + }, + "text/jsx": { + "compressible": true, + "extensions": ["jsx"] + }, + "text/less": { + "compressible": true, + "extensions": ["less"] + }, + "text/markdown": { + "source": "iana", + "compressible": true, + "extensions": ["markdown","md"] + }, + "text/mathml": { + "source": "nginx", + "extensions": ["mml"] + }, + "text/mdx": { + "compressible": true, + "extensions": ["mdx"] + }, + "text/mizar": { + "source": "iana" + }, + "text/n3": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["n3"] + }, + "text/parameters": { + "source": "iana", + "charset": "UTF-8" + }, + "text/parityfec": { + "source": "iana" + }, + "text/plain": { + "source": "iana", + "compressible": true, + "extensions": ["txt","text","conf","def","list","log","in","ini"] + }, + "text/provenance-notation": { + "source": "iana", + "charset": "UTF-8" + }, + "text/prs.fallenstein.rst": { + "source": "iana" + }, + "text/prs.lines.tag": { + "source": "iana", + "extensions": ["dsc"] + }, + "text/prs.prop.logic": { + "source": "iana" + }, + "text/raptorfec": { + "source": "iana" + }, + "text/red": { + "source": "iana" + }, + "text/rfc822-headers": { + "source": "iana" + }, + "text/richtext": { + "source": "iana", + "compressible": true, + "extensions": ["rtx"] + }, + "text/rtf": { + "source": "iana", + "compressible": true, + "extensions": ["rtf"] + }, + "text/rtp-enc-aescm128": { + "source": "iana" + }, + "text/rtploopback": { + "source": "iana" + }, + "text/rtx": { + "source": "iana" + }, + "text/sgml": { + "source": "iana", + "extensions": ["sgml","sgm"] + }, + "text/shaclc": { + "source": "iana" + }, + "text/shex": { + "source": "iana", + "extensions": ["shex"] + }, + "text/slim": { + "extensions": ["slim","slm"] + }, + "text/spdx": { + "source": "iana", + "extensions": ["spdx"] + }, + "text/strings": { + "source": "iana" + }, + "text/stylus": { + "extensions": ["stylus","styl"] + }, + "text/t140": { + "source": "iana" + }, + "text/tab-separated-values": { + "source": "iana", + "compressible": true, + "extensions": ["tsv"] + }, + "text/troff": { + "source": "iana", + "extensions": ["t","tr","roff","man","me","ms"] + }, + "text/turtle": { + "source": "iana", + "charset": "UTF-8", + "extensions": ["ttl"] + }, + "text/ulpfec": { + "source": "iana" + }, + "text/uri-list": { + "source": "iana", + "compressible": true, + "extensions": ["uri","uris","urls"] + }, + "text/vcard": { + "source": "iana", + "compressible": true, + "extensions": ["vcard"] + }, + "text/vnd.a": { + "source": "iana" + }, + "text/vnd.abc": { + "source": "iana" + }, + "text/vnd.ascii-art": { + "source": "iana" + }, + "text/vnd.curl": { + "source": "iana", + "extensions": ["curl"] + }, + "text/vnd.curl.dcurl": { + "source": "apache", + "extensions": ["dcurl"] + }, + "text/vnd.curl.mcurl": { + "source": "apache", + "extensions": ["mcurl"] + }, + "text/vnd.curl.scurl": { + "source": "apache", + "extensions": ["scurl"] + }, + "text/vnd.debian.copyright": { + "source": "iana", + "charset": "UTF-8" + }, + "text/vnd.dmclientscript": { + "source": "iana" + }, + "text/vnd.dvb.subtitle": { + "source": "iana", + "extensions": ["sub"] + }, + "text/vnd.esmertec.theme-descriptor": { + "source": "iana", + "charset": "UTF-8" + }, + "text/vnd.ficlab.flt": { + "source": "iana" + }, + "text/vnd.fly": { + "source": "iana", + "extensions": ["fly"] + }, + "text/vnd.fmi.flexstor": { + "source": "iana", + "extensions": ["flx"] + }, + "text/vnd.gml": { + "source": "iana" + }, + "text/vnd.graphviz": { + "source": "iana", + "extensions": ["gv"] + }, + "text/vnd.hans": { + "source": "iana" + }, + "text/vnd.hgl": { + "source": "iana" + }, + "text/vnd.in3d.3dml": { + "source": "iana", + "extensions": ["3dml"] + }, + "text/vnd.in3d.spot": { + "source": "iana", + "extensions": ["spot"] + }, + "text/vnd.iptc.newsml": { + "source": "iana" + }, + "text/vnd.iptc.nitf": { + "source": "iana" + }, + "text/vnd.latex-z": { + "source": "iana" + }, + "text/vnd.motorola.reflex": { + "source": "iana" + }, + "text/vnd.ms-mediapackage": { + "source": "iana" + }, + "text/vnd.net2phone.commcenter.command": { + "source": "iana" + }, + "text/vnd.radisys.msml-basic-layout": { + "source": "iana" + }, + "text/vnd.senx.warpscript": { + "source": "iana" + }, + "text/vnd.si.uricatalogue": { + "source": "iana" + }, + "text/vnd.sosi": { + "source": "iana" + }, + "text/vnd.sun.j2me.app-descriptor": { + "source": "iana", + "charset": "UTF-8", + "extensions": ["jad"] + }, + "text/vnd.trolltech.linguist": { + "source": "iana", + "charset": "UTF-8" + }, + "text/vnd.wap.si": { + "source": "iana" + }, + "text/vnd.wap.sl": { + "source": "iana" + }, + "text/vnd.wap.wml": { + "source": "iana", + "extensions": ["wml"] + }, + "text/vnd.wap.wmlscript": { + "source": "iana", + "extensions": ["wmls"] + }, + "text/vtt": { + "source": "iana", + "charset": "UTF-8", + "compressible": true, + "extensions": ["vtt"] + }, + "text/x-asm": { + "source": "apache", + "extensions": ["s","asm"] + }, + "text/x-c": { + "source": "apache", + "extensions": ["c","cc","cxx","cpp","h","hh","dic"] + }, + "text/x-component": { + "source": "nginx", + "extensions": ["htc"] + }, + "text/x-fortran": { + "source": "apache", + "extensions": ["f","for","f77","f90"] + }, + "text/x-gwt-rpc": { + "compressible": true + }, + "text/x-handlebars-template": { + "extensions": ["hbs"] + }, + "text/x-java-source": { + "source": "apache", + "extensions": ["java"] + }, + "text/x-jquery-tmpl": { + "compressible": true + }, + "text/x-lua": { + "extensions": ["lua"] + }, + "text/x-markdown": { + "compressible": true, + "extensions": ["mkd"] + }, + "text/x-nfo": { + "source": "apache", + "extensions": ["nfo"] + }, + "text/x-opml": { + "source": "apache", + "extensions": ["opml"] + }, + "text/x-org": { + "compressible": true, + "extensions": ["org"] + }, + "text/x-pascal": { + "source": "apache", + "extensions": ["p","pas"] + }, + "text/x-processing": { + "compressible": true, + "extensions": ["pde"] + }, + "text/x-sass": { + "extensions": ["sass"] + }, + "text/x-scss": { + "extensions": ["scss"] + }, + "text/x-setext": { + "source": "apache", + "extensions": ["etx"] + }, + "text/x-sfv": { + "source": "apache", + "extensions": ["sfv"] + }, + "text/x-suse-ymp": { + "compressible": true, + "extensions": ["ymp"] + }, + "text/x-uuencode": { + "source": "apache", + "extensions": ["uu"] + }, + "text/x-vcalendar": { + "source": "apache", + "extensions": ["vcs"] + }, + "text/x-vcard": { + "source": "apache", + "extensions": ["vcf"] + }, + "text/xml": { + "source": "iana", + "compressible": true, + "extensions": ["xml"] + }, + "text/xml-external-parsed-entity": { + "source": "iana" + }, + "text/yaml": { + "compressible": true, + "extensions": ["yaml","yml"] + }, + "video/1d-interleaved-parityfec": { + "source": "iana" + }, + "video/3gpp": { + "source": "iana", + "extensions": ["3gp","3gpp"] + }, + "video/3gpp-tt": { + "source": "iana" + }, + "video/3gpp2": { + "source": "iana", + "extensions": ["3g2"] + }, + "video/av1": { + "source": "iana" + }, + "video/bmpeg": { + "source": "iana" + }, + "video/bt656": { + "source": "iana" + }, + "video/celb": { + "source": "iana" + }, + "video/dv": { + "source": "iana" + }, + "video/encaprtp": { + "source": "iana" + }, + "video/ffv1": { + "source": "iana" + }, + "video/flexfec": { + "source": "iana" + }, + "video/h261": { + "source": "iana", + "extensions": ["h261"] + }, + "video/h263": { + "source": "iana", + "extensions": ["h263"] + }, + "video/h263-1998": { + "source": "iana" + }, + "video/h263-2000": { + "source": "iana" + }, + "video/h264": { + "source": "iana", + "extensions": ["h264"] + }, + "video/h264-rcdo": { + "source": "iana" + }, + "video/h264-svc": { + "source": "iana" + }, + "video/h265": { + "source": "iana" + }, + "video/iso.segment": { + "source": "iana", + "extensions": ["m4s"] + }, + "video/jpeg": { + "source": "iana", + "extensions": ["jpgv"] + }, + "video/jpeg2000": { + "source": "iana" + }, + "video/jpm": { + "source": "apache", + "extensions": ["jpm","jpgm"] + }, + "video/jxsv": { + "source": "iana" + }, + "video/mj2": { + "source": "iana", + "extensions": ["mj2","mjp2"] + }, + "video/mp1s": { + "source": "iana" + }, + "video/mp2p": { + "source": "iana" + }, + "video/mp2t": { + "source": "iana", + "extensions": ["ts"] + }, + "video/mp4": { + "source": "iana", + "compressible": false, + "extensions": ["mp4","mp4v","mpg4"] + }, + "video/mp4v-es": { + "source": "iana" + }, + "video/mpeg": { + "source": "iana", + "compressible": false, + "extensions": ["mpeg","mpg","mpe","m1v","m2v"] + }, + "video/mpeg4-generic": { + "source": "iana" + }, + "video/mpv": { + "source": "iana" + }, + "video/nv": { + "source": "iana" + }, + "video/ogg": { + "source": "iana", + "compressible": false, + "extensions": ["ogv"] + }, + "video/parityfec": { + "source": "iana" + }, + "video/pointer": { + "source": "iana" + }, + "video/quicktime": { + "source": "iana", + "compressible": false, + "extensions": ["qt","mov"] + }, + "video/raptorfec": { + "source": "iana" + }, + "video/raw": { + "source": "iana" + }, + "video/rtp-enc-aescm128": { + "source": "iana" + }, + "video/rtploopback": { + "source": "iana" + }, + "video/rtx": { + "source": "iana" + }, + "video/scip": { + "source": "iana" + }, + "video/smpte291": { + "source": "iana" + }, + "video/smpte292m": { + "source": "iana" + }, + "video/ulpfec": { + "source": "iana" + }, + "video/vc1": { + "source": "iana" + }, + "video/vc2": { + "source": "iana" + }, + "video/vnd.cctv": { + "source": "iana" + }, + "video/vnd.dece.hd": { + "source": "iana", + "extensions": ["uvh","uvvh"] + }, + "video/vnd.dece.mobile": { + "source": "iana", + "extensions": ["uvm","uvvm"] + }, + "video/vnd.dece.mp4": { + "source": "iana" + }, + "video/vnd.dece.pd": { + "source": "iana", + "extensions": ["uvp","uvvp"] + }, + "video/vnd.dece.sd": { + "source": "iana", + "extensions": ["uvs","uvvs"] + }, + "video/vnd.dece.video": { + "source": "iana", + "extensions": ["uvv","uvvv"] + }, + "video/vnd.directv.mpeg": { + "source": "iana" + }, + "video/vnd.directv.mpeg-tts": { + "source": "iana" + }, + "video/vnd.dlna.mpeg-tts": { + "source": "iana" + }, + "video/vnd.dvb.file": { + "source": "iana", + "extensions": ["dvb"] + }, + "video/vnd.fvt": { + "source": "iana", + "extensions": ["fvt"] + }, + "video/vnd.hns.video": { + "source": "iana" + }, + "video/vnd.iptvforum.1dparityfec-1010": { + "source": "iana" + }, + "video/vnd.iptvforum.1dparityfec-2005": { + "source": "iana" + }, + "video/vnd.iptvforum.2dparityfec-1010": { + "source": "iana" + }, + "video/vnd.iptvforum.2dparityfec-2005": { + "source": "iana" + }, + "video/vnd.iptvforum.ttsavc": { + "source": "iana" + }, + "video/vnd.iptvforum.ttsmpeg2": { + "source": "iana" + }, + "video/vnd.motorola.video": { + "source": "iana" + }, + "video/vnd.motorola.videop": { + "source": "iana" + }, + "video/vnd.mpegurl": { + "source": "iana", + "extensions": ["mxu","m4u"] + }, + "video/vnd.ms-playready.media.pyv": { + "source": "iana", + "extensions": ["pyv"] + }, + "video/vnd.nokia.interleaved-multimedia": { + "source": "iana" + }, + "video/vnd.nokia.mp4vr": { + "source": "iana" + }, + "video/vnd.nokia.videovoip": { + "source": "iana" + }, + "video/vnd.objectvideo": { + "source": "iana" + }, + "video/vnd.radgamettools.bink": { + "source": "iana" + }, + "video/vnd.radgamettools.smacker": { + "source": "iana" + }, + "video/vnd.sealed.mpeg1": { + "source": "iana" + }, + "video/vnd.sealed.mpeg4": { + "source": "iana" + }, + "video/vnd.sealed.swf": { + "source": "iana" + }, + "video/vnd.sealedmedia.softseal.mov": { + "source": "iana" + }, + "video/vnd.uvvu.mp4": { + "source": "iana", + "extensions": ["uvu","uvvu"] + }, + "video/vnd.vivo": { + "source": "iana", + "extensions": ["viv"] + }, + "video/vnd.youtube.yt": { + "source": "iana" + }, + "video/vp8": { + "source": "iana" + }, + "video/vp9": { + "source": "iana" + }, + "video/webm": { + "source": "apache", + "compressible": false, + "extensions": ["webm"] + }, + "video/x-f4v": { + "source": "apache", + "extensions": ["f4v"] + }, + "video/x-fli": { + "source": "apache", + "extensions": ["fli"] + }, + "video/x-flv": { + "source": "apache", + "compressible": false, + "extensions": ["flv"] + }, + "video/x-m4v": { + "source": "apache", + "extensions": ["m4v"] + }, + "video/x-matroska": { + "source": "apache", + "compressible": false, + "extensions": ["mkv","mk3d","mks"] + }, + "video/x-mng": { + "source": "apache", + "extensions": ["mng"] + }, + "video/x-ms-asf": { + "source": "apache", + "extensions": ["asf","asx"] + }, + "video/x-ms-vob": { + "source": "apache", + "extensions": ["vob"] + }, + "video/x-ms-wm": { + "source": "apache", + "extensions": ["wm"] + }, + "video/x-ms-wmv": { + "source": "apache", + "compressible": false, + "extensions": ["wmv"] + }, + "video/x-ms-wmx": { + "source": "apache", + "extensions": ["wmx"] + }, + "video/x-ms-wvx": { + "source": "apache", + "extensions": ["wvx"] + }, + "video/x-msvideo": { + "source": "apache", + "extensions": ["avi"] + }, + "video/x-sgi-movie": { + "source": "apache", + "extensions": ["movie"] + }, + "video/x-smv": { + "source": "apache", + "extensions": ["smv"] + }, + "x-conference/x-cooltalk": { + "source": "apache", + "extensions": ["ice"] + }, + "x-shader/x-fragment": { + "compressible": true + }, + "x-shader/x-vertex": { + "compressible": true + } +} diff --git a/src/dep/style-vendorizer.mjs b/src/dep/style-vendorizer.mjs new file mode 100644 index 0000000..fca0d2a --- /dev/null +++ b/src/dep/style-vendorizer.mjs @@ -0,0 +1,58 @@ +/** + * style-vendorizer v2.0.0 + * @license MIT + * @source https://unpkg.com/style-vendorizer@^2.0.0?module + */ + +var i = new Map([ + ['align-self', '-ms-grid-row-align'], + ['color-adjust', '-webkit-print-color-adjust'], + ['column-gap', 'grid-column-gap'], + ['gap', 'grid-gap'], + ['grid-template-columns', '-ms-grid-columns'], + ['grid-template-rows', '-ms-grid-rows'], + ['justify-self', '-ms-grid-column-align'], + ['margin-inline-end', '-webkit-margin-end'], + ['margin-inline-start', '-webkit-margin-start'], + ['overflow-wrap', 'word-wrap'], + ['padding-inline-end', '-webkit-padding-end'], + ['padding-inline-start', '-webkit-padding-start'], + ['row-gap', 'grid-row-gap'], + ['scroll-margin-bottom', 'scroll-snap-margin-bottom'], + ['scroll-margin-left', 'scroll-snap-margin-left'], + ['scroll-margin-right', 'scroll-snap-margin-right'], + ['scroll-margin-top', 'scroll-snap-margin-top'], + ['scroll-margin', 'scroll-snap-margin'], + ['text-combine-upright', '-ms-text-combine-horizontal'], +]); +function r(r) { + return i.get(r); +} +function n(i) { + var r = + /^(?:(text-(?:decoration$|e|or|si)|back(?:ground-cl|d|f)|box-d|(?:mask(?:$|-[ispro]|-cl)))|(tab-|column(?!-s)|text-align-l)|(ap)|(u|hy))/i.exec( + i + ); + return r ? (r[1] ? 1 : r[2] ? 2 : r[3] ? 3 : 5) : 0; +} +function t(i, r) { + var n = /^(?:(pos)|(background-i)|((?:max-|min-)?(?:block-s|inl|he|widt))|(dis))/i.exec(i); + return n + ? n[1] + ? /^sti/i.test(r) + ? 1 + : 0 + : n[2] + ? /^image-/i.test(r) + ? 1 + : 0 + : n[3] + ? '-' === r[3] + ? 2 + : 0 + : /^(inline-)?grid$/i.test(r) + ? 4 + : 0 + : 0; +} +export { r as cssPropertyAlias, n as cssPropertyPrefixFlags, t as cssValuePrefixFlags }; diff --git a/src/dep/twind-content.mjs b/src/dep/twind-content.mjs new file mode 100644 index 0000000..ca4c2bf --- /dev/null +++ b/src/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/src/dep/twind-css.mjs b/src/dep/twind-css.mjs new file mode 100644 index 0000000..f337731 --- /dev/null +++ b/src/dep/twind-css.mjs @@ -0,0 +1,134 @@ +/** + * Twind v0.16.16 + * @license MIT + * @source https://unpkg.com/twind@0.16.16/css/css.js?module + */ + +// src/css/index.ts +import { apply, hash, directive } from "./twind.mjs"; + +// src/internal/util.ts +var includes = (value, search) => !!~value.indexOf(search); +var join = (parts, separator = "-") => parts.join(separator); +var hyphenate = value => value.replace(/[A-Z]/g, "-$&").toLowerCase(); +var evalThunk = (value, context) => { + while (typeof value == "function") { + value = value(context); + } + return value; +}; +var isCSSProperty = (key, value) => !includes("@:&", key[0]) && (includes("rg", (typeof value)[5]) || Array.isArray(value)); +var merge = (target, source, context) => source ? Object.keys(source).reduce((target2, key) => { + const value = evalThunk(source[key], context); + if (isCSSProperty(key, value)) { + target2[hyphenate(key)] = value; + } else { + target2[key] = key[0] == "@" && includes("figa", key[1]) ? (target2[key] || []).concat(value) : merge(target2[key] || {}, value, context); + } + return target2; +}, target) : target; +var escape = typeof CSS !== "undefined" && CSS.escape || (className => className.replace(/[!"'`*+.,;:\\/<=>?@#$%&^|~()[\]{}]/g, "\\$&").replace(/^\d/, "\\3$& ")); +var buildMediaQuery = screen2 => { + if (!Array.isArray(screen2)) { + screen2 = [screen2]; + } + return "@media " + join(screen2.map(screen3 => { + if (typeof screen3 == "string") { + screen3 = { min: screen3 }; + } + return screen3.raw || join(Object.keys(screen3).map(feature => `(${feature}-width:${screen3[feature]})`), " and "); + }), ","); +}; + +// src/css/index.ts +var translate = (tokens, context) => { + const collect = (target, token) => Array.isArray(token) ? token.reduce(collect, target) : merge(target, evalThunk(token, context), context); + return tokens.reduce(collect, {}); +}; +var newRule = /\s*(?:([\w-%@]+)\s*:?\s*([^{;]+?)\s*(?:;|$|})|([^;}{]*?)\s*{)|(})/gi; +var ruleClean = /\/\*[\s\S]*?\*\/|\s+|\n/gm; +var decorate = (selectors, currentBlock) => selectors.reduceRight((rules, selector) => ({ [selector]: rules }), currentBlock); +var saveBlock = (rules, selectors, currentBlock) => { + if (currentBlock) { + rules.push(decorate(selectors, currentBlock)); + } +}; +var interleave = (strings, interpolations, context) => { + let buffer = strings[0]; + const result = []; + for (let index = 0; index < interpolations.length;) { + const interpolation = evalThunk(interpolations[index], context); + if (interpolation && typeof interpolation == "object") { + result.push(buffer, interpolation); + buffer = strings[++index]; + } else { + buffer += (interpolation || "") + strings[++index]; + } + } + result.push(buffer); + return result; +}; +var astish = (values, context) => { + const selectors = []; + const rules = []; + let currentBlock; + let match; + for (let index = 0; index < values.length; index++) { + const value = values[index]; + if (typeof value == "string") { + while (match = newRule.exec(value.replace(ruleClean, " "))) { + if (!match[0]) + continue; + if (match[4]) { + currentBlock = saveBlock(rules, selectors, currentBlock); + selectors.pop(); + } + if (match[3]) { + currentBlock = saveBlock(rules, selectors, currentBlock); + selectors.push(match[3]); + } else if (!match[4]) { + if (!currentBlock) + currentBlock = {}; + const value2 = match[2] && /\S/.test(match[2]) ? match[2] : values[++index]; + if (value2) { + if (match[1] == "@apply") { + merge(currentBlock, evalThunk(apply(value2), context), context); + } else { + currentBlock[match[1]] = value2; + } + } + } + } + } else { + currentBlock = saveBlock(rules, selectors, currentBlock); + rules.push(decorate(selectors, value)); + } + } + saveBlock(rules, selectors, currentBlock); + return rules; +}; +var cssFactory = (tokens, context) => translate(Array.isArray(tokens[0]) && Array.isArray(tokens[0].raw) ? astish(interleave(tokens[0], tokens.slice(1), context), context) : tokens, context); +var css = (...tokens) => directive(cssFactory, tokens); +var keyframesFactory = (tokens, context) => { + const waypoints = cssFactory(tokens, context); + const id = hash(JSON.stringify(waypoints)); + context.tw(() => ({ [`@keyframes ${id}`]: waypoints })); + return id; +}; +var keyframes = (...tokens) => directive(keyframesFactory, tokens); +var animation = (value, waypoints) => waypoints === void 0 ? (...args) => animation(value, keyframes(...args)) : css({ + ...(value && typeof value == "object" ? value : { animation: value }), + animationName: typeof waypoints == "function" ? waypoints : keyframes(waypoints) }); + +var screenFactory = ({ size, rules }, context) => { + const media = buildMediaQuery(context.theme("screens", size)); + return rules === void 0 ? media : { + [media]: typeof rules == "function" ? evalThunk(rules, context) : cssFactory([rules], context) }; + +}; +var screen = (size, rules) => directive(screenFactory, { size, rules }); +export { +animation, +css, +keyframes, +screen }; \ No newline at end of file diff --git a/src/dep/twind.mjs b/src/dep/twind.mjs new file mode 100644 index 0000000..6d0ad35 --- /dev/null +++ b/src/dep/twind.mjs @@ -0,0 +1,2402 @@ +/** + * Twind v0.16.16 + * @license MIT + * @source https://unpkg.com/twind@0.16.16/twind.js?module + */ + +// src/internal/util.ts +var includes = (value, search) => !!~value.indexOf(search); +var join = (parts, separator = '-') => parts.join(separator); +var joinTruthy = (parts, separator) => join(parts.filter(Boolean), separator); +var tail = (array, startIndex = 1) => array.slice(startIndex); +var identity = (value) => value; +var noop = () => {}; +var capitalize = (value) => value[0].toUpperCase() + tail(value); +var hyphenate = (value) => value.replace(/[A-Z]/g, '-$&').toLowerCase(); +var evalThunk = (value, context) => { + while (typeof value == 'function') { + value = value(context); + } + return value; +}; +var ensureMaxSize = (map, max) => { + if (map.size > max) { + map.delete(map.keys().next().value); + } +}; +var isCSSProperty = (key, value) => + !includes('@:&', key[0]) && (includes('rg', (typeof value)[5]) || Array.isArray(value)); +var merge = (target, source, context) => + source + ? Object.keys(source).reduce((target2, key) => { + const value = evalThunk(source[key], context); + if (isCSSProperty(key, value)) { + target2[hyphenate(key)] = value; + } else { + target2[key] = + key[0] == '@' && includes('figa', key[1]) + ? (target2[key] || []).concat(value) + : merge(target2[key] || {}, value, context); + } + return target2; + }, target) + : target; +var escape = + (typeof CSS !== 'undefined' && CSS.escape) || + ((className) => + className.replace(/[!"'`*+.,;:\\/<=>?@#$%&^|~()[\]{}]/g, '\\$&').replace(/^\d/, '\\3$& ')); +var buildMediaQuery = (screen) => { + if (!Array.isArray(screen)) { + screen = [screen]; + } + return ( + '@media ' + + join( + screen.map((screen2) => { + if (typeof screen2 == 'string') { + screen2 = { min: screen2 }; + } + return ( + screen2.raw || + join( + Object.keys(screen2).map((feature) => `(${feature}-width:${screen2[feature]})`), + ' and ' + ) + ); + }), + ',' + ) + ); +}; +var cyrb32 = (value) => { + for (var h = 9, index = value.length; index--; ) { + h = Math.imul(h ^ value.charCodeAt(index), 1597334677); + } + return 'tw-' + ((h ^ (h >>> 9)) >>> 0).toString(36); +}; +var sortedInsertionIndex = (array, element) => { + for (var low = 0, high = array.length; low < high; ) { + const pivot = (high + low) >> 1; + if (array[pivot] <= element) { + low = pivot + 1; + } else { + high = pivot; + } + } + return high; +}; + +// src/twind/parse.ts +var groupings; +var rules; +var startGrouping = (value = '') => { + groupings.push(value); + return ''; +}; +var endGrouping = (isWhitespace) => { + groupings.length = Math.max(groupings.lastIndexOf('') + ~~isWhitespace, 0); +}; +var onlyPrefixes = (s) => s && !includes('!:', s[0]); +var onlyVariants = (s) => s[0] == ':'; +var addRule = (directive2, negate) => { + rules.push({ + v: groupings.filter(onlyVariants), + d: directive2, + n: negate, + i: includes(groupings, '!'), + $: '', + }); +}; +var saveRule = (buffer) => { + const negate = buffer[0] == '-'; + if (negate) { + buffer = tail(buffer); + } + const prefix = join(groupings.filter(onlyPrefixes)); + addRule(buffer == '&' ? prefix : (prefix && prefix + '-') + buffer, negate); + return ''; +}; +var parseString = (token, isVariant) => { + let buffer = ''; + for (let char, dynamic = false, position2 = 0; (char = token[position2++]); ) { + if (dynamic || char == '[') { + buffer += char; + dynamic = char != ']'; + continue; + } + switch (char) { + case ':': + buffer = + buffer && + startGrouping(':' + (token[position2] == char ? token[position2++] : '') + buffer); + break; + case '(': + buffer = buffer && startGrouping(buffer); + startGrouping(); + break; + case '!': + startGrouping(char); + break; + case ')': + case ' ': + case ' ': + case '\n': + case '\r': + buffer = buffer && saveRule(buffer); + endGrouping(char !== ')'); + break; + default: + buffer += char; + } + } + if (buffer) { + if (isVariant) { + startGrouping(':' + buffer); + } else if (buffer.slice(-1) == '-') { + startGrouping(buffer.slice(0, -1)); + } else { + saveRule(buffer); + } + } +}; +var parseGroupedToken = (token) => { + startGrouping(); + parseToken(token); + endGrouping(); +}; +var parseGroup = (key, token) => { + if (token) { + startGrouping(); + const isVariant = includes('tbu', (typeof token)[1]); + parseString(key, isVariant); + if (isVariant) { + parseGroupedToken(token); + } + endGrouping(); + } +}; +var parseToken = (token) => { + switch (typeof token) { + case 'string': + parseString(token); + break; + case 'function': + addRule(token); + break; + case 'object': + if (Array.isArray(token)) { + token.forEach(parseGroupedToken); + } else if (token) { + Object.keys(token).forEach((key) => { + parseGroup(key, token[key]); + }); + } + } +}; +var staticsCaches = new WeakMap(); +var buildStatics = (strings) => { + let statics = staticsCaches.get(strings); + if (!statics) { + let slowModeIndex = NaN; + let buffer = ''; + statics = strings.map((token, index) => { + if ( + slowModeIndex !== slowModeIndex && + (token.slice(-1) == '[' || includes(':-(', (strings[index + 1] || '')[0])) + ) { + slowModeIndex = index; + } + if (index >= slowModeIndex) { + return (interpolation) => { + if (index == slowModeIndex) { + buffer = ''; + } + buffer += token; + if (includes('rg', (typeof interpolation)[5])) { + buffer += interpolation; + } else if (interpolation) { + parseString(buffer); + buffer = ''; + parseToken(interpolation); + } + if (index == strings.length - 1) { + parseString(buffer); + } + }; + } + const staticRules = (rules = []); + parseString(token); + const activeGroupings = [...groupings]; + rules = []; + return (interpolation) => { + rules.push(...staticRules); + groupings = [...activeGroupings]; + if (interpolation) { + parseToken(interpolation); + } + }; + }); + staticsCaches.set(strings, statics); + } + return statics; +}; +var parse = (tokens) => { + groupings = []; + rules = []; + if (Array.isArray(tokens[0]) && Array.isArray(tokens[0].raw)) { + buildStatics(tokens[0]).forEach((apply2, index) => apply2(tokens[index + 1])); + } else { + parseToken(tokens); + } + return rules; +}; + +// src/twind/directive.ts +var isFunctionFree; +var detectFunction = (key, value) => { + if (typeof value == 'function') { + isFunctionFree = false; + } + return value; +}; +var stringify = (data) => { + isFunctionFree = true; + const key = JSON.stringify(data, detectFunction); + return isFunctionFree && key; +}; +var cacheByFactory = new WeakMap(); +var directive = (factory, data) => { + const key = stringify(data); + let directive2; + if (key) { + var cache = cacheByFactory.get(factory); + if (!cache) { + cacheByFactory.set(factory, (cache = new Map())); + } + directive2 = cache.get(key); + } + if (!directive2) { + directive2 = Object.defineProperty( + (params, context) => { + context = Array.isArray(params) ? context : params; + return evalThunk(factory(data, context), context); + }, + 'toJSON', + { + value: () => key || data, + } + ); + + if (cache) { + cache.set(key, directive2); + ensureMaxSize(cache, 1e4); + } + } + return directive2; +}; + +// src/twind/apply.ts +var applyFactory = (tokens, { css }) => css(parse(tokens)); +var apply = (...tokens) => directive(applyFactory, tokens); + +// src/twind/helpers.ts +var positions = (resolve) => (value, position2, prefix, suffix) => { + if (value) { + const properties = position2 && resolve(position2); + if (properties && properties.length > 0) { + return properties.reduce((declarations, property2) => { + declarations[joinTruthy([prefix, property2, suffix])] = value; + return declarations; + }, {}); + } + } +}; +var corners = /* @__PURE__ */ positions( + (key) => + ({ + t: ['top-left', 'top-right'], + r: ['top-right', 'bottom-right'], + b: ['bottom-left', 'bottom-right'], + l: ['bottom-left', 'top-left'], + tl: ['top-left'], + tr: ['top-right'], + bl: ['bottom-left'], + br: ['bottom-right'], + }[key]) +); +var expandEdges = (key) => { + const parts = ({ x: 'lr', y: 'tb' }[key] || key || '').split('').sort(); + for (let index = parts.length; index--; ) { + if ( + !(parts[index] = { + t: 'top', + r: 'right', + b: 'bottom', + l: 'left', + }[parts[index]]) + ) + return; + } + if (parts.length) return parts; +}; +var edges = /* @__PURE__ */ positions(expandEdges); + +// src/twind/plugins.ts +var _; +var __; +var $; +var toColumnsOrRows = (x) => (x == 'cols' ? 'columns' : 'rows'); +var property = (property2) => (params, context, id) => ({ + [property2]: id + ((_ = join(params)) && '-' + _), +}); + +var propertyValue = (property2, separator) => (params, context, id) => + (_ = join(params, separator)) && { + [property2 || id]: _, + }; + +var themeProperty = + (section) => + (params, { theme: theme2 }, id) => + (_ = theme2(section || id, params)) && { + [section || id]: _, + }; + +var themePropertyFallback = + (section, separator) => + (params, { theme: theme2 }, id) => + (_ = theme2(section || id, params, join(params, separator))) && { + [section || id]: _, + }; + +var alias = (handler, name) => (params, context) => handler(params, context, name); +var display = property('display'); +var position = property('position'); +var textTransform = property('textTransform'); +var textDecoration = property('textDecoration'); +var fontStyle = property('fontStyle'); +var fontVariantNumeric = (key) => (params, context, id) => ({ + ['--tw-' + key]: id, + fontVariantNumeric: + 'var(--tw-ordinal,/*!*/ /*!*/) var(--tw-slashed-zero,/*!*/ /*!*/) var(--tw-numeric-figure,/*!*/ /*!*/) var(--tw-numeric-spacing,/*!*/ /*!*/) var(--tw-numeric-fraction,/*!*/ /*!*/)', +}); + +var inset = (params, { theme: theme2 }, id) => (_ = theme2('inset', params)) && { [id]: _ }; +var opacityProperty = (params, theme2, id, section = id) => + (_ = theme2(section + 'Opacity', tail(params))) && { + [`--tw-${id}-opacity`]: _, + }; + +var parseColorComponent = (chars, factor) => Math.round(parseInt(chars, 16) * factor); +var asRGBA = (color, opacityProperty2, opacityDefault) => { + if ( + color && + color[0] == '#' && + (_ = (color.length - 1) / 3) && + ($ = [17, 1, 0.062272][_ - 1]) + ) { + return `rgba(${parseColorComponent(color.substr(1, _), $)},${parseColorComponent( + color.substr(1 + _, _), + $ + )},${parseColorComponent(color.substr(1 + 2 * _, _), $)},${ + opacityProperty2 + ? `var(--tw-${opacityProperty2}${opacityDefault ? ',' + opacityDefault : ''})` + : opacityDefault || 1 + })`; + } + return color; +}; +var withOpacityFallback = (property2, kind, color) => + color && typeof color == 'string' + ? (_ = asRGBA(color, kind + '-opacity')) && _ !== color + ? { + [`--tw-${kind}-opacity`]: '1', + [property2]: [color, _], + } + : { [property2]: color } + : void 0; +var transparentTo = (color) => (($ = asRGBA(color, '', '0')) == _ ? 'transparent' : $); +var reversableEdge = (params, { theme: theme2 }, id, section, prefix, suffix) => + (_ = { x: ['right', 'left'], y: ['bottom', 'top'] }[params[0]]) && + ($ = `--tw-${id}-${params[0]}-reverse`) + ? params[1] == 'reverse' + ? { + [$]: '1', + } + : { + [$]: '0', + [joinTruthy([prefix, _[0], suffix])]: + (__ = theme2(section, tail(params))) && `calc(${__} * var(${$}))`, + [joinTruthy([prefix, _[1], suffix])]: __ && [ + __, + `calc(${__} * calc(1 - var(${$})))`, + ], + } + : void 0; +var placeHelper = (property2, params) => + params[0] && { + [property2]: (includes('wun', (params[0] || '')[3]) ? 'space-' : '') + params[0], + }; + +var contentPluginFor = (property2) => (params) => + includes(['start', 'end'], params[0]) + ? { [property2]: 'flex-' + params[0] } + : placeHelper(property2, params); +var gridPlugin = + (kind) => + (params, { theme: theme2 }) => { + if ((_ = theme2('grid' + capitalize(kind), params, ''))) { + return { ['grid-' + kind]: _ }; + } + switch (params[0]) { + case 'span': + return ( + params[1] && { + ['grid-' + kind]: `span ${params[1]} / span ${params[1]}`, + } + ); + + case 'start': + case 'end': + return ( + (_ = theme2( + 'grid' + capitalize(kind) + capitalize(params[0]), + tail(params), + join(tail(params)) + )) && { + [`grid-${kind}-${params[0]}`]: _, + } + ); + } + }; +var border = (params, { theme: theme2 }, id) => { + switch (params[0]) { + case 'solid': + case 'dashed': + case 'dotted': + case 'double': + case 'none': + return propertyValue('borderStyle')(params); + case 'collapse': + case 'separate': + return propertyValue('borderCollapse')(params); + case 'opacity': + return opacityProperty(params, theme2, id); + } + + return (_ = theme2(id + 'Width', params, '')) + ? { borderWidth: _ } + : withOpacityFallback('borderColor', id, theme2(id + 'Color', params)); +}; +var transform = (gpu) => + (gpu + ? 'translate3d(var(--tw-translate-x,0),var(--tw-translate-y,0),0)' + : 'translateX(var(--tw-translate-x,0)) translateY(var(--tw-translate-y,0))') + + ' rotate(var(--tw-rotate,0)) skewX(var(--tw-skew-x,0)) skewY(var(--tw-skew-y,0)) scaleX(var(--tw-scale-x,1)) scaleY(var(--tw-scale-y,1))'; +var transformXYFunction = (params, context, id) => + params[0] && + (_ = context.theme(id, params[1] || params[0])) && { + [`--tw-${id}-x`]: params[0] !== 'y' && _, + [`--tw-${id}-y`]: params[0] !== 'x' && _, + transform: [`${id}${params[1] ? params[0].toUpperCase() : ''}(${_})`, transform()], + }; + +var edgesPluginFor = (key) => (params, context, id) => + id[1] + ? edges(context.theme(key, params), id[1], key) + : themeProperty(key)(params, context, id); +var padding = edgesPluginFor('padding'); +var margin = edgesPluginFor('margin'); +var minMax = (params, { theme: theme2 }, id) => + (_ = { w: 'width', h: 'height' }[params[0]]) && { + [(_ = `${id}${capitalize(_)}`)]: theme2(_, tail(params)), + }; + +var filter = (params, { theme: theme2 }, id) => { + const parts = id.split('-'); + const prefix = parts[0] == 'backdrop' ? parts[0] + '-' : ''; + if (!prefix) { + params.unshift(...parts); + } + if (params[0] == 'filter') { + const filters = [ + 'blur', + 'brightness', + 'contrast', + 'grayscale', + 'hue-rotate', + 'invert', + prefix && 'opacity', + 'saturate', + 'sepia', + !prefix && 'drop-shadow', + ].filter(Boolean); + return params[1] == 'none' + ? { [prefix + 'filter']: 'none' } + : filters.reduce( + (css, key) => { + css['--tw-' + prefix + key] = 'var(--tw-empty,/*!*/ /*!*/)'; + return css; + }, + { + [prefix + 'filter']: filters.map((key) => `var(--tw-${prefix}${key})`).join(' '), + } + ); + } + $ = params.shift(); + if (includes(['hue', 'drop'], $)) $ += capitalize(params.shift()); + return ( + (_ = theme2(prefix ? 'backdrop' + capitalize($) : $, params)) && { + ['--tw-' + prefix + $]: (Array.isArray(_) ? _ : [_]) + .map((_4) => `${hyphenate($)}(${_4})`) + .join(' '), + } + ); +}; +var corePlugins = { + group: (params, { tag }, id) => tag(join([id, ...params])), + hidden: alias(display, 'none'), + inline: display, + block: display, + contents: display, + flow: display, + table: (params, context, id) => + includes(['auto', 'fixed'], params[0]) + ? { tableLayout: params[0] } + : display(params, context, id), + flex(params, context, id) { + switch (params[0]) { + case 'row': + case 'col': + return { + flexDirection: join(params[0] == 'col' ? ['column', ...tail(params)] : params), + }; + + case 'nowrap': + case 'wrap': + return { flexWrap: join(params) }; + case 'grow': + case 'shrink': + _ = context.theme('flex' + capitalize(params[0]), tail(params), params[1] || 1); + return ( + _ != null && { + ['flex-' + params[0]]: '' + _, + } + ); + } + + return (_ = context.theme('flex', params, '')) + ? { flex: _ } + : display(params, context, id); + }, + grid(params, context, id) { + switch (params[0]) { + case 'cols': + case 'rows': + return ( + (_ = context.theme( + 'gridTemplate' + capitalize(toColumnsOrRows(params[0])), + tail(params), + params.length == 2 && Number(params[1]) + ? `repeat(${params[1]},minmax(0,1fr))` + : join(tail(params)) + )) && { + ['gridTemplate-' + toColumnsOrRows(params[0])]: _, + } + ); + + case 'flow': + return ( + params.length > 1 && { + gridAutoFlow: join( + params[1] == 'col' ? ['column', ...tail(params, 2)] : tail(params), + ' ' + ), + } + ); + } + + return display(params, context, id); + }, + auto: (params, { theme: theme2 }) => + includes(['cols', 'rows'], params[0]) && + (_ = theme2( + 'gridAuto' + capitalize(toColumnsOrRows(params[0])), + tail(params), + join(tail(params)) + )) && { + ['gridAuto-' + toColumnsOrRows(params[0])]: _, + }, + + static: position, + fixed: position, + absolute: position, + relative: position, + sticky: position, + visible: { visibility: 'visible' }, + invisible: { visibility: 'hidden' }, + antialiased: { + WebkitFontSmoothing: 'antialiased', + MozOsxFontSmoothing: 'grayscale', + }, + + 'subpixel-antialiased': { + WebkitFontSmoothing: 'auto', + MozOsxFontSmoothing: 'auto', + }, + + truncate: { + overflow: 'hidden', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + }, + + 'sr-only': { + position: 'absolute', + width: '1px', + height: '1px', + padding: '0', + margin: '-1px', + overflow: 'hidden', + whiteSpace: 'nowrap', + clip: 'rect(0,0,0,0)', + borderWidth: '0', + }, + + 'not-sr-only': { + position: 'static', + width: 'auto', + height: 'auto', + padding: '0', + margin: '0', + overflow: 'visible', + whiteSpace: 'normal', + clip: 'auto', + }, + + resize: (params) => ({ + resize: { x: 'horizontal', y: 'vertical' }[params[0]] || params[0] || 'both', + }), + + box: (params) => params[0] && { boxSizing: params[0] + '-box' }, + appearance: propertyValue(), + cursor: themePropertyFallback(), + float: propertyValue(), + clear: propertyValue(), + decoration: propertyValue('boxDecorationBreak'), + isolate: { isolation: 'isolate' }, + isolation: propertyValue(), + 'mix-blend': propertyValue('mixBlendMode'), + top: inset, + right: inset, + bottom: inset, + left: inset, + inset: (params, { theme: theme2 }) => + (_ = expandEdges(params[0])) + ? edges(theme2('inset', tail(params)), params[0]) + : (_ = theme2('inset', params)) && { + top: _, + right: _, + bottom: _, + left: _, + }, + + underline: textDecoration, + 'line-through': textDecoration, + 'no-underline': alias(textDecoration, 'none'), + 'text-underline': alias(textDecoration, 'underline'), + 'text-no-underline': alias(textDecoration, 'none'), + 'text-line-through': alias(textDecoration, 'line-through'), + uppercase: textTransform, + lowercase: textTransform, + capitalize: textTransform, + 'normal-case': alias(textTransform, 'none'), + 'text-normal-case': alias(textTransform, 'none'), + italic: fontStyle, + 'not-italic': alias(fontStyle, 'normal'), + 'font-italic': alias(fontStyle, 'italic'), + 'font-not-italic': alias(fontStyle, 'normal'), + font: (params, context, id) => + (_ = context.theme('fontFamily', params, '')) + ? { fontFamily: _ } + : themeProperty('fontWeight')(params, context, id), + items: (params) => + params[0] && { + alignItems: includes(['start', 'end'], params[0]) ? 'flex-' + params[0] : join(params), + }, + + 'justify-self': propertyValue(), + 'justify-items': propertyValue(), + justify: contentPluginFor('justifyContent'), + content: contentPluginFor('alignContent'), + self: contentPluginFor('alignSelf'), + place: (params) => params[0] && placeHelper('place-' + params[0], tail(params)), + overscroll: (params) => + params[0] && { + ['overscrollBehavior' + (params[1] ? '-' + params[0] : '')]: params[1] || params[0], + }, + + col: gridPlugin('column'), + row: gridPlugin('row'), + duration: themeProperty('transitionDuration'), + delay: themeProperty('transitionDelay'), + tracking: themeProperty('letterSpacing'), + leading: themeProperty('lineHeight'), + z: themeProperty('zIndex'), + opacity: themeProperty(), + ease: themeProperty('transitionTimingFunction'), + p: padding, + py: padding, + px: padding, + pt: padding, + pr: padding, + pb: padding, + pl: padding, + m: margin, + my: margin, + mx: margin, + mt: margin, + mr: margin, + mb: margin, + ml: margin, + w: themeProperty('width'), + h: themeProperty('height'), + min: minMax, + max: minMax, + fill: themeProperty(), + order: themeProperty(), + origin: themePropertyFallback('transformOrigin', ' '), + select: propertyValue('userSelect'), + 'pointer-events': propertyValue(), + align: propertyValue('verticalAlign'), + whitespace: propertyValue('whiteSpace'), + 'normal-nums': { fontVariantNumeric: 'normal' }, + ordinal: fontVariantNumeric('ordinal'), + 'slashed-zero': fontVariantNumeric('slashed-zero'), + 'lining-nums': fontVariantNumeric('numeric-figure'), + 'oldstyle-nums': fontVariantNumeric('numeric-figure'), + 'proportional-nums': fontVariantNumeric('numeric-spacing'), + 'tabular-nums': fontVariantNumeric('numeric-spacing'), + 'diagonal-fractions': fontVariantNumeric('numeric-fraction'), + 'stacked-fractions': fontVariantNumeric('numeric-fraction'), + overflow: (params, context, id) => + includes(['ellipsis', 'clip'], params[0]) + ? propertyValue('textOverflow')(params) + : params[1] + ? { ['overflow-' + params[0]]: params[1] } + : propertyValue()(params, context, id), + transform: (params) => + params[0] == 'none' + ? { transform: 'none' } + : { + '--tw-translate-x': '0', + '--tw-translate-y': '0', + '--tw-rotate': '0', + '--tw-skew-x': '0', + '--tw-skew-y': '0', + '--tw-scale-x': '1', + '--tw-scale-y': '1', + transform: transform(params[0] == 'gpu'), + }, + + rotate: (params, { theme: theme2 }) => + (_ = theme2('rotate', params)) && { + '--tw-rotate': _, + transform: [`rotate(${_})`, transform()], + }, + + scale: transformXYFunction, + translate: transformXYFunction, + skew: transformXYFunction, + gap: (params, context, id) => + (_ = { x: 'column', y: 'row' }[params[0]]) + ? { [_ + 'Gap']: context.theme('gap', tail(params)) } + : themeProperty('gap')(params, context, id), + stroke: (params, context, id) => + (_ = context.theme('stroke', params, '')) + ? { stroke: _ } + : themeProperty('strokeWidth')(params, context, id), + outline: (params, { theme: theme2 }) => + (_ = theme2('outline', params)) && { + outline: _[0], + outlineOffset: _[1], + }, + + 'break-normal': { + wordBreak: 'normal', + overflowWrap: 'normal', + }, + + 'break-words': { overflowWrap: 'break-word' }, + 'break-all': { wordBreak: 'break-all' }, + text(params, { theme: theme2 }, id) { + switch (params[0]) { + case 'left': + case 'center': + case 'right': + case 'justify': + return { textAlign: params[0] }; + case 'uppercase': + case 'lowercase': + case 'capitalize': + return textTransform([], _, params[0]); + case 'opacity': + return opacityProperty(params, theme2, id); + } + + const fontSize = theme2('fontSize', params, ''); + if (fontSize) { + return typeof fontSize == 'string' + ? { fontSize } + : { + fontSize: fontSize[0], + ...(typeof fontSize[1] == 'string' ? { lineHeight: fontSize[1] } : fontSize[1]), + }; + } + return withOpacityFallback('color', 'text', theme2('textColor', params)); + }, + bg(params, { theme: theme2 }, id) { + switch (params[0]) { + case 'fixed': + case 'local': + case 'scroll': + return propertyValue('backgroundAttachment', ',')(params); + case 'bottom': + case 'center': + case 'left': + case 'right': + case 'top': + return propertyValue('backgroundPosition', ' ')(params); + case 'no': + return params[1] == 'repeat' && propertyValue('backgroundRepeat')(params); + case 'repeat': + return includes('xy', params[1]) + ? propertyValue('backgroundRepeat')(params) + : { backgroundRepeat: params[1] || params[0] }; + case 'opacity': + return opacityProperty(params, theme2, id, 'background'); + case 'clip': + case 'origin': + return ( + params[1] && { + ['background-' + params[0]]: params[1] + (params[1] == 'text' ? '' : '-box'), + } + ); + + case 'blend': + return propertyValue('background-blend-mode')(tail(params)); + case 'gradient': + if (params[1] == 'to' && (_ = expandEdges(params[2]))) { + return { + backgroundImage: `linear-gradient(to ${join(_, ' ')},var(--tw-gradient-stops))`, + }; + } + } + + return (_ = theme2('backgroundPosition', params, '')) + ? { backgroundPosition: _ } + : (_ = theme2('backgroundSize', params, '')) + ? { backgroundSize: _ } + : (_ = theme2('backgroundImage', params, '')) + ? { backgroundImage: _ } + : withOpacityFallback('backgroundColor', 'bg', theme2('backgroundColor', params)); + }, + from: (params, { theme: theme2 }) => + (_ = theme2('gradientColorStops', params)) && { + '--tw-gradient-from': _, + '--tw-gradient-stops': `var(--tw-gradient-from),var(--tw-gradient-to,${transparentTo( + _ + )})`, + }, + + via: (params, { theme: theme2 }) => + (_ = theme2('gradientColorStops', params)) && { + '--tw-gradient-stops': `var(--tw-gradient-from),${_},var(--tw-gradient-to,${transparentTo( + _ + )})`, + }, + + to: (params, { theme: theme2 }) => + (_ = theme2('gradientColorStops', params)) && { + '--tw-gradient-to': _, + }, + + border: (params, context, id) => + expandEdges(params[0]) + ? edges(context.theme('borderWidth', tail(params)), params[0], 'border', 'width') + : border(params, context, id), + divide: (params, context, id) => + (_ = + reversableEdge(params, context, id, 'divideWidth', 'border', 'width') || + border(params, context, id)) && { + '&>:not([hidden])~:not([hidden])': _, + }, + + space: (params, context, id) => + (_ = reversableEdge(params, context, id, 'space', 'margin')) && { + '&>:not([hidden])~:not([hidden])': _, + }, + + placeholder: (params, { theme: theme2 }, id) => + (_ = + params[0] == 'opacity' + ? opacityProperty(params, theme2, id) + : withOpacityFallback('color', 'placeholder', theme2('placeholderColor', params))) && { + '&::placeholder': _, + }, + + shadow: (params, { theme: theme2 }) => + (_ = theme2('boxShadow', params)) && { + ':global': { + '*': { + '--tw-shadow': '0 0 transparent', + }, + }, + + '--tw-shadow': _ == 'none' ? '0 0 transparent' : _, + boxShadow: [ + _, + `var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)`, + ], + }, + + animate: (params, { theme: theme2, tag }) => { + if (($ = theme2('animation', params))) { + const parts = $.split(' '); + if ((_ = theme2('keyframes', parts[0], (__ = {}))) !== __) { + return ( + ($ = tag(parts[0])) && { + animation: $ + ' ' + join(tail(parts), ' '), + ['@keyframes ' + $]: _, + } + ); + } + return { animation: $ }; + } + }, + ring(params, { theme: theme2 }, id) { + switch (params[0]) { + case 'inset': + return { '--tw-ring-inset': 'inset' }; + case 'opacity': + return opacityProperty(params, theme2, id); + case 'offset': + return (_ = theme2('ringOffsetWidth', tail(params), '')) + ? { + '--tw-ring-offset-width': _, + } + : { + '--tw-ring-offset-color': theme2('ringOffsetColor', tail(params)), + }; + } + + return (_ = theme2('ringWidth', params, '')) + ? { + '--tw-ring-offset-shadow': `var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)`, + '--tw-ring-shadow': `var(--tw-ring-inset) 0 0 0 calc(${_} + var(--tw-ring-offset-width)) var(--tw-ring-color)`, + boxShadow: `var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 transparent)`, + ':global': { + '*': { + '--tw-ring-inset': 'var(--tw-empty,/*!*/ /*!*/)', + '--tw-ring-offset-width': theme2('ringOffsetWidth', '', '0px'), + '--tw-ring-offset-color': theme2('ringOffsetColor', '', '#fff'), + '--tw-ring-color': asRGBA( + theme2('ringColor', '', '#93c5fd'), + 'ring-opacity', + theme2('ringOpacity', '', '0.5') + ), + '--tw-ring-offset-shadow': '0 0 transparent', + '--tw-ring-shadow': '0 0 transparent', + }, + }, + } + : { + '--tw-ring-opacity': '1', + '--tw-ring-color': asRGBA(theme2('ringColor', params), 'ring-opacity'), + }; + }, + object: (params, context, id) => + includes(['contain', 'cover', 'fill', 'none', 'scale-down'], join(params)) + ? { objectFit: join(params) } + : themePropertyFallback('objectPosition', ' ')(params, context, id), + list: (params, context, id) => + join(params) == 'item' + ? display(params, context, id) + : includes(['inside', 'outside'], join(params)) + ? { listStylePosition: params[0] } + : themePropertyFallback('listStyleType')(params, context, id), + rounded: (params, context, id) => + corners(context.theme('borderRadius', tail(params), ''), params[0], 'border', 'radius') || + themeProperty('borderRadius')(params, context, id), + 'transition-none': { transitionProperty: 'none' }, + transition: (params, { theme: theme2 }) => ({ + transitionProperty: theme2('transitionProperty', params), + transitionTimingFunction: theme2('transitionTimingFunction', ''), + transitionDuration: theme2('transitionDuration', ''), + }), + + container: (params, { theme: theme2 }) => { + const { screens = theme2('screens'), center, padding: padding2 } = theme2('container'); + const paddingFor = (screen) => + (_ = + padding2 && + (typeof padding2 == 'string' ? padding2 : padding2[screen] || padding2.DEFAULT)) + ? { + paddingRight: _, + paddingLeft: _, + } + : {}; + return Object.keys(screens).reduce( + (rules2, screen) => { + if (($ = screens[screen]) && typeof $ == 'string') { + rules2[buildMediaQuery($)] = { + '&': { + 'max-width': $, + ...paddingFor(screen), + }, + }; + } + return rules2; + }, + { + width: '100%', + ...(center ? { marginRight: 'auto', marginLeft: 'auto' } : {}), + ...paddingFor('xs'), + } + ); + }, + filter, + blur: filter, + brightness: filter, + contrast: filter, + grayscale: filter, + 'hue-rotate': filter, + invert: filter, + saturate: filter, + sepia: filter, + 'drop-shadow': filter, + backdrop: filter, +}; + +// src/twind/preflight.ts +var createPreflight = (theme2) => ({ + ':root': { tabSize: 4 }, + 'body,blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre,fieldset,ol,ul': { margin: '0' }, + button: { backgroundColor: 'transparent', backgroundImage: 'none' }, + 'button,[type="button"],[type="reset"],[type="submit"]': { WebkitAppearance: 'button' }, + 'button:focus': { outline: ['1px dotted', '5px auto -webkit-focus-ring-color'] }, + 'fieldset,ol,ul,legend': { padding: '0' }, + 'ol,ul': { listStyle: 'none' }, + html: { + lineHeight: '1.5', + WebkitTextSizeAdjust: '100%', + fontFamily: theme2('fontFamily.sans', 'ui-sans-serif,system-ui,sans-serif'), + }, + + body: { fontFamily: 'inherit', lineHeight: 'inherit' }, + '*,::before,::after': { + boxSizing: 'border-box', + border: `0 solid ${theme2('borderColor.DEFAULT', 'currentColor')}`, + }, + + hr: { height: '0', color: 'inherit', borderTopWidth: '1px' }, + img: { borderStyle: 'solid' }, + textarea: { resize: 'vertical' }, + 'input::placeholder,textarea::placeholder': { + opacity: '1', + color: theme2('placeholderColor.DEFAULT', theme2('colors.gray.400', '#a1a1aa')), + }, + + 'button,[role="button"]': { cursor: 'pointer' }, + table: { textIndent: '0', borderColor: 'inherit', borderCollapse: 'collapse' }, + 'h1,h2,h3,h4,h5,h6': { fontSize: 'inherit', fontWeight: 'inherit' }, + a: { color: 'inherit', textDecoration: 'inherit' }, + 'button,input,optgroup,select,textarea': { + fontFamily: 'inherit', + fontSize: '100%', + margin: '0', + padding: '0', + lineHeight: 'inherit', + color: 'inherit', + }, + + 'button,select': { textTransform: 'none' }, + '::-moz-focus-inner': { borderStyle: 'none', padding: '0' }, + ':-moz-focusring': { outline: '1px dotted ButtonText' }, + ':-moz-ui-invalid': { boxShadow: 'none' }, + progress: { verticalAlign: 'baseline' }, + '::-webkit-inner-spin-button,::-webkit-outer-spin-button': { height: 'auto' }, + '[type="search"]': { WebkitAppearance: 'textfield', outlineOffset: '-2px' }, + '::-webkit-search-decoration': { WebkitAppearance: 'none' }, + '::-webkit-file-upload-button': { WebkitAppearance: 'button', font: 'inherit' }, + summary: { display: 'list-item' }, + 'abbr[title]': { textDecoration: 'underline dotted' }, + 'b,strong': { fontWeight: 'bolder' }, + 'pre,code,kbd,samp': { + fontFamily: theme2('fontFamily', 'mono', 'ui-monospace,monospace'), + fontSize: '1em', + }, + + 'sub,sup': { + fontSize: '75%', + lineHeight: '0', + position: 'relative', + verticalAlign: 'baseline', + }, + sub: { bottom: '-0.25em' }, + sup: { top: '-0.5em' }, + 'img,svg,video,canvas,audio,iframe,embed,object': { + display: 'block', + verticalAlign: 'middle', + }, + 'img,video': { maxWidth: '100%', height: 'auto' }, +}); + +// src/twind/variants.ts +var coreVariants = { + dark: '@media (prefers-color-scheme:dark)', + sticky: '@supports ((position: -webkit-sticky) or (position:sticky))', + 'motion-reduce': '@media (prefers-reduced-motion:reduce)', + 'motion-safe': '@media (prefers-reduced-motion:no-preference)', + first: '&:first-child', + last: '&:last-child', + even: '&:nth-child(2n)', + odd: '&:nth-child(odd)', + children: '&>*', + siblings: '&~*', + sibling: '&+*', + override: '&&', +}; + +// src/internal/dom.ts +var STYLE_ELEMENT_ID = '__twind'; +var getStyleElement = (nonce) => { + let element = self[STYLE_ELEMENT_ID]; + if (!element) { + element = document.head.appendChild(document.createElement('style')); + element.id = STYLE_ELEMENT_ID; + nonce && (element.nonce = nonce); + element.appendChild(document.createTextNode('')); + } + return element; +}; + +// src/twind/sheets.ts +var cssomSheet = ({ nonce, target = getStyleElement(nonce).sheet } = {}) => { + const offset = target.cssRules.length; + return { + target, + insert: (rule, index) => target.insertRule(rule, offset + index), + }; +}; +var voidSheet = () => ({ + target: null, + insert: noop, +}); + +// src/twind/modes.ts +var mode = (report) => ({ + unknown(section, key = [], optional, context) { + if (!optional) { + this.report({ id: 'UNKNOWN_THEME_VALUE', key: section + '.' + join(key) }, context); + } + }, + report({ id, ...info }) { + return report(`[${id}] ${JSON.stringify(info)}`); + }, +}); + +var warn = /* @__PURE__ */ mode((message) => console.warn(message)); +var strict = /* @__PURE__ */ mode((message) => { + throw new Error(message); +}); +var silent = /* @__PURE__ */ mode(noop); + +// src/twind/prefix.ts +import { + cssPropertyAlias, + cssPropertyPrefixFlags, + cssValuePrefixFlags, +} from './style-vendorizer.mjs'; +var noprefix = (property2, value, important) => + `${property2}:${value}${important ? ' !important' : ''}`; +var autoprefix = (property2, value, important) => { + let cssText = ''; + const propertyAlias = cssPropertyAlias(property2); + if (propertyAlias) cssText += `${noprefix(propertyAlias, value, important)};`; + let flags = cssPropertyPrefixFlags(property2); + if (flags & 1) cssText += `-webkit-${noprefix(property2, value, important)};`; + if (flags & 2) cssText += `-moz-${noprefix(property2, value, important)};`; + if (flags & 4) cssText += `-ms-${noprefix(property2, value, important)};`; + flags = cssValuePrefixFlags(property2, value); + if (flags & 1) cssText += `${noprefix(property2, `-webkit-${value}`, important)};`; + if (flags & 2) cssText += `${noprefix(property2, `-moz-${value}`, important)};`; + if (flags & 4) cssText += `${noprefix(property2, `-ms-${value}`, important)};`; + cssText += noprefix(property2, value, important); + return cssText; +}; + +// src/twind/theme.ts +var ratios = (start, end) => { + const result = {}; + do { + for (let dividend = 1; dividend < start; dividend++) { + result[`${dividend}/${start}`] = Number(((dividend / start) * 100).toFixed(6)) + '%'; + } + } while (++start <= end); + return result; +}; +var exponential = (stop, unit, start = 0) => { + const result = {}; + for (; start <= stop; start = start * 2 || 1) { + result[start] = start + unit; + } + return result; +}; +var linear = (stop, unit = '', divideBy = 1, start = 0, step = 1, result = {}) => { + for (; start <= stop; start += step) { + result[start] = start / divideBy + unit; + } + return result; +}; +var alias2 = (section) => (theme2) => theme2(section); +var themeFactory = (args, { theme: theme2 }) => theme2(...args); +var theme = (...args) => directive(themeFactory, args); +var defaultTheme = { + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + }, + + colors: { + transparent: 'transparent', + current: 'currentColor', + black: '#000', + white: '#fff', + gray: { + 50: '#f9fafb', + 100: '#f3f4f6', + 200: '#e5e7eb', + 300: '#d1d5db', + 400: '#9ca3af', + 500: '#6b7280', + 600: '#4b5563', + 700: '#374151', + 800: '#1f2937', + 900: '#111827', + }, + + red: { + 50: '#fef2f2', + 100: '#fee2e2', + 200: '#fecaca', + 300: '#fca5a5', + 400: '#f87171', + 500: '#ef4444', + 600: '#dc2626', + 700: '#b91c1c', + 800: '#991b1b', + 900: '#7f1d1d', + }, + + yellow: { + 50: '#fffbeb', + 100: '#fef3c7', + 200: '#fde68a', + 300: '#fcd34d', + 400: '#fbbf24', + 500: '#f59e0b', + 600: '#d97706', + 700: '#b45309', + 800: '#92400e', + 900: '#78350f', + }, + + green: { + 50: '#ecfdf5', + 100: '#d1fae5', + 200: '#a7f3d0', + 300: '#6ee7b7', + 400: '#34d399', + 500: '#10b981', + 600: '#059669', + 700: '#047857', + 800: '#065f46', + 900: '#064e3b', + }, + + blue: { + 50: '#eff6ff', + 100: '#dbeafe', + 200: '#bfdbfe', + 300: '#93c5fd', + 400: '#60a5fa', + 500: '#3b82f6', + 600: '#2563eb', + 700: '#1d4ed8', + 800: '#1e40af', + 900: '#1e3a8a', + }, + + indigo: { + 50: '#eef2ff', + 100: '#e0e7ff', + 200: '#c7d2fe', + 300: '#a5b4fc', + 400: '#818cf8', + 500: '#6366f1', + 600: '#4f46e5', + 700: '#4338ca', + 800: '#3730a3', + 900: '#312e81', + }, + + purple: { + 50: '#f5f3ff', + 100: '#ede9fe', + 200: '#ddd6fe', + 300: '#c4b5fd', + 400: '#a78bfa', + 500: '#8b5cf6', + 600: '#7c3aed', + 700: '#6d28d9', + 800: '#5b21b6', + 900: '#4c1d95', + }, + + pink: { + 50: '#fdf2f8', + 100: '#fce7f3', + 200: '#fbcfe8', + 300: '#f9a8d4', + 400: '#f472b6', + 500: '#ec4899', + 600: '#db2777', + 700: '#be185d', + 800: '#9d174d', + 900: '#831843', + }, + }, + + spacing: { + px: '1px', + 0: '0px', + .../* @__PURE__ */ linear(4, 'rem', 4, 0.5, 0.5), + .../* @__PURE__ */ linear(12, 'rem', 4, 5), + 14: '3.5rem', + .../* @__PURE__ */ linear(64, 'rem', 4, 16, 4), + 72: '18rem', + 80: '20rem', + 96: '24rem', + }, + + durations: { + 75: '75ms', + 100: '100ms', + 150: '150ms', + 200: '200ms', + 300: '300ms', + 500: '500ms', + 700: '700ms', + 1e3: '1000ms', + }, + + animation: { + none: 'none', + spin: 'spin 1s linear infinite', + ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', + pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite', + bounce: 'bounce 1s infinite', + }, + + backdropBlur: /* @__PURE__ */ alias2('blur'), + backdropBrightness: /* @__PURE__ */ alias2('brightness'), + backdropContrast: /* @__PURE__ */ alias2('contrast'), + backdropGrayscale: /* @__PURE__ */ alias2('grayscale'), + backdropHueRotate: /* @__PURE__ */ alias2('hueRotate'), + backdropInvert: /* @__PURE__ */ alias2('invert'), + backdropOpacity: /* @__PURE__ */ alias2('opacity'), + backdropSaturate: /* @__PURE__ */ alias2('saturate'), + backdropSepia: /* @__PURE__ */ alias2('sepia'), + backgroundColor: /* @__PURE__ */ alias2('colors'), + backgroundImage: { + none: 'none', + }, + + backgroundOpacity: /* @__PURE__ */ alias2('opacity'), + backgroundSize: { + auto: 'auto', + cover: 'cover', + contain: 'contain', + }, + + blur: { + 0: '0', + sm: '4px', + DEFAULT: '8px', + md: '12px', + lg: '16px', + xl: '24px', + '2xl': '40px', + '3xl': '64px', + }, + + brightness: { + .../* @__PURE__ */ linear(200, '', 100, 0, 50), + .../* @__PURE__ */ linear(110, '', 100, 90, 5), + 75: '0.75', + 125: '1.25', + }, + + borderColor: (theme2) => ({ + ...theme2('colors'), + DEFAULT: theme2('colors.gray.200', 'currentColor'), + }), + + borderOpacity: /* @__PURE__ */ alias2('opacity'), + borderRadius: { + none: '0px', + sm: '0.125rem', + DEFAULT: '0.25rem', + md: '0.375rem', + lg: '0.5rem', + xl: '0.75rem', + '2xl': '1rem', + '3xl': '1.5rem', + '1/2': '50%', + full: '9999px', + }, + + borderWidth: { + DEFAULT: '1px', + .../* @__PURE__ */ exponential(8, 'px'), + }, + + boxShadow: { + sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)', + DEFAULT: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)', + md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', + lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', + xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', + '2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)', + none: 'none', + }, + + contrast: { + .../* @__PURE__ */ linear(200, '', 100, 0, 50), + 75: '0.75', + 125: '1.25', + }, + + divideColor: /* @__PURE__ */ alias2('borderColor'), + divideOpacity: /* @__PURE__ */ alias2('borderOpacity'), + divideWidth: /* @__PURE__ */ alias2('borderWidth'), + dropShadow: { + sm: '0 1px 1px rgba(0,0,0,0.05)', + DEFAULT: ['0 1px 2px rgba(0, 0, 0, 0.1)', '0 1px 1px rgba(0, 0, 0, 0.06)'], + md: ['0 4px 3px rgba(0, 0, 0, 0.07)', '0 2px 2px rgba(0, 0, 0, 0.06)'], + lg: ['0 10px 8px rgba(0, 0, 0, 0.04)', '0 4px 3px rgba(0, 0, 0, 0.1)'], + xl: ['0 20px 13px rgba(0, 0, 0, 0.03)', '0 8px 5px rgba(0, 0, 0, 0.08)'], + '2xl': '0 25px 25px rgba(0, 0, 0, 0.15)', + none: '0 0 #0000', + }, + + fill: { current: 'currentColor' }, + grayscale: { + 0: '0', + DEFAULT: '100%', + }, + + hueRotate: { + 0: '0deg', + 15: '15deg', + 30: '30deg', + 60: '60deg', + 90: '90deg', + 180: '180deg', + }, + + invert: { + 0: '0', + DEFAULT: '100%', + }, + + flex: { + 1: '1 1 0%', + auto: '1 1 auto', + initial: '0 1 auto', + none: 'none', + }, + + fontFamily: { + sans: 'ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"'.split( + ',' + ), + serif: 'ui-serif,Georgia,Cambria,"Times New Roman",Times,serif'.split(','), + mono: 'ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace'.split( + ',' + ), + }, + + fontSize: { + xs: ['0.75rem', '1rem'], + sm: ['0.875rem', '1.25rem'], + base: ['1rem', '1.5rem'], + lg: ['1.125rem', '1.75rem'], + xl: ['1.25rem', '1.75rem'], + '2xl': ['1.5rem', '2rem'], + '3xl': ['1.875rem', '2.25rem'], + '4xl': ['2.25rem', '2.5rem'], + '5xl': ['3rem', '1'], + '6xl': ['3.75rem', '1'], + '7xl': ['4.5rem', '1'], + '8xl': ['6rem', '1'], + '9xl': ['8rem', '1'], + }, + + fontWeight: { + thin: '100', + extralight: '200', + light: '300', + normal: '400', + medium: '500', + semibold: '600', + bold: '700', + extrabold: '800', + black: '900', + }, + + gridTemplateColumns: {}, + gridTemplateRows: {}, + gridAutoColumns: { + min: 'min-content', + max: 'max-content', + fr: 'minmax(0,1fr)', + }, + + gridAutoRows: { + min: 'min-content', + max: 'max-content', + fr: 'minmax(0,1fr)', + }, + + gridColumn: { + auto: 'auto', + 'span-full': '1 / -1', + }, + + gridRow: { + auto: 'auto', + 'span-full': '1 / -1', + }, + + gap: /* @__PURE__ */ alias2('spacing'), + gradientColorStops: /* @__PURE__ */ alias2('colors'), + height: (theme2) => ({ + auto: 'auto', + ...theme2('spacing'), + ...ratios(2, 6), + full: '100%', + screen: '100vh', + }), + + inset: (theme2) => ({ + auto: 'auto', + ...theme2('spacing'), + ...ratios(2, 4), + full: '100%', + }), + + keyframes: { + spin: { + from: { + transform: 'rotate(0deg)', + }, + + to: { + transform: 'rotate(360deg)', + }, + }, + + ping: { + '0%': { + transform: 'scale(1)', + opacity: '1', + }, + + '75%,100%': { + transform: 'scale(2)', + opacity: '0', + }, + }, + + pulse: { + '0%,100%': { + opacity: '1', + }, + + '50%': { + opacity: '.5', + }, + }, + + bounce: { + '0%, 100%': { + transform: 'translateY(-25%)', + animationTimingFunction: 'cubic-bezier(0.8,0,1,1)', + }, + + '50%': { + transform: 'none', + animationTimingFunction: 'cubic-bezier(0,0,0.2,1)', + }, + }, + }, + + letterSpacing: { + tighter: '-0.05em', + tight: '-0.025em', + normal: '0em', + wide: '0.025em', + wider: '0.05em', + widest: '0.1em', + }, + + lineHeight: { + none: '1', + tight: '1.25', + snug: '1.375', + normal: '1.5', + relaxed: '1.625', + loose: '2', + .../* @__PURE__ */ linear(10, 'rem', 4, 3), + }, + + margin: (theme2) => ({ + auto: 'auto', + ...theme2('spacing'), + }), + + maxHeight: (theme2) => ({ + ...theme2('spacing'), + full: '100%', + screen: '100vh', + }), + + maxWidth: (theme2, { breakpoints }) => ({ + none: 'none', + 0: '0rem', + xs: '20rem', + sm: '24rem', + md: '28rem', + lg: '32rem', + xl: '36rem', + '2xl': '42rem', + '3xl': '48rem', + '4xl': '56rem', + '5xl': '64rem', + '6xl': '72rem', + '7xl': '80rem', + full: '100%', + min: 'min-content', + max: 'max-content', + prose: '65ch', + ...breakpoints(theme2('screens')), + }), + + minHeight: { + 0: '0px', + full: '100%', + screen: '100vh', + }, + + minWidth: { + 0: '0px', + full: '100%', + min: 'min-content', + max: 'max-content', + }, + + opacity: { + .../* @__PURE__ */ linear(100, '', 100, 0, 10), + 5: '0.05', + 25: '0.25', + 75: '0.75', + 95: '0.95', + }, + + order: { + first: '-9999', + last: '9999', + none: '0', + .../* @__PURE__ */ linear(12, '', 1, 1), + }, + + outline: { + none: ['2px solid transparent', '2px'], + white: ['2px dotted white', '2px'], + black: ['2px dotted black', '2px'], + }, + + padding: /* @__PURE__ */ alias2('spacing'), + placeholderColor: /* @__PURE__ */ alias2('colors'), + placeholderOpacity: /* @__PURE__ */ alias2('opacity'), + ringColor: (theme2) => ({ + DEFAULT: theme2('colors.blue.500', '#3b82f6'), + ...theme2('colors'), + }), + + ringOffsetColor: /* @__PURE__ */ alias2('colors'), + ringOffsetWidth: /* @__PURE__ */ exponential(8, 'px'), + ringOpacity: (theme2) => ({ + DEFAULT: '0.5', + ...theme2('opacity'), + }), + + ringWidth: { + DEFAULT: '3px', + .../* @__PURE__ */ exponential(8, 'px'), + }, + + rotate: { + .../* @__PURE__ */ exponential(2, 'deg'), + .../* @__PURE__ */ exponential(12, 'deg', 3), + .../* @__PURE__ */ exponential(180, 'deg', 45), + }, + + saturate: /* @__PURE__ */ linear(200, '', 100, 0, 50), + scale: { + .../* @__PURE__ */ linear(150, '', 100, 0, 50), + .../* @__PURE__ */ linear(110, '', 100, 90, 5), + 75: '0.75', + 125: '1.25', + }, + + sepia: { + 0: '0', + DEFAULT: '100%', + }, + + skew: { + .../* @__PURE__ */ exponential(2, 'deg'), + .../* @__PURE__ */ exponential(12, 'deg', 3), + }, + + space: /* @__PURE__ */ alias2('spacing'), + stroke: { + current: 'currentColor', + }, + + strokeWidth: /* @__PURE__ */ linear(2), + textColor: /* @__PURE__ */ alias2('colors'), + textOpacity: /* @__PURE__ */ alias2('opacity'), + transitionDuration: (theme2) => ({ + DEFAULT: '150ms', + ...theme2('durations'), + }), + + transitionDelay: /* @__PURE__ */ alias2('durations'), + transitionProperty: { + none: 'none', + all: 'all', + DEFAULT: + 'background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter', + colors: 'background-color,border-color,color,fill,stroke', + opacity: 'opacity', + shadow: 'box-shadow', + transform: 'transform', + }, + + transitionTimingFunction: { + DEFAULT: 'cubic-bezier(0.4,0,0.2,1)', + linear: 'linear', + in: 'cubic-bezier(0.4,0,1,1)', + out: 'cubic-bezier(0,0,0.2,1)', + 'in-out': 'cubic-bezier(0.4,0,0.2,1)', + }, + + translate: (theme2) => ({ + ...theme2('spacing'), + ...ratios(2, 4), + full: '100%', + }), + + width: (theme2) => ({ + auto: 'auto', + ...theme2('spacing'), + ...ratios(2, 6), + ...ratios(12, 12), + screen: '100vw', + full: '100%', + min: 'min-content', + max: 'max-content', + }), + + zIndex: { + auto: 'auto', + .../* @__PURE__ */ linear(50, '', 1, 0, 10), + }, +}; + +var flattenColorPalette = (colors, target = {}, prefix = []) => { + Object.keys(colors).forEach((property2) => { + const value = colors[property2]; + if (property2 == 'DEFAULT') { + target[join(prefix)] = value; + target[join(prefix, '.')] = value; + } + const key = [...prefix, property2]; + target[join(key)] = value; + target[join(key, '.')] = value; + if (value && typeof value == 'object') { + flattenColorPalette(value, target, key); + } + }, target); + return target; +}; +var resolveContext = { + negative: () => ({}), + breakpoints: (screens) => + Object.keys(screens) + .filter((key) => typeof screens[key] == 'string') + .reduce((target, key) => { + target['screen-' + key] = screens[key]; + return target; + }, {}), +}; + +var handleArbitraryValues = (section, key) => + (key = key[0] == '[' && key.slice(-1) == ']' && key.slice(1, -1)) && + includes(section, 'olor') == /^(#|(hsl|rgb)a?\(|[a-z]+$)/.test(key) && + (includes(key, 'calc(') + ? key.replace( + /(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, + '$1 $2 ' + ) + : key); +var makeThemeResolver = (config) => { + const cache = new Map(); + const theme2 = { ...defaultTheme, ...config }; + const deref = (theme3, section) => { + const base = theme3 && theme3[section]; + const value = typeof base == 'function' ? base(resolve, resolveContext) : base; + return value && section == 'colors' ? flattenColorPalette(value) : value; + }; + const resolve = (section, key, defaultValue) => { + const keypath = section.split('.'); + section = keypath[0]; + if (keypath.length > 1) { + defaultValue = key; + key = join(tail(keypath), '.'); + } + let base = cache.get(section); + if (!base) { + cache.set(section, (base = { ...deref(theme2, section) })); + Object.assign(base, deref(theme2.extend, section)); + } + if (key != null) { + key = (Array.isArray(key) ? join(key) : key) || 'DEFAULT'; + const value = handleArbitraryValues(section, key) || base[key]; + return value == null + ? defaultValue + : Array.isArray(value) && !includes(['fontSize', 'outline', 'dropShadow'], section) + ? join(value, ',') + : value; + } + return base; + }; + return resolve; +}; + +// src/twind/translate.ts +var translate = (plugins, context) => (rule, isTranslating) => { + if (typeof rule.d == 'function') { + return rule.d(context); + } + const parameters = rule.d.split(/-(?![^[]*])/g); + if (!isTranslating && parameters[0] == 'tw' && rule.$ == rule.d) { + return rule.$; + } + for (let index = parameters.length; index; index--) { + const id = join(parameters.slice(0, index)); + const plugin = plugins[id]; + if (plugin) { + return typeof plugin == 'function' + ? plugin(tail(parameters, index), context, id) + : typeof plugin == 'string' + ? context[isTranslating ? 'css' : 'tw'](plugin) + : plugin; + } + } +}; + +// src/twind/decorate.ts +var _2; +var GROUP_RE = /^:(group(?:(?!-focus).+?)*)-(.+)$/; +var NOT_PREFIX_RE = /^(:not)-(.+)/; +var prepareVariantSelector = (variant) => (variant[1] == '[' ? tail(variant) : variant); +var decorate = (darkMode, variants, { theme: theme2, tag }) => { + const applyVariant = (translation, variant) => { + if ((_2 = theme2('screens', tail(variant), ''))) { + return { [buildMediaQuery(_2)]: translation }; + } + if (variant == ':dark' && darkMode == 'class') { + return { '.dark &': translation }; + } + if ((_2 = GROUP_RE.exec(variant))) { + return { [`.${escape(tag(_2[1]))}:${_2[2]} &`]: translation }; + } + return { + [variants[tail(variant)] || + '&' + + variant.replace( + NOT_PREFIX_RE, + (_4, not, variant2) => not + '(' + prepareVariantSelector(':' + variant2) + ')' + )]: translation, + }; + }; + return (translation, rule) => rule.v.reduceRight(applyVariant, translation); +}; + +// src/twind/presedence.ts +var _3; +var responsivePrecedence = (css) => + (((_3 = /(?:^|min-width: *)(\d+(?:.\d+)?)(p)?/.exec(css)) + ? +_3[1] / (_3[2] ? 15 : 1) / 10 + : 0) & + 31) << + 22; +var seperatorPrecedence = (string) => { + _3 = 0; + for (let index = string.length; index--; ) { + _3 += includes('-:,', string[index]); + } + return _3; +}; +var atRulePresedence = (css) => (seperatorPrecedence(css) & 15) << 18; +var PRECEDENCES_BY_PSEUDO_CLASS = [ + 'rst', + 'st', + 'en', + 'd', + 'nk', + 'sited', + 'pty', + 'ecked', + 'cus-w', + 'ver', + 'cus', + 'cus-v', + 'tive', + 'sable', + 'ad-on', + 'tiona', + 'quire', +]; + +var pseudoPrecedence = (pseudoClass) => + 1 << + (~(_3 = PRECEDENCES_BY_PSEUDO_CLASS.indexOf( + pseudoClass.replace(GROUP_RE, ':$2').slice(3, 8) + )) + ? _3 + : 17); +var makeVariantPresedenceCalculator = (theme2, variants) => (presedence, variant) => + presedence | + ((_3 = theme2('screens', tail(variant), '')) + ? (1 << 27) | responsivePrecedence(buildMediaQuery(_3)) + : variant == ':dark' + ? 1 << 30 + : (_3 = variants[variant] || variant.replace(NOT_PREFIX_RE, ':$2'))[0] == '@' + ? atRulePresedence(_3) + : pseudoPrecedence(variant)); +var declarationPropertyPrecedence = (property2) => + property2[0] == '-' + ? 0 + : seperatorPrecedence(property2) + + ((_3 = /^(?:(border-(?!w|c|sty)|[tlbr].{2,4}m?$|c.{7}$)|([fl].{5}l|g.{8}$|pl))/.exec( + property2 + )) + ? +!!_3[1] || -!!_3[2] + : 0) + + 1; + +// src/twind/serialize.ts +var stringifyBlock = (body, selector) => selector + '{' + body + '}'; +var serialize = (prefix, variants, context) => { + const { theme: theme2, tag } = context; + const tagVar = (_4, property2) => '--' + tag(property2); + const tagVars = (value) => `${value}`.replace(/--(tw-[\w-]+)\b/g, tagVar); + const stringifyDeclaration = (property2, value, important) => { + property2 = tagVars(property2); + return Array.isArray(value) + ? join( + value.filter(Boolean).map((value2) => prefix(property2, tagVars(value2), important)), + ';' + ) + : prefix(property2, tagVars(value), important); + }; + let rules2; + const stringify3 = (atRules, selector, presedence, css, important) => { + if (Array.isArray(css)) { + css.forEach( + (css2) => css2 && stringify3(atRules, selector, presedence, css2, important) + ); + return; + } + let declarations = ''; + let maxPropertyPresedence = 0; + let numberOfDeclarations = 0; + if (css['@apply']) { + css = merge( + evalThunk(apply(css['@apply']), context), + { ...css, '@apply': void 0 }, + context + ); + } + Object.keys(css).forEach((key) => { + const value = evalThunk(css[key], context); + if (isCSSProperty(key, value)) { + if (value !== '' && key.length > 1) { + const property2 = hyphenate(key); + numberOfDeclarations += 1; + maxPropertyPresedence = Math.max( + maxPropertyPresedence, + declarationPropertyPrecedence(property2) + ); + declarations = + (declarations && declarations + ';') + + stringifyDeclaration(property2, value, important); + } + } else if (value) { + if (key == ':global') { + key = '@global'; + } + if (key[0] == '@') { + if (key[1] == 'g') { + stringify3([], '', 0, value, important); + } else if (key[1] == 'f') { + stringify3([], key, 0, value, important); + } else if (key[1] == 'k') { + const currentSize = rules2.length; + stringify3([], '', 0, value, important); + const waypoints = rules2.splice(currentSize, rules2.length - currentSize); + rules2.push({ + r: stringifyBlock( + join( + waypoints.map((p) => p.r), + '' + ), + key + ), + p: waypoints.reduce((sum, p) => sum + p.p, 0), + }); + } else if (key[1] == 'i') { + (Array.isArray(value) ? value : [value]).forEach( + (value2) => value2 && rules2.push({ p: 0, r: `${key} ${value2};` }) + ); + } else { + if (key[2] == 'c') { + key = buildMediaQuery(context.theme('screens', tail(key, 8).trim())); + } + stringify3( + [...atRules, key], + selector, + presedence | responsivePrecedence(key) | atRulePresedence(key), + value, + important + ); + } + } else { + stringify3( + atRules, + selector + ? join( + selector.split(/,(?![^[]*])/g).map((selectorPart) => + join( + key + .split(/,(?![^[]*])/g) + .map((keyPart) => + includes(keyPart, '&') + ? keyPart.replace(/&/g, selectorPart) + : (selectorPart && selectorPart + ' ') + keyPart + ), + ',' + ) + ), + ',' + ) + : key, + presedence, + value, + important + ); + } + } + }); + if (numberOfDeclarations) { + rules2.push({ + r: atRules.reduceRight(stringifyBlock, stringifyBlock(declarations, selector)), + p: + presedence * (1 << 8) + + (((Math.max(0, 15 - numberOfDeclarations) & 15) << 4) | + ((maxPropertyPresedence || 15) & 15)), + }); + } + }; + const variantPresedence = makeVariantPresedenceCalculator(theme2, variants); + return (css, className, rule, layer = 0) => { + layer <<= 28; + rules2 = []; + stringify3( + [], + className ? '.' + escape(className) : '', + rule ? rule.v.reduceRight(variantPresedence, layer) : layer, + css, + rule && rule.i + ); + return rules2; + }; +}; + +// src/twind/inject.ts +var inject = (sheet, mode2, init, context) => { + let sortedPrecedences; + init((value = []) => (sortedPrecedences = value)); + let insertedRules; + init((value = new Set()) => (insertedRules = value)); + return ({ r: css, p: presedence }) => { + if (!insertedRules.has(css)) { + insertedRules.add(css); + const index = sortedInsertionIndex(sortedPrecedences, presedence); + try { + sheet.insert(css, index); + sortedPrecedences.splice(index, 0, presedence); + } catch (error) { + if (!/:-[mwo]/.test(css)) { + mode2.report({ id: 'INJECT_CSS_ERROR', css, error }, context); + } + } + } + }; +}; + +// src/twind/configure.ts +var sanitize = (value, defaultValue, disabled, enabled = defaultValue) => + value === false ? disabled : value === true ? enabled : value || defaultValue; +var loadMode = (mode2) => + (typeof mode2 == 'string' ? { t: strict, a: warn, i: silent }[mode2[1]] : mode2) || warn; +var stringifyVariant = (selector, variant) => + selector + (variant[1] == ':' ? tail(variant, 2) + ':' : tail(variant)) + ':'; +var stringify2 = (rule, directive2 = rule.d) => + typeof directive2 == 'function' + ? '' + : rule.v.reduce(stringifyVariant, '') + + (rule.i ? '!' : '') + + (rule.n ? '-' : '') + + directive2; +var COMPONENT_PROPS = { _: { value: '', writable: true } }; +var configure = (config = {}) => { + const theme2 = makeThemeResolver(config.theme); + const mode2 = loadMode(config.mode); + const hash = sanitize(config.hash, false, false, cyrb32); + const important = config.important; + let activeRule = { v: [] }; + let translateDepth = 0; + const lastTranslations = []; + const context = { + tw: (...tokens) => process(tokens), + theme: (section, key, defaultValue) => { + var _a; + const value = + (_a = theme2(section, key, defaultValue)) != null + ? _a + : mode2.unknown( + section, + key == null || Array.isArray(key) ? key : key.split('.'), + defaultValue != null, + context + ); + return activeRule.n && value && includes('rg', (typeof value)[5]) + ? `calc(${value} * -1)` + : value; + }, + tag: (value) => (hash ? hash(value) : value), + css: (rules2) => { + translateDepth++; + const lastTranslationsIndex = lastTranslations.length; + try { + (typeof rules2 == 'string' ? parse([rules2]) : rules2).forEach(convert); + const css = Object.create(null, COMPONENT_PROPS); + for (let index = lastTranslationsIndex; index < lastTranslations.length; index++) { + const translation = lastTranslations[index]; + if (translation) { + switch (typeof translation) { + case 'object': + merge(css, translation, context); + break; + case 'string': + css._ += (css._ && ' ') + translation; + } + } + } + return css; + } finally { + lastTranslations.length = lastTranslationsIndex; + translateDepth--; + } + }, + }; + + const translate2 = translate({ ...corePlugins, ...config.plugins }, context); + const doTranslate = (rule) => { + const parentRule = activeRule; + activeRule = rule; + try { + return evalThunk(translate2(rule), context); + } finally { + activeRule = parentRule; + } + }; + const variants = { ...coreVariants, ...config.variants }; + const decorate2 = decorate(config.darkMode || 'media', variants, context); + const serialize2 = serialize( + sanitize(config.prefix, autoprefix, noprefix), + variants, + context + ); + const sheet = + config.sheet || (typeof window == 'undefined' ? voidSheet() : cssomSheet(config)); + const { init = (callback) => callback() } = sheet; + const inject2 = inject(sheet, mode2, init, context); + let idToClassName; + init((value = new Map()) => (idToClassName = value)); + const inlineDirectiveName = new WeakMap(); + const evaluateFunctions = (key, value) => + key == '_' + ? void 0 + : typeof value == 'function' + ? JSON.stringify(evalThunk(value, context), evaluateFunctions) + : value; + const convert = (rule) => { + if (!translateDepth && activeRule.v.length) { + rule = { ...rule, v: [...activeRule.v, ...rule.v], $: '' }; + } + if (!rule.$) { + rule.$ = stringify2(rule, inlineDirectiveName.get(rule.d)); + } + let className = translateDepth ? null : idToClassName.get(rule.$); + if (className == null) { + let translation = doTranslate(rule); + if (!rule.$) { + rule.$ = cyrb32(JSON.stringify(translation, evaluateFunctions)); + inlineDirectiveName.set(rule.d, rule.$); + rule.$ = stringify2(rule, rule.$); + } + if (translation && typeof translation == 'object') { + rule.v = rule.v.map(prepareVariantSelector); + if (important) rule.i = important; + translation = decorate2(translation, rule); + if (translateDepth) { + lastTranslations.push(translation); + } else { + const layer = + typeof rule.d == 'function' ? (typeof translation._ == 'string' ? 1 : 3) : 2; + className = + hash || typeof rule.d == 'function' ? (hash || cyrb32)(layer + rule.$) : rule.$; + serialize2(translation, className, rule, layer).forEach(inject2); + if (translation._) { + className += ' ' + translation._; + } + } + } else { + if (typeof translation == 'string') { + className = translation; + } else { + className = rule.$; + mode2.report({ id: 'UNKNOWN_DIRECTIVE', rule: className }, context); + } + if (translateDepth && typeof rule.d !== 'function') { + lastTranslations.push(className); + } + } + if (!translateDepth) { + idToClassName.set(rule.$, className); + ensureMaxSize(idToClassName, 3e4); + } + } + return className; + }; + const process = (tokens) => join(parse(tokens).map(convert).filter(Boolean), ' '); + const preflight = sanitize(config.preflight, identity, false); + if (preflight) { + const css = createPreflight(theme2); + const styles = serialize2( + typeof preflight == 'function' + ? evalThunk(preflight(css, context), context) || css + : { ...css, ...preflight } + ); + init((injected = (styles.forEach(inject2), true)) => injected); + } + return { + init: () => mode2.report({ id: 'LATE_SETUP_CALL' }, context), + process, + }; +}; + +// src/twind/instance.ts +var create = (config) => { + let process = (tokens) => { + init(); + return process(tokens); + }; + let init = (config2) => { + ({ process, init } = configure(config2)); + }; + if (config) init(config); + let context; + const fromContext = (key) => () => { + if (!context) { + process([ + (_4) => { + context = _4; + return ''; + }, + ]); + } + return context[key]; + }; + return { + tw: Object.defineProperties((...tokens) => process(tokens), { + theme: { + get: fromContext('theme'), + }, + }), + + setup: (config2) => init(config2), + }; +}; + +// src/twind/default.ts +var { tw, setup } = /* @__PURE__ */ create(); +export { + apply, + autoprefix, + create, + cssomSheet, + directive, + cyrb32 as hash, + mode, + noprefix, + setup, + silent, + strict, + theme, + tw, + voidSheet, + warn, +}; diff --git a/insert/client.mjs b/src/electron/client.mjs similarity index 100% rename from insert/client.mjs rename to src/electron/client.mjs diff --git a/insert/electronApi.cjs b/src/electron/electronApi.cjs similarity index 100% rename from insert/electronApi.cjs rename to src/electron/electronApi.cjs diff --git a/insert/env/env.mjs b/src/electron/env/env.mjs similarity index 100% rename from insert/env/env.mjs rename to src/electron/env/env.mjs diff --git a/insert/env/fs.mjs b/src/electron/env/fs.mjs similarity index 100% rename from insert/env/fs.mjs rename to src/electron/env/fs.mjs diff --git a/insert/env/storage.mjs b/src/electron/env/storage.mjs similarity index 100% rename from insert/env/storage.mjs rename to src/electron/env/storage.mjs diff --git a/insert/frame.mjs b/src/electron/frame.mjs similarity index 100% rename from insert/frame.mjs rename to src/electron/frame.mjs diff --git a/insert/init.cjs b/src/electron/init.cjs similarity index 100% rename from insert/init.cjs rename to src/electron/init.cjs diff --git a/insert/worker.cjs b/src/electron/worker.cjs similarity index 100% rename from insert/worker.cjs rename to src/electron/worker.cjs diff --git a/src/manifest.json b/src/manifest.json new file mode 100644 index 0000000..36abd33 --- /dev/null +++ b/src/manifest.json @@ -0,0 +1,30 @@ +{ + "manifest_version": 2, + "name": "notion-enhancer", + "version": "0.11.0", + "author": "dragonwocky (https://dragonwocky.me/)", + "description": "an enhancer/customiser for the all-in-one productivity workspace notion.so", + "homepage_url": "https://notion-enhancer.github.io", + "icons": { + "16": "media/colour-x16.png", + "32": "media/colour-x32.png", + "48": "media/colour-x48.png", + "128": "media/colour-x128.png", + "256": "media/colour-x256.png", + "512": "media/colour-x512.png" + }, + "browser_action": {}, + "background": { "scripts": ["worker.js"] }, + "options_ui": { + "page": "mods/menu/menu.html", + "open_in_tab": true + }, + "web_accessible_resources": ["browser/*", "common/*", "dep/*", "media/*", "mods/*"], + "content_scripts": [ + { + "matches": ["https://*.notion.so/*", "https://*.notion.site/*"], + "js": ["browser/init.js"] + } + ], + "permissions": ["tabs", "storage", "clipboardRead", "clipboardWrite", "unlimitedStorage"] +} diff --git a/src/media/animated.gif b/src/media/animated.gif new file mode 100644 index 0000000..56f8300 Binary files /dev/null and b/src/media/animated.gif differ diff --git a/src/media/blackwhite-x128.png b/src/media/blackwhite-x128.png new file mode 100644 index 0000000..bc0b657 Binary files /dev/null and b/src/media/blackwhite-x128.png differ diff --git a/src/media/blackwhite-x16.png b/src/media/blackwhite-x16.png new file mode 100644 index 0000000..69224de Binary files /dev/null and b/src/media/blackwhite-x16.png differ diff --git a/src/media/blackwhite-x256.png b/src/media/blackwhite-x256.png new file mode 100644 index 0000000..6d288a9 Binary files /dev/null and b/src/media/blackwhite-x256.png differ diff --git a/src/media/blackwhite-x32.png b/src/media/blackwhite-x32.png new file mode 100644 index 0000000..e8a517e Binary files /dev/null and b/src/media/blackwhite-x32.png differ diff --git a/src/media/blackwhite-x48.png b/src/media/blackwhite-x48.png new file mode 100644 index 0000000..bf574dc Binary files /dev/null and b/src/media/blackwhite-x48.png differ diff --git a/src/media/blackwhite-x512.png b/src/media/blackwhite-x512.png new file mode 100644 index 0000000..0ecf16c Binary files /dev/null and b/src/media/blackwhite-x512.png differ diff --git a/src/media/blackwhite.svg b/src/media/blackwhite.svg new file mode 100644 index 0000000..7321a08 --- /dev/null +++ b/src/media/blackwhite.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/media/colour-x128.png b/src/media/colour-x128.png new file mode 100644 index 0000000..4012672 Binary files /dev/null and b/src/media/colour-x128.png differ diff --git a/src/media/colour-x16.png b/src/media/colour-x16.png new file mode 100644 index 0000000..21be6b5 Binary files /dev/null and b/src/media/colour-x16.png differ diff --git a/src/media/colour-x256.png b/src/media/colour-x256.png new file mode 100644 index 0000000..30c25a0 Binary files /dev/null and b/src/media/colour-x256.png differ diff --git a/src/media/colour-x32.png b/src/media/colour-x32.png new file mode 100644 index 0000000..8e14320 Binary files /dev/null and b/src/media/colour-x32.png differ diff --git a/src/media/colour-x48.png b/src/media/colour-x48.png new file mode 100644 index 0000000..1e52a45 Binary files /dev/null and b/src/media/colour-x48.png differ diff --git a/src/media/colour-x512.png b/src/media/colour-x512.png new file mode 100644 index 0000000..f3adbac Binary files /dev/null and b/src/media/colour-x512.png differ diff --git a/src/media/colour.svg b/src/media/colour.svg new file mode 100644 index 0000000..99027d9 --- /dev/null +++ b/src/media/colour.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/media/promo-large.jpg b/src/media/promo-large.jpg new file mode 100644 index 0000000..c2718ca Binary files /dev/null and b/src/media/promo-large.jpg differ diff --git a/src/media/promo-marquee.jpg b/src/media/promo-marquee.jpg new file mode 100644 index 0000000..6f22df3 Binary files /dev/null and b/src/media/promo-marquee.jpg differ diff --git a/src/media/promo-small.jpg b/src/media/promo-small.jpg new file mode 100644 index 0000000..23062f6 Binary files /dev/null and b/src/media/promo-small.jpg differ diff --git a/src/mods/.github/workflows/update-parents.yml b/src/mods/.github/workflows/update-parents.yml new file mode 100644 index 0000000..ac428e4 --- /dev/null +++ b/src/mods/.github/workflows/update-parents.yml @@ -0,0 +1,29 @@ +name: 'update parent repositories' + +on: + push: + branches: + - dev + +jobs: + sync: + name: update parent + runs-on: ubuntu-latest + strategy: + matrix: + repo: ['notion-enhancer/extension', 'notion-enhancer/desktop'] + steps: + - name: checkout repo + uses: actions/checkout@v2 + with: + token: ${{ secrets.CI_TOKEN }} + submodules: true + repository: ${{ matrix.repo }} + - name: pull updates + run: | + git pull --recurse-submodules + git submodule update --remote --recursive + - name: commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: '[${{ github.event.repository.name }}] ${{ github.event.head_commit.message }}' diff --git a/src/mods/LICENSE b/src/mods/LICENSE new file mode 100644 index 0000000..b503961 --- /dev/null +++ b/src/mods/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 dragonwocky (https://dragonwocky.me/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/mods/README.md b/src/mods/README.md new file mode 100644 index 0000000..0885816 --- /dev/null +++ b/src/mods/README.md @@ -0,0 +1,5 @@ +# notion-enhancer/repo + +the collection of mods run by the notion-enhancer + +[read the docs online](https://notion-enhancer.github.io/getting-started/features) diff --git a/src/mods/always-on-top/always-on-top.jpg b/src/mods/always-on-top/always-on-top.jpg new file mode 100644 index 0000000..bed945f Binary files /dev/null and b/src/mods/always-on-top/always-on-top.jpg differ diff --git a/src/mods/always-on-top/button.css b/src/mods/always-on-top/button.css new file mode 100644 index 0000000..71150b4 --- /dev/null +++ b/src/mods/always-on-top/button.css @@ -0,0 +1,47 @@ +/** + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.always_on_top--button { + display: flex; + align-items: center; +} +.sidebar > .always_on_top--button { + margin: 0 0 0.75rem auto; +} + +.always_on_top--button button { + user-select: none; + transition: background 20ms ease-in 0s; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + border-radius: 3px; + height: 28px; + width: 33px; + padding: 0 0.25px 0 0; + + margin-left: 2px; + border: none; + background: transparent; + font-size: 18px; +} +.always_on_top--button button svg { + display: block; + width: 20px; + height: 20px; + fill: var(--theme--icon); + color: var(--theme--icon); +} + +.always_on_top--button button:focus, +.always_on_top--button button:hover { + background: var(--theme--ui_interactive-hover); +} +.always_on_top--button button:active { + background: var(--theme--ui_interactive-active); +} diff --git a/src/mods/always-on-top/button.mjs b/src/mods/always-on-top/button.mjs new file mode 100644 index 0000000..50f0454 --- /dev/null +++ b/src/mods/always-on-top/button.mjs @@ -0,0 +1,45 @@ +/** + * notion-enhancer: always on top + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export const createButton = async ({ electron, web, components }, db) => { + let pinIcon = + (await db.get(['pin_icon'])) || + ` + + `, + unpinIcon = + (await db.get(['unpin_icon'])) || + ` + + `; + pinIcon = pinIcon.trim(); + unpinIcon = unpinIcon.trim(); + + pinIcon = + pinIcon.startsWith('') ? pinIcon : web.escape(pinIcon); + unpinIcon = + unpinIcon.startsWith('') + ? unpinIcon + : web.escape(unpinIcon); + + const $button = web.html`
`, + $pin = web.html``, + $unpin = web.html``; + components.addTooltip($pin, '**Pin window to top**'); + components.addTooltip($unpin, '**Unpin window from top**'); + web.render($button, $pin); + + $pin.addEventListener('click', () => { + $pin.replaceWith($unpin); + electron.browser.setAlwaysOnTop(true); + }); + $unpin.addEventListener('click', () => { + $unpin.replaceWith($pin); + electron.browser.setAlwaysOnTop(false); + }); + + return $button; +}; diff --git a/src/mods/always-on-top/client.mjs b/src/mods/always-on-top/client.mjs new file mode 100644 index 0000000..7262f15 --- /dev/null +++ b/src/mods/always-on-top/client.mjs @@ -0,0 +1,17 @@ +/** + * notion-enhancer: always on top + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +import { createButton } from './button.mjs'; + +export default async function (api, db) { + const { web } = api, + topbarActionsSelector = '.notion-topbar-action-buttons'; + + await web.whenReady([topbarActionsSelector]); + const $topbarActions = document.querySelector(topbarActionsSelector), + $button = await createButton(api, db); + $topbarActions.after($button); +} diff --git a/src/mods/always-on-top/menu.mjs b/src/mods/always-on-top/menu.mjs new file mode 100644 index 0000000..1250d12 --- /dev/null +++ b/src/mods/always-on-top/menu.mjs @@ -0,0 +1,20 @@ +/** + * notion-enhancer: always on top + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +import { createButton } from './button.mjs'; + +export default async function (api, db) { + const { web } = api, + sidebarSelector = '.sidebar', + windowButtonsSelector = '.integrated_titlebar--buttons'; + + await web.whenReady([sidebarSelector]); + await new Promise(requestAnimationFrame); + const $sidebar = document.querySelector(sidebarSelector), + $windowButtons = document.querySelector(windowButtonsSelector), + $button = await createButton(api, db); + ($windowButtons || $sidebar).prepend($button); +} diff --git a/src/mods/always-on-top/mod.json b/src/mods/always-on-top/mod.json new file mode 100644 index 0000000..efaee88 --- /dev/null +++ b/src/mods/always-on-top/mod.json @@ -0,0 +1,41 @@ +{ + "name": "always on top", + "id": "be2d75f5-48f5-4ece-98bd-772244e559f3", + "environments": ["linux", "win32", "darwin"], + "version": "0.2.0", + "description": "adds a button that can be used to pin the notion window on top of all other windows at all times.", + "preview": "always-on-top.jpg", + "tags": ["extension", "app"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": { + "client": ["button.css"], + "menu": ["button.css"] + }, + "js": { + "client": ["client.mjs"], + "menu": ["menu.mjs"] + }, + "options": [ + { + "type": "text", + "key": "pin_icon", + "label": "pin window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + }, + { + "type": "text", + "key": "unpin_icon", + "label": "unpin window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + } + ] +} diff --git a/src/mods/bypass-preview/client.css b/src/mods/bypass-preview/client.css new file mode 100644 index 0000000..cae2982 --- /dev/null +++ b/src/mods/bypass-preview/client.css @@ -0,0 +1,9 @@ +/** + * notion-enhancer: bypass preview + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-peek-renderer { + display: none; +} diff --git a/src/mods/bypass-preview/client.mjs b/src/mods/bypass-preview/client.mjs new file mode 100644 index 0000000..af5e597 --- /dev/null +++ b/src/mods/bypass-preview/client.mjs @@ -0,0 +1,37 @@ +/** + * notion-enhancer: bypass preview + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +"use strict"; + +export default function ({ web, notion }, db) { + let _openPage = {}; + + const getCurrentPage = () => ({ + type: web.queryParams().get("p") ? "preview" : "page", + id: notion.getPageID(), + }); + + const interceptPreview = () => { + const currentPage = getCurrentPage(); + if ( + currentPage.id !== _openPage.id || + currentPage.type !== _openPage.type + ) { + const $openAsPage = document.querySelector( + ".notion-peek-renderer a > div" + ); + + if ($openAsPage) { + if (currentPage.id === _openPage.id && currentPage.type === "preview") { + history.back(); + } else $openAsPage.click(); + } + + _openPage = getCurrentPage(); + } + }; + web.addDocumentObserver(interceptPreview, [".notion-peek-renderer"]); +} diff --git a/src/mods/bypass-preview/mod.json b/src/mods/bypass-preview/mod.json new file mode 100644 index 0000000..bda76dc --- /dev/null +++ b/src/mods/bypass-preview/mod.json @@ -0,0 +1,22 @@ +{ + "name": "bypass preview", + "id": "cb6fd684-f113-4a7a-9423-8f0f0cff069f", + "version": "0.2.0", + "description": "go straight to the normal full view when opening a page.", + "tags": ["extension", "automation"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [] +} diff --git a/src/mods/calendar-scroll/calendar-scroll.png b/src/mods/calendar-scroll/calendar-scroll.png new file mode 100644 index 0000000..42ab520 Binary files /dev/null and b/src/mods/calendar-scroll/calendar-scroll.png differ diff --git a/src/mods/calendar-scroll/client.css b/src/mods/calendar-scroll/client.css new file mode 100644 index 0000000..2cbdb62 --- /dev/null +++ b/src/mods/calendar-scroll/client.css @@ -0,0 +1,25 @@ +/** + * notion-enhancer: calendar scroll + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +#enhancer--calendar-scroll { + background: var(--theme--ui_interactive-hover); + border: 1px solid transparent; + font-size: 14px; + color: var(--theme--text); + height: 24px; + border-radius: 3px; + line-height: 1.2; + padding: 0 0.5em; + margin-right: 5px; +} +#enhancer--calendar-scroll:focus, +#enhancer--calendar-scroll:hover { + background: transparent; + border: 1px solid var(--theme--ui_interactive-hover); +} +#enhancer--calendar-scroll:active { + background: var(--theme--ui_interactive-active); +} diff --git a/src/mods/calendar-scroll/client.mjs b/src/mods/calendar-scroll/client.mjs new file mode 100644 index 0000000..a13851e --- /dev/null +++ b/src/mods/calendar-scroll/client.mjs @@ -0,0 +1,41 @@ +/** + * notion-enhancer: calendar scroll + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +const pageSelector = '.notion-page-content', + calendarSelector = '.notion-calendar-view:not([data-calendar-scroll])', + scrollerSelector = '.notion-frame > .notion-scroller', + toolbarSelector = '.notion-calendar-view > :first-child > :first-child > :first-child', + todaySelector = '.notion-calendar-view-day[style*="background:"]'; + +export default function ({ web }, db) { + const insertButton = () => { + document.querySelectorAll(calendarSelector).forEach(($calendar) => { + $calendar.dataset.calendarScroll = true; + const $page = document.querySelector(pageSelector); + if ($page) return; + const $toolbar = $calendar.querySelector(toolbarSelector), + $pageScroller = document.querySelector(scrollerSelector), + $scrollButton = web.html``; + $scrollButton.addEventListener('click', async (event) => { + let $today = $calendar.querySelector(todaySelector); + if (!$today) { + $toolbar.children[4].click(); + await new Promise((res, rej) => setTimeout(res, 500)); + $today = $calendar.querySelector(todaySelector); + } + $pageScroller.scroll({ + top: $today.offsetParent.offsetParent.offsetTop + 70, + behavior: 'auto', + }); + }); + $toolbar.insertBefore($scrollButton, $toolbar.children[2]); + }); + }; + web.addDocumentObserver(insertButton, [calendarSelector]); + insertButton(); +} diff --git a/src/mods/calendar-scroll/mod.json b/src/mods/calendar-scroll/mod.json new file mode 100644 index 0000000..5dbd371 --- /dev/null +++ b/src/mods/calendar-scroll/mod.json @@ -0,0 +1,23 @@ +{ + "name": "calendar scroll", + "id": "b1c7db33-dfee-489a-a76c-0dd66f7ed29a", + "version": "0.2.0", + "description": "adds a button to jump down to the current week in fullpage/infinite-scroll calendars.", + "preview": "calendar-scroll.png", + "tags": ["extension", "shortcut"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [] +} diff --git a/src/mods/cherry-cola/cherry-cola.png b/src/mods/cherry-cola/cherry-cola.png new file mode 100644 index 0000000..86a445e Binary files /dev/null and b/src/mods/cherry-cola/cherry-cola.png differ diff --git a/src/mods/cherry-cola/mod.json b/src/mods/cherry-cola/mod.json new file mode 100644 index 0000000..e81c336 --- /dev/null +++ b/src/mods/cherry-cola/mod.json @@ -0,0 +1,23 @@ +{ + "name": "cherry cola", + "id": "ec5c4640-68d4-4d25-aefd-62c7e9737cfb", + "version": "0.2.0", + "description": "a delightfully plummy, cherry cola flavored theme.", + "preview": "cherry-cola.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "runargs", + "email": "alnbaldon@gmail.com", + "homepage": "http://github.com/runargs", + "avatar": "https://avatars.githubusercontent.com/u/39810066" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/cherry-cola/variables.css b/src/mods/cherry-cola/variables.css new file mode 100644 index 0000000..e2cc5f9 --- /dev/null +++ b/src/mods/cherry-cola/variables.css @@ -0,0 +1,172 @@ +/** + * notion-enhancer: cherry cola + * (c) 2020 Alexa Baldon (https://github.com/runargs) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --cola--main: #180915; + --cola--sec: #1d0919; + --cola--tet: #492341; + --cola--info: #9b6890; + --cola--accent: #bf799b; + --cola--light_gray: #ADADAD; + --cola--gray: #8a8a8a; + --cola--brown: #755241; + --cola--orange: #e6846b; + --cola--yellow: #d7b56e; + --cola--green: #8f9b4f; + --cola--blue: #6ebdb7; + --cola--purple: #813d63; + --cola--pink: #d86f71; + --cola--red: #a33232; + + --theme--accent_blue: var(--cola--accent); + --theme--accent_blue-selection: rgba(78, 32, 69, 0.5); + --theme--accent_blue-hover: #bd6992; + --theme--accent_blue-active: #cc86a8; + --theme--accent_blue-text: #fff; + --theme--accent_red: var(--cola--accent); + --theme--accent_red-button: #bf799b63; + --theme--accent_red-text: #fff; + + --theme--bg: var(--cola--main); + --theme--bg_secondary: var(--cola--sec); + --theme--bg_card: var(--cola--sec); + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: var(--cola--sec); + --theme--scrollbar_thumb-hover: var(--cola--accent); + + --theme--ui_divider: rgba(73, 35, 65, 0.7); + --theme--ui_interactive-hover: var(--cola--tet); + --theme--ui_interactive-active: var(--cola--tet); + --theme--ui_toggle-off: rgba(73, 35, 65, 0.7); + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: #fff; + --theme--icon_secondary: var(--cola--info); + + --theme--text: #fff; + --theme--text_secondary: var(--cola--info); + --theme--text_gray: var(--cola--gray); + --theme--text_brown: var(--cola--brown); + --theme--text_orange: var(--cola--orange); + --theme--text_yellow: var(--cola--yellow); + --theme--text_green: var(--cola--green); + --theme--text_blue: var(--cola--blue); + --theme--text_purple: var(--cola--purple); + --theme--text_pink: var(--cola--pink); + --theme--text_red: var(--cola--red); + + --theme--highlight_gray: var(--theme--tag_gray); + --theme--highlight_gray-text: var(--theme--tag-text); + --theme--highlight_brown: var(--theme--tag_brown); + --theme--highlight_brown-text: var(--theme--tag-text); + --theme--highlight_orange: var(--theme--tag_orange); + --theme--highlight_orange-text: var(--theme--tag-text); + --theme--highlight_yellow: var(--theme--tag_yellow); + --theme--highlight_yellow-text: var(--theme--tag-text); + --theme--highlight_green: var(--theme--tag_green); + --theme--highlight_green-text: var(--theme--tag-text); + --theme--highlight_blue: var(--theme--tag_blue); + --theme--highlight_blue-text: var(--theme--tag-text); + --theme--highlight_purple: var(--theme--tag_purple); + --theme--highlight_purple-text: var(--theme--tag-text); + --theme--highlight_pink: var(--theme--tag_pink); + --theme--highlight_pink-text: var(--theme--tag-text); + --theme--highlight_red: var(--theme--tag_red); + --theme--highlight_red-text: var(--theme--tag-text); + + --theme--callout_gray: var(--theme--tag_gray); + --theme--callout_gray-text: var(--theme--tag-text); + --theme--callout_brown: var(--theme--tag_brown); + --theme--callout_brown-text: var(--theme--tag-text); + --theme--callout_orange: var(--theme--tag_orange); + --theme--callout_orange-text: var(--theme--tag-text); + --theme--callout_yellow: var(--theme--tag_yellow); + --theme--callout_yellow-text: var(--theme--tag-text); + --theme--callout_green: var(--theme--tag_green); + --theme--callout_green-text: var(--theme--tag-text); + --theme--callout_blue: var(--theme--tag_blue); + --theme--callout_blue-text: var(--theme--tag-text); + --theme--callout_purple: var(--theme--tag_purple); + --theme--callout_purple-text: var(--theme--tag-text); + --theme--callout_pink: var(--theme--tag_pink); + --theme--callout_pink-text: var(--theme--tag-text); + --theme--callout_red: var(--theme--tag_red); + --theme--callout_red-text: var(--theme--tag-text); + + --theme--tag_light_gray: var(--cola--light_gray); + --theme--tag_light_gray-text: var(--cola--main); + --theme--tag_gray: var(--cola--gray); + --theme--tag_gray-text: var(--cola--main); + --theme--tag_brown: var(--cola--brown); + --theme--tag_brown-text: #ffffff; + --theme--tag_orange: var(--cola--orange); + --theme--tag_orange-text: var(--cola--main); + --theme--tag_yellow: var(--cola--yellow); + --theme--tag_yellow-text: var(--cola--main); + --theme--tag_green: var(--cola--green); + --theme--tag_green-text: var(--cola--main); + --theme--tag_blue: var(--cola--blue); + --theme--tag_blue-text: var(--cola--main); + --theme--tag_purple: var(--cola--purple); + --theme--tag_purple-text: #ffffff; + --theme--tag_pink: var(--cola--pink); + --theme--tag_pink-text: var(--cola--main); + --theme--tag_red: var(--cola--red); + --theme--tag_red-text: #ffffff; + + --theme--board_light_gray: var(--cola--light_gray); + --theme--board_light_gray-text: var(--cola--main); + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_gray: var(--cola--gray); + --theme--board_gray-text: var(--cola--main); + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_brown: var(--cola--brown); + --theme--board_brown-text: var(--cola--main); + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_orange: var(--cola--orange); + --theme--board_orange-text: var(--cola--main); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_yellow: var(--cola--yellow); + --theme--board_yellow-text: var(--cola--main); + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_green: var(--cola--green); + --theme--board_green-text: var(--cola--main); + --theme--board_green-card: var(--theme--tag_green); + --theme--board_blue: var(--cola--blue); + --theme--board_blue-text: var(--cola--main); + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_purple: var(--cola--purple); + --theme--board_purple-text: var(--cola--main); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_pink: var(--cola--pink); + --theme--board_pink-text: var(--cola--main); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_red: var(--cola--red); + --theme--board_red-text: var(--cola--main); + --theme--board_red-card: var(--theme--tag_red); + + --theme--code_inline: var(--cola--main); + --theme--code_inline-text: var(--cola--accent); + + --theme--code: var(--cola--sec); + --theme--code_function: var(--theme--text_blue); + --theme--code_keyword: var(--theme--text_pink); + --theme--code_tag: var(--theme--text_pink); + --theme--code_operator: var(--theme--text_yellow); + --theme--code_property: var(--theme--text_pink); + --theme--code_builtin: var(--theme--text_yellow); + --theme--code_attr-name: var(--theme--text_yellow); + --theme--code_comment: var(--theme--text_gray); + --theme--code_punctuation: var(--theme--text_gray); + --theme--code_doctype: var(--theme--text_gray); + --theme--code_number: var(--theme--text_purple); + --theme--code_selector: var(--theme--text_orange); + --theme--code_atrule: var(--theme--text_orange); + --theme--code_regex: var(--theme--text_yellow); +} diff --git a/src/mods/code-line-numbers/client.css b/src/mods/code-line-numbers/client.css new file mode 100644 index 0000000..5f3a4df --- /dev/null +++ b/src/mods/code-line-numbers/client.css @@ -0,0 +1,63 @@ +/** + * notion-enhancer: code line numbers + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-code-block.line-numbers { + position: relative; +} +.code_line_numbers--plain:not(:empty) + div, +.code_line_numbers--background:not(:empty) + div, +.code_line_numbers--border:not(:empty) + div { + padding-left: 64px !important; +} + +.code_line_numbers--plain, +.code_line_numbers--background, +.code_line_numbers--border { + position: absolute; + left: 0; + right: calc(100% - 64px); + top: 34px; + bottom: 32px; + padding-right: 27px; + + font-size: 85%; + font-family: var(--theme--font_code); + text-align: right; + line-height: 1.5; + opacity: 0.8; + color: var(--theme--text_secondary); + + overflow: hidden; + pointer-events: none; +} +.code_line_numbers--plain:empty, +.code_line_numbers--background:empty, +.code_line_numbers--border:empty { + display: none; +} +.code_line_numbers--background::before { + content: ''; + position: absolute; + top: 0; + left: 7.25px; + width: calc(100% - 27px); + height: 100%; + display: block; + background-color: var(--theme--bg); + border-radius: 4px; + z-index: -1; +} +.code_line_numbers--border::before { + content: ''; + position: absolute; + top: 0; + right: calc(100% - 52px); + width: 2px; + height: 100%; + display: block; + background-color: var(--theme--ui_divider); +} diff --git a/src/mods/code-line-numbers/client.mjs b/src/mods/code-line-numbers/client.mjs new file mode 100644 index 0000000..5072b39 --- /dev/null +++ b/src/mods/code-line-numbers/client.mjs @@ -0,0 +1,62 @@ +/** + * notion-enhancer: code line numbers + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ web }, db) { + const singleLined = await db.get(['single_lined']), + codeBlockSelector = '.notion-code-block.line-numbers', + numbersClass = `code_line_numbers--${await db.get(['style'])}`, + $temp = web.html``; + + const numberCodeBlock = ($codeBlock) => { + const $numbers = + $codeBlock.querySelector(`.${numbersClass}`) || + web.html`1`; + if (!$codeBlock.contains($numbers)) $codeBlock.prepend($numbers); + + const lines = $codeBlock.lastElementChild.innerText.split(/\r\n|\r|\n/), + wordWrap = $codeBlock.lastElementChild.style.wordBreak === 'break-all'; + if (lines.reverse()[0] === '') lines.pop(); + + let lineNumbers = ''; + for (let i = 1; i <= lines.length + 1; i++) { + lineNumbers += `${i}\n`; + if (wordWrap && lines[i - 1]) { + $temp.innerText = lines[i - 1]; + web.render($codeBlock.lastElementChild, $temp); + const height = parseFloat($temp.getBoundingClientRect().height); + $temp.remove(); + for (let j = 1; j < height / 20.4; j++) lineNumbers += '\n'; + } + } + + if (!singleLined && lines.length < 2) lineNumbers = ''; + if ($numbers.innerText !== lineNumbers) $numbers.innerText = lineNumbers; + }, + numberAllCodeBlocks = () => { + for (const $codeBlock of document.querySelectorAll(codeBlockSelector)) { + numberCodeBlock($codeBlock); + } + }, + observeCodeBlocks = (event) => { + const tempEvent = [...event.addedNodes, ...event.removedNodes].includes($temp), + numbersEvent = + event.target.classList.contains(numbersClass) || + [...event.addedNodes, ...event.removedNodes].some(($node) => + $node?.classList?.contains(numbersClass) + ), + codeEvent = event.target.matches(`${codeBlockSelector}, ${codeBlockSelector} *`); + if (tempEvent || numbersEvent || !codeEvent) return; + + let $codeBlock = event.target; + while (!$codeBlock.matches(codeBlockSelector)) $codeBlock = $codeBlock.parentElement; + numberCodeBlock($codeBlock); + }; + + await web.whenReady(); + numberAllCodeBlocks(); + web.addDocumentObserver(observeCodeBlocks, [codeBlockSelector]); +} diff --git a/src/mods/code-line-numbers/code-line-numbers.png b/src/mods/code-line-numbers/code-line-numbers.png new file mode 100644 index 0000000..c1b6b08 Binary files /dev/null and b/src/mods/code-line-numbers/code-line-numbers.png differ diff --git a/src/mods/code-line-numbers/mod.json b/src/mods/code-line-numbers/mod.json new file mode 100644 index 0000000..4e9c0d4 --- /dev/null +++ b/src/mods/code-line-numbers/mod.json @@ -0,0 +1,36 @@ +{ + "name": "code line numbers", + "id": "d61dc8a7-b195-465b-935f-53eea9efe74e", + "version": "0.4.0", + "description": "adds line numbers to code blocks.", + "preview": "code-line-numbers.png", + "tags": ["extension", "usability"], + "authors": [ + { + "name": "CloudHill", + "email": "rh.cloudhill@gmail.com", + "homepage": "https://github.com/CloudHill", + "avatar": "https://avatars.githubusercontent.com/u/54142180" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [ + { + "type": "toggle", + "key": "single_lined", + "label": "number single-lined code blocks", + "value": false + }, + { + "type": "select", + "key": "style", + "label": "line number style", + "values": ["plain", "background", "border"] + } + ] +} diff --git a/src/mods/collapsible-properties/client.css b/src/mods/collapsible-properties/client.css new file mode 100644 index 0000000..84eba06 --- /dev/null +++ b/src/mods/collapsible-properties/client.css @@ -0,0 +1,56 @@ +/** + * notion-enhancer: collapse properties + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +#enhancer--collapse-properties { + display: flex; + background: var(--theme--ui_interactive-hover); + border: 1px solid transparent; + font-size: 14px; + border-radius: 3px; + line-height: 1.2; + font-weight: 600; + padding: 0.3em 0.5em; + margin: 1em 0; + width: 100%; +} +#enhancer--collapse-properties:focus, +#enhancer--collapse-properties:hover { + background: transparent; + border: 1px solid var(--theme--ui_interactive-hover); +} +#enhancer--collapse-properties:active { + background: var(--theme--ui_interactive-active); +} + +#enhancer--collapse-properties > span { + text-align: left; + color: var(--theme--text); +} +#enhancer--collapse-properties > span:before { + content: 'Properties'; +} +#enhancer--collapse-properties > svg { + fill: var(--theme--icon); + height: 0.8em; + width: 0.8em; + margin: auto 0.5em auto 0; + transition: transform 200ms ease-out 0s; +} + +#enhancer--collapse-properties[data-collapsed='false'] > svg { + transform: rotateZ(180deg); +} +#enhancer--collapse-properties[data-collapsed='true'] > svg { + transform: rotateZ(90deg); +} + +#enhancer--collapse-properties + div { + overflow: hidden; +} +#enhancer--collapse-properties[data-collapsed='true'] + div { + max-height: 0 !important; + opacity: 0; +} diff --git a/src/mods/collapsible-properties/client.mjs b/src/mods/collapsible-properties/client.mjs new file mode 100644 index 0000000..28f88e8 --- /dev/null +++ b/src/mods/collapsible-properties/client.mjs @@ -0,0 +1,35 @@ +/** + * notion-enhancer: collapse properties + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default function ({ web, notion }, db) { + const propertyListSelector = + '.notion-scroller.vertical [style*="env(safe-area-inset-left)"] > [style="width: 100%; font-size: 14px;"]', + $collapseButton = web.html``; + $collapseButton.addEventListener('click', async (event) => { + if ($collapseButton.dataset.collapsed === 'true') { + await db.set([notion.getPageID()], false); + $collapseButton.dataset.collapsed = false; + } else { + await db.set([notion.getPageID()], true); + $collapseButton.dataset.collapsed = true; + } + }); + const insertButton = async () => { + if (document.contains($collapseButton)) return; + const $propertyList = document.querySelector(propertyListSelector); + if ($propertyList) { + $collapseButton.dataset.collapsed = await db.get([notion.getPageID()], true); + $propertyList.before($collapseButton); + } + }; + web.addDocumentObserver(insertButton, [propertyListSelector]); + insertButton(); +} diff --git a/src/mods/collapsible-properties/collapsible-properties.jpg b/src/mods/collapsible-properties/collapsible-properties.jpg new file mode 100644 index 0000000..1489e8f Binary files /dev/null and b/src/mods/collapsible-properties/collapsible-properties.jpg differ diff --git a/src/mods/collapsible-properties/mod.json b/src/mods/collapsible-properties/mod.json new file mode 100644 index 0000000..7ef89df --- /dev/null +++ b/src/mods/collapsible-properties/mod.json @@ -0,0 +1,23 @@ +{ + "name": "collapsible properties", + "id": "4034a578-7dd3-4633-80c6-f47ac5b7b160", + "version": "0.3.0", + "description": "adds a button to quickly collapse/expand page properties that usually push down page content.", + "preview": "collapsible-properties.jpg", + "tags": ["extension", "layout"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": { + "client": ["client.css"] + }, + "js": { + "client": ["client.mjs"] + }, + "options": [] +} diff --git a/src/mods/components/mod.json b/src/mods/components/mod.json new file mode 100644 index 0000000..ceb59ed --- /dev/null +++ b/src/mods/components/mod.json @@ -0,0 +1,33 @@ +{ + "__comment": "pseudo-mod to allow configuration of api-provided components", + "name": "components", + "id": "36a2ffc9-27ff-480e-84a7-c7700a7d232d", + "version": "0.2.0", + "description": "shared notion-style elements.", + "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": {}, + "css": {}, + "options": [ + { + "type": "hotkey", + "key": "panel.hotkey", + "label": "toggle panel hotkey", + "value": "Ctrl+Alt+\\", + "tooltip": "**opens/closes the side panel in notion** if a mod that uses it has been enabled" + } + ] +} diff --git a/src/mods/dark+/dark+.png b/src/mods/dark+/dark+.png new file mode 100644 index 0000000..b00aaec Binary files /dev/null and b/src/mods/dark+/dark+.png differ diff --git a/src/mods/dark+/mod.json b/src/mods/dark+/mod.json new file mode 100644 index 0000000..1bef8f6 --- /dev/null +++ b/src/mods/dark+/mod.json @@ -0,0 +1,42 @@ +{ + "name": "dark+", + "id": "c86cfe98-e645-4822-aa6b-e2de1e08bafa", + "version": "0.2.0", + "description": "a vivid-colour near-black theme, with configurable accents.", + "preview": "dark+.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": { + "frame": ["theme.mjs"], + "client": ["theme.mjs"], + "menu": ["theme.mjs"] + }, + "options": [ + { + "type": "color", + "key": "primary", + "label": "primary accent color", + "tooltip": "**replaces notion's blue accent**", + "value": "rgba(46,170,220,1)" + }, + { + "type": "color", + "key": "secondary", + "label": "secondary accent color", + "tooltip": "**replaces notion's red accent**", + "value": "rgba(235,87,87,1)" + } + ] +} diff --git a/src/mods/dark+/theme.mjs b/src/mods/dark+/theme.mjs new file mode 100644 index 0000000..ea464fb --- /dev/null +++ b/src/mods/dark+/theme.mjs @@ -0,0 +1,55 @@ +/** + * notion-enhancer: dark+ + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ fmt }, db) { + { + const primary = await db.get(['primary']), + [r, g, b] = primary + .slice(5, -1) + .split(',') + .map((i) => parseInt(i)); + if (!(r === 46 && g === 170 && b === 220)) { + document.documentElement.style.setProperty('--dark_plus--accent_blue', primary); + document.documentElement.style.setProperty( + '--dark_plus--accent_blue-selection', + `rgba(${r},${g},${b},0.2)` + ); + document.documentElement.style.setProperty( + '--dark_plus--accent_blue-hover', + fmt.rgbLogShade(0.05, primary) + ); + document.documentElement.style.setProperty( + '--dark_plus--accent_blue-active', + fmt.rgbLogShade(0.025, primary) + ); + document.documentElement.style.setProperty( + '--dark_plus--accent_blue-text', + fmt.rgbContrast(r, g, b) + ); + } + } + + { + const secondary = await db.get(['secondary']), + [r, g, b] = secondary + .slice(5, -1) + .split(',') + .map((i) => parseInt(i)); + if (!(r === 235 && g === 87 && b === 87)) { + document.documentElement.style.setProperty('--dark_plus--accent_red', secondary); + document.documentElement.style.setProperty( + '--dark_plus--accent_red-button', + `rgba(${r},${g},${b},0.2)` + ); + document.documentElement.style.setProperty( + '--dark_plus--accent_red-text', + fmt.rgbContrast(r, g, b) + ); + } + } +} diff --git a/src/mods/dark+/variables.css b/src/mods/dark+/variables.css new file mode 100644 index 0000000..4f757d7 --- /dev/null +++ b/src/mods/dark+/variables.css @@ -0,0 +1,132 @@ +/** + * notion-enhancer: dark+ + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --theme--accent_blue: var(--dark_plus--accent_blue, rgb(46, 170, 220)); + --theme--accent_blue-selection: var( + --dark_plus--accent_blue-selection, + rgb(46, 170, 220, 0.25) + ); + --theme--accent_blue-hover: var(--dark_plus--accent_blue-hover, rgb(6, 156, 205)); + --theme--accent_blue-active: var(--dark_plus--accent_blue-active, rgb(0, 141, 190)); + --theme--accent_blue-text: var(--dark_plus--accent_blue-text, #fff); + --theme--accent_red: var(--dark_plus--accent_red, #eb5757); + --theme--accent_red-button: var(--dark_plus--accent_red-button, rgba(235, 87, 87, 0.1)); + --theme--accent_red-text: var(--dark_plus--accent_red-text, #fff); + + --theme--bg: rgb(14, 14, 14); + --theme--bg_secondary: rgb(0, 0, 0); + --theme--bg_card: rgb(0, 0, 0); + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: #181818; + --theme--scrollbar_thumb-hover: #222222; + + --theme--ui_divider: rgba(36, 36, 36, 0.7); + --theme--ui_interactive-hover: rgba(55, 56, 56, 0.3); + --theme--ui_interactive-active: rgba(55, 56, 56, 0.402); + --theme--ui_toggle-on: var(--theme--accent_blue); + --theme--ui_toggle-off: rgb(40, 40, 40); + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: rgb(228, 228, 228); + --theme--icon_secondary: rgba(211, 211, 211, 0.637); + + --theme--text: rgb(255, 255, 255); + --theme--text_secondary: rgb(228, 228, 228); + --theme--text_gray: rgba(151, 154, 155, 0.95); + --theme--text_brown: rgb(147, 114, 100); + --theme--text_orange: rgb(255, 163, 68); + --theme--text_yellow: rgb(255, 220, 73); + --theme--text_green: rgb(50, 169, 104); + --theme--text_blue: rgb(82, 156, 202); + --theme--text_purple: rgb(154, 109, 215); + --theme--text_pink: rgb(226, 85, 161); + --theme--text_red: rgb(218, 47, 35); + + --theme--highlight_gray: var(--theme--tag_gray); + --theme--highlight_brown: var(--theme--tag_brown); + --theme--highlight_orange: var(--theme--tag_orange); + --theme--highlight_yellow: var(--theme--tag_yellow); + --theme--highlight_green: var(--theme--tag_green); + --theme--highlight_blue: var(--theme--tag_blue); + --theme--highlight_purple: var(--theme--tag_purple); + --theme--highlight_pink: var(--theme--tag_pink); + --theme--highlight_red: var(--theme--tag_red); + + --theme--callout_gray: rgba(126, 128, 129, 0.301); + --theme--callout_brown: #50331fad; + --theme--callout_orange: rgba(255, 153, 0, 0.315); + --theme--callout_yellow: rgba(183, 156, 0, 0.445); + --theme--callout_green: rgba(50, 129, 47, 0.39); + --theme--callout_blue: rgba(0, 90, 146, 0.521); + --theme--callout_purple: rgba(90, 49, 148, 0.349); + --theme--callout_pink: rgba(243, 61, 158, 0.301); + --theme--callout_red: rgba(122, 20, 20, 0.623); + + --theme--tag_light_gray: rgba(165, 166, 167, 0.5); + --theme--tag_gray: rgba(126, 128, 129, 0.5); + --theme--tag_brown: #50331f; + --theme--tag_orange: rgba(255, 155, 0, 0.58); + --theme--tag_yellow: rgba(183, 155, 0, 1); + --theme--tag_green: rgb(50, 129, 47); + --theme--tag_blue: rgba(0, 90, 146, 0.71); + --theme--tag_purple: rgba(91, 49, 148, 0.74); + --theme--tag_pink: rgba(243, 61, 159, 0.5); + --theme--tag_red: rgb(122, 20, 20); + + --theme--board_light_gray: rgba(165, 166, 167, 0.089); + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_light_gray-card_text: var(--theme--tag_light_gray-text); + --theme--board_gray: rgba(126, 128, 129, 0.089); + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_gray-card_text: var(--theme--tag_gray-text); + --theme--board_brown: #50331f59; + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_brown-card_text: var(--theme--tag_brown-text); + --theme--board_orange: rgba(255, 153, 0, 0.164); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_orange-card_text: var(--theme--tag_orange-text); + --theme--board_yellow: rgba(183, 156, 0, 0.274); + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_yellow-card_text: var(--theme--tag_yellow-text); + --theme--board_green: rgba(50, 129, 47, 0.191); + --theme--board_green-card: var(--theme--tag_green); + --theme--board_green-card_text: var(--theme--tag_green-text); + --theme--board_blue: rgba(0, 90, 146, 0.294); + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_purple: rgba(90, 49, 148, 0.219); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_purple-card_text: var(--theme--tag_purple-text); + --theme--board_pink: rgba(243, 61, 158, 0.191); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_pink-card_text: var(--theme--tag_pink-text); + --theme--board_red: rgba(122, 20, 20, 0.376); + --theme--board_red-card: var(--theme--tag_red); + --theme--board_red-card_text: var(--theme--tag_red-text); + + --theme--code_inline-text: #7dc582; + --theme--code_inline-background: rgb(0, 0, 0); + + --theme--code: rgb(0, 0, 0); + --theme--code_plain: var(--theme--text); + --theme--code_function: #c7e1ff; + --theme--code_keyword: #c397d8; + --theme--code_property: #82aed8; + --theme--code_operator: rgb(166, 175, 201); + --theme--code_regex: #da265f; + --theme--code_property: #82aed8; + --theme--code_builtin: #ff6294; + --theme--code_selector: #ff6294; + --theme--code_comment: rgb(166, 175, 201); + --theme--code_punctuation: rgb(166, 175, 201); + --theme--code_doctype: rgb(166, 175, 201); + --theme--code_number: #c397d8; + --theme--code_string: #7dc582; + --theme--code_atrule: #7dc582; +} diff --git a/src/mods/dracula/app.css b/src/mods/dracula/app.css new file mode 100644 index 0000000..449ffe4 --- /dev/null +++ b/src/mods/dracula/app.css @@ -0,0 +1,84 @@ +/** + * notion-enhancer: dracula + * (c) 2016 Dracula Theme + * (c) 2020 CloudHill + * (c) 2020 mimishahzad + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-dark-theme img[src*='/images/onboarding/use-case-note.png'], +.notion-dark-theme img[src*='/images/onboarding/team-features-illustration.png'] { + filter: invert(1) !important; +} +.notion-dark-theme img[src*='/images/onboarding/checked.svg'] { + filter: hue-rotate(45deg) !important; +} +.notion-dark-theme + img[style*='display: block; object-fit: cover; border-radius: 100%; width: 90px; height: 90px;'], +.notion-dark-theme + img[style*='display: block; object-fit: cover; border-radius: 3px; width: 56.832px; height: 56.832px; transition: opacity 100ms ease-out 0s;'] { + transition: filter 0.4s ease !important; +} +.notion-dark-theme + img[style*='display: block; object-fit: cover; border-radius: 100%; width: 90px; height: 90px;']:hover, +.notion-dark-theme + img[style*='display: block; object-fit: cover; border-radius: 3px; width: 56.832px; height: 56.832px; transition: opacity 100ms ease-out 0s;']:hover { + filter: brightness(1.2); +} + +.notion-dark-theme .notion-token-remove-button[role*='button'][tabindex*='0']:hover, +.notion-dark-theme .notion-record-icon { + background: transparent !important; +} + +.notion-dark-theme .notion-focusable:focus-within, +.notion-dark-theme .notion-to_do-block > div > div > div[style*='background:'], +.notion-dark-theme div[role='button'], +[style*='height: 4px;'] > .notion-selectable.notion-collection_view_page-block > *, +.notion-dark-theme .notion-calendar-view-day[style*='background: #282a36;'], +.DayPicker-Day--today, +.notion-dark-theme + .DayPicker:not(.DayPicker--interactionDisabled) + .DayPicker-Day--outside:hover, +.notion-dark-theme + .DayPicker:not(.DayPicker--interactionDisabled) + .DayPicker-Day:not(.DayPicker-Day--disabled):not(.DayPicker-Day--value) + .DayPicker-Day.DayPicker-Day--start.DayPicker-Day--selected, +.notion-dark-theme .DayPicker-Day.DayPicker-Day--range.DayPicker-Day--start, +.notion-dark-theme .DayPicker-Day.DayPicker-Day--range.DayPicker-Day--end { + transition: color 0.4s ease, background 0.4s ease, box-shadow 0.4s ease !important; +} + +.notion-dark-theme [style*='background: #282a36;'], +.notion-dark-theme + [style*='background: rgb(80, 85, 88);'][style*='color: rgba(255, 255, 255, 0.7)'], +.notion-dark-theme + [style*='background: rgb(80, 85, 88);'][style*='width: 18px;'][style*='height: 18px;'], +.notion-dark-theme + [style*='box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.2) 0px 5px 10px, rgba(15, 15, 15, 0.4) 0px 15px 40px;'], +.notion-dark-theme [style*='background: rgba(151, 154, 155, 0.5);'], +.notion-dark-theme [style*='background: rgba(147, 114, 100, 0.5)'], +.notion-dark-theme [style*='background: rgba(255, 163, 68, 0.5)'], +.notion-dark-theme [style*='background: rgba(255, 220, 73, 0.5)'], +.notion-dark-theme [style*='background: rgba(77, 171, 154, 0.5)'], +.notion-dark-theme [style*='background: rgba(82, 156, 202, 0.5)'], +.notion-dark-theme [style*='background: rgba(154, 109, 215, 0.5)'], +.notion-dark-theme [style*='background: rgba(226, 85, 161, 0.5)'], +.notion-dark-theme [style*='background: rgba(255, 115, 105, 0.5)'] { + box-shadow: 0 2px 4px rgb(0 0 0 / 66%) !important; +} + +.notion-dark-theme .notion-code-block .token.parameter, +.notion-dark-theme .notion-code-block .token.decorator, +.notion-dark-theme .notion-code-block .token.id, +.notion-dark-theme .notion-code-block .token.class, +.notion-dark-theme .notion-code-block .token.pseudo-element, +.notion-dark-theme .notion-code-block .token.pseudo-class, +.notion-dark-theme .notion-code-block .token.attribute { + font-style: italic; +} + +.notion-dark-theme .notion-code-block .token.punctuation { + opacity: 1; +} diff --git a/src/mods/dracula/dracula.png b/src/mods/dracula/dracula.png new file mode 100644 index 0000000..035ca0a Binary files /dev/null and b/src/mods/dracula/dracula.png differ diff --git a/src/mods/dracula/mod.json b/src/mods/dracula/mod.json new file mode 100644 index 0000000..3eb186b --- /dev/null +++ b/src/mods/dracula/mod.json @@ -0,0 +1,28 @@ +{ + "name": "dracula", + "id": "033bff54-50ba-4cec-bdc0-b2ca7e307086", + "version": "0.3.0", + "description": "a theme based on the popular dracula color palette originally by zeno rocha and friends.", + "preview": "dracula.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "dracula", + "email": "zno.rocha@gmail.com", + "homepage": "https://draculatheme.com/", + "avatar": "https://draculatheme.com/static/icons/pack-1/045-dracula.svg" + }, + { + "name": "mimishahzad", + "homepage": "https://github.com/mimishahzad", + "avatar": "https://avatars.githubusercontent.com/u/34081810" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css", "app.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/dracula/variables.css b/src/mods/dracula/variables.css new file mode 100644 index 0000000..0ef8311 --- /dev/null +++ b/src/mods/dracula/variables.css @@ -0,0 +1,213 @@ +/** + * notion-enhancer: dracula + * (c) 2016 Dracula Theme + * (c) 2020 mimishahzad + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --dracula--bg: #282a36; + --dracula--bg_lighter: #424450; + --dracula--bg_light: #343746; + --dracula--bg_dark: #21222c; + --dracula--bg_darker: #191a21; + + --dracula--fg: #f8f8f2; + --dracula--fg_dark: #babbba; + + --dracula--comment: #6272a4; + --dracula--selection: #44475a; + --dracula--block_highlight: #44475a75; + + --dracula--light_gray: #CFCFCF; + --dracula--gray: var(--dracula--fg_dark); + --dracula--brown: #6272a4; + --dracula--orange: #ffb86c; + --dracula--yellow: #f1fa8c; + --dracula--green: #50fa7b; + --dracula--blue: #8be9fd; + --dracula--purple: #bd93f9; + --dracula--pink: #ff79c6; + --dracula--red: #ff5555; + + --dracula--bg_light_gray: #71737E; + --dracula--bg_gray: var(--dracula--bg_light); + --dracula--bg_brown: #465079; + --dracula--bg_orange: #8a6345; + --dracula--bg_yellow: #8e9656; + --dracula--bg_green: #3f945f; + --dracula--bg_blue: #498096; + --dracula--bg_purple: #6a549b; + --dracula--bg_pink: #8d4a7c; + --dracula--bg_red: #943844; + + --theme--accent_blue: var(--dracula--purple); + --theme--accent_blue-selection: #bb8dfd3d; + --theme--accent_blue-hover: #b179ff; + --theme--accent_blue-active: #9f5ff8; + --theme--accent_red: var(--dracula--comment); + --theme--accent_red-button: #6273a439; + + --theme--danger_text: var(--dracula--red); + --theme--danger_border: var(--dracula--bg_red); + + --theme--bg: var(--dracula--bg); + --theme--bg_secondary: var(--dracula--bg_dark); + --theme--bg_card: var(--dracula--bg_light); + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: var(--dracula--selection); + --theme--scrollbar_thumb-hover: var(--dracula--comment); + + --theme--ui_divider: var(--dracula--bg_lighter); + --theme--ui_interactive-hover: var(--dracula--block_highlight); + --theme--ui_interactive-active: var(--dracula--bg_lighter); + --theme--ui_toggle-off: var(--dracula--block_highlight); + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: var(--dracula--comment); + --theme--icon_secondary: var(--dracula--comment); + + --theme--text: var(--dracula--fg); + --theme--text_secondary: var(--dracula--fg_dark); + --theme--text_gray: var(--dracula--gray); + --theme--text_brown: var(--dracula--brown); + --theme--text_orange: var(--dracula--orange); + --theme--text_yellow: var(--dracula--yellow); + --theme--text_green: var(--dracula--green); + --theme--text_blue: var(--dracula--blue); + --theme--text_purple: var(--dracula--purple); + --theme--text_pink: var(--dracula--pink); + --theme--text_red: var(--dracula--red); + + --theme--highlight_gray: var(--dracula--gray); + --theme--highlight_gray-text: var(--dracula--bg); + --theme--highlight_brown: var(--dracula--brown); + --theme--highlight_brown-text: var(--dracula--fg); + --theme--highlight_orange: var(--dracula--orange); + --theme--highlight_orange-text: var(--dracula--bg); + --theme--highlight_yellow: var(--dracula--yellow); + --theme--highlight_yellow-text: var(--dracula--bg); + --theme--highlight_green: var(--dracula--green); + --theme--highlight_green-text: var(--dracula--bg); + --theme--highlight_blue: var(--dracula--blue); + --theme--highlight_blue-text: var(--dracula--bg); + --theme--highlight_purple: var(--dracula--purple); + --theme--highlight_purple-text: var(--dracula--bg); + --theme--highlight_pink: var(--dracula--pink); + --theme--highlight_pink-text: var(--dracula--bg); + --theme--highlight_red: var(--dracula--red); + --theme--highlight_red-text: var(--dracula--fg); + + --theme--callout_gray: var(--dracula--bg_gray); + --theme--callout_brown: var(--dracula--bg_brown); + --theme--callout_orange: var(--dracula--bg_orange); + --theme--callout_yellow: var(--dracula--bg_yellow); + --theme--callout_green: var(--dracula--bg_green); + --theme--callout_blue: var(--dracula--bg_blue); + --theme--callout_purple: var(--dracula--bg_purple); + --theme--callout_pink: var(--dracula--bg_pink); + --theme--callout_red: var(--dracula--bg_red); + + --theme--tag_default: var(--dracula--comment); + --theme--tag_default-text: var(--dracula--fg); + --theme--tag_light_gray: var(--dracula--light_gray); + --theme--tag_light_gray-text: var(--dracula--bg); + --theme--tag_gray: var(--dracula--gray); + --theme--tag_gray-text: var(--dracula--bg); + --theme--tag_brown: var(--dracula--brown); + --theme--tag_brown-text: var(--dracula--fg); + --theme--tag_orange: var(--dracula--orange); + --theme--tag_orange-text: var(--dracula--bg); + --theme--tag_yellow: var(--dracula--yellow); + --theme--tag_yellow-text: var(--dracula--bg); + --theme--tag_green: var(--dracula--green); + --theme--tag_green-text: var(--dracula--bg); + --theme--tag_blue: var(--dracula--blue); + --theme--tag_blue-text: var(--dracula--bg); + --theme--tag_purple: var(--dracula--purple); + --theme--tag_purple-text: var(--dracula--bg); + --theme--tag_pink: var(--dracula--pink); + --theme--tag_pink-text: var(--dracula--bg); + --theme--tag_red: var(--dracula--red); + --theme--tag_red-text: var(--dracula--fg); + + --theme--board_light_gray: var(--dracula--bg_light_gray); + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_light_gray-card_text: var(--theme--tag_light_gray-text); + --theme--board_light_gray-text: var(--dracula--fg); + --theme--board_gray: var(--dracula--bg_gray); + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_gray-card_text: var(--theme--tag_gray-text); + --theme--board_gray-text: var(--dracula--fg); + --theme--board_brown: var(--dracula--bg_brown); + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_brown-card_text: var(--theme--tag_brown-text); + --theme--board_brown-text: var(--dracula--fg); + --theme--board_orange: var(--dracula--bg_orange); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_orange-card_text: var(--theme--tag_orange-text); + --theme--board_orange-text: var(--dracula--fg); + --theme--board_yellow: var(--dracula--bg_yellow); + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_yellow-card_text: var(--theme--tag_yellow-text); + --theme--board_yellow-text: var(--dracula--fg); + --theme--board_green: var(--dracula--bg_green); + --theme--board_green-card: var(--theme--tag_green); + --theme--board_green-card_text: var(--theme--tag_green-text); + --theme--board_green-text: var(--dracula--fg); + --theme--board_blue: var(--dracula--bg_blue); + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_blue-card_text: var(--theme--tag_blue-text); + --theme--board_blue-text: var(--dracula--fg); + --theme--board_purple: var(--dracula--bg_purple); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_purple-card_text: var(--theme--tag_purple-text); + --theme--board_purple-text: var(--dracula--fg); + --theme--board_pink: var(--dracula--bg_pink); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_pink-card_text: var(--theme--tag_pink-text); + --theme--board_pink-text: var(--dracula--fg); + --theme--board_red: var(--dracula--bg_red); + --theme--board_red-card: var(--theme--tag_red); + --theme--board_red-card_text: var(--theme--tag_red-text); + --theme--board_red-text: var(--dracula--fg); + + --theme--code_inline: var(--dracula--bg_light); + --theme--code_inline-text: var(--dracula--green); + + --theme--code: var(--dracula--bg_light); + --theme--code_plain: var(--dracula--fg); + --theme--code_function: var(--dracula--green); + --theme--code_parameter: var(--dracula--orange); + --theme--code_keyword: var(--dracula--pink); + --theme--code_constant: var(--dracula--purple); + --theme--code_tag: var(--dracula--pink); + --theme--code_operator: var(--dracula--pink); + --theme--code_important: var(--dracula--pink); + --theme--code_regex: var(--dracula--red); + --theme--code_property: var(--dracula--blue); + --theme--code_builtin: var(--dracula--blue); + --theme--code_class-name: var(--dracula--blue); + --theme--code_attr-name: var(--dracula--green); + --theme--code_attr-value: var(--dracula--yellow); + --theme--code_selector: var(--dracula--pink); + --theme--code_id: var(--dracula--green); + --theme--code_class: var(--dracula--green); + --theme--code_pseudo-element: var(--dracula--green); + --theme--code_pseudo-class: var(--dracula--green); + --theme--code_attribute: var(--dracula--green); + --theme--code_value: var(--dracula--yellow); + --theme--code_unit: var(--dracula--pink); + --theme--code_comment: var(--dracula--comment); + --theme--code_punctuation: var(--dracula--text); + --theme--code_annotation: var(--theme--code_punctuation); + --theme--code_decorator: var(--dracula--green); + --theme--code_doctype: var(--dracula--fg_dark); + --theme--code_number: var(--dracula--purple); + --theme--code_string: var(--dracula--yellow); + --theme--code_boolean: var(--dracula--purple); +} diff --git a/src/mods/emoji-sets/client.css b/src/mods/emoji-sets/client.css new file mode 100644 index 0000000..81e3e7d --- /dev/null +++ b/src/mods/emoji-sets/client.css @@ -0,0 +1,10 @@ +/** + * notion-enhancer: emoji sets + * (c) 2021 Arecsu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +[aria-label][role='image'][style*='Apple Color Emoji']:not([data-emoji-sets-unsupported]) { + margin-left: 2.5px !important; +} diff --git a/src/mods/emoji-sets/client.mjs b/src/mods/emoji-sets/client.mjs new file mode 100644 index 0000000..1e6a9bf --- /dev/null +++ b/src/mods/emoji-sets/client.mjs @@ -0,0 +1,71 @@ +/** + * notion-enhancer: emoji sets + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ web, env }, db) { + const style = await db.get(['style']), + // real emojis are used on macos instead of the twitter set + nativeEmojiSelector = `[aria-label][role="image"][style*="Apple Color Emoji"]:not([data-emoji-sets-unsupported])`, + imgEmojiSelector = '.notion-emoji:not([data-emoji-sets-unsupported])', + imgEmojiOverlaySelector = `${imgEmojiSelector} + [src*="notion-emojis"]`; + + await Promise.any([web.whenReady([nativeEmojiSelector]), web.whenReady([imgEmojiSelector])]); + + const nativeEmojis = document.querySelectorAll(nativeEmojiSelector).length, + imgEmojis = document.querySelectorAll(imgEmojiSelector).length; + + const unsupportedEmojis = [], + emojiReqs = new Map(), + getEmoji = async (emoji) => { + emoji = encodeURIComponent(emoji); + if (unsupportedEmojis.includes(emoji)) return undefined; + try { + if (!emojiReqs.get(emoji)) { + emojiReqs.set(emoji, fetch(`https://emojicdn.elk.sh/${emoji}?style=${style}`)); + } + const res = await emojiReqs.get(emoji); + if (!res.ok) throw new Error(); + return `url("https://emojicdn.elk.sh/${emoji}?style=${style}") 100% 100% / 100%`; + } catch { + unsupportedEmojis.push(emoji); + return undefined; + } + }; + + if (nativeEmojis) { + const updateEmojis = async () => { + const $emojis = document.querySelectorAll(nativeEmojiSelector); + for (const $emoji of $emojis) { + const emojiSrc = await getEmoji($emoji.ariaLabel); + if (emojiSrc) { + $emoji.style.background = emojiSrc; + $emoji.style.width = '1em'; + $emoji.style.height = '1em'; + $emoji.style.display = 'inline-block'; + $emoji.innerText = ''; + } else $emoji.dataset.emojiSetsUnsupported = true; + } + }; + web.addDocumentObserver(updateEmojis, [nativeEmojiSelector]); + } + + if (style !== 'twitter' && imgEmojis) { + const updateEmojis = async () => { + const $emojis = document.querySelectorAll(imgEmojiSelector); + for (const $emoji of $emojis) { + const emojiSrc = await getEmoji($emoji.ariaLabel); + if (emojiSrc) { + $emoji.style.background = emojiSrc; + $emoji.style.opacity = 1; + if ($emoji.nextElementSibling?.matches?.(imgEmojiOverlaySelector)) { + $emoji.nextElementSibling.style.opacity = 0; + } + } else $emoji.dataset.emojiSetsUnsupported = true; + } + }; + updateEmojis(); + web.addDocumentObserver(updateEmojis, [imgEmojiSelector, imgEmojiOverlaySelector]); + } +} diff --git a/src/mods/emoji-sets/emoji-sets.jpg b/src/mods/emoji-sets/emoji-sets.jpg new file mode 100644 index 0000000..1716d4c Binary files /dev/null and b/src/mods/emoji-sets/emoji-sets.jpg differ diff --git a/src/mods/emoji-sets/mod.json b/src/mods/emoji-sets/mod.json new file mode 100644 index 0000000..ac3ba3e --- /dev/null +++ b/src/mods/emoji-sets/mod.json @@ -0,0 +1,46 @@ +{ + "name": "emoji sets", + "id": "a2401ee1-93ba-4b8c-9781-7f570bf5d71e", + "version": "0.4.0", + "description": "pick from a variety of emoji styles to use.", + "preview": "emoji-sets.jpg", + "tags": ["extension", "customisation"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [ + { + "type": "select", + "key": "style", + "label": "emoji style", + "tooltip": "**initial use may involve some lag and load-time for emojis until they have all been cached**", + "values": [ + "twitter", + "apple", + "google", + "microsoft", + "samsung", + "whatsapp", + "facebook", + "messenger", + "joypixels", + "openmoji", + "emojidex", + "lg", + "htc", + "mozilla" + ] + } + ] +} diff --git a/src/mods/focus-mode/client.css b/src/mods/focus-mode/client.css new file mode 100644 index 0000000..d615b0c --- /dev/null +++ b/src/mods/focus-mode/client.css @@ -0,0 +1,21 @@ +/** + * notion-enhancer: focus mode + * (c) 2020 Arecsu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-sidebar-container[style*='width: 0px;'] ~ div .notion-topbar > :first-child { + opacity: 0 !important; + transition: opacity 200ms ease-in-out !important; +} +.notion-sidebar-container[style*='width: 0px;'] ~ div .notion-topbar > :first-child:hover { + opacity: 1 !important; +} + +[data-focus-mode='padded'] + .notion-sidebar-container[style*='width: 0px;'] + ~ div + .notion-frame { + height: calc(100vh - 90px) !important; +} diff --git a/src/mods/focus-mode/client.mjs b/src/mods/focus-mode/client.mjs new file mode 100644 index 0000000..8423026 --- /dev/null +++ b/src/mods/focus-mode/client.mjs @@ -0,0 +1,12 @@ +/** + * notion-enhancer: focus mode + * (c) 2020 Arecsu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function (api, db) { + if (await db.get(['padded'])) document.body.dataset.focusMode = 'padded'; +} diff --git a/src/mods/focus-mode/mod.json b/src/mods/focus-mode/mod.json new file mode 100644 index 0000000..0ffb3c8 --- /dev/null +++ b/src/mods/focus-mode/mod.json @@ -0,0 +1,39 @@ +{ + "name": "focus mode", + "id": "5a08598d-bfac-4167-9ae8-2bd0e2ef141e", + "version": "0.3.0", + "description": "hide the titlebar/menubar when the sidebar is closed (returns on hover).", + "tags": ["extension", "layout"], + "authors": [ + { + "name": "arecsu", + "email": "alejandro9r@gmail.com", + "homepage": "https://github.com/Arecsu", + "avatar": "https://avatars.githubusercontent.com/u/12679098" + } + ], + "css": { + "frame": ["tabs.css"], + "client": ["client.css"] + }, + "js": { + "frame": ["tabs.mjs"], + "client": ["client.mjs"] + }, + "options": [ + { + "type": "toggle", + "key": "padded", + "label": "even padding", + "tooltip": "matches the empty space at the top and sides of the frame with **extra padding at the bottom when the sidebar is hidden**", + "value": true + }, + { + "type": "toggle", + "key": "tabs", + "label": "hide tabs", + "tooltip": "makes the tab bar transparent unless hovered over", + "value": false + } + ] +} diff --git a/src/mods/focus-mode/tabs.css b/src/mods/focus-mode/tabs.css new file mode 100644 index 0000000..8b0414f --- /dev/null +++ b/src/mods/focus-mode/tabs.css @@ -0,0 +1,14 @@ +/** + * notion-enhancer: focus mode + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +[data-focus-mode='hide-tabs'] header:not(:hover) { + background: var(--theme--bg); + border-bottom-color: transparent; +} +[data-focus-mode='hide-tabs'] header:not(:hover) > * { + opacity: 0; + transition: opacity 200ms ease-in-out; +} diff --git a/src/mods/focus-mode/tabs.mjs b/src/mods/focus-mode/tabs.mjs new file mode 100644 index 0000000..6074834 --- /dev/null +++ b/src/mods/focus-mode/tabs.mjs @@ -0,0 +1,12 @@ +/** + * notion-enhancer: focus mode + * (c) 2020 Arecsu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function (api, db) { + if (await db.get(['tabs'])) document.body.dataset.focusMode = 'hide-tabs'; +} diff --git a/src/mods/font-chooser/fonts.css b/src/mods/font-chooser/fonts.css new file mode 100644 index 0000000..7d5b32e --- /dev/null +++ b/src/mods/font-chooser/fonts.css @@ -0,0 +1,25 @@ +/** + * notion-enhancer: font chooser + * (c) 2021 TorchAtlas (https://github.com/torchatlas/) + * (c) 2021 admiraldus (https://github.com/admiraldus) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root { + --theme--font_sans: var(--font_chooser--sans); + --theme--font_serif: var(--font_chooser--serif); + --theme--font_mono: var(--font_chooser--mono); + --theme--font_code: var(--font_chooser--code); +} + +.notion-quote-block { + font-family: var(--font_chooser--quotes, inherit) !important; +} + +[placeholder='Untitled'], +[placeholder='Heading 1'], +[placeholder='Heading 2'], +[placeholder='Heading 3'] { + font-family: var(--font_chooser--headings, inherit) !important; +} diff --git a/src/mods/font-chooser/fonts.mjs b/src/mods/font-chooser/fonts.mjs new file mode 100644 index 0000000..5d344d3 --- /dev/null +++ b/src/mods/font-chooser/fonts.mjs @@ -0,0 +1,28 @@ +/** + * notion-enhancer: font chooser + * (c) 2021 TorchAtlas (https://github.com/torchatlas/) + * (c) 2021 admiraldus (https://github.com/admiraldus + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({}, db) { + const defaults = { + sans: " -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, 'Apple Color Emoji', Arial, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol'", + serif: + "Lyon-Text, Georgia, YuMincho, 'Yu Mincho', 'Hiragino Mincho ProN', 'Hiragino Mincho Pro', 'Songti TC', 'Songti SC', SimSun, 'Nanum Myeongjo', NanumMyeongjo, Batang, serif", + mono: 'iawriter-mono, Nitti, Menlo, Courier, monospace', + code: "SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace", + quotes: 'inherit', + headings: 'inherit', + }; + for (let style of ['sans', 'serif', 'mono', 'code', 'quotes', 'headings']) { + const font = await db.get([style]); + document.documentElement.style.setProperty( + `--font_chooser--${style}`, + font || defaults[style] + ); + } +} diff --git a/src/mods/font-chooser/mod.json b/src/mods/font-chooser/mod.json new file mode 100644 index 0000000..9d36d8a --- /dev/null +++ b/src/mods/font-chooser/mod.json @@ -0,0 +1,68 @@ +{ + "name": "font chooser", + "id": "e0d8d148-45e7-4d79-8313-e7b2ad8abe16", + "version": "0.4.0", + "description": "set custom fonts.", + "tags": ["extension", "customisation"], + "authors": [ + { + "name": "TorchAtlas", + "homepage": "https://github.com/torchatlas/", + "avatar": "https://avatars.githubusercontent.com/u/12666855" + } + ], + "js": { + "client": ["fonts.mjs"], + "menu": ["fonts.mjs"], + "frame": ["fonts.mjs"] + }, + "css": { + "client": ["fonts.css"], + "menu": ["fonts.css"], + "frame": ["fonts.css"] + }, + "options": [ + { + "type": "text", + "key": "sans", + "label": "sans-serif (inc. ui)", + "tooltip": "**this font needs to be installed on your device** - leave the option blank to use notion's default font", + "value": "" + }, + { + "type": "text", + "key": "serif", + "label": "serif", + "tooltip": "**this font needs to be installed on your device** - leave the option blank to use notion's default font", + "value": "" + }, + { + "type": "text", + "key": "mono", + "label": "monospace", + "tooltip": "**this font needs to be installed on your device** - leave the option blank to use notion's default font", + "value": "" + }, + { + "type": "text", + "key": "code", + "label": "code", + "tooltip": "**this font needs to be installed on your device** - leave the option blank to use notion's default font", + "value": "" + }, + { + "type": "text", + "key": "quotes", + "label": "quotes", + "tooltip": "**this font needs to be installed on your device** - leave the option blank to use notion's default font", + "value": "" + }, + { + "type": "text", + "key": "headings", + "label": "headings", + "tooltip": "**this font needs to be installed on your device** - leave the option blank to use notion's default font", + "value": "" + } + ] +} diff --git a/src/mods/global-block-links/client.css b/src/mods/global-block-links/client.css new file mode 100644 index 0000000..66dcc54 --- /dev/null +++ b/src/mods/global-block-links/client.css @@ -0,0 +1,73 @@ +/** + * notion-enhancer: global block links + * (c) 2021 admiraldus (https://github.com/admiraldus) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.global_block_links--topbar_copy { + display: inline-flex; + align-items: center; + border-radius: 3px; + height: 28px; + min-width: 0px; + padding-right: 8px; + padding-left: 6px; + font-size: 14px; + line-height: 1.2; + color: var(--theme--text); + cursor: pointer; + transition: background 20ms ease-in 0s; + user-select: none; +} +.global_block_links--topbar_copy > svg { + display: block; + height: 16px; + width: 16px; + fill: var(--theme--icon); +} +.global_block_links--topbar_copy > span { + margin-left: 2px; + opacity: 1; + transition: opacity 0.4s ease; +} +.global_block_links--topbar_copy > svg + span { + margin-left: 6px; +} + +.global_block_links--block_copy { + display: flex; + align-items: center; + height: 28px; + width: 100%; + font-size: 14px; + line-height: 1.2; + cursor: pointer; + transition: background 20ms ease-in 0s; + user-select: none; +} +.global_block_links--block_copy > svg { + display: block; + margin-left: 14px; + height: 17px; + width: 17px; + color: var(--theme--icon); +} +.global_block_links--block_copy > span { + margin-right: 14px; + margin-left: 8px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.global_block_links--topbar_copy:hover, +.global_block_links--block_copy:hover { + background: var(--theme--ui_interactive-hover); +} + +.global_block_links--hidden { + position: absolute; + top: -9999px; + opacity: 0 !important; +} diff --git a/src/mods/global-block-links/client.mjs b/src/mods/global-block-links/client.mjs new file mode 100644 index 0000000..5cd864e --- /dev/null +++ b/src/mods/global-block-links/client.mjs @@ -0,0 +1,109 @@ +/** + * notion-enhancer: global block links + * (c) 2021 admiraldus (https://github.com/admiraldus) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ web, components, notion }, db) { + const topbarShareSelector = '.notion-topbar-share-menu', + blockActionSelector = + '.notion-overlay-container .notion-scroller.vertical .notion-focusable > div > div > [style*="text-overflow: ellipsis;"]', + hoveredActionSelector = + '.notion-overlay-container .notion-scroller.vertical .notion-focusable[style*="background:"]', + topbarCopyClass = 'global_block_links--topbar_copy', + blockCopyClass = 'global_block_links--block_copy', + hiddenClass = 'global_block_links--hidden'; + + const topbarCopyIcon = await db.get(['topbar_copy_icon']), + topbarCopyText = await db.get(['topbar_copy_text']); + if (topbarCopyIcon || topbarCopyText) { + const $topbarCopyTemplate = web.render( + web.html`
`, + topbarCopyIcon + ? web.html` + + ` + : '', + topbarCopyText + ? web.html` + Copy link + Link copied! + ` + : '' + ); + + const insertTopbarCopy = () => { + const $btns = document.querySelectorAll(topbarShareSelector); + $btns.forEach(($btn) => { + if (!$btn.previousElementSibling?.classList?.contains?.(topbarCopyClass)) { + const $copy = $topbarCopyTemplate.cloneNode(true); + components.addTooltip($copy, '**Copy page link**'); + $btn.before($copy); + + let resetButtonDelay; + $copy.addEventListener('click', () => { + if (topbarCopyText) { + const $copyText = $copy.querySelector('[data-copy]'), + $copiedText = $copy.querySelector('[data-copied]'); + $copyText.classList.add(hiddenClass); + $copiedText.classList.remove(hiddenClass); + clearTimeout(resetButtonDelay); + resetButtonDelay = setTimeout(() => { + $copyText.classList.remove(hiddenClass); + $copiedText.classList.add(hiddenClass); + }, 1250); + } + + web.copyToClipboard(`https://notion.so/${notion.getPageID().replace(/-/g, '')}`); + }); + } + }); + }; + insertTopbarCopy(); + web.addDocumentObserver(insertTopbarCopy, [topbarShareSelector]); + } + + const $blockCopyTemplate = web.html` +
+ ${await components.feather('globe')} + Global link +
`; + + const getLinkButtons = () => + [...document.querySelectorAll(blockActionSelector)] + .filter(($action) => + ['Copy link', '링크 복사', 'リンクをコピー'].includes($action.textContent) + ) + .map(($action) => $action.closest('.notion-focusable')), + insertBlockCopy = () => { + const $btns = getLinkButtons(); + $btns.forEach(($btn) => { + if (!$btn.previousElementSibling?.classList?.contains?.(blockCopyClass)) { + const $copy = $blockCopyTemplate.cloneNode(true); + $btn.before($copy); + + $copy.addEventListener('mouseover', () => { + document.querySelectorAll(hoveredActionSelector).forEach(($action) => { + $action.style.background = ''; + }); + }); + + $copy.addEventListener('click', async () => { + $btn.click(); + const link = await web.readFromClipboard(), + id = link.replace(/.+#(?=\w+)/, ''); + web.copyToClipboard(id.length === 32 ? `https://notion.so/${id}` : link); + }); + } + }); + }; + insertBlockCopy(); + web.addDocumentObserver(insertBlockCopy, [blockActionSelector]); +} diff --git a/src/mods/global-block-links/global-block-links.jpg b/src/mods/global-block-links/global-block-links.jpg new file mode 100644 index 0000000..eb84be2 Binary files /dev/null and b/src/mods/global-block-links/global-block-links.jpg differ diff --git a/src/mods/global-block-links/mod.json b/src/mods/global-block-links/mod.json new file mode 100644 index 0000000..698e9b2 --- /dev/null +++ b/src/mods/global-block-links/mod.json @@ -0,0 +1,35 @@ +{ + "name": "global block links", + "id": "74856af4-6970-455d-bd86-0a385a402dd1", + "version": "0.1.0", + "description": "easily copy the global link of a page or block.", + "preview": "global-block-links.jpg", + "tags": ["extension", "shortcut"], + "authors": [ + { + "name": "admiraldus", + "homepage": "https://github.com/admiraldus", + "avatar": "https://raw.githubusercontent.com/admiraldus/admiraldus/main/module.gif" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [ + { + "key": "topbar_copy_icon", + "label": "copy page links from the topbar (icon)", + "type": "toggle", + "value": true + }, + { + "key": "topbar_copy_text", + "label": "copy page links from the topbar (text)", + "type": "toggle", + "value": true + } + ] +} diff --git a/src/mods/gruvbox-dark/gruvbox-dark.png b/src/mods/gruvbox-dark/gruvbox-dark.png new file mode 100644 index 0000000..e0756e3 Binary files /dev/null and b/src/mods/gruvbox-dark/gruvbox-dark.png differ diff --git a/src/mods/gruvbox-dark/mod.json b/src/mods/gruvbox-dark/mod.json new file mode 100644 index 0000000..13b088b --- /dev/null +++ b/src/mods/gruvbox-dark/mod.json @@ -0,0 +1,29 @@ +{ + "name": "gruvbox dark", + "id": "ad0f5c5c-8b62-4b20-8a54-929e663ea368", + "version": "0.3.0", + "description": "a gray, 'retro groove' palette based on the vim theme of the same name.", + "preview": "gruvbox-dark.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "morhetz", + "email": "morhetz@gmail.com", + "homepage": "https://github.com/morhetz", + "avatar": "https://avatars.githubusercontent.com/u/554231" + }, + { + "name": "jordanrobinson", + "email": "me@jordanrobinson.co.uk", + "homepage": "https://jordanrobinson.co.uk/", + "avatar": "https://avatars.githubusercontent.com/u/1202911" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/gruvbox-dark/variables.css b/src/mods/gruvbox-dark/variables.css new file mode 100644 index 0000000..4096c4e --- /dev/null +++ b/src/mods/gruvbox-dark/variables.css @@ -0,0 +1,191 @@ +/** + * notion-enhancer: gruvbox dark + * (c) 2015-2020 morhetz + * (c) 2021 jordanrobinson (https://jordanrobinson.co.uk/) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --gruvbox_dark--bg: #282828; + --gruvbox_dark--light_gray_dark: #B3A89E; + --gruvbox_dark--gray_dark: #928374; + --gruvbox_dark--red_dark: #cc241d; + --gruvbox_dark--red_light: #fb4934; + --gruvbox_dark--green_dark: #98971a; + --gruvbox_dark--green_light: #b8bb26; + --gruvbox_dark--yellow_dark: #d79921; + --gruvbox_dark--yellow_light: #fabd2f; + --gruvbox_dark--blue_dark: #458588; + --gruvbox_dark--blue_light: #83a598; + --gruvbox_dark--purple_dark: #b16286; + --gruvbox_dark--purple_light: #d3869b; + --gruvbox_dark--aqua_dark: #689d6a; + --gruvbox_dark--aqua_light: #8ec07c; + --gruvbox_dark--gray_light: #a89984; + --gruvbox_dark--orange_dark: #d65d0e; + --gruvbox_dark--orange_light: #fe8019; + --gruvbox_dark--fg: #ebdbb2; + --gruvbox_dark--bg0_h: #1d2021; + --gruvbox_dark--bg0: #282828; + --gruvbox_dark--bg1: #3c3836; + --gruvbox_dark--bg2: #504945; + --gruvbox_dark--bg3: #665c54; + --gruvbox_dark--bg4: #7c6f64; + --gruvbox_dark--bg0_s: #32302f; + --gruvbox_dark--fg4: #a89984; + --gruvbox_dark--fg3: #bdae93; + --gruvbox_dark--fg2: #d5c4a1; + --gruvbox_dark--fg1: #ebdbb2; + --gruvbox_dark--fg0: #fbf1c7; + + --theme--accent_blue: var(--gruvbox_dark--bg3); + --theme--accent_blue-selection: rgba(80, 73, 69, 0.5); + --theme--accent_blue-hover: var(--gruvbox_dark--fg3); + --theme--accent_blue-active: var(--gruvbox_dark--fg3); + --theme--accent_red: var(--gruvbox_dark--blue_dark); + --theme--accent_red-button: var(--gruvbox_dark--blue_light); + + --theme--bg: var(--gruvbox_dark--bg0); + --theme--bg_secondary: var(--gruvbox_dark--bg1); + --theme--bg_card: var(--gruvbox_dark--bg3); + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: var(--gruvbox_dark--bg0_s); + --theme--scrollbar_thumb-hover: var(--gruvbox_dark--bg3); + + --theme--ui_divider: var(--gruvbox_dark--gray_light); + --theme--ui_interactive-hover: rgba(80, 73, 69, 0.7); + --theme--ui_interactive-active: rgba(80, 73, 69, 0.9); + --theme--ui_toggle-off: var(--gruvbox_dark--bg4); + --theme--ui_toggle-feature: var(--gruvbox_dark--fg); + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: var(--gruvbox_dark--fg0); + --theme--icon_secondary: var(--gruvbox_dark--fg3); + + --theme--text: var(--gruvbox_dark--fg0); + --theme--text_secondary: var(--gruvbox_dark--fg3); + --theme--text_gray: var(--gruvbox_dark--gray_dark); + --theme--text_brown: var(--gruvbox_dark--bg4); + --theme--text_orange: var(--gruvbox_dark--orange_dark); + --theme--text_yellow: var(--gruvbox_dark--yellow_dark); + --theme--text_green: var(--gruvbox_dark--green_dark); + --theme--text_blue: var(--gruvbox_dark--blue_dark); + --theme--text_purple: var(--gruvbox_dark--purple_dark); + --theme--text_pink: var(--gruvbox_dark--purple_light); + --theme--text_red: var(--gruvbox_dark--red_dark); + + --theme--highlight_gray: var(--gruvbox_dark--gray_dark); + --theme--highlight_gray-text: var(--gruvbox_dark--bg0); + --theme--highlight_brown: var(--theme--tag_brown); + --theme--highlight_brown-text: var(--gruvbox_dark--bg0); + --theme--highlight_orange: var(--gruvbox_dark--orange_dark); + --theme--highlight_orange-text: var(--gruvbox_dark--bg0); + --theme--highlight_yellow: var(--gruvbox_dark--yellow_dark); + --theme--highlight_yellow-text: var(--gruvbox_dark--bg0); + --theme--highlight_green: var(--gruvbox_dark--green_dark); + --theme--highlight_green-text: var(--gruvbox_dark--bg0); + --theme--highlight_blue: var(--gruvbox_dark--blue_dark); + --theme--highlight_blue-text: var(--gruvbox_dark--bg0); + --theme--highlight_purple: var(--gruvbox_dark--purple_dark); + --theme--highlight_purple-text: var(--gruvbox_dark--bg0); + --theme--highlight_pink: var(--theme--tag_pink); + --theme--highlight_pink-text: var(--gruvbox_dark--bg0); + --theme--highlight_red: var(--gruvbox_dark--red_dark); + --theme--highlight_red-text: var(--gruvbox_dark--bg0); + + --theme--callout_gray: var(--theme--highlight_gray); + --theme--callout_gray-text: var(--gruvbox_dark--bg0); + --theme--callout_brown: var(--theme--highlight_brown); + --theme--callout_brown-text: var(--gruvbox_dark--bg0); + --theme--callout_orange: var(--theme--highlight_orange); + --theme--callout_orange-text: var(--gruvbox_dark--bg0); + --theme--callout_yellow: var(--theme--highlight_yellow); + --theme--callout_yellow-text: var(--gruvbox_dark--bg0); + --theme--callout_green: var(--theme--highlight_green); + --theme--callout_green-text: var(--gruvbox_dark--bg0); + --theme--callout_blue: var(--theme--highlight_blue); + --theme--callout_blue-text: var(--gruvbox_dark--bg0); + --theme--callout_purple: var(--theme--highlight_purple); + --theme--callout_purple-text: var(--gruvbox_dark--bg0); + --theme--callout_pink: var(--theme--highlight_pink); + --theme--callout_pink-text: var(--gruvbox_dark--bg0); + --theme--callout_red: var(--theme--highlight_red); + --theme--callout_red-text: var(--gruvbox_dark--bg0); + + --theme--tag_default: var(--gruvbox_dark--gray_dark); + --theme--tag_default-text: var(--gruvbox_dark--bg2); + --theme--tag_light_gray: var(--gruvbox_dark--light_gray_dark); + --theme--tag_light_gray-text: var(--gruvbox_dark--bg2); + --theme--tag_gray: var(--gruvbox_dark--gray_dark); + --theme--tag_gray-text: var(--gruvbox_dark--bg2); + --theme--tag_brown: var(--gruvbox_dark--fg3); + --theme--tag_brown-text: var(--gruvbox_dark--bg2); + --theme--tag_orange: var(--gruvbox_dark--orange_dark); + --theme--tag_orange-text: var(--gruvbox_dark--bg0); + --theme--tag_yellow: var(--gruvbox_dark--yellow_dark); + --theme--tag_yellow-text: var(--gruvbox_dark--bg0); + --theme--tag_green: var(--gruvbox_dark--green_dark); + --theme--tag_green-text: var(--gruvbox_dark--bg0); + --theme--tag_blue: var(--gruvbox_dark--blue_dark); + --theme--tag_blue-text: var(--gruvbox_dark--bg0); + --theme--tag_purple: var(--gruvbox_dark--purple_dark); + --theme--tag_purple-text: var(--gruvbox_dark--bg0); + --theme--tag_pink: var(--gruvbox_dark--purple_light); + --theme--tag_pink-text: var(--gruvbox_dark--bg0); + --theme--tag_red: var(--gruvbox_dark--red_dark); + --theme--tag_red-text: var(--gruvbox_dark--bg0); + + --theme--board_light_gray: var(--gruvbox_dark--light_gray_dark); + --theme--board_light_gray-text: var(--gruvbox_dark--bg0); + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_gray: var(--theme--text_gray); + --theme--board_gray-text: var(--gruvbox_dark--bg0); + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_brown: var(--theme--text_brown); + --theme--board_brown-text: var(--gruvbox_dark--bg0); + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_orange: var(--theme--text_orange); + --theme--board_orange-text: var(--gruvbox_dark--bg0); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_yellow: var(--theme--text_yellow); + --theme--board_yellow-text: var(--gruvbox_dark--bg0); + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_green: var(--theme--text_green); + --theme--board_green-text: var(--gruvbox_dark--bg0); + --theme--board_green-card: var(--theme--tag_green); + --theme--board_blue: var(--theme--text_blue); + --theme--board_blue-text: var(--gruvbox_dark--bg0); + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_purple: var(--theme--text_purple); + --theme--board_purple-text: var(--gruvbox_dark--bg0); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_pink: var(--theme--text_pink); + --theme--board_pink-text: var(--gruvbox_dark--bg0); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_red: var(--theme--text_red); + --theme--board_red-text: var(--gruvbox_dark--bg0); + --theme--board_red-card: var(--theme--tag_red); + + --theme--code_inline: var(--gruvbox_dark--bg1); + --theme--code_inline-text: var(--gruvbox_dark--blue_light); + + --theme--code: var(--gruvbox_dark--bg1); + --theme--code_function: var(--theme--text_blue); + --theme--code_keyword: var(--theme--text_pink); + --theme--code_tag: var(--theme--text_pink); + --theme--code_operator: var(--theme--text_yellow); + --theme--code_important: var(--theme--text_yellow); + --theme--code_property: var(--theme--text_pink); + --theme--code_builtin: var(--theme--text_yellow); + --theme--code_attr-name: var(--theme--text_yellow); + --theme--code_comment: var(--theme--text_ui); + --theme--code_punctuation: var(--gruvbox_dark--aqua_light); + --theme--code_doctype: var(--gruvbox_dark--aqua_light); + --theme--code_number: var(--theme--text_purple); + --theme--code_string: var(--theme--text_orange); + --theme--code_attr-value: var(--theme--text_orange); +} diff --git a/src/mods/gruvbox-light/gruvbox-light.png b/src/mods/gruvbox-light/gruvbox-light.png new file mode 100644 index 0000000..f9a02c4 Binary files /dev/null and b/src/mods/gruvbox-light/gruvbox-light.png differ diff --git a/src/mods/gruvbox-light/mod.json b/src/mods/gruvbox-light/mod.json new file mode 100644 index 0000000..272b2ec --- /dev/null +++ b/src/mods/gruvbox-light/mod.json @@ -0,0 +1,29 @@ +{ + "name": "gruvbox light", + "id": "54f01911-60fc-4c9d-85b5-5c5ca3dd6b81", + "version": "0.3.0", + "description": "a sepia, 'retro groove' palette based on the vim theme of the same name.", + "preview": "gruvbox-light.png", + "tags": ["theme", "light"], + "authors": [ + { + "name": "morhetz", + "email": "morhetz@gmail.com", + "homepage": "https://github.com/morhetz", + "avatar": "https://avatars.githubusercontent.com/u/554231" + }, + { + "name": "jordanrobinson", + "email": "me@jordanrobinson.co.uk", + "homepage": "https://jordanrobinson.co.uk/", + "avatar": "https://avatars.githubusercontent.com/u/1202911" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/gruvbox-light/variables.css b/src/mods/gruvbox-light/variables.css new file mode 100644 index 0000000..9b3722f --- /dev/null +++ b/src/mods/gruvbox-light/variables.css @@ -0,0 +1,196 @@ +/** + * notion-enhancer: gruvbox light + * (c) 2015-2020 morhetz + * (c) 2021 jordanrobinson (https://jordanrobinson.co.uk/) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.light { + /* + * variables are named for consistency with gruvbox dark, + * but the _light variants are actually darker here + */ + --gruvbox_light--bg: #fbf1c7; + --gruvbox_light--light_gray_dark: #B3A89E; + --gruvbox_light--gray_dark: #928374; + --gruvbox_light--red_dark: #cc241d; + --gruvbox_light--red_light: #9d0006; + --gruvbox_light--green_dark: #98971a; + --gruvbox_light--green_light: #79740e; + --gruvbox_light--yellow_dark: #d79921; + --gruvbox_light--yellow_light: #b57614; + --gruvbox_light--blue_dark: #458588; + --gruvbox_light--blue_light: #076678; + --gruvbox_light--purple_dark: #b16286; + --gruvbox_light--purple_light: #b16286; + --gruvbox_light--aqua_dark: #689d6a; + --gruvbox_light--aqua_light: #427b58; + --gruvbox_light--gray_light: #7c6f64; + --gruvbox_light--orange_dark: #d65d0e; + --gruvbox_light--orange_light: #af3a03; + --gruvbox_light--fg: #3c3836; + --gruvbox_light--bg0_h: #f9f5d7; + --gruvbox_light--bg0: #fbf1c7; + --gruvbox_light--bg1: #ebdbb2; + --gruvbox_light--bg2: #d5c4a1; + --gruvbox_light--bg3: #bdae93; + --gruvbox_light--bg4: #a89984; + --gruvbox_light--bg0_s: #f2e5bc; + --gruvbox_light--fg4: #7c6f64; + --gruvbox_light--fg3: #665c54; + --gruvbox_light--fg2: #504945; + --gruvbox_light--fg1: #3c3836; + --gruvbox_light--fg0: #282828; + + --theme--accent_blue: var(--gruvbox_light--bg3); + --theme--accent_blue-selection: rgba(189, 175, 147, 0.5); + --theme--accent_blue-hover: var(--gruvbox_light--fg3); + --theme--accent_blue-active: var(--gruvbox_light--fg3); + --theme--accent_red: var(--gruvbox_light--blue_light); + --theme--accent_red-button: var(--gruvbox_light--blue_dark); + + --theme--bg: var(--gruvbox_light--bg0); + --theme--bg_secondary: var(--gruvbox_light--bg1); + --theme--bg_card: var(--gruvbox_light--bg2); + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: var(--gruvbox_light--bg0_s); + --theme--scrollbar_thumb-hover: var(--gruvbox_light--bg3); + + --theme--ui_divider: var(--gruvbox_light--gray_light); + --theme--ui_interactive-hover: rgba(124, 111, 100, 0.3); + --theme--ui_interactive-active: rgba(124, 111, 100, 0.4); + --theme--ui_input: var(--gruvbox_light--bg3); + --theme--ui_toggle-off: var(--gruvbox_light--bg4); + --theme--ui_toggle-feature: var(--gruvbox_light--bg); + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: var(--gruvbox_light--fg0); + --theme--icon_secondary: var(--gruvbox_light--fg3); + + --theme--text: var(--gruvbox_light--fg0); + --theme--text_secondary: var(--gruvbox_light--fg3); + --theme--text_gray: var(--gruvbox_light--gray_dark); + --theme--text_brown: var(--gruvbox_light--bg4); + --theme--text_orange: var(--gruvbox_light--orange_dark); + --theme--text_yellow: var(--gruvbox_light--yellow_dark); + --theme--text_green: var(--gruvbox_light--green_dark); + --theme--text_blue: var(--gruvbox_light--blue_dark); + --theme--text_purple: var(--gruvbox_light--purple_dark); + --theme--text_pink: var(--gruvbox_light--purple_light); + --theme--text_red: var(--gruvbox_light--red_dark); + + --theme--highlight_gray: var(--gruvbox_light--gray_dark); + --theme--highlight_gray-text: var(--gruvbox_light--bg0); + --theme--highlight_brown: var(--theme--tag_brown); + --theme--highlight_brown-text: var(--gruvbox_light--bg0); + --theme--highlight_orange: var(--gruvbox_light--orange_dark); + --theme--highlight_orange-text: var(--gruvbox_light--bg0); + --theme--highlight_yellow: var(--gruvbox_light--yellow_dark); + --theme--highlight_yellow-text: var(--gruvbox_light--bg0); + --theme--highlight_green: var(--gruvbox_light--green_dark); + --theme--highlight_green-text: var(--gruvbox_light--bg0); + --theme--highlight_blue: var(--gruvbox_light--blue_dark); + --theme--highlight_blue-text: var(--gruvbox_light--bg0); + --theme--highlight_purple: var(--gruvbox_light--purple_dark); + --theme--highlight_purple-text: var(--gruvbox_light--bg0); + --theme--highlight_pink: var(--theme--tag_pink); + --theme--highlight_pink-text: var(--gruvbox_light--bg0); + --theme--highlight_red: var(--gruvbox_light--red_dark); + --theme--highlight_red-text: var(--gruvbox_light--bg0); + + --theme--callout_gray: var(--theme--highlight_gray); + --theme--callout_gray-text: var(--gruvbox_light--bg0); + --theme--callout_brown: var(--theme--highlight_brown); + --theme--callout_brown-text: var(--gruvbox_light--bg0); + --theme--callout_orange: var(--theme--highlight_orange); + --theme--callout_orange-text: var(--gruvbox_light--bg0); + --theme--callout_yellow: var(--theme--highlight_yellow); + --theme--callout_yellow-text: var(--gruvbox_light--bg0); + --theme--callout_green: var(--theme--highlight_green); + --theme--callout_green-text: var(--gruvbox_light--bg0); + --theme--callout_blue: var(--theme--highlight_blue); + --theme--callout_blue-text: var(--gruvbox_light--bg0); + --theme--callout_purple: var(--theme--highlight_purple); + --theme--callout_purple-text: var(--gruvbox_light--bg0); + --theme--callout_pink: var(--theme--highlight_pink); + --theme--callout_pink-text: var(--gruvbox_light--bg0); + --theme--callout_red: var(--theme--highlight_red); + --theme--callout_red-text: var(--gruvbox_light--bg0); + + --theme--tag_default: var(--gruvbox_light--gray_dark); + --theme--tag_default-text: var(--gruvbox_light--bg2); + --theme--tag_light_gray: var(--gruvbox_light--light_gray_dark); + --theme--tag_light_gray-text: var(--gruvbox_light--bg0); + --theme--tag_gray: var(--gruvbox_light--gray_dark); + --theme--tag_gray-text: var(--gruvbox_light--bg2); + --theme--tag_brown: var(--gruvbox_light--fg3); + --theme--tag_brown-text: var(--gruvbox_light--bg2); + --theme--tag_orange: var(--gruvbox_light--orange_dark); + --theme--tag_orange-text: var(--gruvbox_light--bg0); + --theme--tag_yellow: var(--gruvbox_light--yellow_dark); + --theme--tag_yellow-text: var(--gruvbox_light--bg0); + --theme--tag_green: var(--gruvbox_light--green_dark); + --theme--tag_green-text: var(--gruvbox_light--bg0); + --theme--tag_blue: var(--gruvbox_light--blue_dark); + --theme--tag_blue-text: var(--gruvbox_light--bg0); + --theme--tag_purple: var(--gruvbox_light--purple_dark); + --theme--tag_purple-text: var(--gruvbox_light--bg0); + --theme--tag_pink: var(--gruvbox_light--purple_light); + --theme--tag_pink-text: var(--gruvbox_light--bg0); + --theme--tag_red: var(--gruvbox_light--red_dark); + --theme--tag_red-text: var(--gruvbox_light--bg0); + + --theme--board_light_gray: var(--gruvbox_light--light_gray_dark); + --theme--board_light_gray-text: var(--gruvbox_light--bg0_h); + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_gray: var(--theme--text_gray); + --theme--board_gray-text: var(--gruvbox_light--bg0); + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_brown: var(--theme--text_brown); + --theme--board_brown-text: var(--gruvbox_light--bg0); + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_orange: var(--theme--text_orange); + --theme--board_orange-text: var(--gruvbox_light--bg0); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_yellow: var(--theme--text_yellow); + --theme--board_yellow-text: var(--gruvbox_light--bg0); + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_green: var(--theme--text_green); + --theme--board_green-text: var(--gruvbox_light--bg0); + --theme--board_green-card: var(--theme--tag_green); + --theme--board_blue: var(--theme--text_blue); + --theme--board_blue-text: var(--gruvbox_light--bg0); + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_purple: var(--theme--text_purple); + --theme--board_purple-text: var(--gruvbox_light--bg0); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_pink: var(--theme--text_pink); + --theme--board_pink-text: var(--gruvbox_light--bg0); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_red: var(--theme--text_red); + --theme--board_red-text: var(--gruvbox_light--bg0); + --theme--board_red-card: var(--theme--tag_red); + + --theme--code_inline: var(--gruvbox_light--bg1); + --theme--code_inline-text: var(--gruvbox_light--blue_light); + + --theme--code: var(--gruvbox_light--bg1); + --theme--code_function: var(--theme--text_blue); + --theme--code_keyword: var(--theme--text_pink); + --theme--code_tag: var(--theme--text_pink); + --theme--code_operator: var(--theme--text_yellow); + --theme--code_important: var(--theme--text_yellow); + --theme--code_property: var(--theme--text_pink); + --theme--code_builtin: var(--theme--text_yellow); + --theme--code_attr-name: var(--theme--text_yellow); + --theme--code_comment: var(--theme--text_ui); + --theme--code_punctuation: var(--gruvbox_light--aqua_light); + --theme--code_doctype: var(--gruvbox_light--aqua_light); + --theme--code_number: var(--theme--text_purple); + --theme--code_string: var(--theme--text_orange); + --theme--code_attr-value: var(--theme--text_orange); +} diff --git a/src/mods/icon-sets/client.css b/src/mods/icon-sets/client.css new file mode 100644 index 0000000..0259d1e --- /dev/null +++ b/src/mods/icon-sets/client.css @@ -0,0 +1,236 @@ +/** + * notion-enhancer: icon sets + * (c) 2019 jayhxmo (https://jaymo.io/) + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.icon_sets--tab_button { + position: relative; + padding-top: 6px; + padding-bottom: 6px; + flex-shrink: 0; +} +.icon_sets--tab_button > .notion-focusable { + user-select: none; + transition: background 20ms ease-in 0s; + cursor: pointer; + display: inline-flex; + align-items: center; + height: 28px; + border-radius: 3px; + font-size: 14px; + line-height: 1.2; + padding-left: 8px; + padding-right: 8px; + color: var(--theme--text); +} +.icon_sets--tab_button:hover > .notion-focusable { + background: var(--theme--ui_interactive-hover); +} +.icon_sets--tab_button:active > .notion-focusable { + background: var(--theme--ui_interactive-active); +} + +.icon_sets--view { + padding: 0; + overflow: hidden; + display: flex; + flex-direction: column; + flex-grow: 1; + z-index: 1; +} +.icon_sets--actions { + display: flex; + padding: 10px 14px; +} + +.icon_sets--actions > .notion-focusable-within { + flex-grow: 1; +} +.icon_sets--link_input { + flex-grow: 1; + font-size: 14px; + line-height: 20px; + padding: 4px 6px; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + box-shadow: var(--theme--ui_shadow) 0px 0px 0px 1px inset; + background: var(--theme--ui_input); + cursor: text; + height: 28px; +} +.icon_sets--link_input > input { + font-size: inherit; + line-height: inherit; + border: none; + background: none; + width: 100%; + display: block; + resize: none; + padding: 0px; +} + +.icon_sets--link_submit { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} + +.icon_sets--upload, +.icon_sets--link_submit { + user-select: none; + transition: background 20ms ease-in 0s; + cursor: pointer; + border: none; + background: var(--theme--accent_blue); + color: var(--theme--accent_blue-text); + line-height: 1.2; + padding: 6px 8px; + height: 28px; + font-size: 14px; + font-weight: 500; +} +.icon_sets--upload:hover, +.icon_sets--link_submit:hover { + background: var(--theme--accent_blue-hover); +} +.icon_sets--upload:active, +.icon_sets--link_submit:active { + background: var(--theme--accent_blue-active); +} + +.icon_sets--upload { + margin-left: 0.5em; + border-radius: 3px; +} + +.icon_sets--list { + /* scroller */ + height: 100%; + word-break: break-all; + overflow: hidden auto; + padding: 0 14px 10px 14px; +} + +.icon_sets--error { + color: var(--theme--accent_red); +} +.icon_sets--title, +.icon_sets--error { + margin: 6px 0 8px 0; + font-size: 11px; + font-weight: 500; + line-height: 1.2; + user-select: none; + text-transform: uppercase; + border-radius: 2px; + padding: 0.25em; + display: flex; + align-items: center; +} + +.icon_sets--title { + cursor: pointer; + color: var(--theme--text_secondary); +} +.icon_sets--title:hover { + background: var(--theme--ui_interactive-hover); +} +.icon_sets--title:active { + background: var(--theme--ui_interactive-active); +} + +.icon_sets--title .info { + /* tooltips */ + height: 1em; + margin-left: 0.5em; +} + +.icon_sets--spinner { + margin-left: 0.5em; + height: 1em; + width: 1em; +} +.icon_sets--spinner img { + width: 100%; + height: 100%; + animation: rotation 1.3s infinite linear; +} +@keyframes rotation { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} + +.icon_sets--title a { + color: currentColor; + transition: color 100ms ease-in; +} +.icon_sets--title a:hover { + color: var(--theme--accent_blue); +} + +.icon_sets--title .triangle { + height: 1em; + width: 0.9em; + margin: 0 0.5em 0 0.25em; + transition: transform 200ms ease-out 0s; + transform: rotateZ(180deg); +} +.icon_sets--title[data-collapsed='true'] .triangle { + transform: rotateZ(90deg); +} +.icon_sets--title[data-collapsed='true'] + .icon_sets--set { + height: 0 !important; +} + +.icon_sets--set { + display: flex; + flex-wrap: wrap; + overflow: hidden; + transition: height 200ms ease-out 0s; +} +.icon_sets--icon { + user-select: none; + transition: background 20ms ease-in 0s; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + border-radius: 3px; + width: 32px; + height: 32px; + font-size: 24px; +} +.icon_sets--icon:hover { + background: var(--theme--ui_interactive-hover); +} +.icon_sets--icon:active { + background: var(--theme--ui_interactive-active); +} +.icon_sets--icon > img { + max-width: 24px; + max-height: 24px; +} +.icon_sets--sprite { + width: 24px; + height: 24px; + background-size: 24px; + background-repeat: no-repeat; + pointer-events: none; +} + +.icon_sets--divider { + height: 1px; + margin: 1em 0; + border-bottom: 1px solid var(--theme--ui_divider); +} +.icon_sets--title[data-collapsed='true'] + .icon_sets--set + .icon_sets--divider { + margin-top: 0.5em; +} diff --git a/src/mods/icon-sets/client.mjs b/src/mods/icon-sets/client.mjs new file mode 100644 index 0000000..df6e491 --- /dev/null +++ b/src/mods/icon-sets/client.mjs @@ -0,0 +1,313 @@ +/** + * notion-enhancer: icon sets + * (c) 2019 jayhxmo (https://jaymo.io/) + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +const getImgData = (url) => + new Promise(async (res, rej) => { + const blob = await fetch(url).then((res) => res.blob()), + reader = new FileReader(); + reader.onload = (e) => res(e.target.result); + reader.readAsDataURL(blob); + }); + +export default async function ({ web, fs, components, notion }, db) { + const recentUploads = await db.get(['recent_uploads'], []), + preventQualityReduction = await db.get(['prevent_quality_reduction']), + $triangleSvg = web.html` + + `; + + const customIconSets = [], + customIconsFile = await db.get(['json']); + if (customIconsFile?.content) { + const iconsData = JSON.parse(customIconsFile.content); + customIconSets.push(...(iconsData.icons || iconsData)); + } + + const enhancerIconSets = [], + enhancerIconsUrl = 'https://raw.githubusercontent.com/notion-enhancer/icons/main/'; + if (await db.get(['default_sets'])) { + const iconsData = await fs.getJSON(`${enhancerIconsUrl}/icons.json`); + enhancerIconSets.push(...(iconsData.icons || iconsData)); + } + + const mediaMenuSelector = '.notion-media-menu', + mediaScrollerSelector = '.notion-media-menu > .notion-scroller', + mediaFilterSelector = '.notion-media-menu > :first-child > :last-child', + mediaLinkInputSelector = '.notion-focusable-within > input[type=url]', + tabBtnSelector = (n) => + `.notion-media-menu > :first-child > :first-child > :nth-child(${n})`; + + const renderSetTitle = async (title, loadPromises = [], $tooltip = undefined) => { + const isCollapsed = await db.get(['collapsed', title], false), + $title = web.html`

`, + $spinner = web.html` + + `; + web.render( + $title, + $triangleSvg.cloneNode(true), + web.html`${title}`, + $spinner + ); + $title.addEventListener('click', () => { + const newState = $title.dataset.collapsed !== 'true'; + db.set(['collapsed', title], newState); + $title.dataset.collapsed = newState; + }); + // hide spinner after all icons finish loading + // doesn't need to be waited on by renderers + (async () => { + await Promise.all(loadPromises); + $spinner.remove(); + if ($tooltip) { + const $infoSvg = web.html`${await components.feather('info', { class: 'info' })}`; + components.addTooltip($infoSvg, $tooltip, { offsetDirection: 'right' }); + web.render($title, $infoSvg); + } + })(); + return $title; + }; + + const $iconsTab = web.html`
+
Icons
+
`, + // actions + $iconsLinkInput = web.html``, + $iconsLinkSubmit = web.html``, + $iconsUploadFile = web.html``, + $iconsUploadSubmit = web.render( + web.html``, + 'Upload an image', + $iconsUploadFile + ), + // sets + $setsList = web.html`
`, + // container + $iconsView = web.render( + web.html``, + web.render( + web.html`
`, + web.render( + web.html`
`, + $iconsLinkInput, + $iconsLinkSubmit + ), + $iconsUploadSubmit + ), + web.render($setsList) + ); + + let $mediaMenu, $activeTabUnderline; + const insertIconsTab = async () => { + if (document.contains($mediaMenu)) return; + + // prevent injection into file upload menus + $mediaMenu = document.querySelector(mediaMenuSelector); + if (!$mediaMenu || !$mediaMenu.textContent.includes('Emoji')) return; + + const $emojiTab = document.querySelector(tabBtnSelector(1)), + $emojiScroller = document.querySelector(mediaScrollerSelector), + $emojiFilter = document.querySelector(mediaFilterSelector), + $uploadTab = document.querySelector(tabBtnSelector(2)), + $linkTab = document.querySelector(tabBtnSelector(3)); + $uploadTab.style.display = 'none'; + $linkTab.style.display = 'none'; + if ($activeTabUnderline) $activeTabUnderline.remove(); + $activeTabUnderline = + $emojiTab.children[1] || $uploadTab.children[1] || $linkTab.children[1]; + $emojiTab.after($iconsTab); + $emojiScroller.after($iconsView); + + const renderRecentUploads = async () => { + const $recentUploads = web.html`
`, + loadPromises = []; + for (let i = recentUploads.length - 1; i >= 0; i--) { + const { signed, url } = recentUploads[i], + $icon = web.html` + + `; + web.render($recentUploads, $icon); + $icon.addEventListener('click', (event) => { + if (event.shiftKey) { + recentUploads.splice(i, 1); + db.set(['recent_uploads'], recentUploads); + $icon.remove(); + } else setIcon({ signed, url }); + }); + loadPromises.push( + new Promise((res, rej) => { + $icon.firstElementChild.onload = res; + $icon.firstElementChild.onerror = res; + }) + ); + } + + const $recentUploadsTitle = await renderSetTitle( + 'Recent', + loadPromises, + web.html`

Click to reuse an icon
Shift-click to remove it

` + ); + web.render($setsList, $recentUploadsTitle, $recentUploads); + }, + renderIconSet = async (iconData, enhancerSet = false) => { + try { + const $set = web.html`
`; + if (iconData.sourceUrl?.endsWith?.('/')) { + iconData.sourceUrl = iconData.sourceUrl.slice(0, -1); + } + + const loadPromises = []; + for (let i = 0; i < (iconData.count || iconData.source.length); i++) { + const iconUrl = iconData.sourceUrl + ? Array.isArray(iconData.source) + ? `${iconData.sourceUrl}/${iconData.source[i]}.${iconData.extension}` + : `${iconData.sourceUrl}/${iconData.source}_${i}.${iconData.extension}` + : iconData.source[i], + sprite = enhancerSet + ? `style=" + background-image: url(${enhancerIconsUrl}${iconData.source}/sprite.png); + background-position: 0 -${i * 24}px; + "` + : '', + $img = sprite + ? web.html`
` + : web.html``, + $icon = web.render(web.html``, $img); + web.render($set, $icon); + $icon.addEventListener('click', (event) => { + if (!event.shiftKey) setIcon({ signed: iconUrl, url: iconUrl }); + }); + if (!sprite) { + loadPromises.push( + new Promise((res, rej) => { + $img.onload = res; + $img.onerror = res; + }) + ); + } + } + + const author = iconData.author + ? iconData.authorUrl + ? web.raw`by + ${iconData.author} + ` + : web.raw`by ${web.escape(iconData.author)}` + : '', + $title = await renderSetTitle( + `${web.escape(iconData.name)} ${author}`, + loadPromises + ); + web.render($setsList, $title, $set); + } catch (err) { + console.error(err); + web.render( + $setsList, + web.html`
+ Invalid set: ${web.escape(iconData?.name || 'Unknown')} +
` + ); + } + }, + renderCustomIconSets = async () => { + if (customIconSets.length) { + web.render($setsList, web.html`
`); + } + await Promise.all(customIconSets.map((set) => renderIconSet(set))); + }, + renderEnhancerIconSets = async () => { + if (enhancerIconSets.length) { + web.render($setsList, web.html`
`); + } + await Promise.all( + enhancerIconSets.map((set) => { + set.sourceUrl = set.sourceUrl || enhancerIconsUrl + set.source; + return renderIconSet(set, true); + }) + ); + }; + + const displayIconsTab = async (force = false) => { + if ($iconsTab.contains($activeTabUnderline) && !force) return; + web.render($iconsTab, $activeTabUnderline); + $iconsView.style.display = ''; + $emojiScroller.style.display = 'none'; + $emojiFilter.style.display = 'none'; + web.empty($setsList); + await renderRecentUploads(); + await renderCustomIconSets(); + await renderEnhancerIconSets(); + $iconsView.querySelectorAll('.icon_sets--set').forEach(($set) => { + $set.style.height = `${$set.scrollHeight}px`; + }); + }, + displayEmojiTab = (force = false) => { + if ($emojiTab.contains($activeTabUnderline) && !force) return; + web.render($emojiTab, $activeTabUnderline); + $iconsView.style.display = 'none'; + $emojiScroller.style.display = ''; + $emojiFilter.style.display = ''; + }; + // use onclick instead of eventlistener to override prev + $iconsTab.onclick = displayIconsTab; + $emojiTab.onclick = displayEmojiTab; + // otherwise both may be visible on reopen + displayEmojiTab(true); + + async function setIcon({ signed, url }) { + // without this react gets upset + displayEmojiTab(); + $linkTab.firstChild.click(); + await new Promise(requestAnimationFrame); + + $mediaMenu.parentElement.style.opacity = '0'; + const iconUrl = preventQualityReduction ? await getImgData(signed) : url; + + // call native setter, imitate human input + const $notionLinkInput = $mediaMenu.querySelector(mediaLinkInputSelector), + proto = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value'); + proto.set.call($notionLinkInput, iconUrl); + const inputEvent = new Event('input', { bubbles: true }), + enterKeydownEvent = new KeyboardEvent('keydown', { + bubbles: true, + cancelable: true, + keyCode: 13, + }); + $notionLinkInput.dispatchEvent(inputEvent); + $notionLinkInput.dispatchEvent(enterKeydownEvent); + } + + const submitLinkIcon = () => { + const url = $iconsLinkInput.firstElementChild.value; + if (!url) return; + const icon = { signed: notion.sign(url, notion.getPageID()), url: url }; + setIcon(icon); + recentUploads.push(icon); + db.set(['recent_uploads'], recentUploads); + }; + $iconsLinkInput.onkeyup = (event) => { + if (event.code === 13) submitLinkIcon(); + }; + $iconsLinkSubmit.onclick = submitLinkIcon; + + // upload file to aws, then submit link + $iconsUploadSubmit.onclick = () => $iconsUploadFile.click(); + $iconsUploadFile.onchange = async (event) => { + const file = event.target.files[0], + url = await notion.upload(file), + icon = { signed: notion.sign(url, notion.getPageID()), url: url }; + setIcon(icon); + recentUploads.push(icon); + db.set(['recent_uploads'], recentUploads); + }; + }; + web.addDocumentObserver(insertIconsTab, [mediaMenuSelector]); +} diff --git a/src/mods/icon-sets/icon-sets.jpg b/src/mods/icon-sets/icon-sets.jpg new file mode 100644 index 0000000..f44e245 Binary files /dev/null and b/src/mods/icon-sets/icon-sets.jpg differ diff --git a/src/mods/icon-sets/mod.json b/src/mods/icon-sets/mod.json new file mode 100644 index 0000000..72c616b --- /dev/null +++ b/src/mods/icon-sets/mod.json @@ -0,0 +1,43 @@ +{ + "name": "icon sets", + "id": "2d1f4809-9581-40dd-9bf3-4239db406483", + "version": "0.4.0", + "description": "upload, save and reuse custom icons directly from the icon picker. check out the [icons page](https://notion-enhancer.github.io/advanced/icons) for instructions on loading entire sets.", + "preview": "icon-sets.jpg", + "tags": ["integration", "customisation"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [ + { + "type": "toggle", + "key": "default_sets", + "label": "load default icon sets from github", + "value": true + }, + { + "type": "toggle", + "key": "prevent_quality_reduction", + "label": "prevent icon quality reduction", + "tooltip": "**this may break icons in widgets or be rejected by notion if images are too high quality** - encodes and submits images as data urls to prevent notion from optimising them (reduces image quality by ~20%)", + "value": true + }, + { + "type": "file", + "key": "json", + "label": "custom icon sets (.json)", + "extensions": [".json"] + } + ] +} diff --git a/src/mods/indentation-lines/client.css b/src/mods/indentation-lines/client.css new file mode 100644 index 0000000..bcb867a --- /dev/null +++ b/src/mods/indentation-lines/client.css @@ -0,0 +1,101 @@ +/** + * notion-enhancer: indentation lines + * (c) 2020 Alexa Baldon (https://github.com/runargs) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-page-content .notion-bulleted_list-block > div > div:last-child, +.notion-page-content .notion-numbered_list-block > div > div:last-child, +.notion-page-content .notion-to_do-block > div > div:last-child, +.notion-page-content .notion-toggle-block > div > div:last-child, +.notion-page-content [class$='header-block'] > div > div > div:last-child, +.notion-page-content .notion-table_of_contents-block > div > div > a > div > div { + position: relative; +} + +.notion-page-content .notion-bulleted_list-block > div > div:last-child::before, +.notion-page-content .notion-numbered_list-block > div > div:last-child::before, +.notion-page-content .notion-to_do-block > div > div:last-child::before, +.notion-page-content .notion-toggle-block > div > div:last-child::before, +.notion-page-content [class$='header-block'] > div > div > div:last-child::before, +.notion-page-content .pseudoSelection > div > div:last-child::before { + content: ''; + position: absolute; + height: calc(100% - 2em); + top: 2em; + margin-inline-start: -14.48px; +} +.notion-page-content [class$='header-block'] > div > div > div:last-child::before { + margin-inline-start: -15.48px; +} + +.notion-page-content + .notion-table_of_contents-block + > div + > div + > a + > div + > div:not([style*='margin-left: 0px']) + > div::before { + content: ''; + position: absolute; + height: 100%; + top: 0; + margin-inline-start: -14.48px; +} + +/* add background to block dragger */ +.notion-frame + > [style*='position: absolute; top: 0px; left: 0px;'] + [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;'] + [data-block-id], +.notion-peek-renderer + > div + > [style*='position: absolute; top: 0px; left: 0px;'] + [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;'] + [data-block-id] { + background: var(--theme--bg) !important; + border-radius: 3px; +} +.notion-frame + > [style*='position: absolute; top: 0px; left: 0px;'] + [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;'] + + .notion-focusable, +.notion-peek-renderer + > div + > [style*='position: absolute; top: 0px; left: 0px;'] + [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;'] + + .notion-focusable { + left: -42px !important; + background: transparent !important; +} +.notion-frame + > [style*='position: absolute; top: 0px; left: 0px;'] + [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;'] + + .notion-focusable + > .plus, +.notion-peek-renderer + > div + > [style*='position: absolute; top: 0px; left: 0px;'] + [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;'] + + .notion-focusable + > .plus { + border-radius: 3px; + width: 18px !important; + padding: 0 1px !important; + background: var(--theme--bg) !important; +} +.notion-frame + > [style*='position: absolute; top: 0px; left: 0px;'] + [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;'] + + .notion-focusable + > .plus:hover, +.notion-peek-renderer + > div + > [style*='position: absolute; top: 0px; left: 0px;'] + [style*='position: absolute; top: 3px; left: -20px; width: 18px; height: 24px; pointer-events: auto; cursor: -webkit-grab;'] + + .notion-focusable + > .plus:hover { + background: var(--theme--ui_interactive-hover) !important; +} diff --git a/src/mods/indentation-lines/client.mjs b/src/mods/indentation-lines/client.mjs new file mode 100644 index 0000000..3cb8736 --- /dev/null +++ b/src/mods/indentation-lines/client.mjs @@ -0,0 +1,109 @@ +/** + * notion-enhancer: indentation lines + * (c) 2020 Alexa Baldon (https://github.com/runargs) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web }, db) { + let style = 'solid', + opacity = 1, + rainbow = false; + switch (await db.get(['style'])) { + case 'dashed': + style = 'dashed'; + break; + case 'dotted': + style = 'dotted'; + break; + case 'soft': + opacity = 0.25; + break; + case 'rainbow': + opacity = 0.7; + rainbow = true; + break; + } + + let css = ''; + const colors = ['red', 'pink', 'purple', 'blue', 'green', 'yellow']; + colors.push(...colors, ...colors, ...colors, 'gray'); + + for (const listType of ['bulleted_list', 'numbered_list', 'to_do', 'toggle']) { + if (!(await db.get([listType]))) continue; + css += ` + .notion-page-content .notion-${listType}-block > div > div:last-child::before { + border-left: 1px ${style} var(--indentation_lines--color, currentColor); + opacity: ${opacity}; + }`; + + if (rainbow) { + for (let i = 0; i < colors.length; i++) { + css += ` + .notion-page-content ${`.notion-${listType}-block `.repeat(i + 1)} + > div > div:last-child::before { + --indentation_lines--color: var(--theme--text_${colors[i]}); + }`; + } + } + } + + if (await db.get(['toggle_header'])) { + css += ` + .notion-page-content [class$=header-block] > div > div > div:last-child::before { + border-left: 1px ${style} var(--indentation_lines--color, currentColor); + opacity: ${opacity}; + }`; + + if (rainbow) { + for (let i = 0; i < colors.length; i++) { + css += ` + .notion-page-content ${`[class$=header-block] `.repeat(i + 1)} + > div > div > div:last-child::before{ + --indentation_lines--color: var(--theme--text_${colors[i]}); + }`; + } + } + } + + if (await db.get(['table_of_contents'])) { + css += ` + .notion-page-content .notion-table_of_contents-block > div > div > a > div + > div:not([style*='margin-left: 0px']) > div::before { + border-left: 1px ${style} var(--indentation_lines--color, currentColor); + opacity: ${opacity}; + }`; + + if (rainbow) { + css += ` + .notion-page-content .notion-table_of_contents-block > div > div > a > div + > div[style*='margin-left: 24px'] > div::before { + --indentation_lines--color: var(--theme--text_${colors[0]}); + } + .notion-page-content .notion-table_of_contents-block > div > div > a > div + > div[style*='margin-left: 48px'] > div::before { + --indentation_lines--color: var(--theme--text_${colors[1]}); + }`; + } + } + + if (await db.get(['outliner'])) { + css += ` + .outliner--header:not([style='--outliner--indent:0px;'])::before { + border-left: 1px ${style} var(--indentation_lines--color, currentColor); + opacity: ${opacity}; + }`; + if (rainbow) { + css += ` + .outliner--header[style='--outliner--indent:18px;']::before { + --indentation_lines--color: var(--theme--text_${colors[0]}); + } + .outliner--header[style='--outliner--indent:36px;']::before { + --indentation_lines--color: var(--theme--text_${colors[1]}); + }`; + } + } + + web.render(document.head, web.html``); +} diff --git a/src/mods/indentation-lines/indentation-lines.jpg b/src/mods/indentation-lines/indentation-lines.jpg new file mode 100644 index 0000000..0587e6d Binary files /dev/null and b/src/mods/indentation-lines/indentation-lines.jpg differ diff --git a/src/mods/indentation-lines/mod.json b/src/mods/indentation-lines/mod.json new file mode 100644 index 0000000..33a82d9 --- /dev/null +++ b/src/mods/indentation-lines/mod.json @@ -0,0 +1,72 @@ +{ + "name": "indentation lines", + "id": "35815b3b-3916-4dc6-8769-c9c2448f8b57", + "version": "0.2.0", + "description": "adds vertical relationship lines to make list trees easier to follow.", + "preview": "indentation-lines.jpg", + "tags": ["extension", "usability"], + "authors": [ + { + "name": "runargs", + "email": "alnbaldon@gmail.com", + "homepage": "http://github.com/runargs", + "avatar": "https://avatars.githubusercontent.com/u/39810066" + } + ], + "css": { + "client": ["client.css"] + }, + "js": { + "client": ["client.mjs"] + }, + "options": [ + { + "type": "select", + "key": "style", + "label": "style", + "values": ["solid", "dashed", "dotted", "soft", "rainbow"] + }, + { + "type": "toggle", + "key": "bulleted_list", + "label": "bulleted lists", + "value": true + }, + { + "type": "toggle", + "key": "numbered_list", + "label": "numbered lists", + "value": true + }, + { + "type": "toggle", + "key": "to_do", + "label": "to-do lists", + "value": true + }, + { + "type": "toggle", + "key": "toggle", + "label": "toggle lists", + "value": true + }, + { + "type": "toggle", + "key": "toggle_header", + "label": "toggle headers", + "value": true + }, + { + "type": "toggle", + "key": "table_of_contents", + "label": "tables of contents", + "value": true + }, + { + "type": "toggle", + "key": "outliner", + "label": "outliner (panel extension)", + "value": true + } + ] +} diff --git a/src/mods/integrated-titlebar/buttons.css b/src/mods/integrated-titlebar/buttons.css new file mode 100644 index 0000000..3a59d9c --- /dev/null +++ b/src/mods/integrated-titlebar/buttons.css @@ -0,0 +1,76 @@ +/** + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.integrated_titlebar--dragarea { + width: 100%; + display: block; + -webkit-app-region: drag; + background: var(--theme--bg_secondary); + height: var(--integrated_titlebar--dragarea-height); +} + +.integrated_titlebar--buttons { + display: flex; + align-items: center; +} +.sidebar > .integrated_titlebar--buttons { + margin: -0.5rem 0 0.75rem auto; +} + +.integrated_titlebar--buttons button { + user-select: none; + transition: background 20ms ease-in 0s; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + border-radius: 3px; + height: 28px; + width: 33px; + padding: 0 0.25px 0 0; + + margin-left: 2px; + border: none; + background: transparent; + font-size: 18px; + -webkit-app-region: no-drag; +} +.integrated_titlebar--buttons button svg { + display: block; + width: 20px; + height: 20px; + fill: var(--theme--icon); + color: var(--theme--icon); +} + +.integrated_titlebar--buttons button:focus, +.integrated_titlebar--buttons button:hover { + background: var(--theme--ui_interactive-hover); +} +.integrated_titlebar--buttons button:active { + background: var(--theme--ui_interactive-active); +} +#integrated_titlebar--close:focus, +#integrated_titlebar--close:hover, +#integrated_titlebar--close:active { + background: var(--theme--accent_red); + color: var(--theme--accent_red-text); +} + +.notion-update-sidebar [style*='margin-top: 45px;'] { + margin-top: calc(45px + var(--integrated_titlebar--dragarea-height, 0px)) !important; +} +.notion-topbar { + height: calc(45px + var(--integrated_titlebar--dragarea-height, 0px)) !important; +} +.notion-frame { + height: calc(100vh - 45px - var(--integrated_titlebar--dragarea-height, 0px)) !important; +} + +body > .body-container { + height: calc(100% - var(--integrated_titlebar--dragarea-height, 0px)) !important; +} diff --git a/src/mods/integrated-titlebar/buttons.mjs b/src/mods/integrated-titlebar/buttons.mjs new file mode 100644 index 0000000..da307f5 --- /dev/null +++ b/src/mods/integrated-titlebar/buttons.mjs @@ -0,0 +1,73 @@ +/** + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export const createWindowButtons = async ({ electron, web, components }, db) => { + let minimizeIcon = (await db.get(['minimize_icon'])) || (await components.feather('minus')), + maximizeIcon = (await db.get(['maximize_icon'])) || (await components.feather('maximize')), + unmaximizeIcon = + (await db.get(['unmaximize_icon'])) || (await components.feather('minimize')), + closeIcon = (await db.get(['close_icon'])) || (await components.feather('x')); + minimizeIcon = minimizeIcon.trim(); + maximizeIcon = maximizeIcon.trim(); + unmaximizeIcon = unmaximizeIcon.trim(); + closeIcon = closeIcon.trim(); + + minimizeIcon = + minimizeIcon.startsWith('') + ? minimizeIcon + : web.escape(minimizeIcon); + maximizeIcon = + maximizeIcon.startsWith('') + ? maximizeIcon + : web.escape(maximizeIcon); + unmaximizeIcon = + unmaximizeIcon.startsWith('') + ? unmaximizeIcon + : web.escape(unmaximizeIcon); + closeIcon = + closeIcon.startsWith('') + ? closeIcon + : web.escape(closeIcon); + + const $windowButtons = web.html`
`, + $minimize = web.html``, + $maximize = web.html``, + $unmaximize = web.html``, + $close = web.html``; + components.addTooltip($minimize, '**Minimize window**'); + components.addTooltip($maximize, '**Maximize window**'); + components.addTooltip($unmaximize, '**Unmaximize window**'); + components.addTooltip($close, '**Close window**'); + + $minimize.addEventListener('click', () => electron.browser.minimize()); + $maximize.addEventListener('click', () => electron.browser.maximize()); + $unmaximize.addEventListener('click', () => electron.browser.unmaximize()); + $close.addEventListener('click', () => electron.browser.close()); + electron.browser.on('maximize', () => { + $maximize.replaceWith($unmaximize); + }); + electron.browser.on('unmaximize', () => { + $unmaximize.replaceWith($maximize); + }); + + web.render( + $windowButtons, + $minimize, + electron.browser.isMaximized() ? $unmaximize : $maximize, + $close + ); + return $windowButtons; +}; diff --git a/src/mods/integrated-titlebar/client.mjs b/src/mods/integrated-titlebar/client.mjs new file mode 100644 index 0000000..f217e60 --- /dev/null +++ b/src/mods/integrated-titlebar/client.mjs @@ -0,0 +1,58 @@ +/** + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +import { createWindowButtons } from './buttons.mjs'; + +export default async function (api, db) { + const { web, registry, electron } = api, + tilingMode = await db.get(['tiling']), + dragareaHeight = await db.get(['dragarea_height']), + tabsEnabled = await registry.enabled('e1692c29-475e-437b-b7ff-3eee872e1a42'), + sidebarSelector = '.notion-sidebar', + panelSelector = '#enhancer--panel', + topbarSelector = '.notion-topbar', + topbarActionsSelector = '.notion-topbar-action-buttons'; + if (tilingMode || tabsEnabled) return; + + let sidebarWidth = '0px', + panelWidth = '0px'; + const updateDragareaOffsets = () => { + const $sidebar = document.querySelector(sidebarSelector), + newSidebarWidth = + !$sidebar || $sidebar.style.height === 'auto' ? '0px' : $sidebar.style.width, + $panel = document.querySelector(panelSelector), + newPanelWidth = + $panel && $panel.dataset.enhancerPanelPinned === 'true' + ? window + .getComputedStyle(document.documentElement) + .getPropertyValue('--component--panel-width') + : '0px'; + if (newSidebarWidth !== sidebarWidth) { + sidebarWidth = newSidebarWidth; + electron.sendMessageToHost('sidebar-width', sidebarWidth); + } + if (newPanelWidth !== panelWidth) { + panelWidth = newPanelWidth; + electron.sendMessageToHost('panel-width', panelWidth); + } + }; + web.addDocumentObserver(updateDragareaOffsets); + + await web.whenReady([topbarSelector, topbarActionsSelector]); + const $topbar = document.querySelector(topbarSelector), + $dragarea = web.html`
`; + $topbar.prepend($dragarea); + document.documentElement.style.setProperty( + '--integrated_titlebar--dragarea-height', + dragareaHeight + 'px' + ); + + const $topbarActions = document.querySelector(topbarActionsSelector), + $windowButtons = await createWindowButtons(api, db); + web.render($topbarActions.parentElement, $windowButtons); +} diff --git a/src/mods/integrated-titlebar/createWindow.cjs b/src/mods/integrated-titlebar/createWindow.cjs new file mode 100644 index 0000000..39e42e6 --- /dev/null +++ b/src/mods/integrated-titlebar/createWindow.cjs @@ -0,0 +1,18 @@ +/** + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = function (api, db, __exports, __eval) { + __eval(` + const notionRectFromFocusedWindow = getRectFromFocusedWindow; + getRectFromFocusedWindow = (windowState) => { + const rect = notionRectFromFocusedWindow(windowState); + rect.frame = false; + return rect; + }; + `); +}; diff --git a/src/mods/integrated-titlebar/frame.mjs b/src/mods/integrated-titlebar/frame.mjs new file mode 100644 index 0000000..f0e7729 --- /dev/null +++ b/src/mods/integrated-titlebar/frame.mjs @@ -0,0 +1,43 @@ +/** + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +import { createWindowButtons } from './buttons.mjs'; + +export default async function (api, db) { + const { web, registry } = api, + tilingMode = await db.get(['tiling']), + dragareaHeight = await db.get(['dragarea_height']), + tabsEnabled = await registry.enabled('e1692c29-475e-437b-b7ff-3eee872e1a42'); + + if (tabsEnabled && !tilingMode) { + const windowActionsSelector = '#window-actions'; + await web.whenReady([windowActionsSelector]); + + const $topbarActions = document.querySelector(windowActionsSelector), + $windowButtons = await createWindowButtons(api, db); + web.render($topbarActions, $windowButtons); + } else { + const dragareaSelector = '[style*="-webkit-app-region: drag;"]'; + await web.whenReady([dragareaSelector]); + + const dragarea = document.querySelector(dragareaSelector); + dragarea.style.top = '2px'; + dragarea.style.height = tilingMode ? '0' : `${dragareaHeight}px`; + + document.getElementById('notion').addEventListener('ipc-message', (event) => { + switch (event.channel) { + case 'notion-enhancer:sidebar-width': + dragarea.style.left = event.args[0]; + break; + case 'notion-enhancer:panel-width': + dragarea.style.right = event.args[0]; + break; + } + }); + } +} diff --git a/src/mods/integrated-titlebar/integrated-titlebar.jpg b/src/mods/integrated-titlebar/integrated-titlebar.jpg new file mode 100644 index 0000000..0802010 Binary files /dev/null and b/src/mods/integrated-titlebar/integrated-titlebar.jpg differ diff --git a/src/mods/integrated-titlebar/menu.mjs b/src/mods/integrated-titlebar/menu.mjs new file mode 100644 index 0000000..2e015f3 --- /dev/null +++ b/src/mods/integrated-titlebar/menu.mjs @@ -0,0 +1,29 @@ +/** + * notion-enhancer: integrated titlebar + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +import { createWindowButtons } from './buttons.mjs'; + +export default async function (api, db) { + const { web } = api, + tilingMode = await db.get(['tiling']), + dragareaHeight = await db.get(['dragarea_height']), + sidebarSelector = '.sidebar'; + if (tilingMode) return; + + await web.whenReady([sidebarSelector]); + const $dragarea = web.html`
`; + document.body.prepend($dragarea); + document.documentElement.style.setProperty( + '--integrated_titlebar--dragarea-height', + dragareaHeight + 'px' + ); + + const $sidebar = document.querySelector(sidebarSelector), + $windowButtons = await createWindowButtons(api, db); + $sidebar.prepend($windowButtons); +} diff --git a/src/mods/integrated-titlebar/mod.json b/src/mods/integrated-titlebar/mod.json new file mode 100644 index 0000000..c84d400 --- /dev/null +++ b/src/mods/integrated-titlebar/mod.json @@ -0,0 +1,72 @@ +{ + "name": "integrated titlebar", + "id": "a5658d03-21c6-4088-bade-fa4780459133", + "environments": ["linux", "win32"], + "version": "0.11.0", + "description": "replaces the native window titlebar with buttons inset into the app.", + "preview": "integrated-titlebar.jpg", + "tags": ["extension", "layout"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": { + "frame": ["buttons.css"], + "client": ["buttons.css"], + "menu": ["buttons.css"] + }, + "js": { + "frame": ["frame.mjs"], + "client": ["client.mjs"], + "menu": ["menu.mjs"], + "electron": [{ "source": "createWindow.cjs", "target": "main/createWindow.js" }] + }, + "options": [ + { + "type": "toggle", + "key": "tiling", + "label": "tiling window manager mode", + "tooltip": "**completely remove the close/minimise/maximise buttons** (only for advanced use, not recommended)", + "value": false + }, + { + "type": "number", + "key": "dragarea_height", + "label": "dragarea height (px)", + "tooltip": "**the height of the rectangle added to the top of the window, used to drag/move the window around** (dragging is not possible in a frameless window without this bar)", + "value": 12 + }, + { + "type": "text", + "key": "minimize_icon", + "label": "minimize window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + }, + { + "type": "text", + "key": "maximize_icon", + "label": "maximize window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + }, + { + "type": "text", + "key": "unmaximize_icon", + "label": "unmaximize window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + }, + { + "type": "text", + "key": "close_icon", + "label": "close window icon", + "tooltip": "**may be an svg string or any unicode symbol e.g. an emoji** (the default icon will be used if this field is left empty)", + "value": "" + } + ] +} diff --git a/src/mods/light+/light+.png b/src/mods/light+/light+.png new file mode 100644 index 0000000..8d6586d Binary files /dev/null and b/src/mods/light+/light+.png differ diff --git a/src/mods/light+/mod.json b/src/mods/light+/mod.json new file mode 100644 index 0000000..cf7fc57 --- /dev/null +++ b/src/mods/light+/mod.json @@ -0,0 +1,48 @@ +{ + "name": "light+", + "id": "336cbc54-67b9-4b00-b4a2-9cc86eef763b", + "version": "0.2.0", + "description": "a simple white theme that brightens coloured text and blocks, with configurable accents.", + "preview": "light+.png", + "tags": ["theme", "light"], + "authors": [ + { + "name": "Lizishan", + "homepage": "https://www.reddit.com/user/Lizishan", + "avatar": "https://styles.redditmedia.com/t5_110nz4/styles/profileIcon_h1m3b16exoi51.jpg" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": { + "frame": ["theme.mjs"], + "client": ["theme.mjs"], + "menu": ["theme.mjs"] + }, + "options": [ + { + "type": "color", + "key": "primary", + "label": "primary accent color", + "tooltip": "**replaces notion's blue accent**", + "value": "rgba(46,170,220,1)" + }, + { + "type": "color", + "key": "secondary", + "label": "secondary accent color", + "tooltip": "**replaces notion's red accent**", + "value": "rgba(235,87,87,1)" + }, + { + "type": "color", + "key": "highlight", + "label": "highlight accent color", + "tooltip": "**affects dividers, text, icons and hovered scrollbars. set this to rgba(0,0,0,0) to disable it**", + "value": "rgba(0,0,0,0)" + } + ] +} diff --git a/src/mods/light+/theme.mjs b/src/mods/light+/theme.mjs new file mode 100644 index 0000000..64c7302 --- /dev/null +++ b/src/mods/light+/theme.mjs @@ -0,0 +1,70 @@ +/** + * notion-enhancer: light+ + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ fmt }, db) { + { + const primary = await db.get(['primary']), + [r, g, b] = primary + .slice(5, -1) + .split(',') + .map((i) => parseInt(i)); + if (!(r === 46 && g === 170 && b === 220)) { + document.documentElement.style.setProperty('--light_plus--accent_blue', primary); + document.documentElement.style.setProperty( + '--light_plus--accent_blue-selection', + `rgba(${r},${g},${b},0.2)` + ); + document.documentElement.style.setProperty( + '--light_plus--accent_blue-hover', + fmt.rgbLogShade(0.05, primary) + ); + document.documentElement.style.setProperty( + '--light_plus--accent_blue-active', + fmt.rgbLogShade(0.025, primary) + ); + document.documentElement.style.setProperty( + '--light_plus--accent_blue-text', + fmt.rgbContrast(r, g, b) + ); + } + } + + { + const secondary = await db.get(['secondary']), + [r, g, b] = secondary + .slice(5, -1) + .split(',') + .map((i) => parseInt(i)); + if (!(r === 235 && g === 87 && b === 87)) { + document.documentElement.style.setProperty('--light_plus--accent_red', secondary); + document.documentElement.style.setProperty( + '--light_plus--accent_red-button', + `rgba(${r},${g},${b},0.2)` + ); + document.documentElement.style.setProperty( + '--light_plus--accent_red-text', + fmt.rgbContrast(r, g, b) + ); + } + } + + { + const highlight = await db.get(['highlight']), + [r, g, b, a] = highlight + .slice(5, -1) + .split(',') + .map((i) => parseFloat(i)); + if (!(r === 0 && g === 0 && b === 0 && a === 0)) { + document.documentElement.style.setProperty('--light_plus--accent_highlight', highlight); + document.documentElement.style.setProperty( + '--light_plus--accent_highlight-shaded', + fmt.rgbLogShade(0.1, highlight) + ); + } + } +} diff --git a/src/mods/light+/variables.css b/src/mods/light+/variables.css new file mode 100644 index 0000000..60531ff --- /dev/null +++ b/src/mods/light+/variables.css @@ -0,0 +1,137 @@ +/** + * notion-enhancer: light+ + * (c) 2020 Lizishan + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.light { + --theme--accent_blue: var(--light_plus--accent_blue, rgb(46, 170, 220)); + --theme--accent_blue-selection: var( + --light_plus--accent_blue-selection, + rgb(46, 170, 220, 0.25) + ); + --theme--accent_blue-hover: var(--light_plus--accent_blue-hover, rgb(6, 156, 205)); + --theme--accent_blue-active: var(--light_plus--accent_blue-active, rgb(0, 141, 190)); + --theme--accent_blue-text: var(--light_plus--accent_blue-text, #fff); + --theme--accent_red: var(--light_plus--accent_red, #eb5757); + --theme--accent_red-button: var(--light_plus--accent_red-button, rgba(235, 87, 87, 0.1)); + --theme--accent_red-text: var(--light_plus--accent_red-text, #fff); + + --theme--scrollbar_thumb-hover: var(--light_plus--accent_highlight, #aeaca6); + + --theme--ui_divider: var(--light_plus--accent_highlight, rgb(237, 237, 236)); + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: var(--light_plus--accent_highlight, rgba(55, 53, 47, 0.8)); + --theme--icon_secondary: var(--light_plus--accent_highlight, rgba(55, 53, 47, 0.4)); + + --theme--text: var(--light_plus--accent_highlight, rgb(55, 43, 47)); + --theme--text_secondary: var(--light_plus--accent_highlight-shaded, rgba(55, 43, 47, 0.6)); + + --theme--text_gray: rgba(151, 154, 155, 0.95); + --theme--text_brown: rgb(167, 126, 100); + --theme--text_orange: rgb(255, 134, 0); + --theme--text_yellow: rgb(255, 195, 0); + --theme--text_green: rgb(0, 171, 0); + --theme--text_blue: rgb(0, 121, 255); + --theme--text_purple: rgb(126, 0, 255); + --theme--text_pink: rgb(255, 0, 208); + --theme--text_red: rgb(255, 0, 0); + + --theme--highlight_gray: rgb(234, 234, 234); + --theme--highlight_gray-text: rgb(17, 17, 17); + --theme--highlight_brown: rgb(206, 206, 206); + --theme--highlight_brown-text: rgb(85, 35, 1); + --theme--highlight_orange: rgb(254, 214, 155); + --theme--highlight_orange-text: rgb(199, 110, 0); + --theme--highlight_yellow: #fcffd8; + --theme--highlight_yellow-text: #ff8c22; + --theme--highlight_green: #d5fded; + --theme--highlight_green-text: #006a00; + --theme--highlight_blue: #e2f5ff; + --theme--highlight_blue-text: #00b2ff; + --theme--highlight_purple: #efe6ff; + --theme--highlight_purple-text: #8334ff; + --theme--highlight_pink: #ffe9f1; + --theme--highlight_pink-text: rgb(255, 0, 127); + --theme--highlight_red: rgb(248, 215, 218); + --theme--highlight_red-text: rgb(138, 0, 10); + + --theme--callout_gray: #e2e3e5; + --theme--callout_gray-text: #383d41; + --theme--callout_brown: rgb(130, 118, 111); + --theme--callout_brown-text: rgb(85, 35, 1); + --theme--callout_orange: rgb(254, 214, 155); + --theme--callout_orange-text: rgb(255, 140, 0); + --theme--callout_yellow: #fcffd8; + --theme--callout_yellow-text: #c76e00; + --theme--callout_green: #d4edda; + --theme--callout_green-text: #155724; + --theme--callout_blue: #cce5ff; + --theme--callout_blue-text: #004085; + --theme--callout_purple: rgb(199, 178, 230); + --theme--callout_purple-text: rgb(90, 49, 148); + --theme--callout_pink: rgb(255, 206, 228); + --theme--callout_pink-text: rgb(255, 0, 127); + --theme--callout_red: #f8d7da; + --theme--callout_red-text: #721c24; + + --theme--tag_default: rgb(234, 234, 234); + --theme--tag_light_gray: rgb(240, 240, 240); + --theme--tag_light_gray-text: rgb(65, 65, 65); + --theme--tag_gray: rgb(234, 234, 234); + --theme--tag_gray-text: rgb(17, 17, 17); + --theme--tag_brown: rgb(206, 206, 206); + --theme--tag_brown-text: rgb(85, 35, 1); + --theme--tag_orange: rgb(254, 214, 155); + --theme--tag_orange-text: rgb(199, 110, 0); + --theme--tag_yellow: #fcffd8; + --theme--tag_yellow-text: #ff8c22; + --theme--tag_green: #d5fded; + --theme--tag_green-text: #006a00; + --theme--tag_blue: #e2f5ff; + --theme--tag_blue-text: #00b2ff; + --theme--tag_purple: #efe6ff; + --theme--tag_purple-text: #8334ff; + --theme--tag_pink: #ffe9f1; + --theme--tag_pink-text: rgb(255, 0, 127); + --theme--tag_red: rgb(248, 215, 218); + --theme--tag_red-text: rgb(138, 0, 10); + + --theme--board_light_gray: #EBEBED; + --theme--board_light_gray-text: #616366; + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_gray: #e2e3e5; + --theme--board_gray-text: #383d41; + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_brown: rgb(130, 118, 111); + --theme--board_brown-text: rgb(85, 35, 1); + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_orange: rgb(254, 214, 155); + --theme--board_orange-text: rgb(255, 140, 0); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_yellow: #fcffd8; + --theme--board_yellow-text: #c76e00; + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_green: #d4edda; + --theme--board_green-text: #155724; + --theme--board_green-card: var(--theme--tag_green); + --theme--board_blue: #cce5ff; + --theme--board_blue-text: #004085; + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_purple: rgb(199, 178, 230); + --theme--board_purple-text: rgb(90, 49, 148); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_pink: rgb(255, 206, 228); + --theme--board_pink-text: rgb(255, 0, 127); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_red: #f8d7da; + --theme--board_red-text: #721c24; + --theme--board_red-card: var(--theme--tag_red); + + --theme--code_inline: rgb(179, 39, 39); + --theme--code_inline-text: #e0dfe2; +} diff --git a/src/mods/material-ocean/material-ocean.png b/src/mods/material-ocean/material-ocean.png new file mode 100644 index 0000000..838a9ca Binary files /dev/null and b/src/mods/material-ocean/material-ocean.png differ diff --git a/src/mods/material-ocean/mod.json b/src/mods/material-ocean/mod.json new file mode 100644 index 0000000..00a46aa --- /dev/null +++ b/src/mods/material-ocean/mod.json @@ -0,0 +1,23 @@ +{ + "name": "material ocean", + "id": "69e7ccb2-4aef-484c-876d-3de1b433d2b9", + "version": "0.2.0", + "description": "an oceanic colour palette.", + "preview": "material-ocean.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "blacksuan19", + "email": "abubakaryagob@gmail.com", + "homepage": "http://github.com/blacksuan19", + "avatar": "https://avatars.githubusercontent.com/u/10248473" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/material-ocean/variables.css b/src/mods/material-ocean/variables.css new file mode 100644 index 0000000..e8a3579 --- /dev/null +++ b/src/mods/material-ocean/variables.css @@ -0,0 +1,173 @@ +/** + * notion-enhancer: material ocean + * (c) 2020 Abubakar Yagoub (https://blacksuan19.tk) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --ocean--main: #0f111a; + --ocean--sec: #00010a; + --ocean--tet: #000108; + --ocean--accent: #ff4151; + --ocean--light_gray: #E9E9E9; + --ocean--gray: #e0e0e0; + --ocean--brown: #d8b6a6; + --ocean--orange: #fde3c0; + --ocean--yellow: #ebcb8b; + --ocean--green: #a3be8c; + --ocean--blue: #81a1c1; + --ocean--purple: #b48ead; + --ocean--pink: #ffc0cb; + --ocean--red: #bf616a; + + --theme--accent_blue: var(--ocean--accent); + --theme--accent_blue-selection: rgba(255, 65, 81, 0.2); + --theme--accent_blue-hover: #fc3747; + --theme--accent_blue-active: #ff4d5c; + --theme--accent_blue-text: #fff; + --theme--accent_red: var(--ocean--accent); + --theme--accent_red-button: #ff41514b; + --theme--accent_red-text: #fff; + + --theme--bg: var(--ocean--main); + --theme--bg_secondary: var(--ocean--sec); + --theme--bg_card: var(--ocean--sec); + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: var(--ocean--sec); + --theme--scrollbar_thumb-hover: var(--ocean--accent); + + --theme--ui_divider: rgba(255, 255, 255, 0.1); + --theme--ui_interactive-hover: rgb(0, 1, 8, 0.3); + --theme--ui_interactive-active: rgb(0, 1, 8, 0.6); + --theme--ui_toggle-off: #30313c; + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: var(--ocean--gray); + --theme--icon_secondary: var(--ocean--gray); + + --theme--text: #fff; + --theme--text_secondary: var(--ocean--gray); + --theme--text_gray: var(--ocean--gray); + --theme--text_brown: var(--ocean--brown); + --theme--text_orange: var(--ocean--orange); + --theme--text_yellow: var(--ocean--yellow); + --theme--text_green: var(--ocean--green); + --theme--text_blue: var(--ocean--blue); + --theme--text_purple: var(--ocean--purple); + --theme--text_pink: var(--ocean--pink); + --theme--text_red: var(--ocean--red); + + --theme--highlight_gray: var(--ocean--gray); + --theme--highlight_gray-text: var(--ocean--main); + --theme--highlight_brown: var(--ocean--brown); + --theme--highlight_brown-text: var(--ocean--main); + --theme--highlight_orange: var(--ocean--orange); + --theme--highlight_orange-text: var(--ocean--main); + --theme--highlight_yellow: var(--ocean--yellow); + --theme--highlight_yellow-text: var(--ocean--main); + --theme--highlight_green: var(--ocean--green); + --theme--highlight_green-text: var(--ocean--main); + --theme--highlight_blue: var(--ocean--blue); + --theme--highlight_blue-text: var(--ocean--main); + --theme--highlight_purple: var(--ocean--purple); + --theme--highlight_purple-text: var(--ocean--main); + --theme--highlight_pink: var(--ocean--pink); + --theme--highlight_pink-text: var(--ocean--main); + --theme--highlight_red: var(--ocean--red); + --theme--highlight_red-text: var(--ocean--main); + + --theme--callout_gray: #e0e0e089; + --theme--callout_gray-text: var(--ocean--main); + --theme--callout_brown: #d8b6a692; + --theme--callout_brown-text: var(--ocean--main); + --theme--callout_orange: #fde3c09f; + --theme--callout_orange-text: var(--ocean--main); + --theme--callout_yellow: #ffe6a6ad; + --theme--callout_yellow-text: var(--ocean--main); + --theme--callout_green: #a3be8ca3; + --theme--callout_green-text: var(--ocean--main); + --theme--callout_blue: #81a1c1a3; + --theme--callout_blue-text: var(--ocean--main); + --theme--callout_purple: #b48eada8; + --theme--callout_purple-text: var(--ocean--main); + --theme--callout_pink: #ffc0cbb1; + --theme--callout_pink-text: var(--ocean--main); + --theme--callout_red: #bf616a9e; + --theme--callout_red-text: var(--ocean--main); + + --theme--tag_default: var(--ocean--gray); + --theme--tag_default-text: var(--ocean--main); + --theme--tag_light_gray: var(--ocean--light_gray); + --theme--tag_light_gray-text: var(--ocean--main); + --theme--tag_gray: var(--theme--highlight_gray); + --theme--tag_gray-text: var(--ocean--main); + --theme--tag_brown: var(--theme--highlight_brown); + --theme--tag_brown-text: var(--ocean--main); + --theme--tag_orange: var(--theme--highlight_orange); + --theme--tag_orange-text: var(--ocean--main); + --theme--tag_yellow: var(--theme--highlight_yellow); + --theme--tag_yellow-text: var(--ocean--main); + --theme--tag_green: var(--theme--highlight_green); + --theme--tag_green-text: var(--ocean--main); + --theme--tag_blue: var(--theme--highlight_blue); + --theme--tag_blue-text: var(--ocean--main); + --theme--tag_purple: var(--theme--highlight_purple); + --theme--tag_purple-text: var(--ocean--main); + --theme--tag_pink: var(--theme--highlight_pink); + --theme--tag_pink-text: var(--ocean--main); + --theme--tag_red: var(--theme--highlight_red); + --theme--tag_red-text: var(--ocean--main); + + --theme--board_light_gray: var(--ocean--light_gray); + --theme--board_light_gray-text: var(--ocean--main); + --theme--board_light_gray-card: rgba(0, 0, 0, 0.075); + --theme--board_gray: var(--theme--highlight_gray); + --theme--board_gray-text: var(--theme--highlight_gray-text); + --theme--board_gray-card: rgba(0, 0, 0, 0.075); + --theme--board_brown: var(--theme--highlight_brown); + --theme--board_brown-text: var(--theme--highlight_brown-text); + --theme--board_brown-card: rgba(0, 0, 0, 0.075); + --theme--board_orange: var(--theme--highlight_orange); + --theme--board_orange-text: var(--theme--highlight_orange-text); + --theme--board_orange-card: rgba(0, 0, 0, 0.075); + --theme--board_yellow: var(--theme--highlight_yellow); + --theme--board_yellow-text: var(--theme--highlight_yellow-text); + --theme--board_yellow-card: rgba(0, 0, 0, 0.075); + --theme--board_green: var(--theme--highlight_green); + --theme--board_green-text: var(--theme--highlight_green-text); + --theme--board_green-card: rgba(0, 0, 0, 0.075); + --theme--board_blue: var(--theme--highlight_blue); + --theme--board_blue-text: var(--theme--highlight_blue-text); + --theme--board_blue-card: rgba(0, 0, 0, 0.075); + --theme--board_purple: var(--theme--highlight_purple); + --theme--board_purple-text: var(--theme--highlight_purple-text); + --theme--board_purple-card: rgba(0, 0, 0, 0.075); + --theme--board_pink: var(--theme--highlight_pink); + --theme--board_pink-text: var(--theme--highlight_pink-text); + --theme--board_pink-card: rgba(0, 0, 0, 0.075); + --theme--board_red: var(--theme--highlight_red); + --theme--board_red-text: var(--theme--highlight_red-text); + --theme--board_red-card: rgba(0, 0, 0, 0.075); + + --theme--code_inline: var(--ocean--sec); + --theme--code_inline-text: #b3f5c8; + + --theme--code: var(--ocean--sec); + --theme--code_function: var(--theme--text_blue); + --theme--code_keyword: var(--theme--text_pink); + --theme--code_tag: var(--theme--text_pink); + --theme--code_operator: var(--theme--text_yellow); + --theme--code_property: var(--theme--text_pink); + --theme--code_builtin: var(--theme--text_yellow); + --theme--code_attr-name: var(--theme--text_yellow); + --theme--code_comment: var(--theme--text_gray); + --theme--code_punctuation: var(--theme--text_gray); + --theme--code_doctype: var(--theme--text_gray); + --theme--code_number: var(--theme--text_purple); + --theme--code_selector: var(--theme--text_orange); + --theme--code_atrule: var(--theme--text_orange); + --theme--code_regex: var(--theme--text_yellow); +} diff --git a/src/mods/menu/client.css b/src/mods/menu/client.css new file mode 100644 index 0000000..0109a3c --- /dev/null +++ b/src/mods/menu/client.css @@ -0,0 +1,69 @@ +/** + * notion-enhancer: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.enhancer--sidebarMenuLink { + user-select: none; + -webkit-user-select: none; + transition: background 20ms ease-in 0s; + cursor: pointer; + color: var(--theme--text_secondary); +} +.enhancer--sidebarMenuLink:hover { + background: var(--theme--ui_interactive-hover); +} +.enhancer--sidebarMenuLink svg { + width: 16px; + height: 16px; + margin-left: 2px; +} +.enhancer--sidebarMenuLink > div { + display: flex; + align-items: center; + min-height: 27px; + font-size: 14px; + padding: 2px 14px; + width: 100%; +} +.enhancer--sidebarMenuLink > div > :first-child { + flex-shrink: 0; + flex-grow: 0; + border-radius: 3px; + width: 22px; + height: 22px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 8px; +} +.enhancer--sidebarMenuLink > div > :nth-child(2) { + flex: 1 1 auto; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.enhancer--sidebarMenuLink:active { + color: var(--theme--text); +} +.enhancer--sidebarMenuLink > div > .enhancer--notificationBubble { + display: flex; +} +.enhancer--sidebarMenuLink > div > .enhancer--notificationBubble > div { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + font-size: 10px; + font-weight: 600; + border-radius: 3px; + color: var(--theme--accent_red-text); + background: var(--theme--accent_red); +} +.enhancer--sidebarMenuLink > div > .enhancer--notificationBubble > div > span { + margin-bottom: 1px; + margin-left: -0.5px; +} diff --git a/src/mods/menu/client.mjs b/src/mods/menu/client.mjs new file mode 100644 index 0000000..62b06e7 --- /dev/null +++ b/src/mods/menu/client.mjs @@ -0,0 +1,48 @@ +/** + * notion-enhancer: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +const notificationsURL = 'https://notion-enhancer.github.io/notifications.json'; + +export default async function ({ env, fs, storage, registry, web }, db) { + web.addHotkeyListener(await db.get(['hotkey']), env.focusMenu); + + const sidebarSelector = '.notion-sidebar-container .notion-sidebar > div:nth-child(3) > div > div:nth-child(2)'; + await web.whenReady([sidebarSelector]); + + const $sidebarLink = web.html``; + $sidebarLink.addEventListener('click', env.focusMenu); + + const notifications = { + cache: await storage.get(['notifications'], []), + provider: await fs.getJSON(notificationsURL), + count: (await registry.errors()).length, + }; + for (const notification of notifications.provider) { + if ( + !notifications.cache.includes(notification.id) && + notification.version === env.version && + (!notification.environments || notification.environments.includes(env.name)) + ) { + notifications.count++; + } + } + if ((await storage.get(['last_read_changelog'])) !== env.version) notifications.count++; + if (notifications.count) { + web.render( + $sidebarLink.children[0], + web.html`
${notifications.count}
` + ); + } + + web.render(document.querySelector(sidebarSelector), $sidebarLink); +} diff --git a/src/mods/menu/components.mjs b/src/mods/menu/components.mjs new file mode 100644 index 0000000..0e9d876 --- /dev/null +++ b/src/mods/menu/components.mjs @@ -0,0 +1,292 @@ +/** + * notion-enhancer: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +import { fmt, web, registry, components } from '../../api/index.mjs'; +import { notifications } from './notifications.mjs'; +import '../../dep/jscolor.min.js'; + +import '../../dep/markdown-it.min.js'; +const md = markdownit({ linkify: true }); + +export const modComponents = { + preview: (url) => web.html``, + 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`

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

+ ${md.renderInline(description)} +

`; + $description.querySelectorAll('a').forEach((a) => { + a.target = '_blank'; + }); + return $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: (label, checked) => { + const $label = web.html``, + $input = web.html``, + $feature = web.html``; + $label.addEventListener('keyup', (event) => { + if (['Enter', ' '].includes(event.key)) $input.checked = !$input.checked; + }); + return web.render($label, $input, $feature); + }, +}; + +export const options = { + toggle: async (mod, opt) => { + const profileDB = await registry.profileDB(), + checked = await profileDB.get([mod.id, opt.key], opt.value), + $toggle = modComponents.toggle(opt.label, checked), + $tooltipIcon = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, + $label = $toggle.children[0], + $input = $toggle.children[1]; + if (opt.tooltip) { + $label.prepend($tooltipIcon); + components.addTooltip($tooltipIcon, opt.tooltip, { + offsetDirection: 'left', + maxLines: 3, + }); + } + $input.addEventListener('change', async (_event) => { + await profileDB.set([mod.id, opt.key], $input.checked); + notifications.onChange(); + }); + return $toggle; + }, + + select: async (mod, opt) => { + const profileDB = await registry.profileDB(), + value = await profileDB.get([mod.id, opt.key], opt.values[0]), + $tooltipIcon = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, + $label = web.render( + web.html``, + web.render(web.html`

`, opt.tooltip ? $tooltipIcon : '', opt.label) + ), + $options = opt.values.map( + (option) => web.raw`` + ), + $select = web.html``, + $icon = web.html`${await components.feather('chevron-down', { class: 'input-icon' })}`; + if (opt.tooltip) + components.addTooltip($tooltipIcon, opt.tooltip, { + offsetDirection: 'left', + maxLines: 3, + }); + $select.addEventListener('change', async (_event) => { + await profileDB.set([mod.id, opt.key], $select.value); + notifications.onChange(); + }); + return web.render($label, $select, $icon); + }, + + text: async (mod, opt) => { + const profileDB = await registry.profileDB(), + value = await profileDB.get([mod.id, opt.key], opt.value), + $tooltipIcon = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, + $label = web.render( + web.html``, + web.render(web.html`

`, opt.tooltip ? $tooltipIcon : '', opt.label) + ), + $input = web.html``, + $icon = web.html`${await components.feather('type', { class: 'input-icon' })}`; + if (opt.tooltip) + components.addTooltip($tooltipIcon, opt.tooltip, { + offsetDirection: 'left', + maxLines: 3, + }); + $input.addEventListener('change', async (_event) => { + await profileDB.set([mod.id, opt.key], $input.value); + notifications.onChange(); + }); + return web.render($label, $input, $icon); + }, + + number: async (mod, opt) => { + const profileDB = await registry.profileDB(), + value = await profileDB.get([mod.id, opt.key], opt.value), + $tooltipIcon = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, + $label = web.render( + web.html``, + web.render(web.html`

`, opt.tooltip ? $tooltipIcon : '', opt.label) + ), + $input = web.html``, + $icon = web.html`${await components.feather('hash', { class: 'input-icon' })}`; + if (opt.tooltip) + components.addTooltip($tooltipIcon, opt.tooltip, { + offsetDirection: 'left', + maxLines: 3, + }); + $input.addEventListener('change', async (_event) => { + await profileDB.set([mod.id, opt.key], $input.value); + notifications.onChange(); + }); + return web.render($label, $input, $icon); + }, + + color: async (mod, opt) => { + const profileDB = await registry.profileDB(), + value = await profileDB.get([mod.id, opt.key], opt.value), + $tooltipIcon = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, + $label = web.render( + web.html``, + web.render(web.html`

`, opt.tooltip ? $tooltipIcon : '', opt.label) + ), + $input = web.html``, + $icon = web.html`${await components.feather('droplet', { class: 'input-icon' })}`, + paint = () => { + $input.style.background = $picker.toBackground(); + const [r, g, b] = $picker + .toRGBAString() + .slice(5, -1) + .split(',') + .map((i) => parseInt(i)); + $input.style.color = fmt.rgbContrast(r, g, b); + $input.style.padding = ''; + }, + $picker = new JSColor($input, { + value, + format: 'rgba', + previewSize: 0, + borderRadius: 3, + borderColor: 'var(--theme--ui_divider)', + controlBorderColor: 'var(--theme--ui_divider)', + backgroundColor: 'var(--theme--bg)', + onInput: paint, + onChange: paint, + }); + if (opt.tooltip) + components.addTooltip($tooltipIcon, opt.tooltip, { + offsetDirection: 'left', + maxLines: 3, + }); + $input.addEventListener('change', async (_event) => { + await profileDB.set([mod.id, opt.key], $input.value); + notifications.onChange(); + }); + paint(); + return web.render($label, $input, $icon); + }, + + file: async (mod, opt) => { + const profileDB = await registry.profileDB(), + { filename } = (await profileDB.get([mod.id, opt.key], {})) || {}, + $tooltipIcon = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, + $label = web.render( + web.html``, + web.render(web.html`

`, opt.tooltip ? $tooltipIcon : '', opt.label) + ), + $pseudo = web.html`Upload file...`, + $input = web.html``, + $icon = web.html`${await components.feather('file', { class: 'input-icon' })}`, + $filename = web.html`${web.escape(filename || 'none')}`, + $latest = web.render(web.html``, $filename); + if (opt.tooltip) + components.addTooltip($tooltipIcon, opt.tooltip, { + offsetDirection: 'left', + maxLines: 3, + }); + $input.addEventListener('change', (event) => { + const file = event.target.files[0], + reader = new FileReader(); + reader.onload = async (progress) => { + $filename.innerText = file.name; + await profileDB.set([mod.id, opt.key], { + filename: file.name, + content: progress.currentTarget.result, + }); + notifications.onChange(); + }; + reader.readAsText(file); + }); + $latest.addEventListener('click', (_event) => { + $filename.innerText = 'none'; + profileDB.set([mod.id, opt.key], {}); + }); + return web.render( + web.html`
`, + web.render($label, $input, $pseudo, $icon), + $latest + ); + }, + + hotkey: async (mod, opt) => { + const profileDB = await registry.profileDB(), + value = await profileDB.get([mod.id, opt.key], opt.value), + $tooltipIcon = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, + $label = web.render( + web.html``, + web.render(web.html`

`, opt.tooltip ? $tooltipIcon : '', opt.label) + ), + $input = web.html``, + $icon = web.html`${await components.feather('command', { class: 'input-icon' })}`; + if (opt.tooltip) + components.addTooltip($tooltipIcon, opt.tooltip, { + offsetDirection: 'left', + maxLines: 3, + }); + $input.addEventListener('keydown', async (event) => { + event.preventDefault(); + const pressed = [], + modifiers = { + metaKey: 'Meta', + ctrlKey: 'Control', + altKey: 'Alt', + shiftKey: 'Shift', + }; + for (const modifier in modifiers) { + if (event[modifier]) pressed.push(modifiers[modifier]); + } + const empty = ['Backspace', 'Delete'].includes(event.key) && !pressed.length; + if (!empty && !pressed.includes(event.key)) { + let key = event.key; + if (key === ' ') key = 'Space'; + if (key === '+') key = 'Plus'; + if (key.length === 1) key = event.key.toUpperCase(); + pressed.push(key); + } + $input.value = pressed.join('+'); + await profileDB.set([mod.id, opt.key], $input.value); + notifications.onChange(); + }); + return web.render($label, $input, $icon); + }, +}; diff --git a/src/mods/menu/markdown.css b/src/mods/menu/markdown.css new file mode 100644 index 0000000..2a23626 --- /dev/null +++ b/src/mods/menu/markdown.css @@ -0,0 +1,97 @@ +/** + * notion-enhancer: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.markdown table { + border-spacing: 0; + border: 1px solid var(--theme--ui_divider); +} +.markdown table th { + text-align: left; +} +.markdown table th, +.markdown table td { + padding: 5px 8px 6px; + border: 1px solid var(--theme--ui_divider); +} +.markdown h1 { + font-size: 1.875rem; + margin: 1rem 0 0.5rem 0; +} +.markdown h2 { + font-size: 1.5rem; + margin: 1rem 0 0.5rem 0; +} +.markdown h3 { + font-size: 1.25rem; + margin: 1rem 0 0.5rem 0; +} +.markdown h4 { + font-weight: bold; + margin: 0.5rem 0; +} +.markdown ul, +.markdown ol { + padding-left: 1.25rem; +} +.markdown ul { + list-style: disc; +} +.markdown ol { + list-style: decimal; +} +.markdown li { + margin: 1px 0; +} +.markdown ol li { + padding-left: 0.25rem; +} +.markdown blockquote { + border-left: 2px solid currentColor; + padding-left: 0.75rem; + margin: 0.5rem 0; +} +.markdown hr { + border: 0.5px solid var(--theme--ui_divider); +} +.markdown-inline a, +.markdown a { + opacity: 0.7; + text-decoration: none; + border-bottom: 0.05em solid var(--theme--text_secondary); +} +.markdown-inline a:hover, +.markdown a:hover { + opacity: 0.9; +} + +.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); +} +.markdown pre { + padding: 2em 1.25em; + border-radius: 3px; + tab-size: 2; + white-space: pre; + overflow-x: auto; + background: var(--theme--code); + color: var(--theme--code_plain); +} +.markdown pre, +.markdown code, +.markdown-inline code { + font-family: var(--theme--font_code); + font-size: 0.796875rem; + text-align: left; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + hyphens: none; + line-height: 1.5; +} diff --git a/src/mods/menu/menu.css b/src/mods/menu/menu.css new file mode 100644 index 0000000..5a11697 --- /dev/null +++ b/src/mods/menu/menu.css @@ -0,0 +1,25 @@ +/** + * notion-enhancer: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +::selection { + background: var(--theme--accent_blue-selection); +} + +::-webkit-scrollbar { + width: 10px; + height: 10px; + background: transparent; +} +::-webkit-scrollbar-track, +::-webkit-scrollbar-corner { + background: var(--theme--scrollbar_track) !important; +} +::-webkit-scrollbar-thumb { + background: var(--theme--scrollbar_thumb) !important; +} +::-webkit-scrollbar-thumb:hover { + background: var(--theme--scrollbar_thumb-hover) !important; +} diff --git a/src/mods/menu/menu.html b/src/mods/menu/menu.html new file mode 100644 index 0000000..ec6337a --- /dev/null +++ b/src/mods/menu/menu.html @@ -0,0 +1,11 @@ + + + + + + notion-enhancer menu + + + + + diff --git a/src/mods/menu/menu.mjs b/src/mods/menu/menu.mjs new file mode 100644 index 0000000..ab8105b --- /dev/null +++ b/src/mods/menu/menu.mjs @@ -0,0 +1,448 @@ +/** + * notion-enhancer: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +import * as api from '../../api/index.mjs'; +import { notifications, $changelogModal } from './notifications.mjs'; +import { modComponents, options } from './components.mjs'; +import * as router from './router.mjs'; +import './styles.mjs'; + +(async () => { + const { env, fs, storage, electron, registry, web, components } = api; + + for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { + for (let script of mod.js?.menu || []) { + 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) { + console.error('[notion-enhancer] registry errors:'); + console.table(errors); + const $errNotification = await notifications.add({ + icon: 'alert-circle', + message: 'Failed to load mods (check console).', + color: 'red', + }); + if (['win32', 'linux', 'darwin'].includes(env.name)) { + $errNotification.addEventListener('click', () => electron.browser.openDevTools()); + } + } + + const db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'), + profileName = await registry.profileName(), + profileDB = await registry.profileDB(); + + web.addHotkeyListener(await db.get(['hotkey']), env.focusNotion); + + globalThis.addEventListener('beforeunload', (_event) => { + // trigger input save + document.activeElement.blur(); + }); + + const $main = web.html`
`, + $sidebar = web.html``, + $options = web.html`
+

Select a mod to view and configure its options.

+
`, + $profile = web.html``; + + // profile + + let _$profileConfig; + const openProfileMenu = async () => { + if (!_$profileConfig) { + const profileNames = [ + ...new Set([ + ...Object.keys(await storage.get(['profiles'], { default: {} })), + profileName, + ]), + ], + $options = profileNames.map( + (profile) => web.raw`` + ), + $select = web.html``, + $edit = web.html``, + $export = web.html``, + $import = web.html``, + $save = web.html``, + $delete = web.html``, + $error = web.html`

`; + + $export.addEventListener('click', async (_event) => { + const now = new Date(), + $a = web.html``; + web.render(document.body, $a); + $a.click(); + $a.remove(); + }); + + $import.addEventListener('change', (event) => { + const file = event.target.files[0], + reader = new FileReader(); + reader.onload = async (progress) => { + try { + const profileUpload = JSON.parse(progress.currentTarget.result); + if (!profileUpload) throw Error; + await storage.set(['profiles', $select.value], profileUpload); + env.reload(); + } catch { + web.render(web.empty($error), 'Invalid JSON uploaded.'); + } + }; + reader.readAsText(file); + }); + + $select.addEventListener('change', (_event) => { + if ($select.value === '--') { + $edit.value = ''; + } else $edit.value = $select.value; + }); + + $save.addEventListener('click', async (_event) => { + if (profileNames.includes($edit.value) && $select.value !== $edit.value) { + web.render( + web.empty($error), + `The profile "${web.escape($edit.value)}" already exists.` + ); + return false; + } + if (!$edit.value || !$edit.value.match(/^[A-Za-z0-9_-]+$/)) { + web.render( + web.empty($error), + 'Profile names may not be empty & may only contain letters, numbers, hyphens and underscores.' + ); + return false; + } + await storage.set(['currentprofile'], $edit.value); + if ($select.value === '--') { + await storage.set(['profiles', $edit.value], {}); + } else if ($select.value !== $edit.value) { + await storage.set( + ['profiles', $edit.value], + await storage.get(['profiles', $select.value], {}) + ); + await storage.set(['profiles', $select.value], undefined); + } + env.reload(); + }); + + $delete.addEventListener('click', async (_event) => { + await storage.set(['profiles', $select.value], undefined); + await storage.set( + ['currentprofile'], + profileNames.find((profile) => profile !== $select.value) || 'default' + ); + env.reload(); + }); + + _$profileConfig = web.render( + web.html`
`, + web.html`

+ Profiles are used to switch entire configurations.
+ Be careful - deleting a profile deletes all configuration + related to it.
+

`, + web.render( + web.html``, + $select, + web.html`${await components.feather('chevron-down', { class: 'input-icon' })}` + ), + web.render( + web.html``, + $edit, + web.html`${await components.feather('type', { class: 'input-icon' })}` + ), + web.render( + web.html`

`, + $export, + $import, + $save, + $delete + ), + $error + ); + } + web.render(web.empty($options), _$profileConfig); + }; + $profile.addEventListener('click', () => openSidebarMenu('profile')); + + // mods + + const $modLists = {}, + generators = { + options: async (mod) => { + const $fragment = document.createDocumentFragment(); + for (const opt of mod.options) { + if (!opt.environments.includes(env.name)) continue; + web.render($fragment, await options[opt.type](mod, opt)); + } + if (!mod.options.length) { + web.render($fragment, web.html`

No options.

`); + } + return $fragment; + }, + mod: async (mod) => { + const $mod = web.html`
`, + $toggle = modComponents.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', + id = mod.id, + mods = await registry.list( + async (mod) => + (await registry.enabled(mod.id)) && + mod.tags.includes('theme') && + mod.tags.includes(mode) && + mod.id !== id + ); + for (const mod of mods) { + profileDB.set(['_mods', mod.id], false); + document.querySelector( + `[data-id="${web.escape(mod.id)}"] .toggle-check` + ).checked = false; + } + } + profileDB.set(['_mods', mod.id], event.target.checked); + notifications.onChange(); + }); + $mod.addEventListener('click', () => openSidebarMenu(mod.id)); + return web.render( + web.html`
`, + web.render( + $mod, + mod.preview + ? modComponents.preview( + mod.preview.startsWith('http') + ? mod.preview + : fs.localPath(`repo/${mod._dir}/${mod.preview}`) + ) + : '', + web.render( + web.html`
`, + web.render(modComponents.title(mod.name), modComponents.version(mod.version)), + modComponents.tags(mod.tags), + modComponents.description(mod.description), + modComponents.authors(mod.authors), + mod.environments.includes(env.name) && !registry.core.includes(mod.id) + ? $toggle + : '' + ) + ) + ); + }, + modList: async (category, message = '') => { + if (!$modLists[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(); + for (const $mod of $list.children) { + const matches = !query || $mod.innerText.toLowerCase().includes(query); + $mod.classList[matches ? 'remove' : 'add']('hidden'); + } + }); + for (const mod of mods) { + mod.tags = mod.tags.filter((tag) => tag !== category); + web.render($list, await generators.mod(mod)); + mod.tags.unshift(category); + } + $modLists[category] = web.render( + web.html`
`, + web.render( + web.html``, + $search, + web.html`${await components.feather('search', { class: 'input-icon' })}` + ), + message ? web.render(web.html`

`, message) : '', + $list + ); + } + return $modLists[category]; + }, + }; + + async function openModMenu(id) { + let $mod; + for (const $list of Object.values($modLists)) { + $mod = $list.querySelector(`[data-id="${web.escape(id)}"]`); + if ($mod) break; + } + const mod = await registry.get(id); + if (!$mod || !mod || $mod.className === 'mod-selected') return; + + $mod.className = 'mod-selected'; + const fragment = [ + web.render(modComponents.title(mod.name), modComponents.version(mod.version)), + modComponents.tags(mod.tags), + await generators.options(mod), + ]; + web.render(web.empty($options), ...fragment); + } + + // views + + const $notionNavItem = web.html`

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

`; + $notionNavItem.addEventListener('click', env.focusNotion); + + const $coreNavItem = web.html`core`, + $extensionsNavItem = web.html`extensions`, + $themesNavItem = web.html`themes`, + $integrationsNavItem = web.html`integrations`, + $changelogNavItem = web.html``; + components.addTooltip($changelogNavItem, '**Update changelog & welcome message**'); + $changelogNavItem.addEventListener('click', () => { + $changelogModal.scrollTop = 0; + $changelogModal.classList.add('modal-visible'); + }); + + web.render( + document.body, + web.render( + web.html`
`, + web.render( + web.html`
`, + web.render( + web.html``, + $notionNavItem, + $coreNavItem, + $extensionsNavItem, + $themesNavItem, + $integrationsNavItem, + web.html`docs`, + web.html`community`, + $changelogNavItem + ), + $main + ), + web.render($sidebar, $profile, $options) + ) + ); + + function selectNavItem($item) { + for (const $selected of document.querySelectorAll('.nav-item-selected')) { + $selected.className = 'nav-item'; + } + $item.className = 'nav-item-selected'; + } + + await generators.modList( + 'core', + `Core mods provide the basics required for + all other extensions and themes to work. They + can't be disabled, but they can be configured + - just click on a mod to access its options.` + ); + router.addView('core', async () => { + web.empty($main); + selectNavItem($coreNavItem); + return web.render($main, await generators.modList('core')); + }); + + await generators.modList( + 'extension', + `Extensions build on the functionality and layout of + the Notion client, modifying and interacting with + existing interfaces.` + ); + router.addView('extensions', async () => { + web.empty($main); + selectNavItem($extensionsNavItem); + return web.render($main, await generators.modList('extension')); + }); + + await generators.modList( + 'theme', + `Themes change Notion's colour scheme. + Dark themes will only work when Notion is in dark mode, + and light themes will only work when Notion is in light mode. + Only one theme of each mode can be enabled at a time.` + ); + router.addView('themes', async () => { + web.empty($main); + selectNavItem($themesNavItem); + return web.render($main, await generators.modList('theme')); + }); + + await generators.modList( + 'integration', + web.html`Integrations are extensions that use an unofficial API + to access and modify content. They are used just like + normal extensions, but may be more dangerous to use.` + ); + router.addView('integrations', async () => { + web.empty($main); + selectNavItem($integrationsNavItem); + return web.render($main, await generators.modList('integration')); + }); + + router.setDefaultView('extensions'); + + router.addQueryListener('id', openSidebarMenu); + function openSidebarMenu(id) { + if (!id) return; + id = web.escape(id); + + const deselectedMods = `.mod-selected:not([data-id="${id}"])`; + for (const $list of Object.values($modLists)) { + for (const $selected of $list.querySelectorAll(deselectedMods)) { + $selected.className = 'mod'; + } + } + router.updateQuery(`?id=${id}`); + + if (id === 'profile') { + openProfileMenu(); + } else openModMenu(id); + } +})(); diff --git a/src/mods/menu/mod.json b/src/mods/menu/mod.json new file mode 100644 index 0000000..7689da9 --- /dev/null +++ b/src/mods/menu/mod.json @@ -0,0 +1,31 @@ +{ + "name": "menu", + "id": "a6621988-551d-495a-97d8-3c568bca2e9e", + "version": "0.11.0", + "description": "the enhancer's graphical menu, related buttons and shortcuts.", + "tags": ["core"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": { + "client": ["client.css"], + "menu": ["menu.css", "markdown.css"] + }, + "js": { + "client": ["client.mjs"] + }, + "options": [ + { + "type": "hotkey", + "key": "hotkey", + "label": "toggle focus hotkey", + "tooltip": "**switches between notion & the enhancer menu**", + "value": "Ctrl+Alt+E" + } + ] +} diff --git a/src/mods/menu/notifications.mjs b/src/mods/menu/notifications.mjs new file mode 100644 index 0000000..faf884b --- /dev/null +++ b/src/mods/menu/notifications.mjs @@ -0,0 +1,146 @@ +/** + * notion-enhancer: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +import { env, fs, storage, web, components } from '../../api/index.mjs'; +import { tw } from './styles.mjs'; + +import '../../dep/markdown-it.min.js'; +const md = markdownit({ linkify: true }); + +const notificationsURL = 'https://notion-enhancer.github.io/notifications.json'; +export const notifications = { + $container: web.html`
`, + async add({ icon, message, id = undefined, color = undefined, link = undefined }) { + const $notification = link + ? web.html`` + : web.html``, + resolve = async () => { + if (id !== undefined) { + notifications.cache.push(id); + await storage.set(['notifications'], notifications.cache); + } + $notification.remove(); + }; + $notification.addEventListener('click', resolve); + $notification.addEventListener('keyup', (event) => { + if (['Enter', ' '].includes(event.key)) resolve(); + }); + web.render( + notifications.$container, + web.render( + $notification, + web.html` + ${md.renderInline(message)} + `, + web.html`${await components.feather(icon, { class: 'notification-icon' })}` + ) + ); + return $notification; + }, + _onChange: false, + async onChange() { + if (this._onChange) return; + this._onChange = true; + const $notification = await this.add({ + icon: 'refresh-cw', + message: 'Reload to apply changes.', + }); + $notification.addEventListener('click', env.reload); + }, +}; + +(async () => { + notifications.cache = await storage.get(['notifications'], []); + notifications.provider = await fs.getJSON(notificationsURL); + + web.render(document.body, notifications.$container); + for (const notification of notifications.provider) { + const cached = notifications.cache.includes(notification.id), + versionMatches = notification.version === env.version, + envMatches = !notification.environments || notification.environments.includes(env.name); + if (!cached && versionMatches && envMatches) notifications.add(notification); + } +})(); + +export const $changelogModal = web.render( + web.html`` +); + +(async () => { + const $changelogModalButton = web.html``; + $changelogModalButton.addEventListener('click', async () => { + $changelogModal.classList.remove('modal-visible'); + await storage.set(['last_read_changelog'], env.version); + }); + + web.render( + $changelogModal, + web.render( + web.html``, + web.html``, + web.render(web.html``, $changelogModalButton) + ) + ); + + const lastReadChangelog = await storage.get(['last_read_changelog']); + web.render(document.body, $changelogModal); + if (lastReadChangelog !== env.version) { + $changelogModal.classList.add('modal-visible'); + } +})(); diff --git a/src/mods/menu/router.mjs b/src/mods/menu/router.mjs new file mode 100644 index 0000000..2eb88cc --- /dev/null +++ b/src/mods/menu/router.mjs @@ -0,0 +1,88 @@ +/** + * notion-enhancer: menu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +import { web } from '../../api/index.mjs'; + +const _queryListeners = new Set(); + +export function addView(name, loadFunc) { + const handlerFunc = (newView) => { + if (newView === name) return loadFunc(); + return false; + }; + _queryListeners.add({ param: 'view', viewName: name, handlerFunc }); + handlerFunc(web.queryParams().get('view'), null); +} +export function removeView(name) { + const view = [..._queryListeners].find((view) => view.viewName === name); + if (view) _queryListeners.delete(view); +} +export async function setDefaultView(viewName) { + const viewList = [..._queryListeners].filter((q) => q.viewName).map((v) => v.viewName); + if (!viewList.includes(web.queryParams().get('view'))) { + updateQuery(`?view=${viewName}`, true); + } +} + +export function addQueryListener(param, handlerFunc) { + _queryListeners.add({ param: param, handlerFunc }); + handlerFunc(web.queryParams().get(param), null); +} +export function removeQueryListener(handlerFunc) { + const listener = [..._queryListeners].find((view) => view.handlerFunc === handlerFunc); + if (listener) _queryListeners.delete(listener); +} + +export const updateQuery = (search, replace = false) => { + let query = web.queryParams(); + for (const [key, val] of new URLSearchParams(search)) { + query.set(key, val); + } + query = `?${query.toString()}`; + if (location.search !== query) { + if (replace) { + window.history.replaceState(null, null, query); + } else { + window.history.pushState(null, null, query); + } + triggerQueryListeners(); + } +}; + +function router(event) { + event.preventDefault(); + const anchor = event.path + ? event.path.find((anchor) => anchor.nodeName === 'A') + : event.target; + updateQuery(anchor.getAttribute('href')); +} + +let queryCache = ''; +async function triggerQueryListeners() { + if (location.search === queryCache) return; + const newQuery = web.queryParams(), + oldQuery = new URLSearchParams(queryCache); + queryCache = location.search; + for (const listener of _queryListeners) { + const newParam = newQuery.get(listener.param), + oldParam = oldQuery.get(listener.param); + if (newParam !== oldParam) listener.handlerFunc(newParam, oldParam); + } +} + +window.addEventListener('popstate', triggerQueryListeners); + +web.addDocumentObserver( + (mutation) => { + mutation.target.querySelectorAll('a[href^="?"]').forEach((a) => { + a.removeEventListener('click', router); + a.addEventListener('click', router); + }); + }, + ['a[href^="?"]'] +); diff --git a/src/mods/menu/styles.mjs b/src/mods/menu/styles.mjs new file mode 100644 index 0000000..0ce7925 --- /dev/null +++ b/src/mods/menu/styles.mjs @@ -0,0 +1,182 @@ +/** + * notion-enhancer: 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('""'), + mapColorVariables = (color) => ({ + 'text': `var(--theme--text_${color})`, + 'highlight': `var(--theme--highlight_${color})`, + 'highlight-text': `var(--theme--highlight_${color}-text)`, + 'callout': `var(--theme--callout_${color})`, + 'callout-text': `var(--theme--callout_${color}-text)`, + 'tag': `var(--theme--tag_${color})`, + 'tag-text': `var(--theme--tag_${color}-text)`, + 'board': `var(--theme--board_${color})`, + 'board-text': `var(--theme--board_${color}-text)`, + 'board-card': `var(--theme--board_${color}-card)`, + 'board-card_text': `var(--theme--board_${color}-card_text)`, + }); + +const customClasses = { + 'notifications-container': apply`absolute bottom-0 right-0 px-4 py-3 max-w-full w-96 z-10`, + 'notification': ([color = 'default']) => + apply`p-2 border group hover:(filter brightness-125) ${ + color === 'default' + ? 'bg-tag text-tag-text border-divider' + : `bg-${color}-tag text-${color}-tag-text border-${color}-text` + } flex items-center rounded-full mt-3 shadow-md cursor-pointer`, + 'notification-text': apply`text-xs mx-2 flex-auto font-semibold group-hover:(filter brightness-75)`, + 'notification-icon': apply`fill-current opacity-75 h-4 w-4 mx-2`, + 'body-container': apply`flex w-full h-full overflow-hidden`, + 'sidebar': apply`h-full w-96 max-w-3/7 flex-shrink-0 px-4 pt-3 pb-16 overflow-y-auto flex flex-col + bg-notion-secondary border-l border-divider`, + 'content-container': apply`h-full w-full flex flex-col`, + 'nav': apply`px-4 mx-2.5 py-1 flex flex-wrap items-center border-b border-divider + justify-center xl:justify-start`, + 'nav-notion': apply`flex items-center font-semibold text-xl cursor-pointer select-none my-4 + w-full justify-center xl:(mr-4 w-auto justify-start)`, + 'nav-notion-icon': apply`h-6 w-6 mr-2.5`, + 'nav-item': apply`mr-2 px-3 py-2 rounded-md text-sm font-medium + hover:bg-interactive-hover focus:bg-interactive-active mb-2 xl:(mt-1 mb-0)`, + 'nav-item-selected': apply`nav-item ring-1 ring-divider bg-notion-secondary + hover:bg-notion-secondary focus:bg-notion-secondary`, + 'nav-changelog': apply`xl:ml-auto focus:outline-none`, + 'nav-changelog-icon': apply`w-4 h-4`, + 'main': apply`transition px-4 py-3 overflow-y-auto flex-grow`, + 'main-message': apply`mx-2.5 my-2.5 px-px text-sm text-foreground-secondary text-justify`, + '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-divider cursor-pointer`, + 'mod-selected': apply`mod ring ring-accent-blue-active`, + 'mod-body': apply`px-4 py-3 flex flex-col flex-auto children:cursor-pointer`, + '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-secondary 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`, + 'profile-trigger': apply`block px-4 py-3 mb-2 rounded-md text-sm text-left font-semibold shadow-inner + hover:bg-accent-red-button border border-accent-red text-accent-red focus:(outline-none bg-accent-red-button)`, + 'profile-actions': apply`flex`, + 'profile-save': apply`text-sm px-3 py-2 font-medium mt-2 bg-accent-blue text-accent-blue-text rounded-md flex-grow + hover:bg-accent-blue-hover focus:(bg-accent-blue-active outline-none) text-center`, + 'profile-delete': apply`profile-trigger px-3 py-2 mb-0 ml-2 mt-2 text-center font-medium`, + 'profile-export': apply`profile-save mr-2`, + 'profile-import': apply`profile-save mr-2`, + 'profile-error': apply`text-xs mt-2 text-red-text`, + 'profile-icon-action': apply`w-4 h-4 -mt-1 inline-block`, + 'profile-icon-text': apply`w-4 h-4 -mt-1 inline-block mr-1`, + 'options-container': apply`px-4 py-3 shadow-inner rounded-lg bg-notion border border-divider space-y-3`, + 'options-placeholder': apply`text-sm text-foreground-secondary`, + 'toggle-box': apply`w-9 h-5 p-0.5 flex items-center bg-toggle-off rounded-full duration-300 ease-in-out cursor-pointer`, + 'toggle-label': apply`relative text-sm flex w-full mt-auto`, + 'toggle-check': 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) cursor-pointer`, + 'input-label': apply`block text-sm mt-2 relative`, + 'input': apply`transition block w-full mt-2 pl-3 pr-14 py-2 text-sm rounded-md flex bg-input text-foreground + appearance-none placeholder-foreground-secondary ring-1 ring-divider focus:(outline-none ring ring-accent-blue-active)`, + 'input-tooltip': apply`h-4 w-4 -mt-1 inline-block mr-2`, + 'input-icon': apply`absolute w-11 h-9 right-0 bottom-0 py-2 px-3 bg-notion-secondary rounded-r-md text-icon`, + 'input-placeholder': apply`text-foreground-secondary`, + 'select-option': apply`bg-notion-secondary`, + 'file-latest': apply`block w-full text-left text-foreground-secondary text-xs mt-2 hover:line-through cursor-pointer`, + 'search-container': apply`block mx-2.5 my-2.5 relative`, + 'search': apply`input pr-12`, + 'important-link': apply`text-accent-red border-b border-accent-red opacity-80 hover:opacity-100`, + 'danger': apply`text-red-text`, + 'link': apply`no-underline border-b border-foreground-secondary opacity-70 hover:opacity-90`, + 'modal': apply`fixed flex z-10 inset-0 overflow-y-auto min-h-screen text-center + ease-out duration-300 transition-opacity opacity-0 pointer-events-none`, + 'modal-visible': { + '@apply': apply`ease-in duration-200 opacity-100 pointer-events-auto`, + '& .modal-box': apply`ease-out duration-300 opacity-100 scale-100`, + }, + 'modal-overlay': apply`fixed inset-0 bg-black bg-opacity-50 transition-opacity`, + 'modal-box': apply`inline-block rounded-lg text-left overflow-hidden shadow-xl + transform transition-all m-auto align-middle + ease-in duration-200 opacity-0 scale-95`, + 'modal-body': apply`bg-notion-secondary p-6 pt-4 max-w-xl w-full`, + 'modal-actions': apply`bg-notion py-3 px-6 flex flex-row-reverse`, + 'modal-title': apply`flex`, + 'modal-title-icon': apply`w-20 mr-6`, + 'modal-title-heading': apply`text-xl leading-6 font-medium`, + 'modal-title-description': apply`mt-2 text-sm text-foreground-secondary`, + 'modal-content': { + '@apply': apply`mt-4 text-sm`, + 'p': apply`mt-2`, + }, + 'modal-button': apply`w-full inline-flex justify-center rounded-md text-base font-medium shadow-sm px-4 py-2 + not-focus:hover:bg-interactive-hover focus:bg-interactive-active focus:outline-none`, +}; + +setup({ + preflight: { + html: apply`w-full h-full`, + body: apply`w-full h-full bg-notion font-sans text-foreground`, + }, + theme: { + fontFamily: { + sans: ['var(--theme--font_sans)'], + mono: ['var(--theme--font_code)'], + }, + colors: { + 'black': 'rgba(0,0,0,var(--tw-bg-opacity));', + 'notion': 'var(--theme--bg)', + 'notion-secondary': 'var(--theme--bg_secondary)', + 'notion-card': 'var(--theme--bg_card)', + 'divider': 'var(--theme--ui_divider)', + 'input': 'var(--theme--ui_input)', + 'icon': 'var(--theme--icon)', + 'icon-secondary': 'var(--theme--icon_secondary)', + 'foreground': 'var(--theme--text)', + 'foreground-secondary': 'var(--theme--text_secondary)', + 'interactive-hover': 'var(--theme--ui_interactive-hover)', + 'interactive-active': 'var(--theme--ui_interactive-active)', + '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-button': 'var(--theme--accent_red-button)', + 'red-text': 'var(--theme--accent_red-text)', + }, + 'gray': mapColorVariables('gray'), + '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: { + maxWidth: { '3/7': 'calc((3 / 7) * 100%);' }, + }, + }, + plugins: customClasses, +}); + +tw`hidden ${Object.keys(customClasses).join(' ')}`; + +export { tw }; diff --git a/src/mods/neutral/app.css b/src/mods/neutral/app.css new file mode 100644 index 0000000..7a3c3b5 --- /dev/null +++ b/src/mods/neutral/app.css @@ -0,0 +1,48 @@ +/** + * notion-enhancer: neutral + * (c) 2020 Arecsu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-dark-theme [placeholder='Heading 1'], +.notion-dark-theme [placeholder='Heading 2'], +.notion-dark-theme [placeholder='Heading 3'] { + padding: 3px 1px !important; +} + +/* hide sidebar "new page" button */ +.notion-dark-theme .notion-sidebar > [style*='flex: 0 0 auto; margin-top: auto;'] { + display: none !important; +} + +/* 1.3 supreme ratio. https://www.modularscale.com/ */ +.notion-collection_view_page-block > div[placeholder='Untitled'], +.notion-frame .notion-page-block > div[placeholder='Untitled'], +.notion-overlay-container .notion-page-block > div[placeholder='Untitled'] { + font-size: 33px !important; +} +[placeholder='Heading 1'] { + font-size: 2.2rem !important; +} +[placeholder='Heading 2'] { + font-size: 1.687rem !important; +} +[placeholder='Heading 3'] { + font-size: 1.3rem !important; +} +.notion-frame .notion-scroller.vertical.horizontal .notion-page-content, +.notion-overlay-container .notion-scroller.vertical .notion-page-content { + font-size: 15px !important; +} +.notion-frame + .notion-scroller.vertical.horizontal + .notion-page-content[style*='font-size: 14px'], +.notion-overlay-container + .notion-scroller.vertical + .notion-page-content[style*='font-size: 14px'] { + font-size: 13.5px !important; +} +.notion-code-block [placeholder=' '] { + font-size: 0.9em !important; +} diff --git a/src/mods/neutral/mod.json b/src/mods/neutral/mod.json new file mode 100644 index 0000000..dc17747 --- /dev/null +++ b/src/mods/neutral/mod.json @@ -0,0 +1,23 @@ +{ + "name": "neutral", + "id": "c4435543-4705-4d68-8cf7-d11c342f8089", + "version": "0.2.0", + "description": "smoother colours and text sizing, designed to be more pleasing to the eye.", + "preview": "neutral.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "arecsu", + "email": "alejandro9r@gmail.com", + "homepage": "https://github.com/Arecsu", + "avatar": "https://avatars.githubusercontent.com/u/12679098" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css", "app.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/neutral/neutral.png b/src/mods/neutral/neutral.png new file mode 100644 index 0000000..7e68fb7 Binary files /dev/null and b/src/mods/neutral/neutral.png differ diff --git a/src/mods/neutral/variables.css b/src/mods/neutral/variables.css new file mode 100644 index 0000000..0e7b76f --- /dev/null +++ b/src/mods/neutral/variables.css @@ -0,0 +1,141 @@ +/** + * notion-enhancer: neutral + * (c) 2020 Arecsu + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --theme--accent_blue: #404040; + --theme--accent_blue-selection: #52525244; + --theme--accent_blue-hover: #6d6d6d; + --theme--accent_blue-active: #cacaca; + --theme--accent_red: #ce535f; + --theme--accent_red-button: #8c3d3dad; + + --theme--bg: #131313; + --theme--bg_secondary: #161616; + --theme--bg_card: #181818; + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: #232425; + --theme--scrollbar_thumb-hover: #373838; + + --theme--ui_shadow: rgba(15, 15, 15, 0.5); + --theme--ui_divider: rgba(78, 78, 78, 0.7); + --theme--ui_interactive-hover: rgb(29, 29, 29); + --theme--ui_interactive-active: rgba(29, 29, 29, 0.7); + --theme--ui_input: #1d1d1d; + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: #dadada; + --theme--icon_secondary: #dadadac0; + + --theme--text: #dadada; + --theme--text_secondary: #dadadac0; + --theme--text_gray: #858585; + --theme--text_brown: #484848; + --theme--text_orange: #ec9873; + --theme--text_yellow: #e2c06f; + --theme--text_green: #92b178; + --theme--text_blue: #719cca; + --theme--text_purple: #ab82bb; + --theme--text_pink: #d285aa; + --theme--text_red: #ce535f; + + --theme--highlight_gray: #585858; + --theme--highlight_brown: #333333; + --theme--highlight_orange: #9a5a3f; + --theme--highlight_yellow: #b58a46; + --theme--highlight_green: #657953; + --theme--highlight_blue: #355475; + --theme--highlight_purple: #775186; + --theme--highlight_pink: #8e4b63; + --theme--highlight_red: #8c3d3d; + + --theme--callout_gray: #585858; + --theme--callout_brown: #333333; + --theme--callout_orange: #9a5a3f; + --theme--callout_yellow: #b58a46; + --theme--callout_green: #657953; + --theme--callout_blue: #355475; + --theme--callout_purple: #775186; + --theme--callout_pink: #8e4b63; + --theme--callout_red: #8c3d3d; + + --theme--tag-text: var(--theme--text); + --theme--tag_default: var(--theme--highlight_gray); + --theme--tag_light_gray: #8A8A8A; + --theme--tag_gray: var(--theme--highlight_gray); + --theme--tag_brown: var(--theme--highlight_brown); + --theme--tag_orange: var(--theme--highlight_orange); + --theme--tag_yellow: var(--theme--highlight_yellow); + --theme--tag_green: var(--theme--highlight_green); + --theme--tag_blue: var(--theme--highlight_blue); + --theme--tag_purple: var(--theme--highlight_purple); + --theme--tag_pink: var(--theme--highlight_pink); + --theme--tag_red: var(--theme--highlight_red); + + --theme--board_light_gray: rgba(112, 112, 112, 0.175); + --theme--board_light_gray-text: #AAAAAA; + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_light_gray-card_text: var(--theme--tag_light_gray-text); + --theme--board_gray: rgba(88, 88, 88, 0.175); + --theme--board_gray-text: var(--theme--text_gray); + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_gray-card_text: var(--theme--tag_gray-text); + --theme--board_brown: rgb(51, 51, 51, 0.175); + --theme--board_brown-text: var(--theme--text_brown); + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_brown-card_text: var(--theme--tag_brown-text); + --theme--board_orange: rgb(154, 90, 63, 0.175); + --theme--board_orange-text: var(--theme--text_orange); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_orange-card_text: var(--theme--tag_orange-text); + --theme--board_yellow: rgb(181, 138, 70, 0.175); + --theme--board_yellow-text: var(--theme--text_yellow); + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_yellow-card_text: var(--theme--tag_yellow-text); + --theme--board_green: rgb(101, 121, 83, 0.175); + --theme--board_green-text: var(--theme--text_green); + --theme--board_green-card: var(--theme--tag_green); + --theme--board_green-card_text: var(--theme--tag_green-text); + --theme--board_blue: rgb(53, 84, 117, 0.175); + --theme--board_blue-text: var(--theme--text_blue); + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_blue-card_text: var(--theme--tag_blue-text); + --theme--board_purple: rgb(119, 81, 134, 0.175); + --theme--board_purple-text: var(--theme--text_purple); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_purple-card_text: var(--theme--tag_purple-text); + --theme--board_pink: rgb(142, 75, 99, 0.175); + --theme--board_pink-text: var(--theme--text_pink); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_pink-card_text: var(--theme--tag_pink-text); + --theme--board_red: rgb(140, 61, 61, 0.175); + --theme--board_red-text: var(--theme--text_red); + --theme--board_red-card: var(--theme--tag_red); + --theme--board_red-card_text: var(--theme--tag_red-text); + + --theme--code_inline: #333333; + --theme--code_inline-text: var(--theme--text); + + --theme--code: #0e0e0e; + --theme--code_plain: var(--theme--text); + --theme--code_function: var(--theme--text_blue); + --theme--code_keyword: var(--theme--text_pink); + --theme--code_tag: var(--theme--text_pink); + --theme--code_operator: var(--theme--text_yellow); + --theme--code_important: var(--theme--text_yellow); + --theme--code_property: var(--theme--text_pink); + --theme--code_builtin: var(--theme--text_yellow); + --theme--code_attr-name: var(--theme--text_yellow); + --theme--code_comment: var(--theme--text_gray); + --theme--code_punctuation: var(--theme--text_gray); + --theme--code_doctype: var(--theme--text_gray); + --theme--code_number: var(--theme--text_purple); + --theme--code_string: var(--theme--text_orange); + --theme--code_attr-value: var(--theme--text_orange); +} diff --git a/src/mods/nord/mod.json b/src/mods/nord/mod.json new file mode 100644 index 0000000..67f266f --- /dev/null +++ b/src/mods/nord/mod.json @@ -0,0 +1,28 @@ +{ + "name": "nord", + "id": "d64ad391-1494-4112-80ae-0a3b6f4b0c3f", + "version": "0.2.0", + "description": "an arctic, north-bluish color palette.", + "preview": "nord.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "Artic Ice Studio", + "email": "development@arcticicestudio.com", + "homepage": "https://www.nordtheme.com/", + "avatar": "https://avatars.githubusercontent.com/u/7836623" + }, + { + "name": "MANNNNEN", + "homepage": "https://github.com/MANNNNEN", + "avatar": "https://avatars.githubusercontent.com/u/35939149" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/nord/nord.png b/src/mods/nord/nord.png new file mode 100644 index 0000000..008a6ca Binary files /dev/null and b/src/mods/nord/nord.png differ diff --git a/src/mods/nord/variables.css b/src/mods/nord/variables.css new file mode 100644 index 0000000..7aaa8da --- /dev/null +++ b/src/mods/nord/variables.css @@ -0,0 +1,160 @@ +/** + * notion-enhancer: dracula + * (c) 2016 Artic Ice Studio + * (c) 2020 MANNNNEN + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --nord--dark1: #2e3440; + --nord--dark2: #3b4252; + --nord--dark3: #434c5e; + --nord--dark4: #4c566a; + --nord--light1: #d8dee9; + --nord--light2: #e5e9f0; + --nord--light3: #eceff4; + --nord--frost1: #8fbcbb; + --nord--frost2: #88c0d0; + --nord--frost3: #81a1c1; + --nord--frost4: #5e81ac; + --nord--red: #bf616a; + --nord--orange: #d08770; + --nord--yellow: #ebcb8b; + --nord--green: #a3be8c; + --nord--purple: #b48ead; + + --theme--accent_blue: var(--nord--frost2); + --theme--accent_blue-selection: rgb(136 192 208 / 50%); + --theme--accent_blue-hover: var(--nord--frost1); + --theme--accent_blue-active: var(--nord--frost2); + --theme--accent_red: var(--nord--red); + --theme--accent_red-button: rgba(235, 87, 87, 0.2); + + --theme--bg: var(--nord--dark1); + --theme--bg_secondary: var(--nord--dark2); + --theme--bg_card: var(--nord--dark2); + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: var(--nord--dark3); + --theme--scrollbar_thumb-hover: var(--nord--dark4); + + --theme--ui_divider: rgba(255, 255, 255, 0.1); + --theme--ui_interactive-hover: var(--nord--dark4); + --theme--ui_interactive-active: #4c566ade; + --theme--ui_toggle-off: var(--nord--dark4); + --theme--ui_toggle-feature: var(--nord--light1); + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: var(--nord--light1); + --theme--icon_secondary: var(--nord--light1); + + --theme--text: var(--nord--light1); + --theme--text_secondary: var(--nord--light1); + + --theme--text_gray: rgba(151, 154, 155, 0.95); + --theme--text_brown: rgb(147, 114, 100); + --theme--text_orange: var(--nord--orange); + --theme--text_yellow: var(--nord--yellow); + --theme--text_green: var(--nord--green); + --theme--text_blue: var(--nord--frost3); + --theme--text_purple: var(--nord--purple); + --theme--text_pink: rgb(193 106 153); + --theme--text_red: var(--nord--red); + + --theme--highlight_gray: rgb(69, 75, 78); + --theme--highlight_brown: rgb(67, 64, 64); + --theme--highlight_orange: var(--nord--orange); + --theme--highlight_yellow: var(--nord--yellow); + --theme--highlight_yellow-text: var(--nord--dark1); + --theme--highlight_green: var(--nord--green); + --theme--highlight_blue: var(--nord--frost3); + --theme--highlight_purple: var(--nord--purple); + --theme--highlight_pink: rgb(193 106 153); + --theme--highlight_red: var(--nord--red); + + --theme--callout_gray: rgb(69, 75, 78); + --theme--callout_brown: rgb(67, 64, 64); + --theme--callout_orange: var(--nord--orange); + --theme--callout_yellow: var(--nord--yellow); + --theme--callout_yellow-text: var(--nord--dark1); + --theme--callout_green: var(--nord--green); + --theme--callout_blue: var(--nord--frost3); + --theme--callout_purple: var(--nord--purple); + --theme--callout_pink: rgb(193 106 153); + --theme--callout_red: var(--nord--red); + + --theme--tag_default: rgba(151, 154, 155, 0.5); + --theme--tag_light_gray: rgba(182, 184, 185, 0.5); + --theme--tag_gray: rgba(151, 154, 155, 0.5); + --theme--tag_brown: rgba(147, 114, 100, 0.5); + --theme--tag_orange: rgba(255, 163, 68, 0.5); + --theme--tag_yellow: rgba(255, 220, 73, 0.5); + --theme--tag_green: rgba(77, 171, 154, 0.5); + --theme--tag_blue: rgba(82, 156, 202, 0.5); + --theme--tag_purple: rgba(154, 109, 215, 0.5); + --theme--tag_pink: rgba(226, 85, 161, 0.5); + --theme--tag_red: rgba(255, 115, 105, 0.5); + + --theme--board_light_gray: rgba(69, 75, 78, 0.3); + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_light_gray-card_text: var(--theme--tag_light_gray-text); + --theme--board_light_gray-text: rgba(161, 162, 163, 0.95); + --theme--board_gray: rgba(69, 75, 78, 0.3); + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_gray-card_text: var(--theme--tag_gray-text); + --theme--board_gray-text: var(--theme--text_gray); + --theme--board_brown: rgba(67, 64, 64, 0.3); + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_brown-card_text: var(--theme--tag_brown-text); + --theme--board_brown-text: var(--theme--text_brown); + --theme--board_orange: rgba(89, 74, 58, 0.3); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_orange-card_text: var(--theme--tag_orange-text); + --theme--board_orange-text: var(--theme--text_orange); + --theme--board_yellow: rgba(89, 86, 59, 0.3); + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_yellow-card_text: var(--theme--tag_yellow-text); + --theme--board_yellow-text: var(--theme--text_yellow); + --theme--board_green: rgba(53, 76, 75, 0.3); + --theme--board_green-card: var(--theme--tag_green); + --theme--board_green-card_text: var(--theme--tag_green-text); + --theme--board_green-text: var(--theme--text_green); + --theme--board_blue: rgba(54, 73, 84, 0.3); + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_blue-card_text: var(--theme--tag_blue-text); + --theme--board_blue-text: var(--theme--text_blue); + --theme--board_purple: rgba(68, 63, 87, 0.3); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_purple-card_text: var(--theme--tag_purple-text); + --theme--board_purple-text: var(--theme--text_purple); + --theme--board_pink: rgba(83, 59, 76, 0.3); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_pink-card_text: var(--theme--tag_pink-text); + --theme--board_pink-text: var(--theme--text_pink); + --theme--board_red: rgba(89, 65, 65, 0.3); + --theme--board_red-card: var(--theme--tag_red); + --theme--board_red-card_text: var(--theme--tag_red-text); + --theme--board_red-text: var(--theme--text_red); + + --theme--code_inline: rgba(135, 131, 120, 0.15); + --theme--code_inline-text: var(--nord--red); + + --theme--code: var(--theme--bg_card); + --theme--code_function: var(--theme--text_blue); + --theme--code_keyword: var(--theme--text_pink); + --theme--code_tag: var(--theme--text_pink); + --theme--code_operator: var(--theme--text_yellow); + --theme--code_important: var(--theme--text_yellow); + --theme--code_property: var(--theme--text_pink); + --theme--code_builtin: var(--theme--text_yellow); + --theme--code_attr-name: var(--theme--text_yellow); + --theme--code_comment: var(--theme--text_gray); + --theme--code_punctuation: var(--theme--text_gray); + --theme--code_doctype: var(--theme--text_gray); + --theme--code_number: var(--theme--text_purple); + --theme--code_string: var(--theme--text_orange); + --theme--code_attr-value: var(--theme--text_orange); +} diff --git a/src/mods/outliner/client.css b/src/mods/outliner/client.css new file mode 100644 index 0000000..a37d938 --- /dev/null +++ b/src/mods/outliner/client.css @@ -0,0 +1,47 @@ +/** + * notion-enhancer: outliner + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +#outliner--notice { + color: var(--theme--text_secondary); + font-size: 14px; + margin-top: 0; + margin-bottom: 1rem; +} + +.outliner--header { + position: relative; + margin: 0 -1rem; + padding: 0 1rem; + display: block; + font-size: 14px; + line-height: 2.2; + white-space: nowrap; + overflow: hidden; + user-select: none; + text-overflow: ellipsis; + text-decoration: none; + text-indent: var(--outliner--indent); + color: inherit; + cursor: pointer !important; + transition: background 20ms ease-in; +} +.outliner--header:hover { + background: var(--theme--ui_interactive-hover); +} + +.outliner--header:empty::after { + color: var(--theme--text_secondary); + content: attr(placeholder); +} + +/* indentation lines */ +.outliner--header:not([style='--outliner--indent:0px;'])::before { + content: ''; + height: 100%; + position: absolute; + left: calc((1rem + var(--outliner--indent)) - 11px); +} diff --git a/src/mods/outliner/client.mjs b/src/mods/outliner/client.mjs new file mode 100644 index 0000000..73942fd --- /dev/null +++ b/src/mods/outliner/client.mjs @@ -0,0 +1,90 @@ +/** + * notion-enhancer: outliner + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ web, components }, db) { + const dbNoticeText = 'Open a page to see its table of contents.', + pageNoticeText = 'Click on a heading to jump to it.', + $notice = web.html`

${dbNoticeText}

`; + + const $headingList = web.html`
`; + + let viewFocused = false, + $page; + await components.addPanelView({ + id: '87e077cc-5402-451c-ac70-27cc4ae65546', + icon: web.html` + + + + + + + `, + title: 'Outliner', + $content: web.render(web.html`
`, $notice, $headingList), + onFocus: () => { + viewFocused = true; + $page = document.getElementsByClassName('notion-page-content')[0]; + updateHeadings(); + }, + onBlur: () => { + viewFocused = false; + }, + }); + await web.whenReady(); + + function updateHeadings() { + if (!$page) return; + $notice.innerText = pageNoticeText; + $headingList.style.display = ''; + const $headerBlocks = $page.querySelectorAll('[class^="notion-"][class*="header-block"]'), + $fragment = web.html`
`; + let depth = 0, + indent = 0; + for (const $header of $headerBlocks) { + const id = $header.dataset.blockId.replace(/-/g, ''), + placeholder = $header.querySelector('[placeholder]').getAttribute('placeholder'), + headerDepth = +[...placeholder].reverse()[0]; + if (depth && depth < headerDepth) { + indent += 18; + } else if (depth > headerDepth) { + indent = Math.max(indent - 18, 0); + } + depth = headerDepth; + const $outlineHeader = web.render( + web.html``, + $header.innerText + ); + $outlineHeader.addEventListener('click', (event) => { + location.hash = ''; + }); + web.render($fragment, $outlineHeader); + } + if ($fragment.innerHTML !== $headingList.innerHTML) { + web.render(web.empty($headingList), ...$fragment.children); + } + } + const pageObserver = () => { + if (!viewFocused) return; + if (document.contains($page)) { + updateHeadings(); + } else { + $page = document.querySelector('.notion-page-content'); + if (!$page) { + $notice.innerText = dbNoticeText; + $headingList.style.display = 'none'; + } else updateHeadings(); + } + }; + web.addDocumentObserver(pageObserver, [ + '.notion-page-content', + '.notion-collection_view_page-block', + ]); + pageObserver(); +} diff --git a/src/mods/outliner/mod.json b/src/mods/outliner/mod.json new file mode 100644 index 0000000..2ebbb48 --- /dev/null +++ b/src/mods/outliner/mod.json @@ -0,0 +1,23 @@ +{ + "name": "outliner", + "id": "87e077cc-5402-451c-ac70-27cc4ae65546", + "version": "0.4.0", + "description": "adds a table of contents to the side panel.", + "preview": "outliner.png", + "tags": ["extension", "panel"], + "authors": [ + { + "name": "CloudHill", + "email": "rh.cloudhill@gmail.com", + "homepage": "https://github.com/CloudHill", + "avatar": "https://avatars.githubusercontent.com/u/54142180" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [] +} diff --git a/src/mods/outliner/outliner.png b/src/mods/outliner/outliner.png new file mode 100644 index 0000000..925ee6c Binary files /dev/null and b/src/mods/outliner/outliner.png differ diff --git a/src/mods/pastel-dark/app.css b/src/mods/pastel-dark/app.css new file mode 100644 index 0000000..9664dd3 --- /dev/null +++ b/src/mods/pastel-dark/app.css @@ -0,0 +1,72 @@ +/** + * notion-enhancer: pastel dark + * (c) 2020 u/zenith_illinois + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-dark-theme img[src*='/images/onboarding/use-case-note.png'], +.notion-dark-theme img[src*='/images/onboarding/team-features-illustration.png'] { + filter: invert(1) !important; +} +.notion-dark-theme img[src*='/images/onboarding/checked.svg'] { + filter: hue-rotate(45deg) !important; +} +.notion-dark-theme + img[style*='display: block; object-fit: cover; border-radius: 100%; width: 90px; height: 90px;'], +.notion-dark-theme + img[style*='display: block; object-fit: cover; border-radius: 3px; width: 56.832px; height: 56.832px; transition: opacity 100ms ease-out 0s;'] { + transition: filter 0.4s ease !important; +} +.notion-dark-theme + img[style*='display: block; object-fit: cover; border-radius: 100%; width: 90px; height: 90px;']:hover, +.notion-dark-theme + img[style*='display: block; object-fit: cover; border-radius: 3px; width: 56.832px; height: 56.832px; transition: opacity 100ms ease-out 0s;']:hover { + filter: brightness(1.2); +} + +.notion-dark-theme .notion-token-remove-button[role*='button'][tabindex*='0']:hover, +.notion-dark-theme .notion-record-icon { + background: transparent !important; +} + +.notion-dark-theme .notion-focusable:focus-within, +.notion-dark-theme .notion-to_do-block > div > div > div[style*='background:'], +.notion-dark-theme div[role='button'], +[style*='height: 4px;'] > .notion-selectable.notion-collection_view_page-block > *, +.notion-dark-theme .notion-calendar-view-day[style*='background: rgb(235, 87, 87);'], +.DayPicker-Day--today, +.notion-dark-theme + .DayPicker:not(.DayPicker--interactionDisabled) + .DayPicker-Day--outside:hover, +.notion-dark-theme + .DayPicker:not(.DayPicker--interactionDisabled) + .DayPicker-Day:not(.DayPicker-Day--disabled):not(.DayPicker-Day--value) + .DayPicker-Day.DayPicker-Day--start.DayPicker-Day--selected, +.notion-dark-theme .DayPicker-Day.DayPicker-Day--range.DayPicker-Day--start, +.notion-dark-theme .DayPicker-Day.DayPicker-Day--range.DayPicker-Day--end { + transition: color 0.4s ease, background 0.4s ease, box-shadow 0.4s ease !important; +} + +.notion-dark-theme :not(.notion-code-block) > * > [style*='background: rgb(63, 68, 71);'], +.notion-dark-theme + .notion-code-block + [style*='background: rgb(63, 68, 71);'] + .notion-code-block, +.notion-dark-theme + [style*='background: rgb(80, 85, 88);'][style*='color: rgba(255, 255, 255, 0.7)'], +.notion-dark-theme + [style*='background: rgb(80, 85, 88);'][style*='width: 18px;'][style*='height: 18px;'], +.notion-dark-theme + [style*='box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.2) 0px 5px 10px, rgba(15, 15, 15, 0.4) 0px 15px 40px;'], +.notion-dark-theme [style*='background: rgba(151, 154, 155, 0.5);'], +.notion-dark-theme [style*='background: rgba(147, 114, 100, 0.5)'], +.notion-dark-theme [style*='background: rgba(255, 163, 68, 0.5)'], +.notion-dark-theme [style*='background: rgba(255, 220, 73, 0.5)'], +.notion-dark-theme [style*='background: rgba(77, 171, 154, 0.5)'], +.notion-dark-theme [style*='background: rgba(82, 156, 202, 0.5)'], +.notion-dark-theme [style*='background: rgba(154, 109, 215, 0.5)'], +.notion-dark-theme [style*='background: rgba(226, 85, 161, 0.5)'], +.notion-dark-theme [style*='background: rgba(255, 115, 105, 0.5)'] { + box-shadow: 0 2px 4px rgb(0 0 0 / 66%) !important; +} diff --git a/src/mods/pastel-dark/mod.json b/src/mods/pastel-dark/mod.json new file mode 100644 index 0000000..ba6058b --- /dev/null +++ b/src/mods/pastel-dark/mod.json @@ -0,0 +1,22 @@ +{ + "name": "pastel dark", + "id": "ed48c585-4a0d-4756-a3e6-9ae662732dac", + "version": "0.2.0", + "description": "a smooth-transition true dark theme with a hint of pastel.", + "preview": "pastel-dark.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "zenith_illinois", + "homepage": "https://www.reddit.com/user/zenith_illinois/", + "avatar": "https://www.redditstatic.com/avatars/defaults/v2/avatar_default_5.png" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css", "app.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/pastel-dark/pastel-dark.png b/src/mods/pastel-dark/pastel-dark.png new file mode 100644 index 0000000..e6dc5a4 Binary files /dev/null and b/src/mods/pastel-dark/pastel-dark.png differ diff --git a/src/mods/pastel-dark/variables.css b/src/mods/pastel-dark/variables.css new file mode 100644 index 0000000..81f697a --- /dev/null +++ b/src/mods/pastel-dark/variables.css @@ -0,0 +1,147 @@ +/** + * notion-enhancer: pastel dark + * (c) 2020 u/zenith_illinois + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --theme--accent_blue: #b887f7; + --theme--accent_blue-selection: rgba(184, 135, 247, 0.3); + --theme--accent_blue-hover: #08d7c2; + --theme--accent_blue-active: #b887f7; + --theme--accent_red: #08d7c2; + --theme--accent_red-button: #08d7c226; + + --theme--bg: #0b0b0b; + --theme--bg_secondary: #0f0f0f; + --theme--bg_card: #141414; + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: #141414; + --theme--scrollbar_thumb-hover: #1b1b1b; + + --theme--ui_divider: rgba(255, 255, 255, 0.1); + --theme--ui_interactive-hover: #15161c; + --theme--ui_interactive-active: #18191c; + --theme--ui_toggle-off: #b1aeab; + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: #ffffff; + --theme--icon_secondary: #909090; + + --theme--text: #ffffff; + --theme--text_secondary: #909090; + --theme--text_gray: #b1aeab; + --theme--text_brown: #d8b6a6; + --theme--text_orange: #fde3c0; + --theme--text_yellow: #fcde93; + --theme--text_green: #b3f5c8; + --theme--text_blue: #bfe0fd; + --theme--text_purple: #dac7fa; + --theme--text_pink: #f7b8dc; + --theme--text_red: #f8acb4; + + --theme--highlight_gray: #b1aeab; + --theme--highlight_gray-text: rgb(55, 53, 47); + --theme--highlight_brown: #d8b6a6; + --theme--highlight_brown-text: rgb(55, 53, 47); + --theme--highlight_orange: #fde3c0; + --theme--highlight_orange-text: rgb(55, 53, 47); + --theme--highlight_yellow: #fcde93; + --theme--highlight_yellow-text: rgb(55, 53, 47); + --theme--highlight_green: #b3f5c8; + --theme--highlight_green-text: rgb(55, 53, 47); + --theme--highlight_blue: #bfe0fd; + --theme--highlight_blue-text: rgb(55, 53, 47); + --theme--highlight_purple: #dac7fa; + --theme--highlight_purple-text: rgb(55, 53, 47); + --theme--highlight_pink: #f7b8dc; + --theme--highlight_pink-text: rgb(55, 53, 47); + --theme--highlight_red: #f8acb4; + --theme--highlight_red-text: rgb(55, 53, 47); + + --theme--callout_gray: #c2c1c089; + --theme--callout_brown: #dacec992; + --theme--callout_orange: #fff0dc9f; + --theme--callout_yellow: #ffe6a6ad; + --theme--callout_green: #c8fdd9a3; + --theme--callout_blue: #d1e9ffa3; + --theme--callout_purple: #e3d3ffa8; + --theme--callout_pink: #fdcce8b1; + --theme--callout_red: #ffc8ce9e; + + --theme--tag_light_gray: #C8C6C4; + --theme--tag_light_gray-text: rgb(55, 53, 47); + --theme--tag_gray: #b1aeab; + --theme--tag_gray-text: rgb(55, 53, 47); + --theme--tag_brown: #d8b6a6; + --theme--tag_brown-text: rgb(55, 53, 47); + --theme--tag_orange: #fde3c0; + --theme--tag_orange-text: rgb(55, 53, 47); + --theme--tag_yellow: #fcde93; + --theme--tag_yellow-text: rgb(55, 53, 47); + --theme--tag_green: #b3f5c8; + --theme--tag_green-text: rgb(55, 53, 47); + --theme--tag_blue: #bfe0fd; + --theme--tag_blue-text: rgb(55, 53, 47); + --theme--tag_purple: #dac7fa; + --theme--tag_purple-text: rgb(55, 53, 47); + --theme--tag_pink: #f7b8dc; + --theme--tag_pink-text: rgb(55, 53, 47); + --theme--tag_red: #f8acb4; + --theme--tag_red-text: rgb(55, 53, 47); + + --theme--board_light_gray: #D4D4D389; + --theme--board_light_gray-text: #C8C6C4; + --theme--board_light_gray-card: rgb(255, 255, 255, 0.2); + --theme--board_gray: #c2c1c089; + --theme--board_gray-text: #b1aeab; + --theme--board_gray-card: rgb(255, 255, 255, 0.2); + --theme--board_brown: #dacec992; + --theme--board_brown-text: #d8b6a6; + --theme--board_brown-card: rgb(255, 255, 255, 0.2); + --theme--board_orange: #fff0dc9f; + --theme--board_orange-text: #fde3c0; + --theme--board_orange-card: rgb(255, 255, 255, 0.2); + --theme--board_yellow: #ffe6a6ad; + --theme--board_yellow-text: #fcde93; + --theme--board_yellow-card: rgb(255, 255, 255, 0.2); + --theme--board_green: #c8fdd9a3; + --theme--board_green-text: #b3f5c8; + --theme--board_green-card: rgb(255, 255, 255, 0.2); + --theme--board_blue: #d1e9ffa3; + --theme--board_blue-text: #bfe0fd; + --theme--board_blue-card: rgb(255, 255, 255, 0.2); + --theme--board_purple: #e3d3ffa8; + --theme--board_purple-text: #dac7fa; + --theme--board_purple-card: rgb(255, 255, 255, 0.2); + --theme--board_pink: #fdcce8b1; + --theme--board_pink-text: #f7b8dc; + --theme--board_pink-card: rgb(255, 255, 255, 0.2); + --theme--board_red: #ffc8ce9e; + --theme--board_red-text: #f8acb4; + --theme--board_red-card: rgb(255, 255, 255, 0.2); + + --theme--code_inline: rgb(8, 8, 8); + --theme--code_inline-text: #b3f5c8; + + --theme--code: #0f0f0f; + --theme--code_plain: var(--theme--text); + --theme--code_function: var(--theme--text_blue); + --theme--code_keyword: var(--theme--text_pink); + --theme--code_tag: var(--theme--text_pink); + --theme--code_operator: var(--theme--text_yellow); + --theme--code_important: var(--theme--text_yellow); + --theme--code_property: var(--theme--text_pink); + --theme--code_builtin: var(--theme--text_yellow); + --theme--code_attr-name: var(--theme--text_yellow); + --theme--code_comment: var(--theme--text_gray); + --theme--code_punctuation: var(--theme--text_gray); + --theme--code_doctype: var(--theme--text_gray); + --theme--code_number: var(--theme--text_purple); + --theme--code_string: var(--theme--text_orange); + --theme--code_attr-value: var(--theme--text_orange); +} diff --git a/src/mods/pinky-boom/mod.json b/src/mods/pinky-boom/mod.json new file mode 100644 index 0000000..f6e1f74 --- /dev/null +++ b/src/mods/pinky-boom/mod.json @@ -0,0 +1,22 @@ +{ + "name": "pinky boom", + "id": "b5f43232-391b-415a-9099-4334dc6c7b55", + "version": "0.2.0", + "description": "pinkify your life.", + "preview": "pinky-boom.png", + "tags": ["theme", "light"], + "authors": [ + { + "name": "mugiwarafx", + "homepage": "https://github.com/mugiwarafx", + "avatar": "https://avatars.githubusercontent.com/u/58401248" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/pinky-boom/pinky-boom.png b/src/mods/pinky-boom/pinky-boom.png new file mode 100644 index 0000000..04473f0 Binary files /dev/null and b/src/mods/pinky-boom/pinky-boom.png differ diff --git a/src/mods/pinky-boom/variables.css b/src/mods/pinky-boom/variables.css new file mode 100644 index 0000000..a5eefdc --- /dev/null +++ b/src/mods/pinky-boom/variables.css @@ -0,0 +1,151 @@ +/** + * notion-enhancer: pinky boom + * (c) 2020 mugiwarafx + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.light { + --pinky_boom--brown: #a52a2a80; + --pinky_boom--orange: #ffa60080; + --pinky_boom--yellow: #ffff0080; + --pinky_boom--green: #00ff0080; + --pinky_boom--blue: #00ffff80; + --pinky_boom--purple: #9b00ff80; + --pinky_boom--pink: #ff149180; + --pinky_boom--red: #ff000080; + + --theme--accent_blue: deeppink; + --theme--accent_blue-selection: rgba(255, 20, 145, 0.2); + --theme--accent_blue-hover: rgb(255, 57, 163); + --theme--accent_blue-active: rgb(255, 48, 158); + --theme--accent_red: deeppink; + --theme--accent_red-button: rgba(255, 20, 145, 0.2); + + --theme--bg: #fdf0f4; + --theme--bg_secondary: #fce4ec; + --theme--bg_card: #ffd5e4; + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: #f8bbd0; + --theme--scrollbar_thumb-hover: #f48fb1; + + --theme--ui_shadow: #ff89c842; + --theme--ui_divider: #ffbbdf; + --theme--ui_interactive-hover: #fcb9d0; + --theme--ui_interactive-active: #ffccdc; + --theme--ui_toggle-off: #ffc9e6; + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: #56002e; + --theme--icon_secondary: rgba(255, 20, 145, 0.75); + + --theme--text: #56002e; + --theme--text_secondary: rgba(255, 20, 145, 0.75); + + --theme--text_gray: rgb(155, 154, 151); + --theme--text_brown: rgb(100, 71, 58); + --theme--text_orange: rgb(217, 115, 13); + --theme--text_yellow: rgb(223, 171, 1); + --theme--text_green: rgb(15, 123, 108); + --theme--text_blue: rgb(11, 110, 153); + --theme--text_purple: var(--pinky_boom--purple); + --theme--text_pink: var(--pinky_boom--deep-pink); + --theme--text_red: rgb(224, 62, 62); + + --theme--highlight_gray: rgba(128, 128, 128, 0.25); + --theme--highlight_brown: var(--pinky_boom--brown); + --theme--highlight_orange: var(--pinky_boom--orange); + --theme--highlight_orange-text: black; + --theme--highlight_yellow: var(--pinky_boom--yellow); + --theme--highlight_yellow-text: black; + --theme--highlight_green: var(--pinky_boom--green); + --theme--highlight_green-text: black; + --theme--highlight_blue: var(--pinky_boom--blue); + --theme--highlight_blue-text: black; + --theme--highlight_purple: var(--pinky_boom--purple); + --theme--highlight_purple-text: white; + --theme--highlight_pink: var(--pinky_boom--pink); + --theme--highlight_pink-text: white; + --theme--highlight_red: var(--pinky_boom--red); + + --theme--callout_gray: rgba(128, 128, 128, 0.25); + --theme--callout_gray-text: black; + --theme--callout_brown: var(--pinky_boom--brown); + --theme--callout_brown-text: white; + --theme--callout_orange: var(--pinky_boom--orange); + --theme--callout_orange-text: black; + --theme--callout_yellow: var(--pinky_boom--yellow); + --theme--callout_yellow-text: black; + --theme--callout_green: var(--pinky_boom--green); + --theme--callout_green-text: black; + --theme--callout_blue: var(--pinky_boom--blue); + --theme--callout_blue-text: black; + --theme--callout_purple: var(--pinky_boom--purple); + --theme--callout_purple-text: white; + --theme--callout_pink: var(--pinky_boom--pink); + --theme--callout_pink-text: white; + --theme--callout_red: var(--pinky_boom--red); + --theme--callout_red-text: white; + + --theme--board_light_gray: rgba(166, 166, 166, 0.5); + --theme--board_light_gray-text: white; + --theme--board_light_gray-card: rgba(166, 166, 166, 0.25); + --theme--board_gray: rgba(128, 128, 128, 0.5); + --theme--board_gray-text: white; + --theme--board_gray-card: rgba(128, 128, 128, 0.25); + --theme--board_brown: var(--pinky_boom--brown); + --theme--board_brown-text: white; + --theme--board_brown-card: var(--pinky_boom--brown); + --theme--board_orange: var(--pinky_boom--orange); + --theme--board_orange-text: white; + --theme--board_orange-card: var(--pinky_boom--orange); + --theme--board_yellow: var(--pinky_boom--yellow); + --theme--board_yellow-text: white; + --theme--board_yellow-card: var(--pinky_boom--yellow); + --theme--board_green: var(--pinky_boom--green); + --theme--board_green-text: white; + --theme--board_green-card: var(--pinky_boom--green); + --theme--board_blue: var(--pinky_boom--blue); + --theme--board_blue-text: white; + --theme--board_blue-card: var(--pinky_boom--blue); + --theme--board_purple: var(--pinky_boom--purple); + --theme--board_purple-text: white; + --theme--board_purple-card: var(--pinky_boom--purple); + --theme--board_pink: var(--pinky_boom--pink); + --theme--board_pink-text: white; + --theme--board_pink-card: var(--pinky_boom--pink); + --theme--board_red: var(--pinky_boom--red); + --theme--board_red-text: white; + --theme--board_red-card: var(--pinky_boom--red); + + --theme--tag_default: #ffc9e6; + --theme--tag_light_gray: rgba(166, 166, 166, 0.25); + --theme--tag_light_gray-text: black; + --theme--tag_gray: rgba(128, 128, 128, 0.25); + --theme--tag_gray-text: black; + --theme--tag_brown: var(--pinky_boom--brown); + --theme--tag_brown-text: white; + --theme--tag_orange: var(--pinky_boom--orange); + --theme--tag_orange-text: black; + --theme--tag_yellow: var(--pinky_boom--yellow); + --theme--tag_yellow-text: black; + --theme--tag_green: var(--pinky_boom--green); + --theme--tag_green-text: black; + --theme--tag_blue: var(--pinky_boom--blue); + --theme--tag_blue-text: black; + --theme--tag_purple: var(--pinky_boom--purple); + --theme--tag_purple-text: white; + --theme--tag_pink: var(--pinky_boom--pink); + --theme--tag_pink-text: white; + --theme--tag_red: var(--pinky_boom--red); + --theme--tag_red-text: white; + + --theme--code_inline: var(--theme--code); + --theme--code_inline-text: var(--theme--text); + + --theme--code: var(--theme--bg_secondary); + --theme--code_punctuation: #ff46a8; +} diff --git a/src/mods/playful-purple/mod.json b/src/mods/playful-purple/mod.json new file mode 100644 index 0000000..ce4c4af --- /dev/null +++ b/src/mods/playful-purple/mod.json @@ -0,0 +1,27 @@ +{ + "name": "playful purple", + "id": "ae7862a9-5dbe-4f2c-9931-940f3ba5015d", + "version": "0.2.0", + "description": "a purple-shaded theme with bright highlights.", + "preview": "playful-purple.png", + "tags": ["theme", "dark"], + "authors": [ + { + "name": "Lizishan", + "homepage": "https://www.reddit.com/user/Lizishan", + "avatar": "https://styles.redditmedia.com/t5_110nz4/styles/profileIcon_h1m3b16exoi51.jpg" + }, + { + "name": "LVL100ShrekCultist", + "homepage": "https://www.reddit.com/user/LVL100ShrekCultist/", + "avatar": "https://styles.redditmedia.com/t5_2js69j/styles/profileIcon_jvnzmo30fyq41.jpg" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css"], + "menu": ["variables.css"] + }, + "js": {}, + "options": [] +} diff --git a/src/mods/playful-purple/playful-purple.png b/src/mods/playful-purple/playful-purple.png new file mode 100644 index 0000000..43930cf Binary files /dev/null and b/src/mods/playful-purple/playful-purple.png differ diff --git a/src/mods/playful-purple/variables.css b/src/mods/playful-purple/variables.css new file mode 100644 index 0000000..cb0d704 --- /dev/null +++ b/src/mods/playful-purple/variables.css @@ -0,0 +1,152 @@ +/** + * notion-enhancer: playful dark + * (c) 2020 Lizishan + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root.dark { + --theme--accent_blue: rgb(117, 65, 200); + --theme--accent_blue-selection: #9500ff1e; + --theme--accent_blue-hover: rgb(110, 48, 211); + --theme--accent_blue-active: rgb(117, 65, 200); + --theme--accent_red: rgb(150, 84, 226); + --theme--accent_red-button: rgba(150, 84, 226, 0.5); + + --theme--bg: #1e1c26; + --theme--bg_secondary: #24222c; + --theme--bg_card: #19181f; + + --theme--scrollbar_track: transparent; + --theme--scrollbar_thumb: #221f29; + --theme--scrollbar_thumb-hover: #312d3c; + + --theme--ui_divider: rgba(148, 148, 184, 0.5); + --theme--ui_interactive-hover: #282632; + --theme--ui_interactive-active: #2b293a; + --theme--ui_toggle-off: rgb(20, 0, 51); + --theme--ui_corner_action: var(--theme--bg_card); + --theme--ui_corner_action-hover: var(--theme--ui_interactive-hover); + --theme--ui_corner_action-active: var(--theme--ui_interactive-active); + + --theme--icon: #978ec7; + --theme--icon_secondary: #978ec7; + + --theme--text: rgb(239, 235, 255); + --theme--text_secondary: #978ec7; + + --theme--text_brown: rgb(177, 144, 131); + --theme--text_green: rgb(66, 222, 137); + --theme--text_blue: rgb(0, 157, 255); + --theme--text_purple: rgb(162, 94, 255); + --theme--text_red: rgb(240, 52, 38); + + --theme--highlight_gray: rgb(234, 234, 234); + --theme--highlight_gray-text: rgb(17, 17, 17); + --theme--highlight_brown: rgb(206, 206, 206); + --theme--highlight_brown-text: rgb(85, 35, 1); + --theme--highlight_orange: rgb(254, 214, 155); + --theme--highlight_orange-text: rgb(199, 110, 0); + --theme--highlight_yellow: #fcffd8; + --theme--highlight_yellow-text: #ff8c22; + --theme--highlight_green: #d5fded; + --theme--highlight_green-text: #006a00; + --theme--highlight_blue: #e2f5ff; + --theme--highlight_blue-text: #00b2ff; + --theme--highlight_purple: #efe6ff; + --theme--highlight_purple-text: #8334ff; + --theme--highlight_pink: #ffe9f1; + --theme--highlight_pink-text: rgb(255, 0, 127); + --theme--highlight_red: rgb(251, 228, 228); + --theme--highlight_red-text: rgb(138, 0, 10); + + --theme--callout_gray: #e2e3e5; + --theme--callout_gray-text: #383d41; + --theme--callout_brown: rgb(130, 118, 111); + --theme--callout_brown-text: rgb(85, 35, 1); + --theme--callout_orange: rgb(254, 214, 155); + --theme--callout_orange-text: rgb(255, 140, 0); + --theme--callout_yellow: #fcffd8; + --theme--callout_yellow-text: #c76e00; + --theme--callout_green: #d4edda; + --theme--callout_green-text: #155724; + --theme--callout_blue: #cce5ff; + --theme--callout_blue-text: #004085; + --theme--callout_purple: rgb(199, 178, 230); + --theme--callout_purple-text: rgb(90, 49, 148); + --theme--callout_pink: rgb(255, 206, 228); + --theme--callout_pink-text: rgb(255, 0, 127); + --theme--callout_red: #f8d7da; + --theme--callout_red-text: #721c24; + + --theme--tag_default: rgb(234, 234, 234); + --theme--tag_default-text: rgb(17, 17, 17); + --theme--tag_light_gray: rgb(240, 240, 240); + --theme--tag_light_gray-text: rgb(88, 88, 88); + --theme--tag_gray: rgb(234, 234, 234); + --theme--tag_gray-text: rgb(17, 17, 17); + --theme--tag_brown: rgb(206, 206, 206); + --theme--tag_brown-text: rgb(85, 35, 1); + --theme--tag_orange: rgb(254, 214, 155); + --theme--tag_orange-text: rgb(199, 110, 0); + --theme--tag_yellow: #fcffd8; + --theme--tag_yellow-text: #ff8c22; + --theme--tag_green: #d5fded; + --theme--tag_green-text: #006a00; + --theme--tag_blue: #e2f5ff; + --theme--tag_blue-text: #00b2ff; + --theme--tag_purple: #efe6ff; + --theme--tag_purple-text: #8334ff; + --theme--tag_pink: #ffe9f1; + --theme--tag_pink-text: rgb(255, 0, 127); + --theme--tag_red: rgb(251, 228, 228); + --theme--tag_red-text: rgb(138, 0, 10); + + --theme--board_light_gray: #EBEBED; + --theme--board_light_gray-text: #74777A; + --theme--board_light_gray-card: var(--theme--tag_light_gray); + --theme--board_light_gray-card_text: var(--theme--tag_light_gray-text); + --theme--board_gray: #e2e3e5; + --theme--board_gray-text: #383d41; + --theme--board_gray-card: var(--theme--tag_gray); + --theme--board_gray-card_text: var(--theme--tag_gray-text); + --theme--board_brown: rgb(130, 118, 111); + --theme--board_brown-text: rgb(85, 35, 1); + --theme--board_brown-card: var(--theme--tag_brown); + --theme--board_brown-card_text: var(--theme--tag_brown-text); + --theme--board_orange: rgb(254, 214, 155); + --theme--board_orange-text: rgb(255, 140, 0); + --theme--board_orange-card: var(--theme--tag_orange); + --theme--board_orange-card_text: var(--theme--tag_orange-text); + --theme--board_yellow: #fcffd8; + --theme--board_yellow-text: #c76e00; + --theme--board_yellow-card: var(--theme--tag_yellow); + --theme--board_yellow-card_text: var(--theme--tag_yellow-text); + --theme--board_green: #d4edda; + --theme--board_green-text: #155724; + --theme--board_green-card: var(--theme--tag_green); + --theme--board_green-card_text: var(--theme--tag_green-text); + --theme--board_blue: #cce5ff; + --theme--board_blue-text: #004085; + --theme--board_blue-card: var(--theme--tag_blue); + --theme--board_blue-card_text: var(--theme--tag_blue-text); + --theme--board_purple: rgb(199, 178, 230); + --theme--board_purple-text: rgb(90, 49, 148); + --theme--board_purple-card: var(--theme--tag_purple); + --theme--board_purple-card_text: var(--theme--tag_purple-text); + --theme--board_pink: rgb(255, 206, 228); + --theme--board_pink-text: rgb(255, 0, 127); + --theme--board_pink-card: var(--theme--tag_pink); + --theme--board_pink-card_text: var(--theme--tag_pink-text); + --theme--board_red: #f8d7da; + --theme--board_red-text: #721c24; + --theme--board_red-card: var(--theme--tag_red); + --theme--board_red-card_text: var(--theme--tag_red-text); + + --theme--code_inline: rgb(179, 39, 39); + --theme--code_inline-text: #e0dfe2; + + --theme--code: var(--theme--bg_card); + --theme--code_function: rgb(179, 146, 240); + --theme--code_number: hsl(159, 69%, 39%); +} diff --git a/src/mods/quick-note/client.mjs b/src/mods/quick-note/client.mjs new file mode 100644 index 0000000..89a7d66 --- /dev/null +++ b/src/mods/quick-note/client.mjs @@ -0,0 +1,33 @@ +/** + * notion-enhancer: quick note + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web, components, notion }, db) { + const targetDbId = await db.get(['target_db']); + if (!targetDbId) return; + + const newQuickNote = async () => { + try { + const { collection_id } = await notion.get(targetDbId), + noteID = await notion.create( + { + recordValue: { + properties: { title: [[`quick note: ${new Date().toLocaleString()}`]] }, + }, + recordType: 'page', + }, + { parentID: collection_id, parentTable: 'collection' } + ); + location.assign(`https://www.notion.so/${noteID.replace(/-/g, '')}`); + } catch { + alert('quick note failed: target database id did not match any known databases'); + } + }; + + await components.addCornerAction(await components.feather('feather'), newQuickNote); + web.addHotkeyListener(await db.get(['hotkey']), newQuickNote); +} diff --git a/src/mods/quick-note/mod.json b/src/mods/quick-note/mod.json new file mode 100644 index 0000000..5166a3f --- /dev/null +++ b/src/mods/quick-note/mod.json @@ -0,0 +1,35 @@ +{ + "name": "quick note", + "id": "5d7c1677-6f6d-4fb5-8d6f-5067bcd0e24c", + "version": "0.1.0", + "description": "adds a hotkey & a button in the bottom right corner to jump to a new page in a notes database (target database id must be set).", + "preview": "quick-note.png", + "tags": ["integration", "shortcut"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": {}, + "js": { + "client": ["client.mjs"] + }, + "options": [ + { + "type": "text", + "key": "target_db", + "label": "target database id", + "tooltip": "**the uuid of the database in which pages should be created** (the bit after the '/' in the database url, e.g. edbafbd8bb7045cab1408aadaf4edf9d)", + "value": "" + }, + { + "type": "hotkey", + "key": "hotkey", + "label": "hotkey", + "value": "Ctrl+Alt+=" + } + ] +} diff --git a/src/mods/quick-note/quick-note.png b/src/mods/quick-note/quick-note.png new file mode 100644 index 0000000..faa3ad5 Binary files /dev/null and b/src/mods/quick-note/quick-note.png differ diff --git a/src/mods/registry.json b/src/mods/registry.json new file mode 100644 index 0000000..6093766 --- /dev/null +++ b/src/mods/registry.json @@ -0,0 +1,47 @@ +[ + "menu", + "theming", + "components", + + "tweaks", + "font-chooser", + + "integrated-titlebar", + "tray", + "tabs", + "always-on-top", + "view-scale", + + "outliner", + "scroll-to-top", + "indentation-lines", + "right-to-left", + "simpler-databases", + "emoji-sets", + "bypass-preview", + "topbar-icons", + "word-counter", + "code-line-numbers", + "calendar-scroll", + "collapsible-properties", + "weekly-view", + "truncated-titles", + "focus-mode", + "global-block-links", + + "icon-sets", + "quick-note", + + "material-ocean", + "cherry-cola", + "dark+", + "light+", + "dracula", + "pastel-dark", + "neutral", + "nord", + "gruvbox-dark", + "gruvbox-light", + "playful-purple", + "pinky-boom" +] diff --git a/src/mods/right-to-left/client.css b/src/mods/right-to-left/client.css new file mode 100644 index 0000000..3597fcc --- /dev/null +++ b/src/mods/right-to-left/client.css @@ -0,0 +1,26 @@ +/** + * notion-enhancer: right to left + * (c) 2021 obahareth (https://omar.engineer) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.notion-page-content + .notion-table_of_contents-block + > div + > div + > a + > div + > div[style*='margin-left: 24px'] { + margin-inline-start: 24px; +} + +.notion-page-content + .notion-table_of_contents-block + > div + > div + > a + > div + > div[style*='margin-left: 48px'] { + margin-inline-start: 48px; +} diff --git a/src/mods/right-to-left/client.mjs b/src/mods/right-to-left/client.mjs new file mode 100644 index 0000000..decba98 --- /dev/null +++ b/src/mods/right-to-left/client.mjs @@ -0,0 +1,50 @@ +/** + * notion-enhancer: right to left + * (c) 2021 obahareth (https://omar.engineer) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web }, db) { + const pageContentSelector = ` + .notion-page-content > + div[data-block-id]:not([dir]):not(.notion-column_list-block):not(.notion-collection_view_page-block), + [placeholder="Untitled"]:not([dir]), + .notion-column-block > div[data-block-id]:not([dir]), + .notion-collection_view-block:not([dir]), + .notion-table-view:not([dir]), + .notion-board-view:not([dir]), + .notion-gallery-view:not([dir])`, + listItemSelector = ` + div[placeholder="List"]:not([style*="text-align: start"]), + div[placeholder="To-do"]:not([style*="text-align: start"]), + div[placeholder="Toggle"]:not([style*="text-align: start"])`, + inlineEquationSelector = + '.notion-text-equation-token .katex-html:not([style*="direction: rtl;"])'; + + const autoAlignText = () => { + document + .querySelectorAll(pageContentSelector) + .forEach(($block) => $block.setAttribute('dir', 'auto')); + document.querySelectorAll(listItemSelector).forEach(($item) => { + $item.style['text-align'] = 'start'; + }); + document.querySelectorAll(inlineEquationSelector).forEach(($equation) => { + $equation.style.direction = 'rtl'; + $equation.style.display = 'inline-flex'; + $equation.style.flexDirection = 'row-reverse'; + for (const $symbol of $equation.children) { + $symbol.style.direction = 'ltr'; + } + }); + }; + web.addDocumentObserver(autoAlignText, [ + pageContentSelector, + listItemSelector, + inlineEquationSelector, + ]); + await web.whenReady(); + autoAlignText(); +} diff --git a/src/mods/right-to-left/mod.json b/src/mods/right-to-left/mod.json new file mode 100644 index 0000000..1a1d396 --- /dev/null +++ b/src/mods/right-to-left/mod.json @@ -0,0 +1,23 @@ +{ + "name": "right to left", + "id": "b28ee2b9-4d34-4e36-be8a-ab5be3d79f51", + "version": "1.5.0", + "description": "enables auto rtl/ltr text direction detection.", + "preview": "right-to-left.jpg", + "tags": ["extension", "usability"], + "authors": [ + { + "name": "obahareth", + "email": "omar@omar.engineer", + "homepage": "https://omar.engineer", + "avatar": "https://avatars.githubusercontent.com/u/3428118" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [] +} diff --git a/src/mods/right-to-left/right-to-left.jpg b/src/mods/right-to-left/right-to-left.jpg new file mode 100644 index 0000000..38956ed Binary files /dev/null and b/src/mods/right-to-left/right-to-left.jpg differ diff --git a/src/mods/scroll-to-top/client.mjs b/src/mods/scroll-to-top/client.mjs new file mode 100644 index 0000000..396984e --- /dev/null +++ b/src/mods/scroll-to-top/client.mjs @@ -0,0 +1,43 @@ +/** + * notion-enhancer: scroll to top + * (c) 2021 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web, components }, db) { + const scrollStyle = (await db.get(['smooth'])) ? 'smooth' : 'auto', + $scrollButton = await components.addCornerAction( + await components.feather('chevron-up'), + () => { + document.querySelector('.notion-frame > .notion-scroller').scroll({ + top: 0, + left: 0, + behavior: scrollStyle, + }); + } + ); + + let $scroller; + const topDistancePx = +(await db.get(['top_distance_px'])), + topDistancePercent = 0.01 * (await db.get(['top_distance_percent'])), + adjustButtonVisibility = async () => { + if (!$scroller) return; + $scrollButton.classList.add('hidden'); + const scrolledDistance = + $scroller.scrollTop >= topDistancePx || + $scroller.scrollTop >= + ($scroller.scrollHeight - $scroller.clientHeight) * topDistancePercent; + if (scrolledDistance) $scrollButton.classList.remove('hidden'); + }; + web.addDocumentObserver(() => { + $scroller = document.querySelector('.notion-frame > .notion-scroller'); + $scroller.removeEventListener('scroll', adjustButtonVisibility); + $scroller.addEventListener('scroll', adjustButtonVisibility); + }, ['.notion-frame > .notion-scroller']); + adjustButtonVisibility(); + + if (topDistancePx && topDistancePercent) $scrollButton.classList.add('hidden'); +} diff --git a/src/mods/scroll-to-top/mod.json b/src/mods/scroll-to-top/mod.json new file mode 100644 index 0000000..cf57cd8 --- /dev/null +++ b/src/mods/scroll-to-top/mod.json @@ -0,0 +1,42 @@ +{ + "name": "scroll to top", + "id": "0a958f5a-17c5-48b5-8713-16190cae1959", + "version": "0.3.0", + "description": "adds an arrow in the bottom right corner to scroll back to the top of a page.", + "preview": "scroll-to-top.png", + "tags": ["extension", "shortcut"], + "authors": [ + { + "name": "CloudHill", + "email": "rh.cloudhill@gmail.com", + "homepage": "https://github.com/CloudHill", + "avatar": "https://avatars.githubusercontent.com/u/54142180" + } + ], + "css": {}, + "js": { + "client": ["client.mjs"] + }, + "options": [ + { + "type": "toggle", + "key": "smooth", + "label": "smooth scrolling", + "value": true + }, + { + "type": "number", + "key": "top_distance_px", + "label": "distance scrolled until button shown (px)", + "tooltip": "**the distance in pixels that must be scrolled down before the button appears**", + "value": 5000 + }, + { + "type": "number", + "key": "top_distance_percent", + "label": "distance scrolled until button shown (%)", + "tooltip": "**the percentage of the page that must be scrolled down before the button appears**", + "value": 80 + } + ] +} diff --git a/src/mods/scroll-to-top/scroll-to-top.png b/src/mods/scroll-to-top/scroll-to-top.png new file mode 100644 index 0000000..0f1c55c Binary files /dev/null and b/src/mods/scroll-to-top/scroll-to-top.png differ diff --git a/src/mods/simpler-databases/client.css b/src/mods/simpler-databases/client.css new file mode 100644 index 0000000..eeb47e8 --- /dev/null +++ b/src/mods/simpler-databases/client.css @@ -0,0 +1,429 @@ +/** + * notion-enhancer: simpler databases + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.simpler_databases--config_button:hover { + background: linear-gradient( + var(--theme--ui_interactive-hover), + var(--theme--ui_interactive-hover) + ), + linear-gradient(var(--theme--bg), var(--theme--bg)); +} +.simpler_databases--config_button svg { + width: 12px; + height: 12px; + fill: var(--theme--icon_secondary); +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + [style*=' height: 42px;'] + > :last-child { + position: absolute; + top: 4px; + right: 0; + z-index: 99; + pointer-events: auto; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + .simpler_databases--config_button { + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.05)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.1)) 0px 3px 6px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.2)) 0px 9px 24px !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + .simpler_databases--config_button:not(:hover) { + background: var(--theme--bg); +} + +.simpler_databases--overlay_container { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 999; + overflow: hidden; +} + +.simpler_databases--config_menu { + position: relative; + width: 220px; + max-height: 70vh; + padding: 8px 0; + border-radius: 3px; + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.05)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.1)) 0px 3px 6px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.2)) 0px 9px 24px; + background: var(--theme--bg_card); + overflow: hidden auto; +} + +.simpler_databases--config_item-toggle, +.simpler_databases--config_item-input { + display: flex; + align-items: center; + width: 100%; + min-height: 28px; + font-size: 14px; + line-height: 1.2; + user-select: none; +} +.simpler_databases--config_item-toggle { + cursor: pointer; +} +.simpler_databases--config_item-toggle:hover, +.simpler_databases--config_item-toggle:focus { + background: var(--theme--ui_interactive-hover); +} +.simpler_databases--config_item-input { + padding: 6px 0; +} + +.simpler_databases--config_title { + margin: 0 14px; + min-width: 0; + flex: 1 1 auto; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.simpler_databases--config_toggle { + flex-shrink: 0; + position: relative; + height: 14px; + width: 26px; + margin-left: auto; + margin-right: 14px; + padding: 2px; + border-radius: 44px; + cursor: pointer; + user-select: none; + box-sizing: content-box; + background: var(--theme--ui_toggle-off); + transition: background 200ms ease 0s, box-shadow 200ms ease 0s; +} +.simpler_databases--config_toggle[data-toggled='true'] { + background: var(--theme--ui_toggle-on); +} +.simpler_databases--config_toggle::before { + content: ''; + position: absolute; + width: 14px; + height: 14px; + border-radius: 44px; + background: var(--theme--ui_toggle-feature); + transition: transform 200ms ease-out 0s, background 200ms ease-out 0s; +} +.simpler_databases--config_toggle[data-toggled='true']::before { + transform: translateX(12px); +} + +.simpler_databases--config_input { + display: flex; + align-items: center; + height: 28px; + margin: 0 14px; + padding: 3px 6px; + background: var(--theme--ui_input); + box-shadow: var(--theme--ui_shadow) 0px 0px 0px 1px inset; + border-radius: 3px; + font-size: 14px; + line-height: 20px; + cursor: text; + width: 100%; +} +.simpler_databases--config_input input { + font-size: inherit; + line-height: inherit; + border: none; + background: none; + width: 100%; + display: block; + resize: none; + padding: 0; +} + +.simpler_databases--config_divider { + border-bottom: 1px solid var(--theme--ui_divider); + width: 100%; + margin: 6px 0; +} + +.notion-collection_view-block[data-simpler-db-tweaks*='[config-open]'] + [style*=' height: 42px;'] + > :not(:first-child) { + opacity: 1 !important; +} + +/* TWEAKS */ + +/* Toggle */ +.simpler_databases--toggle { + flex-shrink: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 3px; + cursor: pointer; + transition: background 20ms ease-in 0s; +} +.simpler_databases--toggle svg { + width: 12px; + height: 12px; + transform: rotateZ(180deg); + transition: transform 200ms ease-out 0s; +} +.simpler_databases--toggle:hover { + background: var(--theme--ui_interactive-hover); +} + +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'][data-simpler-db-toggle-hidden='true'] + .simpler_databases--toggle + svg { + transform: rotateZ(90deg); +} + +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'] > div > .notion-scroller { + transition: height 200ms ease-in, opacity 200ms ease-in; +} +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'][data-simpler-db-toggle-hidden='true'] + > div + > .notion-scroller { + opacity: 0; + height: 0 !important; +} + +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'][data-simpler-db-toggle-hidden='true'] + [data-simpler-db-hide-items] + [class$='view'] + > .notion-collection_view-block, +.notion-collection_view-block[data-simpler-db-tweaks*='[toggle]'][data-simpler-db-toggle-hidden='true'] + [data-simpler-db-hide-items] + .notion-collection-item { + display: none !important; +} + +/* Title */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[title]']) + [style*=' height: 42px;'] + > [style*='white-space: nowrap; overflow: hidden;'] + [placeholder] { + display: none !important; +} + +/* Icons + Link Arrows */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']) + [style*=' height: 42px;'] + a + :first-child[style*='margin-right: 6px'] { + display: none !important; +} + +/* Views */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[views]']) + [style*=' height: 42px;'] + > [role='button'] { + display: none !important; +} + +/* Toolbar */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[toolbar]']) + .simpler_databases--config_button + ~ * { + display: none !important; +} + +/* Header - table, calendar */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[header_row]']) + .notion-table-view + > .notion-collection_view-block + > :first-child, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[header_row]']) + .notion-table-view + > .notion-collection_view-block + > :first-child + + div { + display: none !important; +} + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[header_row]']) + .notion-table-view + .notion-collection_view-block + > [style*='height: 34px'] + + div { + border-top: 1px solid var(--theme--ui_divider); +} + +/* New Item - table, board, timeline, list, gallery */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-table-view-add-row, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-board-view + .notion-board-group + > [role='button']:not(.notion-collection-item), +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-timeline-item-row:last-child, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-list-view + > .notion-collection_view-block + > [role='button']:not(.notion-collection-item), +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-gallery-view + > .notion-collection_view-block + [style*='grid'] + > [role='button'] { + display: none !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_item]']) + .notion-timeline-view + > [style*='padding-bottom: 34px;'] { + padding-bottom: 0 !important; +} + +/* Calc Row - table, timeline */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[calc_row]']) + .notion-table-view-add-row + ~ div:not(.notion-selectable-halo):not([role='button']), +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[calc_row]']) + .notion-timeline-view + > [style*='z-index: 4;']:last-child { + display: none !important; +} + +/* Hidden Columns - board */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[hidden_column]']) + .notion-board-view + > .notion-collection_view-block + [style*='width: 220px;'] { + display: none !important; +} + +/* Add Group - board */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[add_group]']) + .notion-board-view + > .notion-collection_view-block + [style*='width: 180px;'] { + display: none !important; +} + +/* New Column - table */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_column]']) + .notion-table-view-add-column, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_column]']) + .notion-table-view + .notion-collection-item + > [style*='width: 32px;'] { + display: none !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[new_column]']) + .notion-table-view-add-row + + [style*='padding-right: 32px;'] { + padding-right: 0 !important; +} + +/* Full Width - table */ + +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + > .notion-collection_view-block { + max-width: fit-content; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > [style*='min-width'] { + min-width: 0 !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection-item { + width: fit-content; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > [style*='height: 34px'] + + div, +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > :first-child { + border-left: 1px solid var(--theme--ui_divider); +} + +/* COMPOUND TWEAKS */ + +/* Title and Link disabled > Hide title container */ +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']) + [style*=' height: 42px;'] + > [style*='white-space: nowrap; overflow: hidden;'] { + display: none !important; +} + +/* New Row and Calc Row disabled > Add bottom border - table */ +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[calc_row]']):not([data-simpler-db-tweaks*='[new_item]']) + .notion-table-view + .notion-collection_view-block + > [style*='height: 34px'] + + div { + border-bottom: 1px solid var(--theme--ui_divider); +} + +/* New Column enabled with Full Width disabled > Add right border - table */ +.notion-collection_view-block[data-simpler-db-tweaks][data-simpler-db-tweaks*='[new_column]']:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > [style*='height: 34px'] + + div, +.notion-collection_view-block[data-simpler-db-tweaks][data-simpler-db-tweaks*='[new_column]']:not([data-simpler-db-tweaks*='[full_width]']) + .notion-table-view + .notion-collection_view-block + > :first-child { + border-right: 1px solid var(--theme--ui_divider); +} + +/* REMOVE DATABASE HEADER < Title, Link, Toggle, Views, and Toolbar disabled */ + +/* Hide Header */ +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + [style*='min-height: 42px'] { + min-height: 0 !important; + max-height: 0; + pointer-events: none; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + [style*='height: 42px'] { + overflow: visible !important; +} + +/* Hide Top Border */ +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + :not(.notion-table-view) + > .notion-collection_view-block + > [style*='box-shadow'] { + box-shadow: none !important; +} +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + :not(.notion-table-view) + > .notion-collection_view-block[style*='border-top'], +.notion-collection_view-block[data-simpler-db-tweaks]:not([data-simpler-db-tweaks*='[icon]']):not([data-simpler-db-tweaks*='[title]']):not([data-simpler-db-tweaks*='[toggle]']):not([data-simpler-db-tweaks*='[views']):not([data-simpler-db-tweaks*='[toolbar]']) + :not(.notion-table-view) + > .notion-collection_view-block + > [style*='border-top'] { + border-top: none !important; +} diff --git a/src/mods/simpler-databases/client.mjs b/src/mods/simpler-databases/client.mjs new file mode 100644 index 0000000..c82a6c9 --- /dev/null +++ b/src/mods/simpler-databases/client.mjs @@ -0,0 +1,452 @@ +/** + * notion-enhancer: simpler databases + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ web, components }, db) { + const collectionViewSelector = + '.notion-collection_view-block[style*="width"][style*="max-width"]', + collectionAddNewSelector = '.notion-collection-view-item-add', + collectionToolbarSelector = '[style*=" height: 42px"]', + linkedCollectionTitleSelector = `${collectionToolbarSelector} > a [placeholder]`, + viewContainerSelector = '.notion-scroller [class$="view"]', + configButtonClass = 'simpler_databases--config_button', + configButtonSvg = web.raw` + + `, + overlayContainerClass = 'simpler_databases--overlay_container', + configMenuClass = 'simpler_databases--config_menu', + configDividerClass = 'simpler_databases--config_divider', + configItemClass = 'simpler_databases--config_item', + configTitleClass = 'simpler_databases--config_title', + configToggleClass = 'simpler_databases--config_toggle', + configInputClassName = 'simpler_databases--config_input notion-focusable', + configOpenCollectionSelector = + '.notion-collection_view-block[data-simpler-db-tweaks*="[config-open]"]', + collectionToggleClass = 'simpler_databases--toggle', + notionAppSelector = '.notion-app-inner'; + + const replaceTitle = ($collection, state) => { + const $title = $collection.querySelector(linkedCollectionTitleSelector), + blockId = $collection.dataset.blockId; + if (!$title) return; + if (!$title.dataset.originalTitle && state) { + $title.dataset.originalTitle = $title.innerText; + } + + if (!$title.titleObserver) { + if (!state) return; + $title.titleObserver = new MutationObserver(async () => { + const customTitle = await db.get(['collections', blockId, 'replace_title'], false); + if (customTitle && $title.innerText !== customTitle) $title.innerText = customTitle; + }); + } else $title.titleObserver.disconnect(); + + if (state) { + // observe + $title.innerText = state; + $title.titleObserver.observe($title, { characterData: true, childList: true }); + } else { + // reset + $title.titleObserver.disconnect(); + $title.innerText = $title.dataset.originalTitle; + delete $title.dataset.originalTitle; + } + }, + insertToggle = async ($collection, state) => { + const datasetKey = 'simplerDbToggleHidden', + blockId = $collection.dataset.blockId, + $toolbar = $collection.querySelector(collectionToolbarSelector); + if (!$toolbar) return; + + const $collectionView = $collection.querySelector('.notion-scroller'), + hideCollection = () => { + $collectionView.style.height = $collectionView.offsetHeight + 'px'; + requestAnimationFrame(() => { + $collection.dataset[datasetKey] = true; + setTimeout(() => ($collectionView.dataset.simplerDbHideItems = 'true'), 200); // hide drag handles + }); + }, + showCollection = () => { + $collection.dataset[datasetKey] = false; + $collectionView.style.height = ''; + $collectionView.style.height = $collectionView.offsetHeight + 'px'; + $collection.dataset[datasetKey] = true; + + delete $collectionView.dataset.simplerDbHideItems; + requestAnimationFrame(() => { + $collection.dataset[datasetKey] = false; + setTimeout(() => ($collectionView.style.height = ''), 200); + }); + }; + + if (!$collection.dataset[datasetKey]) { + const storedState = await db.get(['collections', blockId, 'toggle_hidden'], false); + if (storedState) { + hideCollection(); + } + } + + let $toggle = $toolbar.querySelector(`.${collectionToggleClass}`); + if ($toggle) { + if (!state) $toggle.remove(); + return; + } else if (state) { + $toggle = web.html` +
+ +
+ `; + $toggle.addEventListener('click', async () => { + const hide = !($collection.dataset[datasetKey] === 'true'); + await db.set(['collections', blockId, 'toggle_hidden'], hide); + if (hide) { + hideCollection(); + } else showCollection(); + }); + $toolbar.prepend($toggle); + } + }; + + const menuItems = [ + { + key: 'replace_title', + name: 'Replace title...', + type: 'input', + linkedOnly: true, + default: '', + action: replaceTitle, + }, + { + key: 'icon', + name: 'Icon', + type: 'toggle', + default: true, + }, + { + key: 'title', + name: 'Title', + type: 'toggle', + default: true, + }, + { + key: 'toggle', + name: 'Toggle', + type: 'toggle', + default: false, + action: insertToggle, + }, + { + key: 'views', + name: 'Views', + type: 'toggle', + default: true, + }, + { + key: 'toolbar', + name: 'Toolbar', + type: 'toggle', + default: true, + }, + { + key: 'divider', + views: ['table', 'board', 'timeline', 'list', 'gallery'], + }, + { + key: 'header_row', + name: 'Header row', + type: 'toggle', + default: true, + views: ['table'], + }, + { + key: 'new_item', + name: 'New row', + type: 'toggle', + default: true, + views: ['table', 'timeline'], + }, + { + key: 'new_item', + name: 'New item', + type: 'toggle', + default: true, + views: ['board', 'list', 'gallery'], + }, + { + key: 'calc_row', + name: 'Calculation row', + type: 'toggle', + default: true, + views: ['table', 'timeline'], + }, + { + key: 'divider', + views: ['table', 'board'], + }, + { + key: 'hidden_column', + name: 'Hidden columns', + type: 'toggle', + default: true, + views: ['board'], + }, + { + key: 'add_group', + name: 'Add group', + type: 'toggle', + default: true, + views: ['board'], + }, + { + key: 'new_column', + name: 'New column', + type: 'toggle', + default: true, + views: ['table'], + }, + { + key: 'full_width', + name: 'Full width', + type: 'toggle', + default: true, + views: ['table'], + }, + ]; + + const isLinked = ($collection) => !!$collection.querySelector(linkedCollectionTitleSelector), + getViewType = ($collection) => + $collection.querySelector(viewContainerSelector)?.className.split('-')[1], + setTweakState = ($collection, key, state) => { + const datasetKey = 'simplerDbTweaks'; + if (!$collection.dataset[datasetKey]) $collection.dataset[datasetKey] = ''; + + key = web.escape(key); + const isActive = $collection.dataset[datasetKey].includes(`[${key}]`); + + if (state && !isActive) { + $collection.dataset[datasetKey] += `[${key}]`; + } else if (!state && isActive) { + const prev = $collection.dataset[datasetKey]; + $collection.dataset[datasetKey] = prev.replace(`[${key}]`, ''); + } + }; + + const clickItem = (event) => { + event.stopPropagation(); + const focusedItem = event.target.closest(`[class^="${configItemClass}"]`); + if (focusedItem) focusedItem.click(); + }, + focusNextItem = (event) => { + event.stopPropagation(); + event.preventDefault(); + const $focusedItem = event.target.closest(`[class^="${configItemClass}"]`); + if (!$focusedItem) return; + let $targetItem = $focusedItem.nextElementSibling; + if (!$targetItem) $targetItem = $focusedItem.parentElement.firstElementChild; + if ($targetItem.classList.contains(configDividerClass)) { + $targetItem = $targetItem.nextElementSibling; + } + const $input = $targetItem.querySelector('input'); + if ($input) { + $input.focus(); + } else $targetItem.focus(); + }, + focusPrevItem = (event) => { + event.stopPropagation(); + event.preventDefault(); + const $focusedItem = event.target.closest(`[class^="${configItemClass}"]`); + if (!$focusedItem) return; + let $targetItem = $focusedItem.previousElementSibling; + if (!$targetItem) $targetItem = $focusedItem.parentElement.lastElementChild; + if ($targetItem.classList.contains(configDividerClass)) { + $targetItem = $targetItem.previousElementSibling; + } + const $input = $targetItem.querySelector('input'); + if ($input) { + $input.focus(); + } else $targetItem.focus(); + }, + keyListeners = [ + { + keys: ['Escape'], + listener: (event) => { + event.stopPropagation(); + hideConfig(); + }, + opts: { listenInInput: true, keydown: true }, + }, + { + keys: [' '], + listener: (event) => clickItem(event), + opts: { keydown: true }, + }, + { + keys: ['Enter'], + listener: (event) => clickItem(event), + opts: { keydown: true }, + }, + { + keys: ['ArrowDown'], + listener: focusNextItem, + opts: { listenInInput: true, keydown: true }, + }, + { + keys: ['ArrowUp'], + listener: focusPrevItem, + opts: { listenInInput: true, keydown: true }, + }, + { + keys: ['Tab'], + listener: focusNextItem, + opts: { listenInInput: true, keydown: true }, + }, + { + keys: ['Shift', 'Tab'], + listener: focusPrevItem, + opts: { listenInInput: true, keydown: true }, + }, + ]; + + const renderConfigItem = async ($collection, menuItem) => { + if (menuItem.key === 'divider') + return web.html`
`; + + const blockId = $collection.dataset.blockId, + storedState = await db.get(['collections', blockId, menuItem.key], menuItem.default), + $item = web.html`
`; + + switch (menuItem.type) { + case 'toggle': + const $label = web.html`
${menuItem.name}
`, + $toggle = web.html`
`; + web.render($item, $label, $toggle); + $item.setAttribute('tabindex', 0); + $item.addEventListener('click', async (e) => { + e.stopPropagation(); + const newState = !($toggle.dataset.toggled === 'true'); + $toggle.dataset.toggled = newState; + await db.set(['collections', blockId, menuItem.key], newState); + setTweakState($collection, menuItem.key, newState); + if (menuItem.action) menuItem.action($collection, newState); + }); + break; + + case 'input': + const $input = web.html`
+ +
`; + web.render($item, $input); + $item.addEventListener('click', (e) => e.stopPropagation()); + if (menuItem.action) { + $input.firstElementChild.addEventListener('input', async (e) => { + e.stopPropagation(); + const newState = e.target.value; + await db.set(['collections', blockId, menuItem.key], newState); + menuItem.action($collection, newState); + }); + } + break; + } + return $item; + }, + renderConfig = async ($collection, $button) => { + if (document.querySelector(`.${overlayContainerClass}`)) return; + + const collectionViewType = getViewType($collection); + if (!collectionViewType) return; + + const $overlay = web.html`
`; + $overlay.addEventListener('click', hideConfig); + web.render(document.querySelector(notionAppSelector), $overlay); + + const $config = web.html`
`, + viewMenuItems = menuItems.filter( + (item) => + (!item.views || item.views.includes(collectionViewType)) && + (!item.linkedOnly || isLinked($collection)) + ), + $menuItemElements = await Promise.all( + viewMenuItems.map((item) => renderConfigItem($collection, item)) + ); + web.render($config, ...$menuItemElements); + const $firstMenuItem = + $config.firstElementChild.getElementsByTagName('input')[0] || + $config.firstElementChild; + + const $position = web.html` +
+
+
+ `; + $position.firstElementChild.appendChild($config); + web.render($overlay, $position); + + const rect = $button.getBoundingClientRect(); + $position.style.left = + Math.min(rect.left + rect.width / 2, window.innerWidth - ($config.offsetWidth + 14)) + + 'px'; + $position.style.top = + Math.min( + rect.top + rect.height / 2, + window.innerHeight - ($config.offsetHeight + 14) + ) + 'px'; + + setTweakState($collection, 'config-open', true); + for (const { keys, listener, opts } of keyListeners) { + web.addHotkeyListener(keys, listener, opts); + } + await $config.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 200 }).finished; + $firstMenuItem.focus(); + }; + async function hideConfig() { + const $overlay = document.querySelector(`.${overlayContainerClass}`), + $collection = document.querySelector(configOpenCollectionSelector); + if (!$overlay) return; + + $overlay.removeEventListener('click', hideConfig); + for (const { listener } of keyListeners) web.removeHotkeyListener(listener); + + await document + .querySelector(`.${configMenuClass}`) + .animate([{ opacity: 1 }, { opacity: 0 }], { duration: 200 }).finished; + setTweakState($collection, 'config-open', false); + $overlay.remove(); + } + + const simplifyCollection = async () => { + for (const $collection of document.querySelectorAll(collectionViewSelector)) { + const blockId = $collection.dataset.blockId, + $addNew = $collection.querySelector(collectionAddNewSelector); + if ($collection.querySelector(`.${configButtonClass}`) || !$addNew) continue; + + const $configButton = $addNew.previousElementSibling.cloneNode(); + $configButton.className = configButtonClass; + $configButton.innerHTML = configButtonSvg; + $configButton.addEventListener('click', () => { + renderConfig($collection, $configButton); + }); + $addNew.parentElement.prepend($configButton); + + for (const item of menuItems) { + if (item.key === 'divider') continue; + const state = await db.get(['collections', blockId, item.key], item.default); + if ((item.type !== 'input' && !item.linkedOnly) || isLinked($collection)) { + setTweakState($collection, item.key, state); + } + if (state && item.action) item.action($collection, state); + } + } + }; + web.addDocumentObserver(simplifyCollection, [collectionViewSelector]); +} diff --git a/src/mods/simpler-databases/mod.json b/src/mods/simpler-databases/mod.json new file mode 100644 index 0000000..62d45e3 --- /dev/null +++ b/src/mods/simpler-databases/mod.json @@ -0,0 +1,23 @@ +{ + "name": "simpler databases", + "id": "752933b5-1258-44e3-b49a-61b4885f8bda", + "version": "0.2.0", + "description": "adds a menu to inline databases to toggle ui elements.", + "preview": "simpler-databases.jpg", + "tags": ["extension", "layout"], + "authors": [ + { + "name": "CloudHill", + "email": "rh.cloudhill@gmail.com", + "homepage": "https://github.com/CloudHill", + "avatar": "https://avatars.githubusercontent.com/u/54142180" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [] +} diff --git a/src/mods/simpler-databases/simpler-databases.jpg b/src/mods/simpler-databases/simpler-databases.jpg new file mode 100644 index 0000000..eec09ae Binary files /dev/null and b/src/mods/simpler-databases/simpler-databases.jpg differ diff --git a/src/mods/tabs/client.mjs b/src/mods/tabs/client.mjs new file mode 100644 index 0000000..9373351 --- /dev/null +++ b/src/mods/tabs/client.mjs @@ -0,0 +1,82 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web, electron }, db) { + const newTabHotkey = await db.get(['new_tab']), + closeTabHotkey = await db.get(['close_tab']), + restoreTabHotkey = await db.get(['restore_tab']), + selectTabModifier = await db.get(['select_modifier']), + prevTabHotkey = await db.get(['prev_tab']), + nextTabHotkey = await db.get(['next_tab']); + web.addHotkeyListener(newTabHotkey, () => { + electron.sendMessageToHost('new-tab'); + }); + web.addHotkeyListener(restoreTabHotkey, () => { + electron.sendMessageToHost('restore-tab'); + }); + web.addHotkeyListener(closeTabHotkey, () => electron.sendMessageToHost('close-tab')); + for (let i = 1; i < 10; i++) { + web.addHotkeyListener([selectTabModifier, i.toString()], () => { + electron.sendMessageToHost('select-tab', i); + }); + } + web.addHotkeyListener(prevTabHotkey, () => { + electron.sendMessageToHost('select-prev-tab') + }); + web.addHotkeyListener(nextTabHotkey, () => { + electron.sendMessageToHost('select-next-tab') + }); + + const breadcrumbSelector = + '.notion-topbar > div > [class="notranslate"] > .notion-focusable:last-child', + imgIconSelector = `${breadcrumbSelector} .notion-record-icon img:not(.notion-emoji)`, + emojiIconSelector = `${breadcrumbSelector} .notion-record-icon img.notion-emoji`, + nativeIconSelector = `${breadcrumbSelector} .notion-record-icon [role="image"]`, + titleSelector = `${breadcrumbSelector} > :not(.notion-record-icon)`, + viewSelector = '.notion-collection-view-select'; + + let title = '', + icon = ''; + const notionSetWindowTitle = __electronApi.setWindowTitle, + getIcon = () => { + const $imgIcon = document.querySelector(imgIconSelector), + $emojiIcon = document.querySelector(emojiIconSelector), + $nativeIcon = document.querySelector(nativeIconSelector); + if ($imgIcon) { + return `url("${$imgIcon.src}") 0 / 100%`; + } + if ($emojiIcon) { + return $emojiIcon.style.background.replace( + /url\("\/images/, + 'url("notion://www.notion.so/images' + ); + } + if ($nativeIcon) return $nativeIcon.ariaLabel; + return ''; + }, + updateTitle = (newTitle = title) => { + if (!newTitle) return; + title = newTitle; + icon = getIcon(); + electron.sendMessageToHost('set-tab-title', title); + electron.sendMessageToHost('set-tab-icon', icon); + notionSetWindowTitle(title); + }; + __electronApi.setWindowTitle = (newTitle) => updateTitle(newTitle); + document.addEventListener('focus', updateTitle); + electron.onMessage('trigger-title-update', () => updateTitle()); + + await web.whenReady([titleSelector]); + const $title = document.querySelector(titleSelector), + $view = document.querySelector(viewSelector); + if (!title && $title) { + if ($view) { + updateTitle(`${$title.innerText} | ${$view.innerText}`); + } else updateTitle($title.innerText); + } +} diff --git a/src/mods/tabs/createWindow.cjs b/src/mods/tabs/createWindow.cjs new file mode 100644 index 0000000..bda9a95 --- /dev/null +++ b/src/mods/tabs/createWindow.cjs @@ -0,0 +1,23 @@ +/** + * notion-enhancer: tabs + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function (api, db, __exports, __eval) { + const notionCreateWindow = __exports.createWindow; + __exports.createWindow = (relativeUrl = '', args) => { + const windows = api.electron.getNotionWindows(); + // '/' is used to create new windows intentionally + if (relativeUrl && relativeUrl !== '/' && windows.length) { + const window = api.electron.getFocusedNotionWindow() || windows[0]; + window.webContents.send('notion-enhancer:open-tab', { + notionUrl: `notion://www.notion.so${relativeUrl}`, + }); + return window; + } + return notionCreateWindow(relativeUrl, args); + }; +}; diff --git a/src/mods/tabs/main.cjs b/src/mods/tabs/main.cjs new file mode 100644 index 0000000..9900eee --- /dev/null +++ b/src/mods/tabs/main.cjs @@ -0,0 +1,37 @@ +/** + * notion-enhancer: tabs + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function ({}, db, __exports, __eval) { + const electron = require('electron'); + electron.ipcMain.on('notion-enhancer:close-tab', (event, { window, id }) => { + electron.webContents.fromId(window).send('notion-enhancer:close-tab', id); + }); + + __eval(` + const notionHandleActivate = handleActivate; + handleActivate = (relativeUrl) => { + const api = require('notion-enhancer/api/index.cjs'), + { BrowserWindow } = require('electron'), + windows = api.electron.getNotionWindows(), + electronWindows = BrowserWindow.getAllWindows(); + if (relativeUrl && windows.length) { + const win = api.electron.getFocusedNotionWindow() || windows[0]; + win.webContents.send('notion-enhancer:open-tab', { + notionUrl: \`notion://www.notion.so\$\{relativeUrl\}\`, + }); + win.show(); + win.focus(); + } else if (relativeUrl && electronWindows.length && !windows.length) { + // enhancer menu is open: prevent override + const { createWindow } = api.electron.notionRequire('main/createWindow'), + win = createWindow(relativeUrl); + win.focus(); + } else notionHandleActivate(relativeUrl); + }; + `); +}; diff --git a/src/mods/tabs/mod.json b/src/mods/tabs/mod.json new file mode 100644 index 0000000..0d3004b --- /dev/null +++ b/src/mods/tabs/mod.json @@ -0,0 +1,96 @@ +{ + "name": "tabs", + "id": "e1692c29-475e-437b-b7ff-3eee872e1a42", + "environments": ["linux", "win32", "darwin"], + "version": "0.3.0", + "description": "open multiple notion pages in a single window.", + "preview": "tabs.jpg", + "tags": ["extension", "app"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": { + "frame": ["tabs.css"] + }, + "js": { + "client": ["client.mjs"], + "electron": [ + { "source": "main.cjs", "target": "main/main.js" }, + { "source": "systemMenu.cjs", "target": "main/systemMenu.js" }, + { "source": "createWindow.cjs", "target": "main/createWindow.js" }, + { "source": "rendererIndex.cjs", "target": "renderer/index.js" } + ] + }, + "options": [ + { + "type": "toggle", + "key": "remember_last_open", + "label": "remember last open tabs", + "tooltip": "**a continue-where-you-left-off experience** (reopens recently active tabs after an app relaunch)", + "value": true + }, + { + "type": "select", + "key": "label_type", + "label": "tab labels", + "values": ["page icon & title", "page icon only", "page title only"] + }, + { + "type": "select", + "key": "layout_style", + "label": "tab layout", + "values": ["traditional tabbed", "rectangular", "bubble", "compact"] + }, + { + "type": "select", + "key": "select_modifier", + "label": "tab select modifier", + "tooltip": "**usage: Modifier+1 to Modifier+9, Modifier+ArrowLeft and Modifier+ArrowRight**", + "values": [ + "Alt", + "Command", + "Control", + "Super", + "Alt+Shift", + "Command+Shift", + "Control+Shift", + "Super+Shift" + ] + }, + { + "type": "hotkey", + "key": "prev_tab", + "label": "previous tab hotkey", + "value": "Control+Shift+Tab" + }, + { + "type": "hotkey", + "key": "next_tab", + "label": "next tab hotkey", + "value": "Control+Tab" + }, + { + "type": "hotkey", + "key": "new_tab", + "label": "new tab hotkey", + "value": "Control+T" + }, + { + "type": "hotkey", + "key": "close_tab", + "label": "close tab hotkey", + "value": "Control+W" + }, + { + "type": "hotkey", + "key": "restore_tab", + "label": "restore previously opened tab hotkey", + "value": "Control+Shift+T" + } + ] +} diff --git a/src/mods/tabs/rendererIndex.cjs b/src/mods/tabs/rendererIndex.cjs new file mode 100644 index 0000000..8a5a5b1 --- /dev/null +++ b/src/mods/tabs/rendererIndex.cjs @@ -0,0 +1,142 @@ +/** + * notion-enhancer: tabs + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function (api, db, __exports, __eval) { + const url = require('url'), + electron = require('electron'), + electronWindow = electron.remote.getCurrentWindow(), + { components, web, env } = api; + + window['__start'] = async () => { + // display:none; to prevent content flash while css loads + document.body.style.display = 'none'; + + const tabCache = new Map(), + Tab = await require('./tab.cjs')(api, db, tabCache); + document.body.dataset.tabLabels = await db.get(['label_type']); + document.body.dataset.tabStyle = await db.get(['layout_style']); + + const $header = web.html`
`, + $tabs = web.html`
`, + $newTab = web.html`
${await components.feather('plus')}
`, + $root = document.querySelector('#root'), + $windowActions = web.html`
`; + document.body.prepend(web.render($header, $tabs, $newTab, $windowActions)); + + // make space for native window buttons on mac + if (env.name === 'darwin') $tabs.style.paddingLeft = '72px'; + + $newTab.addEventListener('click', () => new Tab($tabs, $root)); + electron.ipcRenderer.on('notion-enhancer:close-tab', (event, id) => { + const tab = tabCache.get(id); + if (tab) tab.close(); + }); + electron.ipcRenderer.on( + 'notion-enhancer:open-tab', + (event, opts) => new Tab($tabs, $root, opts) + ); + + const rememberLastOpen = await db.get(['remember_last_open']), + openTabs = await db.get(['last_open_tabs_cache']); + if (rememberLastOpen && openTabs && Array.isArray(openTabs)) { + for (const tab of openTabs) { + new Tab($tabs, $root, { ...tab, cancelAnimation: true }); + } + } else { + new Tab($tabs, $root, { + notionUrl: url.parse(window.location.href, true).query.path, + cancelAnimation: true, + }); + } + window.addEventListener('beforeunload', () => { + const openTabs = [...$tabs.children] + .filter(($tab) => tabCache.get($tab.id)) + .map(($tab) => { + const tab = tabCache.get($tab.id); + return { + notionUrl: tab.$notion.src, + icon: tab.icon, + title: tab.title, + }; + }); + db.set(['last_open_tabs_cache'], openTabs); + }); + + let $draggedTab; + const $dragIndicator = web.html``, + getDragTarget = ($el) => { + while (!$el.matches('.tab, header, body')) $el = $el.parentElement; + if ($el.matches('header')) $el = $el.firstElementChild; + return $el.matches('#tabs, .tab') ? $el : undefined; + }, + resetDraggedTabs = () => { + if ($draggedTab) { + $dragIndicator.remove(); + $draggedTab.style.opacity = ''; + $draggedTab = undefined; + } + }; + $header.addEventListener('dragstart', (event) => { + $draggedTab = getDragTarget(event.target); + $draggedTab.style.opacity = 0.5; + const tab = tabCache.get($draggedTab.id); + event.dataTransfer.setData( + 'text', + JSON.stringify({ + window: electronWindow.webContents.id, + tab: $draggedTab.id, + icon: tab.$tabIcon.innerText || tab.$tabIcon.style.background, + title: tab.$tabTitle.innerText, + url: tab.$notion.src, + }) + ); + }); + $header.addEventListener('dragover', (event) => { + const $target = getDragTarget(event.target); + if ($target) { + if ($target.matches('#tabs')) { + $target.after($dragIndicator); + } else if ($target.matches('#tabs > :first-child')) { + $tabs.before($dragIndicator); + } else $target.before($dragIndicator); + event.preventDefault(); + } + }); + document.addEventListener('drop', (event) => { + const eventData = JSON.parse(event.dataTransfer.getData('text')), + $target = getDragTarget(event.target) || $tabs, + sameWindow = eventData.window === electronWindow.webContents.id, + tabMovement = + !sameWindow || + ($target && + $target !== $draggedTab && + $target !== $draggedTab.nextElementSibling && + ($target.matches('#tabs') ? $target.lastElementChild !== $draggedTab : true)); + if (!sameWindow) { + electron.ipcRenderer.send('notion-enhancer:close-tab', { + window: eventData.window, + id: eventData.tab, + }); + const transferred = new Tab($tabs, $root, { + notionUrl: eventData.url, + cancelAnimation: true, + icon: eventData.icon, + title: eventData.title, + }); + $draggedTab = transferred.$tab; + } + if (tabMovement) { + if ($target.matches('#tabs')) { + $target.append($draggedTab); + } else $target.before($draggedTab); + } + resetDraggedTabs(); + }); + $header.addEventListener('dragend', (event) => resetDraggedTabs()); + }; +}; diff --git a/src/mods/tabs/systemMenu.cjs b/src/mods/tabs/systemMenu.cjs new file mode 100644 index 0000000..9460cff --- /dev/null +++ b/src/mods/tabs/systemMenu.cjs @@ -0,0 +1,20 @@ +/** + * notion-enhancer: tabs + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function ({}, db, __exports, __eval) { + const notionSetupSystemMenu = __exports.setupSystemMenu; + __exports.setupSystemMenu = (locale) => { + const { Menu } = require('electron'), + template = notionSetupSystemMenu(locale); + for (const category of template) { + category.submenu = category.submenu.filter((item) => item.role !== 'close'); + } + Menu.setApplicationMenu(Menu.buildFromTemplate(template)); + return template; + }; +}; diff --git a/src/mods/tabs/tab.cjs b/src/mods/tabs/tab.cjs new file mode 100644 index 0000000..4e44cc0 --- /dev/null +++ b/src/mods/tabs/tab.cjs @@ -0,0 +1,296 @@ +/** + * notion-enhancer: tabs + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +let focusedTab; + +module.exports = async function (api, db, tabCache = new Map()) { + const { components, web, fmt, fs } = api, + electron = require('electron'), + electronWindow = electron.remote.getCurrentWindow(), + notionIpc = api.electron.notionRequire('helpers/notionIpc'), + xIcon = await components.feather('x'); + + return class Tab { + id = fmt.uuidv4(); + + $notion = web.html` + + `; + $search = web.html` + + `; + + $tabIcon = web.html``; + $svgIconPlaceholder = web.html` + + + `; + $tabTitle = web.html``; + $closeTab = web.html`${xIcon}`; + $tab = web.render( + web.html`
`, + this.$tabIcon, + this.$svgIconPlaceholder, + this.$tabTitle, + this.$closeTab + ); + + constructor( + $tabList, + $tabContainer, + { + notionUrl = 'notion://www.notion.so/', + cancelAnimation = false, + icon = '', + title = 'notion.so', + cache = tabCache, + } = {} + ) { + this.tabCache = cache; + this.$tabList = $tabList; + this.$tabContainer = $tabContainer; + + this.$notion.src = notionUrl; + this.setTitle(title); + this.setIcon(icon); + this.tabCache.set(this.$tab.id, this); + + electronWindow.on('focus', () => { + if (focusedTab === this) this.$notion.focus(); + }); + this.$tab.addEventListener('click', (event) => { + if (event.target !== this.$closeTab && !this.$closeTab.contains(event.target)) { + this.focus(); + } + }); + this.$closeTab.addEventListener('click', () => this.close()); + + this.open(cancelAnimation); + this.addNotionListeners(); + return this; + } + + open(cancelAnimation = false) { + this.closed = false; + web.render(this.$tabList, this.$tab); + web.render(this.$tabContainer, this.$search); + web.render(this.$tabContainer, this.$notion); + if (!cancelAnimation) { + this.$tab.animate([{ width: '0px' }, { width: `${this.$tab.clientWidth}px` }], { + duration: 100, + easing: 'ease-in', + }).finished; + } + this.focus(); + } + async focus() { + document.querySelectorAll('.notion-webview, .search-webview').forEach(($webview) => { + if (![this.$notion, this.$search].includes($webview)) $webview.style.display = ''; + }); + document.querySelectorAll('.tab.current').forEach(($tab) => { + if ($tab !== this.$tab) $tab.classList.remove('current'); + }); + this.$tab.classList.add('current'); + this.$notion.style.display = 'flex'; + this.$search.style.display = 'flex'; + if (this.domReady) this.focusNotion(); + focusedTab = this; + } + async close() { + const $sibling = this.$tab.nextElementSibling || this.$tab.previousElementSibling; + if ($sibling) { + this.closed = Date.now(); + if (!focusedTab || focusedTab === this) $sibling.click(); + const width = `${this.$tab.clientWidth}px`; + this.$tab.style.width = 0; + this.$tab.style.pointerEvents = 'none'; + await this.$tab.animate([{ width }, { width: '0px' }], { + duration: 100, + easing: 'ease-out', + }).finished; + this.$tab.remove(); + this.$notion.remove(); + this.$search.remove(); + this.$tab.style.width = ''; + this.$tab.style.pointerEvents = ''; + this.domReady = false; + } else electronWindow.close(); + } + + title = ''; + setTitle(title) { + this.title = title; + this.$tabTitle.innerText = title; + } + icon = ''; + setIcon(icon) { + this.icon = icon; + if (icon.startsWith('url(')) { + // img + this.$tabIcon.style.background = icon; + this.$tabIcon.innerText = ''; + } else { + // unicode (native) + this.$tabIcon.innerText = icon; + this.$tabIcon.style.background = ''; + } + } + + webContents() { + return electron.remote.webContents.fromId(this.$notion.getWebContentsId()); + } + focusNotion() { + document.activeElement?.blur?.(); + this.$notion.blur(); + this.$notion.focus(); + requestAnimationFrame(() => { + notionIpc.sendIndexToNotion(this.$notion, 'notion-enhancer:trigger-title-update'); + }); + } + focusSearch() { + document.activeElement?.blur?.(); + this.$search.blur(); + this.$search.focus(); + } + + domReady = false; + addNotionListeners() { + const fromNotion = (channel, listener) => + notionIpc.receiveIndexFromNotion.addListener(this.$notion, channel, listener), + fromSearch = (channel, listener) => + notionIpc.receiveIndexFromSearch.addListener(this.$search, channel, listener), + toSearch = (channel, data) => notionIpc.sendIndexToSearch(this.$search, channel, data); + + this.$notion.addEventListener('dom-ready', () => { + if (focusedTab === this) this.focus(); + this.domReady = true; + + const navigateHistory = (event, cmd) => { + const swipe = event === 'swipe', + back = swipe ? cmd === 'left' : cmd === 'browser-backward', + fwd = swipe ? cmd === 'right' : cmd === 'browser-forward'; + if (back && this.$notion.canGoBack()) this.$notion.goBack(); + if (fwd && this.$notion.canGoForward()) this.$notion.goForward(); + }; + electronWindow.addListener('app-command', (e, cmd) => navigateHistory('app-cmd', cmd)); + electronWindow.addListener('swipe', (e, dir) => navigateHistory('swipe', dir)); + + this.webContents().addListener('found-in-page', (event, result) => { + const matches = result + ? { count: result.matches, index: result.activeMatchOrdinal } + : { count: 0, index: 0 }; + toSearch('search:result', matches); + }); + }); + + notionIpc.proxyAllMainToNotion(this.$notion); + + fromNotion('search:start', () => this.startSearch()); + fromNotion('search:stop', () => this.stopSearch()); + fromNotion('search:set-theme', (theme) => toSearch('search:set-theme', theme)); + fromSearch('search:clear', () => this.clearSearch()); + fromSearch('search:stop', () => this.stopSearch()); + fromSearch('search:next', (query) => this.searchNext(query)); + fromSearch('search:prev', (query) => this.searchPrev(query)); + + fromNotion('zoom', (zoomFactor) => { + this.webContents().setZoomFactor(zoomFactor); + }); + + fromNotion('notion-enhancer:set-tab-title', (title) => this.setTitle(title)); + fromNotion('notion-enhancer:set-tab-icon', (icon) => this.setIcon(icon)); + + fromNotion( + 'notion-enhancer:new-tab', + () => new this.constructor(this.$tabList, this.$tabContainer) + ); + fromNotion('notion-enhancer:close-tab', () => this.close()); + fromNotion('notion-enhancer:restore-tab', () => { + const tab = [...this.tabCache.values()] + .filter((tab) => tab.closed) + .sort((a, b) => b.closed - a.closed)[0]; + if (tab) tab.open(); + }); + fromNotion('notion-enhancer:select-tab', (i) => { + const $tab = i === 9 ? this.$tabList.lastElementChild : this.$tabList.children[i - 1]; + if ($tab) $tab.click(); + }); + fromNotion('notion-enhancer:select-prev-tab', () => { + if (this.$tabList.count == 1) { + return; + } + const $sibling = this.$tab.previousElementSibling; + if ($sibling) { + $sibling.click(); + } + else { + let $tab = this.$tabList.lastElementChild; + if ($tab) { + $tab.click(); + } + } + }); + fromNotion('notion-enhancer:select-next-tab', () => { + if (this.$tabList.count == 1) { + return; + } + const $sibling = this.$tab.nextElementSibling; + if ($sibling) { + $sibling.click(); + } + else { + let $tab = this.$tabList.children[0] + if ($tab) { + $tab.click(); + } + } + }); + } + + #firstQuery = true; + startSearch() { + this.$search.classList.add('search-active'); + this.focusSearch(); + notionIpc.sendIndexToSearch(this.$search, 'search:start'); + notionIpc.sendIndexToNotion(this.$search, 'search:started'); + } + clearSearch() { + this.#firstQuery = true; + this.webContents().stopFindInPage('clearSelection'); + } + stopSearch() { + this.$search.classList.remove('search-active'); + this.focusNotion(); + this.clearSearch(); + notionIpc.sendIndexToSearch(this.$search, 'search:reset'); + notionIpc.sendIndexToNotion(this.$notion, 'search:stopped'); + } + searchNext(query) { + this.webContents().findInPage(query, { + forward: true, + findNext: !this.#firstQuery, + }); + this.#firstQuery = false; + } + searchPrev(query) { + this.webContents().findInPage(query, { + forward: false, + findNext: !this.#firstQuery, + }); + this.#firstQuery = false; + } + }; +}; diff --git a/src/mods/tabs/tabs.css b/src/mods/tabs/tabs.css new file mode 100644 index 0000000..3b81480 --- /dev/null +++ b/src/mods/tabs/tabs.css @@ -0,0 +1,236 @@ +/** + * notion-enhancer: tabs + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +* { + box-sizing: border-box; +} + +html, +body { + height: 100%; + width: 100%; + margin: 0; + padding: 0; + background: var(--theme--bg) !important; + overflow: hidden; +} + +body { + display: flex !important; + flex-direction: column; +} + +header { + display: flex; + background: var(--theme--bg_secondary); + border-bottom: 1px solid var(--theme--ui_divider); + width: 100%; + padding: 0.5em; + user-select: none; + -webkit-app-region: drag; + z-index: 3; + font-size: 16px; +} + +#tabs { + display: flex; + overflow: hidden; +} +.tab { + display: flex; + flex-grow: 1; + flex-shrink: 1; + width: 14em; + max-width: 14em; + overflow: hidden; + padding: 0.4em 0.6em; + + color: var(--theme--text_secondary); + background: var(--theme--bg); + font-family: var(--theme--font_sans); + font-weight: 500; + border: none; + -webkit-app-region: no-drag; +} +.tab:hover { + background: var(--theme--ui_interactive-hover); +} +.tab.current { + background: var(--theme--ui_interactive-active); +} + +.drag-indicator { + z-index: 1; + width: 0.125em; + margin: 0 -0.0625em; + background: var(--theme--accent_blue-selection); +} + +.tab-title { + white-space: nowrap; + overflow: hidden; + margin-right: 0.25em; +} +.tab-icon { + margin-right: 0.375em; + flex-shrink: 0; +} +.tab-icon[style*='background'] { + width: 0.875em; + height: 0.875em; + align-self: center; + margin-right: 0.5em; +} +.tab-icon:not([style*='background']):empty, +.tab-icon + svg { + display: none; +} +.tab-icon:not([style*='background']):empty + svg { + /* placeholder icon */ + flex-shrink: 0; + display: inline-block; + margin: 1px 0.5em 0 0; + width: 1.125em; + height: 1.125em; + display: block; + backface-visibility: hidden; + fill: var(--theme--icon_secondary); +} + +.new-tab, +.tab-close { + transition: background 20ms ease-in 0s; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + border-radius: 0.1875em; + height: 1.25em; + width: 1.25em; + padding: 0 0.25px 0 0; + + align-self: center; + border: none; + background: transparent; + -webkit-app-region: no-drag; +} +.new-tab svg, +.tab-close svg { + width: 0.875em; + height: 0.875em; + fill: var(--theme--icon_secondary); + color: var(--theme--icon_secondary); +} +.new-tab:focus, +.new-tab:hover, +.tab-close:focus, +.tab-close:hover { + background: var(--theme--ui_interactive-hover); +} +.new-tab:active, +.tab-close:active { + background: var(--theme--ui_interactive-active); +} + +.new-tab { + margin: 0 3em 0 0.375em; +} +.tab-close { + margin-left: auto; +} + +#window-actions { + display: flex; + align-items: center; + margin-left: auto; +} +#window-actions > * { + -webkit-app-region: no-drag; +} + +[data-tab-labels='page title only'] .tab-icon { + display: none; +} +[data-tab-labels='page icon only'] .tab { + width: 4em; + max-width: 4em; +} +[data-tab-labels='page icon only'] .tab-title { + display: none; +} + +[data-tab-style='rectangular'] .new-tab, +[data-tab-style='traditional tabbed'] .new-tab { + margin-bottom: -0.25em; +} +[data-tab-style='rectangular'] .tab-close, +[data-tab-style='traditional tabbed'] .tab-close { + align-self: auto; +} +[data-tab-style='rectangular'] .drag-indicator, +[data-tab-style='traditional tabbed'] .drag-indicator, +[data-tab-style='rectangular'] #tabs { + margin-bottom: -0.5em; +} +[data-tab-style='rectangular'] .tab { + padding: 0.6em 0.6em 0.8em 0.6em; +} +[data-tab-style='traditional tabbed'] header { + padding-top: 0.6875em; +} +[data-tab-style='traditional tabbed'] #tabs { + margin: -0.1875em 0 -0.5em 0; +} +[data-tab-style='traditional tabbed'] .tab { + border-top-left-radius: 0.875em; + border-top-right-radius: 0.875em; + padding: 0.6em; +} +[data-tab-style='bubble'] .tab { + border-radius: 0.375em; +} +[data-tab-style='bubble'] .tab:not(:first-child) { + margin-left: 0.5em; +} +[data-tab-style='bubble'] .drag-indicator { + margin: 0 -0.3125em 0 0.1875em; +} +[data-tab-style='bubble'] .drag-indicator:first-child { + margin: 0 0.187em 0 -0.312em; +} +[data-tab-style='compact'] header { + padding: 0; + font-size: 14px; +} +[data-tab-style='compact'] #window-actions { + transform: scale(0.8); + margin-right: -0.35em; +} + +#root { + flex-grow: 1; +} +.notion-webview { + width: 100%; + height: 100%; + display: none; +} +.search-webview { + width: 100%; + height: 60px; + display: none; + transition: transform 70ms ease-in; + transform: translateY(-100%); + pointer-events: none; + position: absolute; + z-index: 2; +} +.search-webview.search-active { + transition: transform 70ms ease-out; + transform: translateY(0%); + pointer-events: auto; +} diff --git a/src/mods/tabs/tabs.jpg b/src/mods/tabs/tabs.jpg new file mode 100644 index 0000000..2c9d0ab Binary files /dev/null and b/src/mods/tabs/tabs.jpg differ diff --git a/src/mods/theming/_mapColors.js b/src/mods/theming/_mapColors.js new file mode 100644 index 0000000..9798198 --- /dev/null +++ b/src/mods/theming/_mapColors.js @@ -0,0 +1,545 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +// a development tool used for generating color variables +// and the contents of colors.css + +// included for posterity/updates +// -- not executed by the enhancer at runtime + +const lightGray = { + 'light': { + 'tag': 'rgba(227, 226, 224, 0.5)', + 'tag-text': 'rgb(50, 48, 44)', + 'board': 'rgba(249, 249, 245, 0.5)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgba(145, 145, 142, 0.5)', + }, + 'dark': { + 'tag': 'rgba(71, 76, 80, 0.7)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgba(51, 55, 59, 0.7)', + 'board-card': 'rgba(60, 65, 68, 0.7)', + 'board-card_text': 'inherit', + 'board-text': 'rgba(107, 112, 116, 0.7)', + }, +}; + +const colors = { + 'gray': { + 'light': { + 'text': 'rgba(120, 119, 116, 1)', + 'highlight': 'rgba(241, 241, 239, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(241, 241, 239)', + 'callout-text': 'currentColor', + 'tag': 'rgb(227, 226, 224)', + 'tag-text': 'rgb(50, 48, 44)', + 'board': 'rgba(247, 247, 245, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(145, 145, 142)', + }, + 'dark': { + 'text': 'rgba(159, 164, 169, 1)', + 'highlight': 'rgba(60, 65, 68, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(60, 65, 68)', + 'callout-text': 'currentColor', + 'tag': 'rgb(71, 76, 80)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(51, 55, 59)', + 'board-card': 'rgb(60, 65, 68)', + 'board-card_text': 'inherit', + 'board-text': 'rgb(107, 112, 116)', + }, + }, + 'brown': { + 'light': { + 'text': 'rgba(159, 107, 83, 1)', + 'highlight': 'rgba(244, 238, 238, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(244, 238, 238)', + 'callout-text': 'currentColor', + 'tag': 'rgb(238, 224, 218)', + 'tag-text': 'rgb(68, 42, 30)', + 'board': 'rgba(250, 246, 245, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(187, 132, 108)', + }, + 'dark': { + 'text': 'rgba(212, 150, 117, 1)', + 'highlight': 'rgba(76, 61, 53, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(76, 61, 53)', + 'callout-text': 'currentColor', + 'tag': 'rgb(92, 71, 61)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(59, 54, 51)', + 'board-card': 'rgb(76, 61, 53)', + 'board-card_text': 'inherit', + 'board-text': 'rgb(155, 98, 69)', + }, + }, + 'orange': { + 'light': { + 'text': 'rgba(217, 115, 13, 1)', + 'highlight': 'rgba(251, 236, 221, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(251, 236, 221)', + 'callout-text': 'currentColor', + 'tag': 'rgb(250, 222, 201)', + 'tag-text': 'rgb(73, 41, 14)', + 'board': 'rgba(252, 245, 242, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(215, 129, 58)', + }, + 'dark': { + 'text': 'rgba(217, 133, 56, 1)', + 'highlight': 'rgba(85, 59, 41, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(85, 59, 41)', + 'callout-text': 'currentColor', + 'tag': 'rgb(136, 84, 44)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(61, 54, 49)', + 'board-card': 'rgb(85, 59, 41)', + 'board-text': 'rgb(168, 92, 30)', + }, + }, + 'yellow': { + 'light': { + 'text': 'rgba(203, 145, 47, 1)', + 'highlight': 'rgba(251, 243, 219, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(251, 243, 219)', + 'callout-text': 'currentColor', + 'tag': 'rgb(253, 236, 200)', + 'tag-text': 'rgb(64, 44, 27)', + 'board': 'rgba(250, 247, 237, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(203, 148, 51)', + }, + 'dark': { + 'text': 'rgba(201, 145, 38, 1)', + 'highlight': 'rgba(79, 64, 41, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(79, 64, 41)', + 'callout-text': 'currentColor', + 'tag': 'rgb(146, 118, 63)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(56, 55, 49)', + 'board-card': 'rgb(79, 64, 41)', + 'board-card_text': 'inherit', + 'board-text': 'rgb(137, 107, 42)', + }, + }, + 'green': { + 'light': { + 'text': 'rgba(68, 131, 97, 1)', + 'highlight': 'rgba(237, 243, 236, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(237, 243, 236)', + 'callout-text': 'currentColor', + 'tag': 'rgb(219, 237, 219)', + 'tag-text': 'rgb(28, 56, 41)', + 'board': 'rgba(244, 248, 243, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(108, 155, 125)', + }, + 'dark': { + 'text': 'rgba(113, 178, 131, 1)', + 'highlight': 'rgba(46, 68, 58, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(46, 68, 58)', + 'callout-text': 'currentColor', + 'tag': 'rgb(50, 82, 65)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(49, 57, 53)', + 'board-card': 'rgb(46, 68, 58)', + 'board-card_text': 'inherit', + 'board-text': 'rgb(61, 124, 86)', + }, + }, + 'blue': { + 'light': { + 'text': 'rgba(51, 126, 169, 1)', + 'highlight': 'rgba(231, 243, 248, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(231, 243, 248)', + 'callout-text': 'currentColor', + 'tag': 'rgb(211, 229, 239)', + 'tag-text': 'rgb(24, 51, 71)', + 'board': 'rgba(241, 248, 251, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(91, 151, 189)', + }, + 'dark': { + 'text': 'rgba(102, 170, 218, 1)', + 'highlight': 'rgba(45, 66, 86, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(45, 66, 86)', + 'callout-text': 'currentColor', + 'tag': 'rgb(42, 78, 107)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(49, 56, 64)', + 'board-card': 'rgb(45, 66, 86)', + 'board-card_text': 'inherit', + 'board-text': 'rgb(46, 117, 164)', + }, + }, + 'purple': { + 'light': { + 'text': 'rgba(144, 101, 176, 1)', + 'highlight': 'rgba(244, 240, 247, 0.8)', + 'highlight-text': 'currentColor', + 'callout': 'rgba(244, 240, 247, 0.8)', + 'callout-text': 'currentColor', + 'tag': 'rgb(232, 222, 238)', + 'tag-text': 'rgb(65, 36, 84)', + 'board': 'rgba(249, 246, 252, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(167, 130, 195)', + }, + 'dark': { + 'text': 'rgba(176, 152, 217, 1)', + 'highlight': 'rgba(69, 58, 91, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(69, 58, 91)', + 'callout-text': 'currentColor', + 'tag': 'rgb(83, 68, 116)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(57, 53, 65)', + 'board-card': 'rgb(69, 58, 91)', + 'board-card_text': 'inherit', + 'board-text': 'rgb(123, 96, 180)', + }, + }, + 'pink': { + 'light': { + 'text': 'rgba(193, 76, 138, 1)', + 'highlight': 'rgba(249, 238, 243, 0.8)', + 'highlight-text': 'currentColor', + 'callout': 'rgba(249, 238, 243, 0.8)', + 'callout-text': 'currentColor', + 'tag': 'rgb(245, 224, 233)', + 'tag-text': 'rgb(76, 35, 55)', + 'board': 'rgba(251, 245, 251, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(205, 116, 159)', + }, + 'dark': { + 'text': 'rgba(223, 132, 209, 1)', + 'highlight': 'rgba(81, 56, 77, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(81, 56, 77)', + 'callout-text': 'currentColor', + 'tag': 'rgb(106, 59, 99)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(60, 53, 58)', + 'board-card': 'rgb(81, 56, 77)', + 'board-card_text': 'inherit', + 'board-text': 'rgb(169, 76, 157)', + }, + }, + 'red': { + 'light': { + 'text': 'rgba(212, 76, 71, 1)', + 'highlight': 'rgba(253, 235, 236, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(253, 235, 236)', + 'callout-text': 'currentColor', + 'tag': 'rgb(255, 226, 221)', + 'tag-text': 'rgb(93, 23, 21)', + 'board': 'rgba(253, 245, 243, 0.7)', + 'board-card': 'white', + 'board-card_text': 'inherit', + 'board-text': 'rgb(225, 111, 100)', + }, + 'dark': { + 'text': 'rgba(234, 135, 140, 1)', + 'highlight': 'rgba(94, 52, 54, 1)', + 'highlight-text': 'currentColor', + 'callout': 'rgb(94, 52, 54)', + 'callout-text': 'currentColor', + 'tag': 'rgb(122, 54, 59)', + 'tag-text': 'rgba(255, 255, 255, 0.88)', + 'board': 'rgb(66, 51, 51)', + 'board-card': 'rgb(94, 52, 54)', + 'board-card_text': 'inherit', + 'board-text': 'rgb(194, 65, 82)', + }, + }, +}; + +function css() { + const rgb = (color) => + color.startsWith('rgba') && color.endsWith(', 1)') + ? `rgb(${color.slice(5, -4)})` + : color, + notCallout = ":not([style*='border-radius'])", + notBoardCard = ":not([style*='box-shadow'])", + isTag = + "[style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;']", + isTagPalette = "[style*='border-radius: 3px;'][style*='width: 18px; height: 18px;']", + isHighlightPalette = + "[style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;']"; + let css = ''; + + // generate light gray separately + css += ` + + /* light gray */ + + .notion-body:not(.dark) [style*='background: ${lightGray.light['tag']}']${isTag}, + .notion-body.dark [style*='background: ${lightGray.dark['tag']}']${isTag} { + background: var(--theme--tag_light_gray) !important; + color: var(--theme--tag_light_gray-text) !important; + } + + .notion-body:not(.dark) [style*='background: ${ + lightGray.light['tag'] + }']${isTagPalette}, + .notion-body.dark [style*='background: ${ + lightGray.dark['board-text'] + }']${isTagPalette} { + background: var(--theme--tag_light_gray) !important; + color: var(--theme--tag_light_gray-text) !important; + } + + .notion-body:not(.dark) + .notion-board-group[style*='background-color: ${lightGray.light['board']}'], + .notion-body.dark + .notion-board-group[style*='background-color: ${lightGray.dark['board']}'], + .notion-body:not(.dark) .notion-board-view > .notion-selectable > :first-child > :nth-child(2) + [style*='background-color: ${lightGray.light['board']}'], + .notion-body.dark .notion-board-view > .notion-selectable > :first-child > :nth-child(2) + [style*='background-color: ${lightGray.dark['board']}'] { + background: var(--theme--board_light_gray) !important; + color: var(--theme--board_light_gray-text) !important; + } + .notion-body:not(.dark) + .notion-board-group[style*='background-color: ${lightGray.light['board']}'] + > [data-block-id] > [rel='noopener noreferrer'], + .notion-body.dark + .notion-board-group[style*='background-color: ${lightGray.dark['board']}'] + > [data-block-id] > [rel='noopener noreferrer'] { + background: var(--theme--board_light_gray-card) !important; + color: var(--theme--board_light_gray-card_text) !important; + } + .notion-body.dark + .notion-board-group[style*='background-color: ${lightGray.dark['board']}'] + > [data-block-id] > [rel='noopener noreferrer'] [placeholder="Untitled"] { + -webkit-text-fill-color: var(--theme--board_light_gray-card_text, var(--theme--board_light_gray-text)) !important; + } + .notion-body:not(.dark) + .notion-board-group[style*='background-color: ${lightGray.light['board']}'] + > [data-block-id] > [rel='noopener noreferrer'] > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; + } + .notion-body.dark + .notion-board-group[style*='background-color: ${lightGray.dark['board']}'] + > [data-block-id] > [rel='noopener noreferrer'] > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; + } + .notion-body:not(.dark) .notion-board-view + [style*='color: ${lightGray.light['board-text']}'], + .notion-body.dark .notion-board-view [style*='color: ${lightGray.dark['board-text']}'], + .notion-body:not(.dark) .notion-board-view + [style*='fill: ${lightGray.light['board-text']}'], + .notion-body.dark .notion-board-view [style*='fill: ${lightGray.dark['board-text']}'] { + color: var(--theme--board_light_gray-text) !important; + fill: var(--theme--board_light_gray-text) !important; + } + `; + + // generate the rest of the colours + for (const c in colors) { + css += ` + + /* ${c} */ + + .notion-body:not(.dark) [style*='color: ${rgb(colors[c].light['text'])}'], + .notion-body:not(.dark) [style*='color:${colors[c].light['text']}'], + .notion-body.dark [style*='color: ${rgb(colors[c].dark['text'])}'], + .notion-body.dark [style*='color:${colors[c].dark['text']}'] { + color: var(--theme--text_${c}) !important; + fill: var(--theme--text_${c}) !important; + } + + + .notion-body:not(.dark) [style*='background: ${ + colors[c].light['highlight'] + }']${notCallout}${notBoardCard}, + .notion-body:not(.dark) [style*='background:${ + colors[c].light['highlight'] + }']${notCallout}${notBoardCard}, + .notion-body:not(.dark) [style*='background: ${rgb( + colors[c].light['highlight'] + )}']${notCallout}${notBoardCard}, + .notion-body:not(.dark) [style*='background:${rgb( + colors[c].light['highlight'] + )}']${notCallout}${notBoardCard}, + .notion-body:not(.dark) [style*='background-color: ${ + colors[c].light['highlight'] + }']${notCallout}${notBoardCard}, + .notion-body.dark [style*='background: ${ + colors[c].dark['highlight'] + }']${notCallout}${notBoardCard}, + .notion-body.dark [style*='background:${ + colors[c].dark['highlight'] + }']${notCallout}${notBoardCard}, + .notion-body.dark [style*='background: ${rgb( + colors[c].dark['highlight'] + )}']${notCallout}${notBoardCard}, + .notion-body.dark [style*='background:${rgb( + colors[c].dark['highlight'] + )}']${notCallout}${notBoardCard}, + .notion-body.dark [style*='background-color: ${ + colors[c].dark['highlight'] + }']${notCallout}${notBoardCard} { + background: var(--theme--highlight_${c}) !important; + color: var(--theme--highlight_${c}-text) !important; + } + + .notion-body:not(.dark) .notion-callout-block > div + > [style*='background: ${colors[c].light['callout']}'], + .notion-body.dark .notion-callout-block > div + > [style*='background: ${colors[c].dark['callout']}'] { + background: var(--theme--callout_${c}) !important; + color: var(--theme--callout_${c}-text) !important; + } + .notion-body:not(.dark) [style*='background: ${colors[c].light['tag']}']${isTag}, + .notion-body.dark [style*='background: ${colors[c].dark['tag']}']${isTag} { + background: var(--theme--tag_${c}) !important; + color: var(--theme--tag_${c}-text) !important; + } + + .notion-body:not(.dark) [style*='background: ${ + colors[c].light['callout'] + }']${isHighlightPalette}, + .notion-body.dark [style*='background: ${ + colors[c].dark['callout'] + }']${isHighlightPalette} { + background: var(--theme--highlight_${c}) !important; + color: var(--theme--highlight_${c}-text) !important; + } + .notion-body:not(.dark) [style*='background: ${ + colors[c].light['tag'] + }']${isTagPalette}, + .notion-body.dark [style*='background: ${ + colors[c].dark['board-text'] + }']${isTagPalette} { + background: var(--theme--tag_${c}) !important; + color: var(--theme--tag_${c}-text) !important; + } + + .notion-body:not(.dark) + .notion-board-group[style*='background-color: ${colors[c].light['board']}'], + .notion-body.dark + .notion-board-group[style*='background-color: ${colors[c].dark['board']}'], + .notion-body:not(.dark) .notion-board-view > .notion-selectable > :first-child > :nth-child(2) + [style*='background-color: ${colors[c].light['board']}'], + .notion-body.dark .notion-board-view > .notion-selectable > :first-child > :nth-child(2) + [style*='background-color: ${colors[c].dark['board']}'] { + background: var(--theme--board_${c}) !important; + color: var(--theme--board_${c}-text) !important; + } + .notion-body:not(.dark) + .notion-board-group[style*='background-color: ${colors[c].light['board']}'] + > [data-block-id] > [rel='noopener noreferrer'], + .notion-body.dark + .notion-board-group[style*='background-color: ${colors[c].dark['board']}'] + > [data-block-id] > [rel='noopener noreferrer'] { + background: var(--theme--board_${c}-card) !important; + color: var(--theme--board_${c}-card_text) !important; + } + .notion-body.dark + .notion-board-group[style*='background-color: ${colors[c].dark['board']}'] + > [data-block-id] > [rel='noopener noreferrer'] [placeholder="Untitled"] { + -webkit-text-fill-color: var(--theme--board_${c}-card_text, var(--theme--board_${c}-text)) !important; + } + .notion-body:not(.dark) + .notion-board-group[style*='background-color: ${colors[c].light['board']}'] + > [data-block-id] > [rel='noopener noreferrer'] > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; + } + .notion-body.dark + .notion-board-group[style*='background-color: ${colors[c].dark['board']}'] + > [data-block-id] > [rel='noopener noreferrer'] > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; + } + .notion-body:not(.dark) .notion-board-view + [style*='color: ${colors[c].light['board-text']}'], + .notion-body.dark .notion-board-view [style*='color: ${ + colors[c].dark['board-text'] + }'], + .notion-body:not(.dark) .notion-board-view + [style*='fill: ${colors[c].light['board-text']}'], + .notion-body.dark .notion-board-view [style*='fill: ${ + colors[c].dark['board-text'] + }'] { + color: var(--theme--board_${c}-text) !important; + fill: var(--theme--board_${c}-text) !important; + } + `; + } + return css; +} + +// 'light' or 'dark' +function vars(mode) { + // order in which variables will appear + const sets = { + 'text': '', + 'highlight': '', + 'callout': '', + // tag_default has the same color in light and dark + 'tag': '--theme--tag_default: rgba(206, 205, 202, 0.5);\n--theme--tag_default-text: var(--theme--text);\n', + 'board': '' + }; + + // light gray separately + for (let key in lightGray[mode]) { + const prefix = key.split('-')[0], + value = lightGray[mode][key]; + if (!sets[prefix]) sets[prefix] = ''; + key = [`--theme--${prefix}_light_gray`, ...key.split('-').slice(1)].join('-'); + sets[prefix] += `${key}: ${value};\n`; + } + + // other colors + for (const color in colors) { + for (let key in colors[color][mode]) { + const prefix = key.split('-')[0], + value = colors[color][mode][key]; + if (!sets[prefix]) sets[prefix] = ''; + key = [`--theme--${prefix}_${color}`, ...key.split('-').slice(1)].join('-'); + sets[prefix] += `${key}: ${value};\n`; + } + } + let vars = ''; + for (const set in sets) { + vars += `\n${sets[set]}`; + } + return vars; +} + +if (process.argv.includes('css')) { + console.log(css()); +} else if (process.argv.includes('light')) { + console.log(vars('light')); +} else if (process.argv.includes('dark')) { + console.log(vars('dark')); +} diff --git a/src/mods/theming/client.mjs b/src/mods/theming/client.mjs new file mode 100644 index 0000000..88060be --- /dev/null +++ b/src/mods/theming/client.mjs @@ -0,0 +1,58 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web, registry, storage, electron }, db) { + const enabledThemes = await registry.list( + async (m) => (await registry.enabled(m.id)) && m.tags.includes('theme') + ); + if (enabledThemes.length || (await db.get(['force_load']))) { + // only override colors if theme is enable for perf + web.loadStylesheet('repo/theming/theme.css'); + web.loadStylesheet('repo/theming/colors.css'); + } + + const updateTheme = async () => { + if (document.visibilityState !== 'visible' && !document.hasFocus()) return; + const isDark = + document.querySelector('.notion-dark-theme') || + document.querySelector('.notion-body.dark'), + isLight = document.querySelector('.notion-light-theme'), + mode = isDark ? 'dark' : isLight ? 'light' : ''; + if (!mode) return; + await storage.set(['theme'], mode); + document.documentElement.classList.add(mode); + document.documentElement.classList.remove(mode === 'light' ? 'dark' : 'light'); + electron.sendMessage('update-theme'); + const searchThemeVars = [ + 'bg', + 'text', + 'icon', + 'icon_secondary', + 'accent_blue', + 'accent_blue-text', + 'accent_blue-hover', + 'accent_blue-active', + 'ui_shadow', + 'ui_divider', + 'ui_input', + 'ui_interactive-hover', + 'ui_interactive-active', + ].map((key) => [ + key, + window.getComputedStyle(document.documentElement).getPropertyValue(`--theme--${key}`), + ]); + electron.sendMessage('set-search-theme', searchThemeVars); + }; + web.addDocumentObserver((mutation) => { + const potentialThemeChange = mutation.target.matches?.('html, body, .notion-app-inner'); + if (potentialThemeChange && document.hasFocus()) updateTheme(); + }); + updateTheme(); + document.addEventListener('visibilitychange', updateTheme); + document.addEventListener('focus', updateTheme); +} diff --git a/src/mods/theming/colors.css b/src/mods/theming/colors.css new file mode 100644 index 0000000..d615c01 --- /dev/null +++ b/src/mods/theming/colors.css @@ -0,0 +1,1218 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +/* light gray */ + +.notion-body:not(.dark) + [style*='background: rgba(227, 226, 224, 0.5)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgba(71, 76, 80, 0.7)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_light_gray) !important; + color: var(--theme--tag_light_gray-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(227, 226, 224, 0.5)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgba(107, 112, 116, 0.7)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_light_gray) !important; + color: var(--theme--tag_light_gray-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(249, 249, 245, 0.5)'], +.notion-body.dark .notion-board-group[style*='background-color: rgba(51, 55, 59, 0.7)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(249, 249, 245, 0.5)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(51, 55, 59, 0.7)'] { + background: var(--theme--board_light_gray) !important; + color: var(--theme--board_light_gray-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(249, 249, 245, 0.5)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgba(51, 55, 59, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_light_gray-card) !important; + color: var(--theme--board_light_gray-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgba(51, 55, 59, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_light_gray-card_text, + var(--theme--board_light_gray-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(249, 249, 245, 0.5)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgba(51, 55, 59, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgba(145, 145, 142, 0.5)'], +.notion-body.dark .notion-board-view [style*='color: rgba(107, 112, 116, 0.7)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgba(145, 145, 142, 0.5)'], +.notion-body.dark .notion-board-view [style*='fill: rgba(107, 112, 116, 0.7)'] { + color: var(--theme--board_light_gray-text) !important; + fill: var(--theme--board_light_gray-text) !important; +} + +/* gray */ + +.notion-body:not(.dark) [style*='color: rgb(120, 119, 116)'], +.notion-body:not(.dark) [style*='color:rgba(120, 119, 116, 1)'], +.notion-body.dark [style*='color: rgb(159, 164, 169)'], +.notion-body.dark [style*='color:rgba(159, 164, 169, 1)'] { + color: var(--theme--text_gray) !important; + fill: var(--theme--text_gray) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(241, 241, 239, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(241, 241, 239, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgb(241, 241, 239)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgb(241, 241, 239)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(241, 241, 239, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(60, 65, 68, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(60, 65, 68, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(60, 65, 68)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(60, 65, 68)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(60, 65, 68, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_gray) !important; + color: var(--theme--highlight_gray-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgb(241, 241, 239)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(60, 65, 68)'] { + background: var(--theme--callout_gray) !important; + color: var(--theme--callout_gray-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(227, 226, 224)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(71, 76, 80)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_gray) !important; + color: var(--theme--tag_gray-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgb(241, 241, 239)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(60, 65, 68)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_gray) !important; + color: var(--theme--highlight_gray-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(227, 226, 224)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(107, 112, 116)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_gray) !important; + color: var(--theme--tag_gray-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(247, 247, 245, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(51, 55, 59)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(247, 247, 245, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(51, 55, 59)'] { + background: var(--theme--board_gray) !important; + color: var(--theme--board_gray-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(247, 247, 245, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(51, 55, 59)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_gray-card) !important; + color: var(--theme--board_gray-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(51, 55, 59)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_gray-card_text, + var(--theme--board_gray-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(247, 247, 245, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(51, 55, 59)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(145, 145, 142)'], +.notion-body.dark .notion-board-view [style*='color: rgb(107, 112, 116)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(145, 145, 142)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(107, 112, 116)'] { + color: var(--theme--board_gray-text) !important; + fill: var(--theme--board_gray-text) !important; +} + +/* brown */ + +.notion-body:not(.dark) [style*='color: rgb(159, 107, 83)'], +.notion-body:not(.dark) [style*='color:rgba(159, 107, 83, 1)'], +.notion-body.dark [style*='color: rgb(212, 150, 117)'], +.notion-body.dark [style*='color:rgba(212, 150, 117, 1)'] { + color: var(--theme--text_brown) !important; + fill: var(--theme--text_brown) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(244, 238, 238, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(244, 238, 238, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgb(244, 238, 238)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgb(244, 238, 238)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(244, 238, 238, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(76, 61, 53, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(76, 61, 53, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(76, 61, 53)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(76, 61, 53)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(76, 61, 53, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_brown) !important; + color: var(--theme--highlight_brown-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgb(244, 238, 238)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(76, 61, 53)'] { + background: var(--theme--callout_brown) !important; + color: var(--theme--callout_brown-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(238, 224, 218)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(92, 71, 61)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_brown) !important; + color: var(--theme--tag_brown-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgb(244, 238, 238)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(76, 61, 53)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_brown) !important; + color: var(--theme--highlight_brown-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(238, 224, 218)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(155, 98, 69)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_brown) !important; + color: var(--theme--tag_brown-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(250, 246, 245, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(59, 54, 51)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(250, 246, 245, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(59, 54, 51)'] { + background: var(--theme--board_brown) !important; + color: var(--theme--board_brown-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(250, 246, 245, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(59, 54, 51)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_brown-card) !important; + color: var(--theme--board_brown-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(59, 54, 51)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_brown-card_text, + var(--theme--board_brown-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(250, 246, 245, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(59, 54, 51)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(187, 132, 108)'], +.notion-body.dark .notion-board-view [style*='color: rgb(155, 98, 69)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(187, 132, 108)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(155, 98, 69)'] { + color: var(--theme--board_brown-text) !important; + fill: var(--theme--board_brown-text) !important; +} + +/* orange */ + +.notion-body:not(.dark) [style*='color: rgb(217, 115, 13)'], +.notion-body:not(.dark) [style*='color:rgba(217, 115, 13, 1)'], +.notion-body.dark [style*='color: rgb(217, 133, 56)'], +.notion-body.dark [style*='color:rgba(217, 133, 56, 1)'] { + color: var(--theme--text_orange) !important; + fill: var(--theme--text_orange) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(251, 236, 221, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(251, 236, 221, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgb(251, 236, 221)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgb(251, 236, 221)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(251, 236, 221, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(85, 59, 41, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(85, 59, 41, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(85, 59, 41)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(85, 59, 41)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(85, 59, 41, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_orange) !important; + color: var(--theme--highlight_orange-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgb(251, 236, 221)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(85, 59, 41)'] { + background: var(--theme--callout_orange) !important; + color: var(--theme--callout_orange-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(250, 222, 201)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(136, 84, 44)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_orange) !important; + color: var(--theme--tag_orange-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgb(251, 236, 221)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(85, 59, 41)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_orange) !important; + color: var(--theme--highlight_orange-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(250, 222, 201)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(168, 92, 30)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_orange) !important; + color: var(--theme--tag_orange-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(252, 245, 242, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(61, 54, 49)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(252, 245, 242, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(61, 54, 49)'] { + background: var(--theme--board_orange) !important; + color: var(--theme--board_orange-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(252, 245, 242, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(61, 54, 49)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_orange-card) !important; + color: var(--theme--board_orange-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(61, 54, 49)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_orange-card_text, + var(--theme--board_orange-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(252, 245, 242, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(61, 54, 49)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(215, 129, 58)'], +.notion-body.dark .notion-board-view [style*='color: rgb(168, 92, 30)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(215, 129, 58)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(168, 92, 30)'] { + color: var(--theme--board_orange-text) !important; + fill: var(--theme--board_orange-text) !important; +} + +/* yellow */ + +.notion-body:not(.dark) [style*='color: rgb(203, 145, 47)'], +.notion-body:not(.dark) [style*='color:rgba(203, 145, 47, 1)'], +.notion-body.dark [style*='color: rgb(201, 145, 38)'], +.notion-body.dark [style*='color:rgba(201, 145, 38, 1)'] { + color: var(--theme--text_yellow) !important; + fill: var(--theme--text_yellow) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(251, 243, 219, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(251, 243, 219, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgb(251, 243, 219)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgb(251, 243, 219)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(251, 243, 219, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(79, 64, 41, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(79, 64, 41, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(79, 64, 41)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(79, 64, 41)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(79, 64, 41, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_yellow) !important; + color: var(--theme--highlight_yellow-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgb(251, 243, 219)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(79, 64, 41)'] { + background: var(--theme--callout_yellow) !important; + color: var(--theme--callout_yellow-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(253, 236, 200)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(146, 118, 63)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_yellow) !important; + color: var(--theme--tag_yellow-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgb(251, 243, 219)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(79, 64, 41)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_yellow) !important; + color: var(--theme--highlight_yellow-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(253, 236, 200)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(137, 107, 42)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_yellow) !important; + color: var(--theme--tag_yellow-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(250, 247, 237, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(56, 55, 49)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(250, 247, 237, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(56, 55, 49)'] { + background: var(--theme--board_yellow) !important; + color: var(--theme--board_yellow-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(250, 247, 237, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(56, 55, 49)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_yellow-card) !important; + color: var(--theme--board_yellow-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(56, 55, 49)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_yellow-card_text, + var(--theme--board_yellow-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(250, 247, 237, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(56, 55, 49)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(203, 148, 51)'], +.notion-body.dark .notion-board-view [style*='color: rgb(137, 107, 42)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(203, 148, 51)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(137, 107, 42)'] { + color: var(--theme--board_yellow-text) !important; + fill: var(--theme--board_yellow-text) !important; +} + +/* green */ + +.notion-body:not(.dark) [style*='color: rgb(68, 131, 97)'], +.notion-body:not(.dark) [style*='color:rgba(68, 131, 97, 1)'], +.notion-body.dark [style*='color: rgb(113, 178, 131)'], +.notion-body.dark [style*='color:rgba(113, 178, 131, 1)'] { + color: var(--theme--text_green) !important; + fill: var(--theme--text_green) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(237, 243, 236, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(237, 243, 236, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgb(237, 243, 236)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgb(237, 243, 236)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(237, 243, 236, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(46, 68, 58, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(46, 68, 58, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(46, 68, 58)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(46, 68, 58)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(46, 68, 58, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_green) !important; + color: var(--theme--highlight_green-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgb(237, 243, 236)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(46, 68, 58)'] { + background: var(--theme--callout_green) !important; + color: var(--theme--callout_green-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(219, 237, 219)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(50, 82, 65)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_green) !important; + color: var(--theme--tag_green-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgb(237, 243, 236)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(46, 68, 58)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_green) !important; + color: var(--theme--highlight_green-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(219, 237, 219)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(61, 124, 86)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_green) !important; + color: var(--theme--tag_green-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(244, 248, 243, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(49, 57, 53)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(244, 248, 243, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(49, 57, 53)'] { + background: var(--theme--board_green) !important; + color: var(--theme--board_green-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(244, 248, 243, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(49, 57, 53)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_green-card) !important; + color: var(--theme--board_green-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(49, 57, 53)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_green-card_text, + var(--theme--board_green-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(244, 248, 243, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(49, 57, 53)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(108, 155, 125)'], +.notion-body.dark .notion-board-view [style*='color: rgb(61, 124, 86)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(108, 155, 125)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(61, 124, 86)'] { + color: var(--theme--board_green-text) !important; + fill: var(--theme--board_green-text) !important; +} + +/* blue */ + +.notion-body:not(.dark) [style*='color: rgb(51, 126, 169)'], +.notion-body:not(.dark) [style*='color:rgba(51, 126, 169, 1)'], +.notion-body.dark [style*='color: rgb(102, 170, 218)'], +.notion-body.dark [style*='color:rgba(102, 170, 218, 1)'] { + color: var(--theme--text_blue) !important; + fill: var(--theme--text_blue) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(231, 243, 248, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(231, 243, 248, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgb(231, 243, 248)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgb(231, 243, 248)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(231, 243, 248, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(45, 66, 86, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(45, 66, 86, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(45, 66, 86)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(45, 66, 86)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(45, 66, 86, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_blue) !important; + color: var(--theme--highlight_blue-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgb(231, 243, 248)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(45, 66, 86)'] { + background: var(--theme--callout_blue) !important; + color: var(--theme--callout_blue-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(211, 229, 239)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(42, 78, 107)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_blue) !important; + color: var(--theme--tag_blue-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgb(231, 243, 248)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(45, 66, 86)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_blue) !important; + color: var(--theme--highlight_blue-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(211, 229, 239)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(46, 117, 164)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_blue) !important; + color: var(--theme--tag_blue-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(241, 248, 251, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(49, 56, 64)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(241, 248, 251, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(49, 56, 64)'] { + background: var(--theme--board_blue) !important; + color: var(--theme--board_blue-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(241, 248, 251, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(49, 56, 64)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_blue-card) !important; + color: var(--theme--board_blue-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(49, 56, 64)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_blue-card_text, + var(--theme--board_blue-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(241, 248, 251, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(49, 56, 64)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(91, 151, 189)'], +.notion-body.dark .notion-board-view [style*='color: rgb(46, 117, 164)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(91, 151, 189)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(46, 117, 164)'] { + color: var(--theme--board_blue-text) !important; + fill: var(--theme--board_blue-text) !important; +} + +/* purple */ + +.notion-body:not(.dark) [style*='color: rgb(144, 101, 176)'], +.notion-body:not(.dark) [style*='color:rgba(144, 101, 176, 1)'], +.notion-body.dark [style*='color: rgb(176, 152, 217)'], +.notion-body.dark [style*='color:rgba(176, 152, 217, 1)'] { + color: var(--theme--text_purple) !important; + fill: var(--theme--text_purple) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(244, 240, 247, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(244, 240, 247, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgba(244, 240, 247, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(244, 240, 247, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(244, 240, 247, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(69, 58, 91, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(69, 58, 91, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(69, 58, 91)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(69, 58, 91)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(69, 58, 91, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_purple) !important; + color: var(--theme--highlight_purple-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgba(244, 240, 247, 0.8)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(69, 58, 91)'] { + background: var(--theme--callout_purple) !important; + color: var(--theme--callout_purple-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(232, 222, 238)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(83, 68, 116)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_purple) !important; + color: var(--theme--tag_purple-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(244, 240, 247, 0.8)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(69, 58, 91)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_purple) !important; + color: var(--theme--highlight_purple-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(232, 222, 238)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(123, 96, 180)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_purple) !important; + color: var(--theme--tag_purple-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(249, 246, 252, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(57, 53, 65)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(249, 246, 252, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(57, 53, 65)'] { + background: var(--theme--board_purple) !important; + color: var(--theme--board_purple-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(249, 246, 252, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(57, 53, 65)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_purple-card) !important; + color: var(--theme--board_purple-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(57, 53, 65)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_purple-card_text, + var(--theme--board_purple-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(249, 246, 252, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(57, 53, 65)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(167, 130, 195)'], +.notion-body.dark .notion-board-view [style*='color: rgb(123, 96, 180)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(167, 130, 195)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(123, 96, 180)'] { + color: var(--theme--board_purple-text) !important; + fill: var(--theme--board_purple-text) !important; +} + +/* pink */ + +.notion-body:not(.dark) [style*='color: rgb(193, 76, 138)'], +.notion-body:not(.dark) [style*='color:rgba(193, 76, 138, 1)'], +.notion-body.dark [style*='color: rgb(223, 132, 209)'], +.notion-body.dark [style*='color:rgba(223, 132, 209, 1)'] { + color: var(--theme--text_pink) !important; + fill: var(--theme--text_pink) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(249, 238, 243, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(249, 238, 243, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgba(249, 238, 243, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(249, 238, 243, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(249, 238, 243, 0.8)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(81, 56, 77, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(81, 56, 77, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(81, 56, 77)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(81, 56, 77)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(81, 56, 77, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_pink) !important; + color: var(--theme--highlight_pink-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgba(249, 238, 243, 0.8)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(81, 56, 77)'] { + background: var(--theme--callout_pink) !important; + color: var(--theme--callout_pink-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(245, 224, 233)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(106, 59, 99)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_pink) !important; + color: var(--theme--tag_pink-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(249, 238, 243, 0.8)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(81, 56, 77)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_pink) !important; + color: var(--theme--highlight_pink-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(245, 224, 233)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(169, 76, 157)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_pink) !important; + color: var(--theme--tag_pink-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(251, 245, 251, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(60, 53, 58)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(251, 245, 251, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(60, 53, 58)'] { + background: var(--theme--board_pink) !important; + color: var(--theme--board_pink-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(251, 245, 251, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(60, 53, 58)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_pink-card) !important; + color: var(--theme--board_pink-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(60, 53, 58)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_pink-card_text, + var(--theme--board_pink-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(251, 245, 251, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(60, 53, 58)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(205, 116, 159)'], +.notion-body.dark .notion-board-view [style*='color: rgb(169, 76, 157)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(205, 116, 159)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(169, 76, 157)'] { + color: var(--theme--board_pink-text) !important; + fill: var(--theme--board_pink-text) !important; +} + +/* red */ + +.notion-body:not(.dark) [style*='color: rgb(212, 76, 71)'], +.notion-body:not(.dark) [style*='color:rgba(212, 76, 71, 1)'], +.notion-body.dark [style*='color: rgb(234, 135, 140)'], +.notion-body.dark [style*='color:rgba(234, 135, 140, 1)'] { + color: var(--theme--text_red) !important; + fill: var(--theme--text_red) !important; +} + +.notion-body:not(.dark) + [style*='background: rgba(253, 235, 236, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgba(253, 235, 236, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background: rgb(253, 235, 236)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background:rgb(253, 235, 236)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body:not(.dark) + [style*='background-color: rgba(253, 235, 236, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgba(94, 52, 54, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgba(94, 52, 54, 1)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background: rgb(94, 52, 54)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background:rgb(94, 52, 54)']:not([style*='border-radius']):not([style*='box-shadow']), +.notion-body.dark + [style*='background-color: rgba(94, 52, 54, 1)']:not([style*='border-radius']):not([style*='box-shadow']) { + background: var(--theme--highlight_red) !important; + color: var(--theme--highlight_red-text) !important; +} + +.notion-body:not(.dark) + .notion-callout-block + > div + > [style*='background: rgb(253, 235, 236)'], +.notion-body.dark .notion-callout-block > div > [style*='background: rgb(94, 52, 54)'] { + background: var(--theme--callout_red) !important; + color: var(--theme--callout_red-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(255, 226, 221)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'], +.notion-body.dark + [style*='background: rgb(122, 54, 59)'][style*='align-items: center;'][style*='border-radius: 3px; padding-left: '][style*='line-height: 120%;'] { + background: var(--theme--tag_red) !important; + color: var(--theme--tag_red-text) !important; +} + +.notion-body:not(.dark) + [style*='background: rgb(253, 235, 236)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'], +.notion-body.dark + [style*='background: rgb(94, 52, 54)'][style*='align-items: center; justify-content: center; width: 22px; height: 22px;'][style*='border-radius: 3px; font-weight: 500;'] { + background: var(--theme--highlight_red) !important; + color: var(--theme--highlight_red-text) !important; +} +.notion-body:not(.dark) + [style*='background: rgb(255, 226, 221)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'], +.notion-body.dark + [style*='background: rgb(194, 65, 82)'][style*='border-radius: 3px;'][style*='width: 18px; height: 18px;'] { + background: var(--theme--tag_red) !important; + color: var(--theme--tag_red-text) !important; +} + +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(253, 245, 243, 0.7)'], +.notion-body.dark .notion-board-group[style*='background-color: rgb(66, 51, 51)'], +.notion-body:not(.dark) + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgba(253, 245, 243, 0.7)'], +.notion-body.dark + .notion-board-view + > .notion-selectable + > :first-child + > :nth-child(2) + [style*='background-color: rgb(66, 51, 51)'] { + background: var(--theme--board_red) !important; + color: var(--theme--board_red-text) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(253, 245, 243, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'], +.notion-body.dark + .notion-board-group[style*='background-color: rgb(66, 51, 51)'] + > [data-block-id] + > [rel='noopener noreferrer'] { + background: var(--theme--board_red-card) !important; + color: var(--theme--board_red-card_text) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(66, 51, 51)'] + > [data-block-id] + > [rel='noopener noreferrer'] + [placeholder='Untitled'] { + -webkit-text-fill-color: var( + --theme--board_red-card_text, + var(--theme--board_red-text) + ) !important; +} +.notion-body:not(.dark) + .notion-board-group[style*='background-color: rgba(253, 245, 243, 0.7)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(255, 255, 255, 0.2) !important; +} +.notion-body.dark + .notion-board-group[style*='background-color: rgb(66, 51, 51)'] + > [data-block-id] + > [rel='noopener noreferrer'] + > .notion-focusable:hover { + background: rgba(0, 0, 0, 0.1) !important; +} +.notion-body:not(.dark) .notion-board-view [style*='color: rgb(225, 111, 100)'], +.notion-body.dark .notion-board-view [style*='color: rgb(194, 65, 82)'], +.notion-body:not(.dark) .notion-board-view [style*='fill: rgb(225, 111, 100)'], +.notion-body.dark .notion-board-view [style*='fill: rgb(194, 65, 82)'] { + color: var(--theme--board_red-text) !important; + fill: var(--theme--board_red-text) !important; +} diff --git a/src/mods/theming/electronSearch.css b/src/mods/theming/electronSearch.css new file mode 100644 index 0000000..c1dcf0b --- /dev/null +++ b/src/mods/theming/electronSearch.css @@ -0,0 +1,61 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +#container { + background: var(--theme--bg) !important; + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.05)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.1)) 0px 3px 6px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.2)) 0px 9px 24px !important; +} + +#container #results { + color: var(--theme--text) !important; +} + +#container #prev, +#container #next { + border-color: var(--theme--ui_divider) !important; +} +#container .enabled:focus, +#container .enabled:hover { + background: var(--theme--ui_interactive-hover) !important; +} +#container .enabled:active { + background: var(--theme--ui_interactive-active) !important; +} +#container #prev svg, +#container #next svg { + fill: var(--theme--icon_secondary) !important; +} +#container #button-separator { + background: var(--theme--ui_divider) !important; +} + +#container #search-icon svg, +#container #clear-icon svg { + fill: var(--theme--icon) !important; +} +#container #input-wrap #search { + background: var(--theme--ui_input) !important; + color: var(--theme--text) !important; +} + +#container #done { + background: var(--theme--accent_blue) !important; + color: var(--theme--accent_blue-text) !important; +} +#container #done:focus, +#container #done:hover { + background: var(--theme--accent_blue-hover) !important; +} +#container #done:active { + background: var(--theme--accent_blue-active) !important; +} + +#container .enabled::after, +#container #done::after { + background: transparent !important; +} diff --git a/src/mods/theming/frame.mjs b/src/mods/theming/frame.mjs new file mode 100644 index 0000000..a02d873 --- /dev/null +++ b/src/mods/theming/frame.mjs @@ -0,0 +1,19 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web, storage, electron }, db) { + await web.whenReady(); + + const updateTheme = async () => { + const mode = await storage.get(['theme'], 'light'); + document.documentElement.classList.add(mode); + document.documentElement.classList.remove(mode === 'light' ? 'dark' : 'light'); + }; + electron.onMessage('update-theme', updateTheme); + updateTheme(); +} diff --git a/src/mods/theming/main.cjs b/src/mods/theming/main.cjs new file mode 100644 index 0000000..750623f --- /dev/null +++ b/src/mods/theming/main.cjs @@ -0,0 +1,21 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function ({}, db, __exports, __eval) { + const electron = require('electron'); + electron.ipcMain.on('notion-enhancer:update-theme', () => { + electron.webContents + .getAllWebContents() + .forEach((webContents) => webContents.send('notion-enhancer:update-theme')); + }); + electron.ipcMain.on('notion-enhancer:set-search-theme', (event, theme) => { + electron.webContents + .getAllWebContents() + .forEach((webContents) => webContents.send('notion-enhancer:set-search-theme', theme)); + }); +}; diff --git a/src/mods/theming/menu.mjs b/src/mods/theming/menu.mjs new file mode 100644 index 0000000..7e65808 --- /dev/null +++ b/src/mods/theming/menu.mjs @@ -0,0 +1,26 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web, registry, storage, electron }, db) { + await web.whenReady(); + + const updateTheme = async () => { + const mode = await storage.get(['theme'], 'light'); + document.documentElement.classList.add(mode); + document.documentElement.classList.remove(mode === 'light' ? 'dark' : 'light'); + }; + document.addEventListener('visibilitychange', updateTheme); + electron.onMessage('update-theme', updateTheme); + updateTheme(); + + 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}`); + } + } +} diff --git a/src/mods/theming/mod.json b/src/mods/theming/mod.json new file mode 100644 index 0000000..dca8b51 --- /dev/null +++ b/src/mods/theming/mod.json @@ -0,0 +1,38 @@ +{ + "name": "theming", + "id": "0f0bf8b6-eae6-4273-b307-8fc43f2ee082", + "version": "0.11.0", + "description": "the default theme variables, required by other themes & extensions.", + "tags": ["core"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": { + "frame": ["variables.css"], + "client": ["variables.css", "prism.css", "patches.css"], + "menu": ["variables.css"] + }, + "js": { + "frame": ["frame.mjs"], + "client": ["client.mjs"], + "menu": ["menu.mjs"], + "electron": [ + { "source": "main.cjs", "target": "main/main.js" }, + { "source": "rendererSearch.cjs", "target": "renderer/search.js" } + ] + }, + "options": [ + { + "type": "toggle", + "key": "force_load", + "label": "force load overrides", + "tooltip": "**override notion's colours even if no themes are enabled**", + "value": false + } + ] +} diff --git a/src/mods/theming/patches.css b/src/mods/theming/patches.css new file mode 100644 index 0000000..7f307d8 --- /dev/null +++ b/src/mods/theming/patches.css @@ -0,0 +1,253 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +/** layout **/ + +.notion-frame + > .notion-scroller.vertical.horizontal + > .pseudoSelection + > div + > div:nth-child(3)[style*='width: 900px'], +.notion-frame + > .notion-scroller.vertical.horizontal + > .pseudoSelection + + div + > :nth-child(1)[style*='width: 900px'], +.notion-frame + > .notion-scroller.vertical.horizontal + > :nth-child(2) + > :nth-child(2)[style*='display: flex; width: 100%; justify-content: center;'] + > :nth-child(1)[style*='width: 900px'] { + width: var(--theme--page-width) !important; +} +.notion-frame + > .notion-scroller.vertical.horizontal + > .pseudoSelection + > div + > div:nth-child(3):not([style*='width: 900px']), +.notion-frame + > .notion-scroller.vertical.horizontal + > .pseudoSelection + + div + > :nth-child(1):not([style*='width: 900px']), +.notion-frame + > .notion-scroller.vertical.horizontal + > :nth-child(2) + > :nth-child(2)[style*='display: flex; width: 100%; justify-content: center;'] + > :nth-child(1):not([style*='width: 900px']) { + width: var(--theme--page-width_full) !important; +} +.notion-page-content [style*='width: 100%; max-width:'][style*='align-self: center;'] { + max-width: 100% !important; +} +.notion-frame [style*='padding-right: calc(96px + env(safe-area-inset-right));'] { + padding-right: var(--theme--page-padding) !important; +} +.notion-frame [style*='padding-left: calc(96px + env(safe-area-inset-left));'] { + padding-left: var(--theme--page-padding) !important; +} +[style^='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;'], +[style^='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;'] + img { + height: var(--theme--page_banner-height) !important; + background: transparent !important; +} + +.notion-peek-renderer > :nth-child(2) { + max-width: var(--theme--page_preview-width) !important; +} +.notion-peek-renderer + .notion-scroller.vertical + [style*='padding-left: calc(126px + env(safe-area-inset-left));'] { + padding-left: var(--theme--page_preview-padding) !important; +} +.notion-peek-renderer + .notion-scroller.vertical + [style*='padding-right: calc(126px + env(safe-area-inset-right));'] { + padding-right: var(--theme--page_preview-padding) !important; +} +.notion-peek-renderer + .notion-scroller.vertical + [style*='margin-left: calc(126px + env(safe-area-inset-left));'] { + margin-left: var(--theme--page_preview-padding) !important; +} +.notion-peek-renderer + .notion-scroller.vertical + [style*='margin-right: calc(126px + env(safe-area-inset-right));'] { + margin-right: var(--theme--page_preview-padding) !important; +} +.notion-peek-renderer .notion-page-content { + padding-left: var(--theme--page_preview-padding) !important; + padding-right: var(--theme--page_preview-padding) !important; + width: 100%; +} +.notion-peek-renderer + .notion-scroller.vertical + [style*='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 20vh;'], +.notion-peek-renderer + .notion-scroller.vertical + [style*='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 20vh;'] + img { + height: var(--theme--page_preview_banner-height) !important; + background: transparent !important; +} + +.notion-topbar-action-buttons { + width: auto !important; +} +.notion-cursor-listener > [style*='z-index: 102'] { + z-index: 99 !important; +} + +/* typography */ + +[style*='Segoe UI'] { + font-family: var(--theme--font_sans) !important; +} +[style*='Georgia'] { + font-family: var(--theme--font_serif) !important; +} +[style*='iawriter-mono'] { + font-family: var(--theme--font_mono) !important; +} +[style*='SFMono-Regular'] { + font-family: var(--theme--font_code) !important; +} + +/** remove white pixels in iframe corners **/ + +.notion-page-content iframe { + border-radius: 0px !important; +} + +/** scrollbars **/ + +::-webkit-scrollbar-track, +::-webkit-scrollbar-corner { + background: var(--theme--scrollbar_track) !important; +} +::-webkit-scrollbar-thumb { + background: var(--theme--scrollbar_thumb) !important; +} +::-webkit-scrollbar-thumb:hover { + background: var(--theme--scrollbar_thumb-hover) !important; +} + +/** consistent corner button styling **/ + +.notion-overlay-container + [style*='border-radius: 3px;'][style*='position: relative; max-width: calc(100vw - 24px); box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px; overflow: hidden;'][style*='padding: 4px 8px; font-size: 12px; line-height: 1.4; font-weight: 500;'] { + background: var(--theme--ui_tooltip) !important; + box-shadow: var(--theme--ui_shadow) 0px 1px 4px !important; + color: var(--theme--ui_tooltip-title) !important; +} +.notion-overlay-container + [style*='border-radius: 3px;'][style*='position: relative; max-width: calc(100vw - 24px); box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px; overflow: hidden;'][style*='padding: 4px 8px; font-size: 12px; line-height: 1.4; font-weight: 500;'] + [style*='color: '] { + color: var(--theme--ui_tooltip-description) !important; +} +.onboarding-checklist-button > .graduationCap + .notion-focusable { + background: var(--theme--ui_tooltip) !important; +} +.onboarding-checklist-button .closeSmall { + fill: var(--theme--ui_tooltip-title) !important; +} +.graduationCap { + fill: var(--theme--icon) !important; +} +.notion-help-button, +.onboarding-checklist-button { + opacity: 1 !important; + color: var(--theme--icon); + fill: var(--theme--icon); + background: var(--theme--ui_corner_action) !important; + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.15)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.15)) 0px 2px 4px !important; + cursor: pointer; +} +.notion-help-button:hover, +.onboarding-checklist-button:hover { + background: var(--theme--ui_corner_action-hover) !important; +} +.notion-help-button:active, +.onboarding-checklist-button:active { + background: var(--theme--ui_corner_action-active) !important; +} + +/* backgrounds */ + +[style*='height: calc(100% + 17px); background:'][style*='width: 20px; margin-left: -20px; margin-top: -17px;'], +.notion-board-view .notion-focusable[role='button'][style*='height: 33px; width: 35px'], +.notion-board-view + > :first-child + > :first-child + > :last-child[style*='background'][style*='margin-left: 20px'], +.notion-discussion-input > div > div[style*='background'], +.notion-body.dark + .notion-default-overlay-container + [style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'] + [style*='grid-column: property-start / value-end; background: rgba(255, 255, 255, 0.02);'], +.notion-body:not(.dark) + .notion-default-overlay-container + [style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'] + [style*='grid-column: property-start / value-end; background: rgba(0, 0, 0, 0.02);'], +.notion-board-view [style*='width: 20px; margin-left: -20px; margin-top: -8px;'], +.notion-page-block > div > div > div[style*='background-color: white;'], +.line-numbers.notion-code-block + div .notion-focusable:not(:hover), +.notion-overlay-container + [style*='position: relative; max-width: calc(100vw - 24px); box-shadow:'] + > [style*='display: flex; align-items: center; padding: 8px 10px; width: 100%; background:'], +.notion-default-overlay-container + > div:nth-child(3) + > div + > div:nth-child(2) + > div:nth-child(2) + > div + > div + > div + > div + > div + > div:nth-child(2)[style*='position: absolute; display: inline-flex; min-width: 100%; height: 32px; z-index: 1; background:'], +.notion-default-overlay-container + > div:nth-child(2) + > div + > div:nth-child(2) + > div:nth-child(2) + > div + > div + > div + > div + > div + > div:nth-child(2)[style*='position: absolute; display: inline-flex; min-width: 100%; height: 32px; z-index: 1; background:'], +.notion-frame .notion-scroller[style*='background:'], +.notion-page-template-modal .notion-scroller[style*='background:'], +.notion-peek-renderer .notion-scroller[style*='background:'], +.notion-frame > div > div > div[style*='max-width: 100%'][style*='background-color'], +.notion-peek-renderer + > div + > .notion-scroller + > div + > div[style*='max-width: 100%'][style*='background-color'], +.notion-page-template-modal + > div + > .notion-scroller + > div + > div[style*='max-width: 100%'][style*='background-color'], +.notion-selectable.notion-collection_view-block + .notion-board-view + > .notion-selectable.notion-collection_view-block + > :first-child + > [style*='width: 20px'], +.notion-body.dark + [style*='background: linear-gradient(rgba(47, 52, 55, 0) 0px, rgb(47, 52, 55) 10px, rgb(47, 52, 55) 100%);'], +.notion-body:not(.dark) + [style*='background: linear-gradient(rgba(255, 255, 255, 0) 0px, white 10px, white 100%);'], +.notion-body.dark .notion-collection_view_page-block [style*='background: rgb(47, 52, 55);'], +.notion-body:not(.dark) + .notion-collection_view_page-block + [style*='background: white;']:not([style*='box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 2px 4px;']) { + background: transparent !important; +} diff --git a/src/mods/theming/prism.css b/src/mods/theming/prism.css new file mode 100644 index 0000000..42d82bc --- /dev/null +++ b/src/mods/theming/prism.css @@ -0,0 +1,156 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.token.property { + color: var(--theme--code_property) !important; +} +.token.tag { + color: var(--theme--code_tag) !important; +} +.token.boolean { + color: var(--theme--code_boolean) !important; +} +.token.number { + color: var(--theme--code_number) !important; +} +.token.constant { + color: var(--theme--code_constant) !important; +} +.token.symbol { + color: var(--theme--code_symbol) !important; +} +.token.deleted { + color: var(--theme--code_deleted) !important; +} +.token.selector { + color: var(--theme--code_selector) !important; +} +.token.attr-name { + color: var(--theme--code_attr-name) !important; +} +.token.string { + color: var(--theme--code_string) !important; +} +.token.char { + color: var(--theme--code_char) !important; +} +.token.builtin { + color: var(--theme--code_builtin) !important; +} +.token.inserted { + color: var(--theme--code_inserted) !important; +} +.token.operator { + color: var(--theme--code_operator) !important; +} +.token.entity { + color: var(--theme--code_entity) !important; +} +.token.url { + color: var(--theme--code_url) !important; +} +.token.variable { + color: var(--theme--code_variable) !important; +} +.token.comment { + color: var(--theme--code_comment) !important; +} +.token.cdata { + color: var(--theme--code_cdata) !important; +} +.token.prolog { + color: var(--theme--code_prolog) !important; +} +.token.doctype { + color: var(--theme--code_doctype) !important; +} +.token.atrule { + color: var(--theme--code_atrule) !important; +} +.token.attr-value { + color: var(--theme--code_attr-value) !important; +} +.token.keyword { + color: var(--theme--code_keyword) !important; +} +.token.regex { + color: var(--theme--code_regex) !important; +} +.token.important { + color: var(--theme--code_important) !important; +} +.token.function { + color: var(--theme--code_function) !important; +} +.token.class-name { + color: var(--theme--code_class-name) !important; +} +.token.parameter { + color: var(--theme--code_parameter) !important; +} +.token.decorator { + color: var(--theme--code_decorator) !important; +} +.token.id { + color: var(--theme--code_id) !important; +} +.token.class { + color: var(--theme--code_class) !important; +} +.token.pseudo-element { + color: var(--theme--code_pseudo-element) !important; +} +.token.pseudo-class { + color: var(--theme--code_pseudo-class) !important; +} +.token.attribute { + color: var(--theme--code_attribute) !important; +} +.token.value { + color: var(--theme--code_value) !important; +} +.token.unit { + color: var(--theme--code_unit) !important; +} +.token.punctuation { + color: var(--theme--code_punctuation) !important; + opacity: 0.7 !important; +} +.token.annotation { + color: var(--theme--code_annotation) !important; +} + +.token.operator { + background: transparent !important; +} +.token.namespace { + opacity: 0.7 !important; +} +.token.important, +.token.bold { + font-weight: bold !important; +} +.token.italic { + font-style: italic !important; +} +.token.entity { + cursor: help !important; +} +.token a { + color: inherit !important; +} +.token.punctuation.brace-hover, +.token.punctuation.brace-selected { + outline: solid 1px !important; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + background: none !important; +} diff --git a/src/mods/theming/rendererSearch.cjs b/src/mods/theming/rendererSearch.cjs new file mode 100644 index 0000000..ed6a2cb --- /dev/null +++ b/src/mods/theming/rendererSearch.cjs @@ -0,0 +1,18 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function ({ web, electron }, db, __exports, __eval) { + await web.whenReady(); + web.loadStylesheet('repo/theming/electronSearch.css'); + + electron.onMessage('set-search-theme', (event, theme) => { + for (const [key, value] of theme) { + document.documentElement.style.setProperty(`--theme--${key}`, value); + } + }); +}; diff --git a/src/mods/theming/theme.css b/src/mods/theming/theme.css new file mode 100644 index 0000000..4473a89 --- /dev/null +++ b/src/mods/theming/theme.css @@ -0,0 +1,773 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +/* backgrounds */ + +.OnboardingHighlight + div > [style*='background-color'] { + background-color: rgba(0, 0, 0, 0.5) !important; +} + +body, +.notion-cursor-listener, +.notion-frame, +.notion-timeline-view, +.notion-workspace-plan-choose, +.notion-onboarding-popup, +.notion-workspace-create, +.notion-workspace-invite, +.notion-scroller.vertical + > [style*='display: flex; justify-content: center; z-index: 3; flex-shrink: 0;'][style*='background: '], +.notion-cursor-listener > div > :first-child[style*='z-index: 100;'], +.notion-space-settings > div > div > div:nth-child(2) > div[style*='background'], +.notion-body.dark .notion-collection_view_page-block > [style*='background: rgb(47, 52, 55)'], +.notion-body.dark .notion-collection_view_page-block[style*='background: rgb(47, 52, 55)'], +.notion-body:not(.dark) .notion-collection_view_page-block > [style*='background: white'], +.notion-body:not(.dark) .notion-collection_view_page-block[style*='background: white'], +.notion-body.dark .notion-collection_view-block > [style*='background: rgb(47, 52, 55)'], +.notion-body.dark .notion-collection_view-block[style*='background: rgb(47, 52, 55)'], +.notion-body:not(.dark) .notion-collection_view-block > [style*='background: white'], +.notion-body:not(.dark) .notion-collection_view-block[style*='background: white'], +.notion-body.dark .notion-timeline-view [style*='background: rgb(47, 52, 55)'], +.notion-body:not(.dark) + .notion-timeline-view + [style*='background: white']:not(.notion-timeline-item), +.notion-body:not(.dark) .notion-timeline-view [style*='background: rgb(253, 253, 253);'], +.notion-updates-menu footer > div[style*='background'], +:not(.notion-sidebar-container) > div > div > .notion-sidebar > :nth-child(2), +:not(.notion-sidebar-container) > div > div > .notion-sidebar > :nth-child(3), +:not(.notion-sidebar-container) > div > div > .notion-sidebar > :nth-child(3) > :nth-child(2), +.notion-peek-renderer > div[style*='background'], +.notion-peek-renderer > div[style*='background'] > :first-child, +.notion-peek-renderer > div[style*='background'] > :first-child > div > :nth-child(3), +.notion-page-template-modal, +.notion-update-sidebar-tab-updates-header, +.notion-update-sidebar-tab-updates-header + .notion-scroller, +.notion-update-sidebar-tab-comments-header, +.notion-update-sidebar-tab-comments-header + div, +.notion-code-block > div > div > [style*='background: '][style$='padding-right: 105px;'], +[style*='z-index: 84'] { + background: var(--theme--bg) !important; +} +.notion-timeline-item-row + div > div > div, +.notion-timeline-view > :nth-child(2) > :first-child > div > div { + border: 1px solid var(--theme--bg) !important; + background: var(--theme--ui_toggle-off) !important; +} +.notion-timeline-item-row + div > div > div svg, +.notion-timeline-view > :nth-child(2) > :first-child > div > div svg { + fill: var(--theme--bg) !important; +} + +.notion-sidebar-container, +.notion-sidebar > [style*='border-top-right-radius'], +.notion-space-settings > div > div > div:first-child[style*='background'], +.notion-body.dark .notion-collection_view_page-block [style*='background: rgb(55, 60, 63)'], +.notion-body:not(.dark) + .notion-collection_view_page-block + [style*='background: rgb(247, 246, 243)'], +.notion-body.dark .notion-collection_view-block [style*='background: rgb(55, 60, 63)'], +.notion-body:not(.dark) + .notion-collection_view-block + [style*='background: rgb(247, 246, 243)'], +.notion-body.dark .notion-timeline-view [style*='background: rgb(55, 60, 63)'], +.notion-body:not(.dark) .notion-timeline-view [style*='background: rgb(247, 246, 243)'], +.notion-space-settings + > div + > div + > div:nth-child(2) + table + td[style*='background:']:not([style*='background: transparent']), +.notion-timeline-view > :first-child > div, +.notion-body:not(.dark) + .notion-timeline-view + > div + > div + > [style*='background: rgb(247, 247, 247); border-radius: 11px;'], +.notion-page-template-modal > :last-child, +.notion-page-template-modal > :last-child > div > :last-child, +.notion-embed-block .pseudoSelection, +.notion-video-block .pseudoSelection, +.notion-image-block .pseudoSelection, +.notion-file-block .pseudoSelection, +.notion-pdf-block .pseudoSelection, +.notion-bookmark-block .pseudoSelection, +.notion-miro-block .pseudoSelection, +.notion-codepen-block .pseudoSelection, +.notion-framer-block .pseudoSelection, +.notion-figma-block .pseudoSelection, +.notion-drive-block .pseudoSelection, +.notion-gist-block .pseudoSelection, +.notion-tweet-block .pseudoSelection, +.notion-maps-block .pseudoSelection, +.notion-replit-block .pseudoSelection, +.notion-typeform-block .pseudoSelection, +.notion-abstract-block .pseudoSelection, +.notion-invision-block .pseudoSelection, +.notion-loom-block .pseudoSelection, +.notion-excalidraw-block .pseudoSelection, +.notion-sketch-block .pseudoSelection, +.notion-whimsical-block .pseudoSelection, +.notion-equation-block > div > div, +.notion-factory-block > div > div > div > div > [style*='background'][style*='margin-top'] { + background: var(--theme--bg_secondary) !important; +} + +.notion-default-overlay-container + [style*='position: absolute; inset: 0px; background: rgba(15, 15, 15, 0.6);']:empty { + background: var(--theme--ui_shadow) !important; +} + +.notion-body.dark + [style*='box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.2) 0px 3px 6px, rgba(15, 15, 15, 0.4) 0px 9px 24px;'], +.notion-body:not(.dark) + [style*='box-shadow: rgba(15, 15, 15, 0.05) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 3px 6px, rgba(15, 15, 15, 0.2) 0px 9px 24px;'], +.notion-body.dark + [style*='box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.2) 0px 3px 6px, rgba(15, 15, 15, 0.4) 0px 9px 24px;'], +.notion-body:not(.dark) + [style*='box-shadow: rgba(15, 15, 15, 0.05) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 3px 6px, rgba(15, 15, 15, 0.2) 0px 9px 24px;'], +[data-overlay] + > div + > [style*='position: relative; z-index: 1; box-shadow:'][style*='border-radius: 3px;'][style*='margin-bottom: 0px; top: 90px; overflow: hidden; width: 75%; max-width: 600px; min-height: 50px; max-height: 80vh;']:nth-child(2), +.notion-overlay-container.notion-default-overlay-container + [style*='display: flex'] + > [style*='position: relative; max-width:'][style*='overflow: hidden'] + footer + > [style*='background-color:'], +.notion-updates-menu > :first-child > div[style*='background'], +#notion-app + > div + > div.notion-overlay-container.notion-default-overlay-container + > div:nth-child(2) + > div + > div:nth-child(2)[style*='margin-bottom: 0px; top: 90px; overflow: hidden; width: 75%;'], +.notion-default-overlay-container + > div + > div:not(.notion-peek-renderer) + > [style*='box-shadow'], +[style*='z-index:'][style*='box-shadow: '][style*='font-size: 12px;'][style*='min-height: 24px; overflow: hidden; pointer-events:'], +:not(.notion-login) + > .notion-focusable[role='button'][tabindex='0'][style*='box-shadow:'][style*='background:'][style*='transition: background 20ms ease-in 0s; cursor: pointer;']:not([style*='rgb(46, 170, 220);']):not([style*='rgb(6, 156, 205);']):not([style*='rgb(0, 141, 190);']):not([style*='flex: 1 1 0%; white-space: nowrap; height: 26px; border-radius: 3px 0px 0px 3px;']):not([style*='rgb(225, 98, 89)']), +.notion-text-action-menu > div > div, +.notion-default-overlay-container + [style*='min-width: 300px;'] + [style*='width: 240px'] + > .notion-focusable:not(:hover), +.notion-transclusion_reference-block > div > div > :nth-child(3), +.notion-transclusion_container-block > div > div > :nth-child(3), +.notion-page-block > div > div > div > .notion-focusable:not(:hover), +.notion-workspace-create + [style*='font-size: 12px;'] + + .notion-focusable[role='button']:not(:hover) { + background: var(--theme--bg_card) !important; + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.05)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.1)) 0px 3px 6px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.2)) 0px 9px 24px !important; +} +[style*='z-index:'][style*='box-shadow: '][style*='font-size: 12px;'][style*='min-height: 24px; overflow: hidden; pointer-events:'] + > .notion-focusable { + color: var(--theme--text) !important; +} + +.notion-calendar-view + .notion-selectable.notion-page-block.notion-collection-item + > a[style*='background:'] { + background: var(--theme--bg_card) !important; + box-shadow: var(--theme--ui_shadow, rgba(15, 15, 15, 0.05)) 0px 0px 0px 1px, + var(--theme--ui_shadow, rgba(15, 15, 15, 0.1)) 0px 2px 4px !important; +} + +.notion-media-menu > div > div > div[style*='background'], +.notion-media-menu > div > div > div > div[style*='background']:not(.notion-focusable), +.notion-body.dark + .notion-default-overlay-container + [style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'], +.notion-focusable[style*='background: rgb(80, 85, 88);']:not(.notion-help-button):not(.onboarding-checklist-button), +.notion-body:not(.dark) + .notion-default-overlay-container + [style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'] + .notion-focusable[style*='background: white;'], +.notion-timeline-item, +.notion-collection-item > a { + background: var(--theme--bg_card) !important; +} + +.notion-timeline-view + > div + > div + > [style*='height: 100%; background-image: linear-gradient(to right, '] { + background-image: linear-gradient( + to right, + var(--theme--bg) 20%, + transparent 100% + ) !important; +} +.notion-timeline-view + > div + > div + > [style*='height: 100%; background-image: linear-gradient(to left, '] { + background-image: linear-gradient( + to left, + var(--theme--bg) 20%, + transparent 100% + ) !important; +} + +/** ui **/ + +.notion-page-mention-token.notion-enable-hover:hover { + box-shadow: 0 0 0 3px var(--theme--ui_interactive-hover) !important; + background: var(--theme--ui_interactive-hover) !important; +} + +.notion-to_do-block [style*='background: rgb(46, 170, 220);'], +.notion-focusable + > [style*='width: 16px; height: 16px;'][style*='background: rgb(46, 170, 220);'], +.notion-focusable > [style*='border-radius: 44px;'][style*='background: rgb(46, 170, 220);'] { + background: var(--theme--ui_toggle-on) !important; +} +.notion-body.dark + .notion-focusable + > [style*='border-radius: 44px;'][style*='background: rgba(202, 204, 206, 0.3);'], +.notion-body:not(.dark) + .notion-focusable + > [style*='border-radius: 44px;'][style*='background: rgba(135, 131, 120, 0.3);'] { + background: var(--theme--ui_toggle-off) !important; +} + +.notion-focusable + > [style*='width: 16px; height: 16px;'][style*='background: rgb(46, 170, 220);'] + .check, +.notion-to_do-block .check { + fill: var(--theme--ui_toggle-feature) !important; +} +.notion-focusable > [style*='border-radius: 44px;'] > div:empty { + background: var(--theme--ui_toggle-feature) !important; +} + +.notion-body.dark [style*='background: rgba(255, 255, 255, 0.1)'], +.notion-body:not(.dark) [style*='background: rgba(55, 53, 47, 0.08)'], +.notion-focusable[style*='z-index:'][style*='box-shadow: '][style*='font-size: 12px;'][style*='min-height: 24px; overflow: hidden; pointer-events:']:hover, +:not(.notion-login) + > .notion-focusable[role='button'][tabindex='0'][style*='box-shadow:'][style*='background:'][style*='transition: background 20ms ease-in 0s; cursor: pointer;']:not([style*='rgb(46, 170, 220);']):not([style*='rgb(6, 156, 205);']):not([style*='rgb(0, 141, 190);']):not([style*='flex: 1 1 0%; white-space: nowrap; height: 26px; border-radius: 3px 0px 0px 3px;']):not([style*='rgb(225, 98, 89)']):hover, +[style*='z-index:'][style*='box-shadow: '][style*='font-size: 12px;'][style*='min-height: 24px; overflow: hidden; pointer-events:'] + > .notion-focusable[style*='background']:hover, +.notion-body:not(.dark) + .notion-default-overlay-container + [style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'] + .notion-focusable[style*='background: rgb(239, 239, 238);'], +.line-numbers.notion-code-block + div .notion-focusable:hover { + background: var(--theme--ui_interactive-hover) !important; + color: var(--theme--text) !important; + fill: var(--theme--text) !important; +} +.notion-body:not(.dark) [style*='background: rgba(55, 53, 47, 0.16)'], +.notion-body.dark .notion-focusable[role='button'][style*='background: rgb(63, 68, 71);'], +.notion-body:not(.dark) + .notion-focusable[role='button'][style*='background: rgba(55, 53, 47, 0.16)'], +[style*='z-index:'][style*='box-shadow: '][style*='font-size: 12px;'][style*='min-height: 24px; overflow: hidden; pointer-events:'] + > .notion-focusable[style*='background']:active, +.notion-body:not(.dark) + .notion-default-overlay-container + [style*='grid-template-columns: [boolean-start] 60px [boolean-end property-start] 120px [property-end opererator-start] 110px [operator-end value-start] auto [value-end menu-start] 32px [menu-end];'] + .notion-focusable[style*='background: rgb(233, 233, 231);'] { + background: var(--theme--ui_interactive-active) !important; + color: var(--theme--text) !important; + fill: var(--theme--text) !important; +} +.notion-body:not(.dark) [style*='background: rgba(55, 53, 47, 0.16)'] svg, +.notion-body.dark .notion-focusable[role='button'][style*='background: rgb(63, 68, 71);'] svg { + color: var(--theme--text) !important; + fill: var(--theme--text) !important; +} + +.notion-focusable-within, +.notion-share-menu + .notion-block-permission-settings-public-access + + div + > div + > div + > div + > div + > .notion-focusable:first-child[role='button'][tabindex='0'][style*='user-select: none;'], +.notion-overlay-container + > div:nth-child(2) + > div + > div:nth-child(2) + > div:nth-child(2) + > div + > div + > div + > div + > div + > div:nth-child(1)[style*='display: flex; width: 100%; position: relative; z-index: 2; padding: 6px 10px; font-size: 14px; background:'], +.notion-overlay-container + > div:nth-child(3) + > div + > div:nth-child(2) + > div:nth-child(2) + > div + > div + > div + > div + > div + > div:nth-child(1)[style*='display: flex; width: 100%; position: relative; z-index: 2; padding: 6px 10px; font-size: 14px; background:'], +#notion-app + > div + > div.notion-overlay-container.notion-default-overlay-container + > [data-overlay='true'] + > div + > div:nth-child(2) + > div:nth-child(2) + > div + > div + > div + > div + > div:nth-child(1) + > div[style*='background'] { + background: var(--theme--ui_input) !important; +} + +.notion-body.dark + [style*='border-radius: 20px; box-shadow: rgba(255, 255, 255, 0.07) 0px 0px 0px 2px inset;'], +.notion-body:not(.dark) + [style*='border-radius: 20px; box-shadow: rgba(55, 53, 47, 0.09) 0px 0px 0px 2px inset;'] { + box-shadow: var(--theme--ui_divider) 0px 0px 0px 2px inset !important; +} +.notion-body.dark + [style*='box-shadow: rgba(255, 255, 255, 0.07) 0px 0px 0px 1px inset; border-radius: 3px;'], +.notion-body:not(.dark) + [style*='box-shadow: rgba(55, 53, 47, 0.09) 0px 0px 0px 1px inset; border-radius: 3px;'], +.notion-gallery-view + .notion-focusable[role='button'][style*='font-size: 14px; border-radius: 3px; box-shadow:']:last-child { + box-shadow: var(--theme--ui_divider) 0px 0px 0px 1px inset !important; +} +.notion-body.dark + [style*='border-radius: 3px; box-shadow: rgba(255, 255, 255, 0.1) 0px 0px 0px 1px;'], +.notion-body:not(.dark) + [style*='border-radius: 3px; box-shadow: rgba(55, 53, 47, 0.1) 0px 0px 0px 1px;'] { + box-shadow: var(--theme--ui_divider) 0px 0px 0px 1px !important; +} + +#notion-app + .DayPicker-Day--today:not(.DayPicker-Day--selected):not(.DayPicker-Day--value):not(.DayPicker-Day--start):not(.DayPicker-Day--end) { + color: var(--theme--accent_red-text) !important; +} +#notion-app + .DayPicker-Day--today:not(.DayPicker-Day--selected):not(.DayPicker-Day--value):not(.DayPicker-Day--start):not(.DayPicker-Day--end)::after, +.notion-timeline-view [style*='background: rgb(211, 79, 67); width: 22px;'], +.notion-timeline-view + [style*='width: 7px; height: 7px; background: rgb(211, 79, 67); border-radius: 100%;'] { + background: var(--theme--accent_red) !important; +} +#notion-app .DayPicker-Day.DayPicker-Day--range.DayPicker-Day--start, +#notion-app .DayPicker-Day.DayPicker-Day--range.DayPicker-Day--end { + background-color: var(--theme--accent_blue) !important; +} +#notion-app .DayPicker-Day.DayPicker-Day--range:hover { + border-radius: 0 !important; +} +.notion-calendar-view-day[style*='background'] { + background-color: var(--theme--accent_red) !important; + color: var(--theme--accent_red-text) !important; +} +.DayPicker-Day--outside, +.DayPicker-Weekday { + color: var(--theme--text_secondary) !important; +} +.notion-timeline-view [style*='height: 100%; border-right: 1px solid rgb(211, 79, 67);'] { + border-right: 1px solid var(--theme--accent_red) !important; +} + +/* link underline */ + +.notion-body.dark + [style*='background-image: linear-gradient(to right, rgba(255, 255, 255, 0.14) 0%, rgba(255, 255, 255, 0.14) 100%);'], +.notion-body:not(.dark) + [style*='background-image: linear-gradient(to right, rgba(55, 53, 47, 0.16) 0%, rgba(55, 53, 47, 0.16) 100%);'] { + background-image: linear-gradient( + to right, + var(--theme--ui_divider) 0%, + var(--theme--ui_divider) 100% + ) !important; +} + +/** dividers **/ + +.notion-body.dark + .notion-page-content + .notranslate[style*='border-bottom: 1px solid rgba(255, 255, 255, 0.14);'], +.notion-body:not(.dark) + .notion-page-content + .notranslate[style*='border-bottom: 1px solid rgba(55, 53, 47, 0.16);'] { + border-bottom: 1px solid var(--theme--ui_divider) !important; +} + +.notion-body.dark [style*='border-top: 1px solid rgb(77, 81, 83)'], +.notion-body.dark [style*='border-top: 1px solid rgb(63, 66, 69)'], +.notion-body.dark [style*='border-top: 1px solid rgb(63, 66, 67)'], +.notion-body.dark [style*='border-top: 1px solid rgb(60, 63, 67)'], +.notion-body.dark [style*='border-top: 1px solid rgba(255, 255, 255, 0.14);'], +.notion-body.dark [style*='border-top: 1px solid rgba(255, 255, 255, 0.07)'], +.notion-body:not(.dark) [style*='border-top: 1px solid rgb(233, 233, 231)'], +.notion-body:not(.dark) [style*='border-top: 1px solid rgb(237, 237, 236)'], +.notion-body:not(.dark) [style*='border-top: 1px solid rgb(238, 238, 237)'], +.notion-body:not(.dark) [style*='border-top: 1px solid rgba(55, 53, 47, 0.09)'], +.notion-body:not(.dark) [style*='border-top: 1px solid rgba(55, 53, 47, 0.16)'] { + border-top: 1px solid var(--theme--ui_divider) !important; +} +.notion-body.dark [style*='border-bottom: 1px solid rgb(77, 81, 83)'], +.notion-body.dark [style*='border-bottom: 1px solid rgb(63, 66, 69)'], +.notion-body.dark [style*='border-bottom: 1px solid rgb(63, 66, 67)'], +.notion-body.dark [style*='border-bottom: 1px solid rgb(60, 63, 67)'], +.notion-body.dark [style*='border-bottom: 1px solid rgba(255, 255, 255, 0.14);'], +.notion-body.dark [style*='border-bottom: 1px solid rgba(255, 255, 255, 0.07)'], +.notion-body:not(.dark) [style*='border-bottom: 1px solid rgb(233, 233, 231)'], +.notion-body:not(.dark) [style*='border-bottom: 1px solid rgb(237, 237, 236)'], +.notion-body:not(.dark) [style*='border-bottom: 1px solid rgb(238, 238, 237)'], +.notion-body:not(.dark) [style*='border-bottom: 1px solid rgba(55, 53, 47, 0.09)'], +.notion-body:not(.dark) [style*='border-bottom: 1px solid rgba(55, 53, 47, 0.16)'] { + border-bottom: 1px solid var(--theme--ui_divider) !important; +} +.notion-body.dark [style*='border-right: 1px solid rgb(77, 81, 83)'], +.notion-body.dark [style*='border-right: 1px solid rgb(63, 66, 69)'], +.notion-body.dark [style*='border-right: 1px solid rgb(63, 66, 67)'], +.notion-body.dark [style*='border-right: 1px solid rgb(60, 63, 67)'], +.notion-body.dark [style*='border-right: 1px solid rgba(255, 255, 255, 0.14);'], +.notion-body.dark [style*='border-right: 1px solid rgba(255, 255, 255, 0.07)'], +.notion-body:not(.dark) [style*='border-right: 1px solid rgb(233, 233, 231)'], +.notion-body:not(.dark) [style*='border-right: 1px solid rgb(237, 237, 236)'], +.notion-body:not(.dark) [style*='border-right: 1px solid rgb(238, 238, 237)'], +.notion-body:not(.dark) [style*='border-right: 1px solid rgba(55, 53, 47, 0.09)'], +.notion-body:not(.dark) [style*='border-right: 1px solid rgba(55, 53, 47, 0.16)'] { + border-right: 1px solid var(--theme--ui_divider) !important; +} +.notion-body.dark [style*='border-left: 1px solid rgb(77, 81, 83)'], +.notion-body.dark [style*='border-left: 1px solid rgb(63, 66, 69)'], +.notion-body.dark [style*='border-left: 1px solid rgb(63, 66, 67)'], +.notion-body.dark [style*='border-left: 1px solid rgb(60, 63, 67)'], +.notion-body.dark [style*='border-left: 1px solid rgba(255, 255, 255, 0.14);'], +.notion-body.dark [style*='border-left: 1px solid rgba(255, 255, 255, 0.07)'], +.notion-body:not(.dark) [style*='border-left: 1px solid rgb(233, 233, 231)'], +.notion-body:not(.dark) [style*='border-left: 1px solid rgb(237, 237, 236)'], +.notion-body:not(.dark) [style*='border-left: 1px solid rgb(238, 238, 237)'], +.notion-body:not(.dark) [style*='border-left: 1px solid rgba(55, 53, 47, 0.09)'], +.notion-body:not(.dark) [style*='border-left: 1px solid rgba(55, 53, 47, 0.16)'] { + border-left: 1px solid var(--theme--ui_divider) !important; +} +.notion-body.dark [style*='border: 1px solid rgb(77, 81, 83)'], +.notion-body.dark [style*='border: 1px solid rgb(63, 66, 69)'], +.notion-body.dark [style*='border: 1px solid rgb(63, 66, 67)'], +.notion-body.dark [style*='border: 1px solid rgb(60, 63, 67)'], +.notion-body.dark [style*='border: 1px solid rgba(255, 255, 255, 0.14);'], +.notion-body.dark [style*='border: 1px solid rgba(255, 255, 255, 0.07)'], +.notion-body:not(.dark) [style*='border: 1px solid rgb(233, 233, 231)'], +.notion-body:not(.dark) [style*='border: 1px solid rgb(237, 237, 236)'], +.notion-body:not(.dark) [style*='border: 1px solid rgb(238, 238, 237)'], +.notion-body:not(.dark) [style*='border: 1px solid rgba(55, 53, 47, 0.09)'], +.notion-body:not(.dark) [style*='border: 1px solid rgba(55, 53, 47, 0.16)'] { + border: 1px solid var(--theme--ui_divider) !important; +} +.notion-body.dark [style*='border-color: 1px solid rgb(77, 81, 83)'], +.notion-body.dark [style*='border-color: 1px solid rgb(63, 66, 69)'], +.notion-body.dark [style*='border-color: 1px solid rgb(63, 66, 67)'], +.notion-body.dark [style*='border-color: 1px solid rgb(60, 63, 67)'], +.notion-body.dark [style*='border-color: 1px solid rgba(255, 255, 255, 0.14);'], +.notion-body.dark [style*='border-color: 1px solid rgba(255, 255, 255, 0.07)'], +.notion-body:not(.dark) [style*='border-color: 1px solid rgb(233, 233, 231)'], +.notion-body:not(.dark) [style*='border-color: 1px solid rgb(237, 237, 236)'], +.notion-body:not(.dark) [style*='border-color: 1px solid rgb(238, 238, 237)'], +.notion-body:not(.dark) [style*='border-color: 1px solid rgba(55, 53, 47, 0.09)'], +.notion-body:not(.dark) [style*='border-color: 1px solid rgba(55, 53, 47, 0.16)'], +.notion-callout-block > div > :not([style*='border-color: transparent']) { + border-color: var(--theme--ui_divider) !important; +} + +.notion-body.dark [style*='box-shadow: rgb(77, 81, 83) -1px 0px 0px'], +.notion-body.dark [style*='box-shadow: rgba(255, 255, 255, 0.07) -1px 0px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgb(233, 233, 231) -1px 0px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgba(55, 53, 47, 0.09) -1px 0px 0px'], +.notion-body.dark [style*='box-shadow: rgb(63, 66, 69) -1px 0px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgb(237, 237, 236) -1px 0px 0px'] { + box-shadow: var(--theme--ui_divider) -1px 0px 0px !important; +} +.notion-body.dark [style*='box-shadow: rgb(77, 81, 83) 1px 0px 0px'], +.notion-body.dark [style*='box-shadow: rgba(255, 255, 255, 0.07) 1px 0px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgb(233, 233, 231) 1px 0px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgba(55, 53, 47, 0.09) 1px 0px 0px'], +.notion-body.dark [style*='box-shadow: rgb(63, 66, 69) 1px 0px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgb(237, 237, 236) 1px 0px 0px'] { + box-shadow: var(--theme--ui_divider) 1px 0px 0px !important; +} +.notion-body.dark [style*='box-shadow: rgb(77, 81, 83) 0px -1px 0px'], +.notion-body.dark [style*='box-shadow: rgba(255, 255, 255, 0.07) 0px -1px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgb(233, 233, 231) 0px -1px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgba(55, 53, 47, 0.09) 0px -1px 0px'], +.notion-body.dark [style*='box-shadow: rgb(63, 66, 69) 0px -1px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgb(237, 237, 236) 0px -1px 0px'] { + box-shadow: var(--theme--ui_divider) 0px -1px 0px !important; +} +.notion-body.dark [style*='box-shadow: rgb(77, 81, 83) 0px 1px 0px'], +.notion-body.dark [style*='box-shadow: rgba(255, 255, 255, 0.07) 0px 1px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgb(233, 233, 231) 0px 1px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgba(55, 53, 47, 0.09) 0px 1px 0px'], +.notion-body.dark [style*='box-shadow: rgb(63, 66, 69) 0px 1px 0px'], +.notion-body:not(.dark) [style*='box-shadow: rgb(237, 237, 236) 0px 1px 0px'] { + box-shadow: var(--theme--ui_divider) 0px 1px 0px !important; +} + +.notion-body.dark [style*='height: 1px;'][style*='background: rgba(255, 255, 255, 0.07);'], +.notion-body:not(.dark) [style*='height: 1px;'][style*='background: rgba(55, 53, 47, 0.09);'] { + background: var(--theme--ui_divider) !important; +} +.notion-body.dark + [style*='box-shadow: rgb(47, 52, 55) -3px 0px 0px, rgb(63, 66, 69) 0px 1px 0px;'], +.notion-body:not(.dark) + [style*='box-shadow: white -3px 0px 0px, rgb(233, 233, 231) 0px 1px 0px;'] { + box-shadow: var(--theme--ui_divider) 0px 1px 0px !important; +} +.notion-body.dark + .notion-collection_view_page-block + > [style*='box-shadow: rgb(47, 52, 55) -3px 0px 0px;'], +.notion-body:not(.dark) + .notion-collection_view_page-block + > [style*='box-shadow: white -3px 0px 0px;'], +.notion-body.dark + .notion-collection_view-block + > [style*='box-shadow: rgb(47, 52, 55) -3px 0px 0px;'], +.notion-body:not(.dark) + .notion-collection_view-block + > [style*='box-shadow: white -3px 0px 0px;'] { + box-shadow: transparent -3px 0px 0px !important; +} +.notion-focusable[role='button'][style*='box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px;'] { + box-shadow: var(--theme--ui_divider) 0px 0px 0px 1px !important; +} + +.notion-sidebar-container[style*='box-shadow:'] { + box-shadow: var(--theme--ui_divider) -2px 0px 0px 0px inset !important; +} + +/** colours **/ + +[style*='background: rgb(46, 170, 220)'] { + background: var(--theme--accent_blue) !important; + color: var(--theme--accent_blue-text) !important; +} +[style*='background: rgb(6, 156, 205);'] { + background: var(--theme--accent_blue-hover) !important; + color: var(--theme--accent_blue-text) !important; +} +[style*='background: rgb(0, 141, 190);'] { + background: var(--theme--accent_blue-active) !important; + color: var(--theme--accent_blue-text) !important; +} +[style*='background: rgb(46, 170, 220)'] .chevronDown, +[style*='background: rgb(6, 156, 205);'] .chevronDown, +[style*='background: rgb(0, 141, 190);'] .chevronDown { + fill: var(--theme--accent_blue-text) !important; +} +[style*='fill: rgb(46, 170, 220)'] { + fill: var(--theme--accent_blue) !important; +} +[style*=' color: rgb(46, 170, 220)'], +[style^='color: rgb(46, 170, 220)'] { + color: var(--theme--accent_blue) !important; +} +[style*='background: rgba(46, 170, 220, 0.'], +[style*='background-color: rgba(46, 170, 220, 0.'] { + background-color: var(--theme--accent_blue-selection) !important; +} +*::selection { + background: var(--theme--accent_blue-selection, rgba(26, 170, 220, 0.3)) !important; +} +.notion-page-mention-token::selection, +.notion-selectable-halo { + background: var(--theme--accent_blue-selection, rgba(26, 170, 220, 0.2)) !important; +} +.notion-focusable-within:focus-within, +.notion-focusable:focus-visible { + box-shadow: var(--theme--accent_blue-active, rgba(26, 170, 220, 0.7)) 0px 0px 0px 1px inset, + var(--theme--accent_blue-active, rgba(26, 170, 220, 0.4)) 0px 0px 0px 2px !important; +} +.notion-sidebar-switcher:focus-visible { + box-shadow: none !important; +} +.notion-onboarding-plan-type-team[style*='box-shadow: rgb(46, 170, 220)'], +.notion-onboarding-plan-type-personal[style*='box-shadow: rgb(46, 170, 220)'] { + box-shadow: var(--theme--accent_blue) 0px 0px 0px 2px !important; +} + +@keyframes pulsing-button-border { + 0% { + border-color: var(--theme--accent_blue) !important; + } + 50% { + border-color: rgba(255, 255, 255, 0) !important; + } + 100% { + border-color: var(--theme--accent_blue) !important; + } +} + +[style*='background-color: rgb(235, 87, 87); height: 28px; width: 28px;'], +.notion-login [style*='background: rgb(225, 98, 89)'], +.notion-login [style*='background: rgb(207, 83, 74)'], +.notion-login [style*='background: rgb(191, 77, 69)'] { + background: var(--theme--accent_red) !important; + border-color: var(--theme--accent_red) !important; +} +[style*='background: rgb(235, 87, 87); color: white; border-radius: 3px;']:not([role='button']) { + background: var(--theme--accent_red) !important; + color: var(--theme--accent_red-text) !important; +} +[style*='color: rgb(235, 87, 87); border: 1px solid rgba(235, 87, 87, 0.5);'][role='button'] { + color: var(--theme--accent_red) !important; + border: 1px solid var(--theme--accent_red) !important; +} +.notion-focusable[style*='border-radius: 3px;'][style*='color: rgb(235, 87, 87);'][role='button'], +[style*='font-size: 12px; font-weight: 600; color: rgb(235, 87, 87);'], +[style*='flex-shrink: 0; margin-top: -1px; margin-right: 4px; fill: rgb(235, 87, 87);'], +[style*='font-size: 12px;'] > [style*='pointer-events: none; color: rgb(235, 87, 87);'] { + color: var(--theme--accent_red) !important; + fill: var(--theme--accent_red) !important; +} +.notion-focusable[style*='border-radius: 3px;'][style*='background: rgba(235, 87, 87, 0.1);'][role='button']:hover { + background: var(--theme--accent_red-button) !important; +} + +.notion-transclusion_container-block > div > div > div[style*='border: 2px'], +.notion-transclusion_reference-block > div > div > div[style*='border: 2px'] { + border-color: var(--theme--accent_red, #e38676) !important; +} + +.notion-text-mention-token[style*='color:#2EAADC;'] { + color: var(--theme--accent_blue) !important; +} +.notion-text-mention-token[style*='color:#EB5757;'], +.notion-link:hover { + color: var(--theme--accent_red) !important; +} + +.notion-body.dark [style*='fill: rgb(202, 204, 206)'], +.notion-body:not(.dark) [style*='fill: rgba(55, 53, 47, 0.8)'] { + fill: var(--theme--icon) !important; +} +.notion-body.dark [style*='fill: rgba(202, 204, 206, 0.'], +.notion-body.dark [style*='fill: rgba(255, 255, 255, 0.'], +.notion-body:not(.dark) [style*='fill: rgba(25, 23, 17, 0.'], +.notion-body:not(.dark) [style*='fill: rgb(55, 53, 47)'], +.notion-body:not(.dark) + [style*='fill: rgba(55, 53, 47, 0.']:not([style*='fill: rgba(55, 53, 47, 0.8)']) { + fill: var(--theme--icon_secondary) !important; +} +.alarmClock, +.notion-to_do-block .checkboxSquare[style*='fill: inherit'] { + fill: currentColor !important; +} + +.notion-app-inner, +.notion-page-content, +.notion-selectable.notion-page-block .notion-focusable > [style*=';color:'], +.notion-record-icon.notranslate.notion-focusable, +.notion-topbar-share-menu.notion-focusable, +.notion-collection-view-select.notion-focusable, +.notion-body.dark [style*=' color: rgba(255, 255, 255, 0.9);'], +.notion-body.dark [style^='color: rgba(255, 255, 255, 0.9);'], +.notion-body:not(.dark) [style*=' color: rgb(55, 53, 47)'], +.notion-body:not(.dark) [style^='color: rgb(55, 53, 47)'] { + color: var(--theme--text) !important; +} +.notion-body.dark [style*='border-bottom: 2px solid rgba(255, 255, 255, 0.9);'], +.notion-body:not(.dark) [style*='border-bottom: 2px solid rgb(55, 53, 47);'] { + border-bottom: 2px solid var(--theme--text) !important; +} +.notion-body.dark [style*='caret-color: rgba(255, 255, 255, 0.9)'], +.notion-body:not(.dark) [style*='caret-color: rgb(55, 53, 47)'] { + caret-color: var(--theme--text) !important; +} +.notion-body.dark [style*=' color: rgba(255, 255, 255, 0.6)'], +.notion-body.dark [style^='color: rgba(255, 255, 255, 0.6)'], +.notion-body.dark [style^='color:rgba(255, 255, 255, 0.6)'], +.notion-body:not(.dark) [style*=' color: rgba(55, 53, 47, 0.6)'], +.notion-body:not(.dark) [style^='color: rgba(55, 53, 47, 0.6)'], +.notion-body:not(.dark) [style^='color:rgba(55, 53, 47, 0.6)'], +.notion-sidebar-container > [style*='color'], +.notion-gallery-view + .notion-focusable[role='button'][style*='font-size: 14px; border-radius: 3px; box-shadow:']:last-child + svg + + div, +.notion-body.dark [style*=' color: rgba(255, 255, 255, 0.4)'], +.notion-body.dark [style^='color: rgba(255, 255, 255, 0.4)'], +.notion-body:not(.dark) [style*=' color: rgba(55, 53, 47, 0.4)'], +.notion-body:not(.dark) [style^='color: rgba(55, 53, 47, 0.4)'], +.notion-page-controls, +.notion-page-details-controls, +.notion-calendar-view-day { + color: var(--theme--text_secondary) !important; +} + +.notion-page-mention-token__title { + border-bottom: 0.05em solid var(--theme--text_secondary) !important; +} +.notion-to_do-block [placeholder='To-do'][style*='text-decoration: line-through'] { + text-decoration: line-through var(--theme--text_secondary) !important; +} +.notion-body.dark [style*='-webkit-text-fill-color: rgba(255, 255, 255, 0.4)'], +.notion-body:not(.dark) [style*='-webkit-text-fill-color: rgba(55, 53, 47, 0.4)'] { + -webkit-text-fill-color: var(--theme--text_secondary) !important; +} +.notion-body.dark [style*='border-color:rgba(255,255,255,0.4)'], +.notion-body:not(.dark) [style*='border-color:rgba(55,53,47,0.4)'] { + border-color: var(--theme--text_secondary) !important; +} + +/* make sure to change in _mapColors.js as well if colors ever change */ +.notion-body.dark [style*='background: rgb(80, 85, 88)']:not([role='button']), +.notion-body.dark [style*='background-color: rgb(80, 85, 88)']:not([role='button']), +.notion-body:not(.dark) [style*='background: rgba(206, 205, 202, 0.5)']:not([role='button']), +.notion-body:not(.dark) + [style*='background-color: rgba(206, 205, 202, 0.5)']:not([role='button']), +.notion-sidebar-container + .notion-sidebar + [style*='margin-top'] + > .notion-focusable[style*='border-radius'] { + background: var(--theme--tag_default) !important; + color: var(--theme--tag_default-text) !important; +} + +/** code **/ + +.notion-page-content [style*='color:#EB5757']:not(.notion-text-mention-token) { + color: var(--theme--code_inline-text) !important; + background: var(--theme--code_inline) !important; +} + +.notion-code-block > div > div { + background: var(--theme--code) !important; +} +.notion-code-block > div { + color: var(--theme--code_plain) !important; +} + +/** simple tables **/ + +.notion-table-selection-overlay [style*='border: 2px solid rgb(116, 182, 219);'] { + border: 2px solid var(--theme--accent_blue) !important; +} +.notion-table-row-selector, +.notion-table-column-selector, +.notion-table-block .notion-focusable [style*='background: rgba(55, 53, 47, 0.06);'] { + background: var(--theme--ui_interactive-hover) !important; +} +.notion-table-row [style*='background: rgb(116, 182, 219);'], +.notion-table-row-selector[style*='background: rgb(116, 182, 219);'], +.notion-table-column-selector[style*='background: rgb(116, 182, 219);'], +.notion-table-block .notion-focusable [style*='background: rgb(116, 182, 219);'] { + background: var(--theme--accent_blue) !important; +} +.notion-table-row td[style*='background:'] { + background: var(--theme--bg_secondary) !important; +} diff --git a/src/mods/theming/variables.css b/src/mods/theming/variables.css new file mode 100644 index 0000000..8ff0aad --- /dev/null +++ b/src/mods/theming/variables.css @@ -0,0 +1,407 @@ +/** + * notion-enhancer: theming + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +:root { + --theme--page-padding: calc(96px + env(safe-area-inset-left)); + --theme--page-width: 900px; + --theme--page-width_full: 100%; + --theme--page_banner-height: 30vh; + --theme--page_preview-padding: 8rem; + --theme--page_preview-width: 977px; + --theme--page_preview_banner-height: 20vh; + + --theme--font_sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, + 'Apple Color Emoji', Arial, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol'; + --theme--font_serif: Lyon-Text, Georgia, YuMincho, 'Yu Mincho', 'Hiragino Mincho ProN', + 'Hiragino Mincho Pro', 'Songti TC', 'Songti SC', SimSun, 'Nanum Myeongjo', NanumMyeongjo, + Batang, serif; + --theme--font_mono: iawriter-mono, Nitti, Menlo, Courier, monospace; + --theme--font_code: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace; + + --theme--accent_blue: rgb(46, 170, 220); + --theme--accent_blue-selection: rgb(46, 170, 220, 0.25); + --theme--accent_blue-hover: rgb(6, 156, 205); + --theme--accent_blue-active: rgb(0, 141, 190); + --theme--accent_blue-text: #fff; + --theme--accent_red: #eb5757; + --theme--accent_red-button: rgba(235, 87, 87, 0.1); + --theme--accent_red-text: #fff; +} + +:root.light { + --theme--bg: #fff; + --theme--bg_secondary: rgb(247, 246, 243); + --theme--bg_card: #fff; + + --theme--scrollbar_track: #edece9; + --theme--scrollbar_thumb: #d3d1cb; + --theme--scrollbar_thumb-hover: #aeaca6; + + --theme--ui_shadow: rgba(15, 15, 15, 0.05); + --theme--ui_divider: rgb(237, 237, 236); + --theme--ui_interactive-hover: rgba(55, 53, 47, 0.08); + --theme--ui_interactive-active: rgba(55, 53, 47, 0.16); + --theme--ui_toggle-on: var(--theme--accent_blue); + --theme--ui_toggle-off: rgba(135, 131, 120, 0.3); + --theme--ui_toggle-feature: #fff; + --theme--ui_input: rgba(242, 241, 238, 0.6); + --theme--ui_tooltip: rgb(15, 15, 15); + --theme--ui_tooltip-title: rgba(255, 255, 255, 0.9); + --theme--ui_tooltip-description: rgba(206, 205, 202, 0.6); + --theme--ui_corner_action: white; + --theme--ui_corner_action-hover: rgb(239, 239, 238); + --theme--ui_corner_action-active: rgb(223, 223, 222); + + --theme--icon: rgba(55, 53, 47, 0.8); + --theme--icon_secondary: rgba(55, 53, 47, 0.4); + + --theme--text: rgb(55, 43, 47); + --theme--text_secondary: rgba(55, 43, 47, 0.6); + + --theme--text_gray: rgba(120, 119, 116, 1); + --theme--text_brown: rgba(159, 107, 83, 1); + --theme--text_orange: rgba(217, 115, 13, 1); + --theme--text_yellow: rgba(203, 145, 47, 1); + --theme--text_green: rgba(68, 131, 97, 1); + --theme--text_blue: rgba(51, 126, 169, 1); + --theme--text_purple: rgba(144, 101, 176, 1); + --theme--text_pink: rgba(193, 76, 138, 1); + --theme--text_red: rgba(212, 76, 71, 1); + + --theme--highlight_gray: rgba(241, 241, 239, 1); + --theme--highlight_gray-text: currentColor; + --theme--highlight_brown: rgba(244, 238, 238, 1); + --theme--highlight_brown-text: currentColor; + --theme--highlight_orange: rgba(251, 236, 221, 1); + --theme--highlight_orange-text: currentColor; + --theme--highlight_yellow: rgba(251, 243, 219, 1); + --theme--highlight_yellow-text: currentColor; + --theme--highlight_green: rgba(237, 243, 236, 1); + --theme--highlight_green-text: currentColor; + --theme--highlight_blue: rgba(231, 243, 248, 1); + --theme--highlight_blue-text: currentColor; + --theme--highlight_purple: rgba(244, 240, 247, 0.8); + --theme--highlight_purple-text: currentColor; + --theme--highlight_pink: rgba(249, 238, 243, 0.8); + --theme--highlight_pink-text: currentColor; + --theme--highlight_red: rgba(253, 235, 236, 1); + --theme--highlight_red-text: currentColor; + + --theme--callout_gray: rgb(241, 241, 239); + --theme--callout_gray-text: currentColor; + --theme--callout_brown: rgb(244, 238, 238); + --theme--callout_brown-text: currentColor; + --theme--callout_orange: rgb(251, 236, 221); + --theme--callout_orange-text: currentColor; + --theme--callout_yellow: rgb(251, 243, 219); + --theme--callout_yellow-text: currentColor; + --theme--callout_green: rgb(237, 243, 236); + --theme--callout_green-text: currentColor; + --theme--callout_blue: rgb(231, 243, 248); + --theme--callout_blue-text: currentColor; + --theme--callout_purple: rgba(244, 240, 247, 0.8); + --theme--callout_purple-text: currentColor; + --theme--callout_pink: rgba(249, 238, 243, 0.8); + --theme--callout_pink-text: currentColor; + --theme--callout_red: rgb(253, 235, 236); + --theme--callout_red-text: currentColor; + + --theme--tag_default: rgba(206, 205, 202, 0.5); + --theme--tag_default-text: var(--theme--text); + --theme--tag_light_gray: rgba(227, 226, 224, 0.5); + --theme--tag_light_gray-text: rgb(50, 48, 44); + --theme--tag_gray: rgb(227, 226, 224); + --theme--tag_gray-text: rgb(50, 48, 44); + --theme--tag_brown: rgb(238, 224, 218); + --theme--tag_brown-text: rgb(68, 42, 30); + --theme--tag_orange: rgb(250, 222, 201); + --theme--tag_orange-text: rgb(73, 41, 14); + --theme--tag_yellow: rgb(253, 236, 200); + --theme--tag_yellow-text: rgb(64, 44, 27); + --theme--tag_green: rgb(219, 237, 219); + --theme--tag_green-text: rgb(28, 56, 41); + --theme--tag_blue: rgb(211, 229, 239); + --theme--tag_blue-text: rgb(24, 51, 71); + --theme--tag_purple: rgb(232, 222, 238); + --theme--tag_purple-text: rgb(65, 36, 84); + --theme--tag_pink: rgb(245, 224, 233); + --theme--tag_pink-text: rgb(76, 35, 55); + --theme--tag_red: rgb(255, 226, 221); + --theme--tag_red-text: rgb(93, 23, 21); + + --theme--board_light_gray: rgba(249, 249, 245, 0.5); + --theme--board_light_gray-card: white; + --theme--board_light_gray-card_text: inherit; + --theme--board_light_gray-text: rgba(145, 145, 142, 0.5); + --theme--board_gray: rgba(247, 247, 245, 0.7); + --theme--board_gray-card: white; + --theme--board_gray-card_text: inherit; + --theme--board_gray-text: rgb(145, 145, 142); + --theme--board_brown: rgba(250, 246, 245, 0.7); + --theme--board_brown-card: white; + --theme--board_brown-card_text: inherit; + --theme--board_brown-text: rgb(187, 132, 108); + --theme--board_orange: rgba(252, 245, 242, 0.7); + --theme--board_orange-card: white; + --theme--board_orange-card_text: inherit; + --theme--board_orange-text: rgb(215, 129, 58); + --theme--board_yellow: rgba(250, 247, 237, 0.7); + --theme--board_yellow-card: white; + --theme--board_yellow-card_text: inherit; + --theme--board_yellow-text: rgb(203, 148, 51); + --theme--board_green: rgba(244, 248, 243, 0.7); + --theme--board_green-card: white; + --theme--board_green-card_text: inherit; + --theme--board_green-text: rgb(108, 155, 125); + --theme--board_blue: rgba(241, 248, 251, 0.7); + --theme--board_blue-card: white; + --theme--board_blue-card_text: inherit; + --theme--board_blue-text: rgb(91, 151, 189); + --theme--board_purple: rgba(249, 246, 252, 0.7); + --theme--board_purple-card: white; + --theme--board_purple-card_text: inherit; + --theme--board_purple-text: rgb(167, 130, 195); + --theme--board_pink: rgba(251, 245, 251, 0.7); + --theme--board_pink-card: white; + --theme--board_pink-card_text: inherit; + --theme--board_pink-text: rgb(205, 116, 159); + --theme--board_red: rgba(253, 245, 243, 0.7); + --theme--board_red-card: white; + --theme--board_red-card_text: inherit; + --theme--board_red-text: rgb(225, 111, 100); + + --theme--code_inline: rgba(135, 131, 120, 0.15); + --theme--code_inline-text: #eb5757; + + --theme--code: #f7f6f3; + --theme--code_plain: var(--theme--text); + --theme--code_property: #905; + --theme--code_tag: var(--theme--code_property); + --theme--code_boolean: var(--theme--code_property); + --theme--code_number: var(--theme--code_property); + --theme--code_constant: var(--theme--code_property); + --theme--code_symbol: var(--theme--code_property); + --theme--code_deleted: var(--theme--code_property); + --theme--code_selector: #690; + --theme--code_attr-name: var(--theme--code_selector); + --theme--code_string: var(--theme--code_selector); + --theme--code_char: var(--theme--code_selector); + --theme--code_builtin: var(--theme--code_selector); + --theme--code_inserted: var(--theme--code_selector); + --theme--code_operator: #9a6e3a; + --theme--code_entity: var(--theme--code_operator); + --theme--code_url: var(--theme--code_operator); + --theme--code_variable: var(--theme--code_regex); + --theme--code_comment: slategray; + --theme--code_cdata: var(--theme--code_comment); + --theme--code_prolog: var(--theme--code_comment); + --theme--code_doctype: var(--theme--code_comment); + --theme--code_atrule: #07a; + --theme--code_attr-value: var(--theme--code_atrule); + --theme--code_keyword: var(--theme--code_atrule); + --theme--code_regex: #e90; + --theme--code_important: var(--theme--code_regex); + --theme--code_function: #dd4a68; + --theme--code_class-name: var(--theme--code_function); + --theme--code_parameter: var(--theme--code_plain); + --theme--code_decorator: var(--theme--code_plain); + --theme--code_id: var(--theme--code_plain); + --theme--code_class: var(--theme--code_plain); + --theme--code_pseudo-element: var(--theme--code_plain); + --theme--code_pseudo-class: var(--theme--code_plain); + --theme--code_attribute: var(--theme--code_plain); + --theme--code_value: var(--theme--code_plain); + --theme--code_unit: var(--theme--code_plain); + --theme--code_punctuation: #999; + --theme--code_annotation: var(--theme--code_plain); +} + +:root.dark { + --theme--bg: rgb(47, 52, 55); + --theme--bg_secondary: rgb(55, 60, 63); + --theme--bg_card: rgb(63, 68, 71); + + --theme--scrollbar_track: rgba(202, 204, 206, 0.04); + --theme--scrollbar_thumb: #474c50; + --theme--scrollbar_thumb-hover: rgba(202, 204, 206, 0.3); + + --theme--ui_shadow: rgba(15, 15, 15, 0.15); + --theme--ui_divider: rgb(255, 255, 255, 0.07); + --theme--ui_interactive-hover: rgba(255, 255, 255, 0.1); + --theme--ui_interactive-active: rgb(63, 68, 71); + --theme--ui_toggle-on: var(--theme--accent_blue); + --theme--ui_toggle-off: rgba(202, 204, 206, 0.3); + --theme--ui_toggle-feature: #fff; + --theme--ui_input: rgba(15, 15, 15, 0.3); + --theme--ui_tooltip: rgb(202, 204, 206); + --theme--ui_tooltip-title: rgb(15, 15, 15); + --theme--ui_tooltip-description: rgba(47, 52, 55, 0.6); + --theme--ui_corner_action: rgb(80, 85, 88); + --theme--ui_corner_action-hover: rgb(98, 102, 104); + --theme--ui_corner_action-active: rgb(120, 123, 123); + + --theme--icon: rgba(202, 204, 206); + --theme--icon_secondary: rgb(202, 204, 206, 0.6); + + --theme--text: rgba(255, 255, 255, 0.9); + --theme--text_secondary: rgba(255, 255, 255, 0.6); + + --theme--text_gray: rgba(159, 164, 169, 1); + --theme--text_brown: rgba(212, 150, 117, 1); + --theme--text_orange: rgba(217, 133, 56, 1); + --theme--text_yellow: rgba(201, 145, 38, 1); + --theme--text_green: rgba(113, 178, 131, 1); + --theme--text_blue: rgba(102, 170, 218, 1); + --theme--text_purple: rgba(176, 152, 217, 1); + --theme--text_pink: rgba(223, 132, 209, 1); + --theme--text_red: rgba(234, 135, 140, 1); + + --theme--highlight_gray: rgba(60, 65, 68, 1); + --theme--highlight_gray-text: currentColor; + --theme--highlight_brown: rgba(76, 61, 53, 1); + --theme--highlight_brown-text: currentColor; + --theme--highlight_orange: rgba(85, 59, 41, 1); + --theme--highlight_orange-text: currentColor; + --theme--highlight_yellow: rgba(79, 64, 41, 1); + --theme--highlight_yellow-text: currentColor; + --theme--highlight_green: rgba(46, 68, 58, 1); + --theme--highlight_green-text: currentColor; + --theme--highlight_blue: rgba(45, 66, 86, 1); + --theme--highlight_blue-text: currentColor; + --theme--highlight_purple: rgba(69, 58, 91, 1); + --theme--highlight_purple-text: currentColor; + --theme--highlight_pink: rgba(81, 56, 77, 1); + --theme--highlight_pink-text: currentColor; + --theme--highlight_red: rgba(94, 52, 54, 1); + --theme--highlight_red-text: currentColor; + + --theme--callout_gray: rgb(60, 65, 68); + --theme--callout_gray-text: currentColor; + --theme--callout_brown: rgb(76, 61, 53); + --theme--callout_brown-text: currentColor; + --theme--callout_orange: rgb(85, 59, 41); + --theme--callout_orange-text: currentColor; + --theme--callout_yellow: rgb(79, 64, 41); + --theme--callout_yellow-text: currentColor; + --theme--callout_green: rgb(46, 68, 58); + --theme--callout_green-text: currentColor; + --theme--callout_blue: rgb(45, 66, 86); + --theme--callout_blue-text: currentColor; + --theme--callout_purple: rgb(69, 58, 91); + --theme--callout_purple-text: currentColor; + --theme--callout_pink: rgb(81, 56, 77); + --theme--callout_pink-text: currentColor; + --theme--callout_red: rgb(94, 52, 54); + --theme--callout_red-text: currentColor; + + --theme--tag_default: rgba(206, 205, 202, 0.5); + --theme--tag_default-text: var(--theme--text); + --theme--tag_light_gray: rgba(71, 76, 80, 0.7); + --theme--tag_light_gray-text: rgba(255, 255, 255, 0.88); + --theme--tag_gray: rgb(71, 76, 80); + --theme--tag_gray-text: rgba(255, 255, 255, 0.88); + --theme--tag_brown: rgb(92, 71, 61); + --theme--tag_brown-text: rgba(255, 255, 255, 0.88); + --theme--tag_orange: rgb(136, 84, 44); + --theme--tag_orange-text: rgba(255, 255, 255, 0.88); + --theme--tag_yellow: rgb(146, 118, 63); + --theme--tag_yellow-text: rgba(255, 255, 255, 0.88); + --theme--tag_green: rgb(50, 82, 65); + --theme--tag_green-text: rgba(255, 255, 255, 0.88); + --theme--tag_blue: rgb(42, 78, 107); + --theme--tag_blue-text: rgba(255, 255, 255, 0.88); + --theme--tag_purple: rgb(83, 68, 116); + --theme--tag_purple-text: rgba(255, 255, 255, 0.88); + --theme--tag_pink: rgb(106, 59, 99); + --theme--tag_pink-text: rgba(255, 255, 255, 0.88); + --theme--tag_red: rgb(122, 54, 59); + --theme--tag_red-text: rgba(255, 255, 255, 0.88); + + --theme--board_light_gray: rgba(51, 55, 59, 0.7); + --theme--board_light_gray-card: rgba(60, 65, 68, 0.7); + --theme--board_light_gray-card_text: inherit; + --theme--board_light_gray-text: rgba(107, 112, 116, 0.7); + --theme--board_gray: rgb(51, 55, 59); + --theme--board_gray-card: rgb(60, 65, 68); + --theme--board_gray-card_text: inherit; + --theme--board_gray-text: rgb(107, 112, 116); + --theme--board_brown: rgb(59, 54, 51); + --theme--board_brown-card: rgb(76, 61, 53); + --theme--board_brown-card_text: inherit; + --theme--board_brown-text: rgb(155, 98, 69); + --theme--board_orange: rgb(61, 54, 49); + --theme--board_orange-card: rgb(85, 59, 41); + --theme--board_orange-text: rgb(168, 92, 30); + --theme--board_yellow: rgb(56, 55, 49); + --theme--board_yellow-card: rgb(79, 64, 41); + --theme--board_yellow-card_text: inherit; + --theme--board_yellow-text: rgb(137, 107, 42); + --theme--board_green: rgb(49, 57, 53); + --theme--board_green-card: rgb(46, 68, 58); + --theme--board_green-card_text: inherit; + --theme--board_green-text: rgb(61, 124, 86); + --theme--board_blue: rgb(49, 56, 64); + --theme--board_blue-card: rgb(45, 66, 86); + --theme--board_blue-card_text: inherit; + --theme--board_blue-text: rgb(46, 117, 164); + --theme--board_purple: rgb(57, 53, 65); + --theme--board_purple-card: rgb(69, 58, 91); + --theme--board_purple-card_text: inherit; + --theme--board_purple-text: rgb(123, 96, 180); + --theme--board_pink: rgb(60, 53, 58); + --theme--board_pink-card: rgb(81, 56, 77); + --theme--board_pink-card_text: inherit; + --theme--board_pink-text: rgb(169, 76, 157); + --theme--board_red: rgb(66, 51, 51); + --theme--board_red-card: rgb(94, 52, 54); + --theme--board_red-card_text: inherit; + --theme--board_red-text: rgb(194, 65, 82); + + --theme--code_inline: rgba(135, 131, 120, 0.15); + --theme--code_inline-text: #eb5757; + + --theme--code: rgb(63, 68, 71); + --theme--code_plain: var(--theme--text); + --theme--code_property: hsl(350, 40%, 70%); + --theme--code_tag: var(--theme--code_property); + --theme--code_boolean: var(--theme--code_property); + --theme--code_number: var(--theme--code_property); + --theme--code_constant: var(--theme--code_property); + --theme--code_symbol: var(--theme--code_property); + --theme--code_deleted: #f00; + --theme--code_selector: hsl(75, 70%, 60%); + --theme--code_attr-name: var(--theme--code_selector); + --theme--code_string: var(--theme--code_selector); + --theme--code_char: var(--theme--code_selector); + --theme--code_builtin: var(--theme--code_selector); + --theme--code_inserted: var(--theme--code_selector); + --theme--code_operator: hsl(40, 90%, 60%); + --theme--code_entity: var(--theme--code_operator); + --theme--code_url: var(--theme--code_operator); + --theme--code_variable: var(--theme--code_operator); + --theme--code_comment: hsl(30, 20%, 50%); + --theme--code_cdata: var(--theme--code_comment); + --theme--code_prolog: var(--theme--code_comment); + --theme--code_doctype: var(--theme--code_comment); + --theme--code_atrule: hsl(350, 40%, 70%); + --theme--code_attr-value: var(--theme--code_atrule); + --theme--code_keyword: var(--theme--code_atrule); + --theme--code_regex: #e90; + --theme--code_important: var(--theme--code_regex); + --theme--code_function: var(--theme--code_plain); + --theme--code_class-name: var(--theme--code_function); + --theme--code_parameter: var(--theme--code_plain); + --theme--code_decorator: var(--theme--code_plain); + --theme--code_id: var(--theme--code_plain); + --theme--code_class: var(--theme--code_plain); + --theme--code_pseudo-element: var(--theme--code_plain); + --theme--code_pseudo-class: var(--theme--code_plain); + --theme--code_attribute: var(--theme--code_plain); + --theme--code_value: var(--theme--code_plain); + --theme--code_unit: var(--theme--code_plain); + --theme--code_punctuation: var(--theme--code_plain); + --theme--code_annotation: var(--theme--code_plain); +} diff --git a/src/mods/topbar-icons/client.mjs b/src/mods/topbar-icons/client.mjs new file mode 100644 index 0000000..0e57db2 --- /dev/null +++ b/src/mods/topbar-icons/client.mjs @@ -0,0 +1,59 @@ +/** + * notion-enhancer: topbar icons + * (c) 2020 CloudHill (https://github.com/CloudHill) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web, components }, db) { + await web.whenReady(['.notion-topbar-action-buttons']); + + const observeButton = (selector, label = '') => { + const updateButton = () => { + const $btns = document.querySelectorAll(selector); + $btns.forEach(($btn) => { + $btn.style.width = 'auto'; + $btn.style.fontSize = '14px'; + $btn.style.lineHeight = '1.2'; + $btn.style.paddingLeft = '8px'; + $btn.style.paddingRight = '8px'; + const innerHTML = label || $btn.ariaLabel; + if ($btn.innerHTML !== innerHTML) $btn.innerHTML = innerHTML; + }); + }; + web.addDocumentObserver(updateButton, [selector]); + updateButton(); + }; + + if ((await db.get(['share'])) === true) { + const selector = '.notion-topbar-share-menu', + label = await components.feather('share-2', { + style: 'width:16px;height:16px;color:var(--theme--icon);', + }); + observeButton(selector, label); + } + + if ((await db.get(['comments'])) === false) { + const selector = '.notion-topbar-comments-button'; + observeButton(selector); + } + + if ((await db.get(['updates'])) === false) { + const selector = + '.notion-topbar-updates-button, .notion-topbar-share-menu ~ [aria-label="Updates"]'; + observeButton(selector); + } + + if ((await db.get(['favorite'])) === false) { + const selector = '.notion-topbar-share-menu ~ [aria-label^="Fav"]'; + observeButton(selector); + } + + if ((await db.get(['more'])) === false) { + const selector = '.notion-topbar-more-button', + label = 'More'; + observeButton(selector, label); + } +} diff --git a/src/mods/topbar-icons/mod.json b/src/mods/topbar-icons/mod.json new file mode 100644 index 0000000..895872e --- /dev/null +++ b/src/mods/topbar-icons/mod.json @@ -0,0 +1,57 @@ +{ + "name": "topbar icons", + "id": "e0700ce3-a9ae-45f5-92e5-610ded0e348d", + "version": "0.3.0", + "description": "choose between text or icons for the topbar buttons.", + "preview": "topbar-icons.jpg", + "tags": ["extension", "customisation"], + "authors": [ + { + "name": "CloudHill", + "email": "rh.cloudhill@gmail.com", + "homepage": "https://github.com/CloudHill", + "avatar": "https://avatars.githubusercontent.com/u/54142180" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": {}, + "options": [ + { + "type": "toggle", + "key": "share", + "label": "share", + "tooltip": "**on = icon, off = text**", + "value": false + }, + { + "type": "toggle", + "key": "comments", + "label": "comments", + "tooltip": "**on = icon, off = text**", + "value": true + }, + { + "type": "toggle", + "key": "updates", + "label": "updates", + "tooltip": "**on = icon, off = text**", + "value": true + }, + { + "type": "toggle", + "key": "favorite", + "label": "favorite", + "tooltip": "**on = icon, off = text**", + "value": true + }, + { + "type": "toggle", + "key": "more", + "label": "more (3 dots)", + "tooltip": "**on = icon, off = text**", + "value": true + } + ] +} diff --git a/src/mods/topbar-icons/topbar-icons.jpg b/src/mods/topbar-icons/topbar-icons.jpg new file mode 100644 index 0000000..04f8c75 Binary files /dev/null and b/src/mods/topbar-icons/topbar-icons.jpg differ diff --git a/src/mods/tray/client.mjs b/src/mods/tray/client.mjs new file mode 100644 index 0000000..051c743 --- /dev/null +++ b/src/mods/tray/client.mjs @@ -0,0 +1,17 @@ +/** + * notion-enhancer: tray + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ electron, env, web }, db) { + const runInBackground = await db.get(['run_in_background']); + if (!runInBackground) return; + + // force new window creation on create new window hotkey + // hotkey is built into notion, so can't be changed, + // but is broken by this mod's window duplication prevention + web.addHotkeyListener([env.name === 'darwin' ? 'Meta' : 'Ctrl', 'Shift', 'N'], () => + electron.sendMessage('create-new-window') + ); +} diff --git a/src/mods/tray/createWindow.cjs b/src/mods/tray/createWindow.cjs new file mode 100644 index 0000000..4589738 --- /dev/null +++ b/src/mods/tray/createWindow.cjs @@ -0,0 +1,58 @@ +/** + * notion-enhancer: tray + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +module.exports = async function (api, db, __exports, __eval) { + const electron = require('electron'), + urlHelpers = api.electron.notionRequire('helpers/urlHelpers'), + runInBackground = await db.get(['run_in_background']); + if (!runInBackground) return; + + let appQuit = false; + electron.app.once('before-quit', () => { + appQuit = true; + }); + + const notionCreateWindow = __exports.createWindow; + __exports.createWindow = (relativeUrl = '', args) => { + const windows = api.electron.getNotionWindows(); + if (windows.length) windows.forEach((win) => win.show()); + + if (relativeUrl || !windows.length) { + // hijack close event to hide instead + const window = notionCreateWindow(relativeUrl, args); + window.prependListener('close', (e) => { + const isLastWindow = electron.BrowserWindow.getAllWindows().length === 1; + if (!appQuit && isLastWindow) { + window.hide(); + e.preventDefault(); + throw new Error(': prevent window close'); + } + }); + + // no other windows yet + opened at startup = hide + const wasOpenedAtStartup = + process.argv.includes('--startup') || + app.getLoginItemSettings({ args: ['--startup'] }).wasOpenedAtLogin; + if (!windows.length && wasOpenedAtStartup) { + window.once('ready-to-show', () => window.hide()); + } + + return window; + } else { + const window = api.electron.getFocusedNotionWindow() || windows[0]; + // prevents duplicate windows on dock/taskbar click + window.focus(); + if (relativeUrl) { + // handle requests passed via the notion:// protocol + // or ctrl+click + window.loadURL(urlHelpers.getIndexUrl(relativeUrl)); + } + return window; + } + }; +}; diff --git a/src/mods/tray/main.cjs b/src/mods/tray/main.cjs new file mode 100644 index 0000000..057161c --- /dev/null +++ b/src/mods/tray/main.cjs @@ -0,0 +1,108 @@ +/** + * notion-enhancer: tray + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +let tray; + +module.exports = async function (api, db, __exports, __eval) { + const { env, registry } = api, + electron = require('electron'), + path = require('path'), + enhancerIcon = path.resolve(`${__dirname}/../../media/colour-x16.png`), + hotkey = await db.get(['hotkey']), + openAtStartup = await db.get(['startup']), + runInBackground = await db.get(['run_in_background']), + menuHotkey = await ( + await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e') + ).get(['hotkey']); + + const toggleWindows = (checkFocus = true) => { + const windows = electron.BrowserWindow.getAllWindows(); + if (runInBackground) { + // hide + if (windows.some((win) => (checkFocus ? win.isFocused() : true) && win.isVisible())) { + windows.forEach((win) => [win.isFocused() && win.blur(), win.hide()]); + } else windows.forEach((win) => win.show()); + } else { + // minimize + if (windows.some((win) => (checkFocus ? win.isFocused() : true) && !win.isMinimized())) { + windows.forEach((win) => win.minimize()); + } else windows.forEach((win) => win.restore()); + } + }; + + await electron.app.whenReady(); + electron.app.setLoginItemSettings({ openAtLogin: openAtStartup, args: ['--startup'] }); + + tray = new electron.Tray(enhancerIcon); + tray.setToolTip('notion-enhancer'); + tray.on('click', () => toggleWindows(false)); + electron.globalShortcut.register(hotkey, toggleWindows); + + // connects to client hotkey listener + // manually forces new window creation + // since notion's default is broken by + // duplicate window prevention + const createWindow = () => { + const { createWindow } = api.electron.notionRequire('main/createWindow.js'); + createWindow('/'); + }; + electron.ipcMain.on('notion-enhancer:create-new-window', createWindow); + + const contextMenu = electron.Menu.buildFromTemplate([ + { + type: 'normal', + label: 'notion-enhancer', + icon: enhancerIcon, + enabled: false, + }, + { type: 'separator' }, + { + type: 'normal', + label: 'docs', + click: () => electron.shell.openExternal('https://notion-enhancer.github.io/'), + }, + { + type: 'normal', + label: 'source code', + click: () => electron.shell.openExternal('https://github.com/notion-enhancer/'), + }, + { + type: 'normal', + label: 'community', + click: () => electron.shell.openExternal('https://discord.gg/sFWPXtA'), + }, + { + type: 'normal', + label: 'enhancements menu', + accelerator: menuHotkey, + click: env.focusMenu, + }, + { type: 'separator' }, + { + type: 'normal', + label: 'toggle visibility', + accelerator: hotkey, + click: toggleWindows, + }, + { + type: 'normal', + label: 'new window', + click: createWindow, + accelerator: 'CmdOrCtrl+Shift+N', + }, + { + label: 'relaunch', + click: env.reload, + }, + { + label: 'quit', + role: 'quit', + }, + ]); + tray.setContextMenu(contextMenu); +}; diff --git a/src/mods/tray/mod.json b/src/mods/tray/mod.json new file mode 100644 index 0000000..7e3cc02 --- /dev/null +++ b/src/mods/tray/mod.json @@ -0,0 +1,47 @@ +{ + "name": "tray", + "id": "f96f4a73-21af-4e3f-a68f-ab4976b020da", + "environments": ["linux", "win32", "darwin"], + "version": "0.11.0", + "description": "adds an icon to the system tray/menubar for extra app/window management features (e.g. open on startup, a global hotkey).", + "preview": "tray.jpg", + "tags": ["extension", "app"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": {}, + "js": { + "client": ["client.mjs"], + "electron": [ + { "source": "main.cjs", "target": "main/main.js" }, + { "source": "createWindow.cjs", "target": "main/createWindow.js" } + ] + }, + "options": [ + { + "type": "toggle", + "key": "startup", + "label": "open notion on startup", + "tooltip": "**if the 'run notion in the background' option is also enabled, the app will open in the background on startup** (this option may require relaunching the app BEFORE restarting your system to properly take effect)", + "value": false + }, + { + "type": "toggle", + "key": "run_in_background", + "label": "run notion in the background", + "tooltip": "**pressing the close button or toggling window visibility will hide the app, running notion in the background** (instead of quitting or minimizing it)", + "value": true + }, + { + "type": "hotkey", + "key": "hotkey", + "label": "toggle window visibility hotkey", + "value": "Ctrl+Shift+A" + } + ] +} diff --git a/src/mods/tray/tray.jpg b/src/mods/tray/tray.jpg new file mode 100644 index 0000000..af425b0 Binary files /dev/null and b/src/mods/tray/tray.jpg differ diff --git a/src/mods/truncated-titles/client.mjs b/src/mods/truncated-titles/client.mjs new file mode 100644 index 0000000..b0fe2d6 --- /dev/null +++ b/src/mods/truncated-titles/client.mjs @@ -0,0 +1,66 @@ +/** + * notion-enhancer: truncated titles + * (c) 2021 admiraldus (https://github.com/admiraldus) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ web, components }, db) { + const enhanceTableTitles = await db.get(['tables']), + enhanceTimelineItems = await db.get(['timelines']), + tableCellSelector = '.notion-table-view-header-cell', + tableTitleSelector = `${tableCellSelector} div[style*="text-overflow"]`, + timelineItemSelector = '.notion-timeline-item', + $elements = []; + + const addTooltips = () => { + if (enhanceTableTitles) { + document.querySelectorAll(tableTitleSelector).forEach(($tableTitle) => { + if ($elements.includes($tableTitle)) return; + + if ($tableTitle.scrollWidth > $tableTitle.clientWidth) { + components.addTooltip( + $tableTitle.parentElement.parentElement.parentElement, + web.html`${web.escape($tableTitle.innerText)}`, + 750 + ); + $elements.push($tableTitle); + } + }); + } + + if (enhanceTimelineItems) { + document.querySelectorAll(timelineItemSelector).forEach(($timelineItem) => { + const $title = $timelineItem.nextElementSibling.firstElementChild; + $title.style.position = 'absolute'; + $title.style.left = $timelineItem.style.left; + + if ($elements.includes($timelineItem)) return; + $elements.push($timelineItem); + + $title.style.width = $timelineItem.clientWidth + 'px'; + $title.firstElementChild.firstElementChild.style.maxWidth = + $timelineItem.clientWidth + 'px'; + $timelineItem.addEventListener('mouseover', (event) => { + $title.style.width = '100%'; + $title.firstElementChild.firstElementChild.style.maxWidth = '400px'; + }); + $timelineItem.addEventListener('mouseout', async (event) => { + if (!$timelineItem.matches(':hover')) { + $title.style.width = $timelineItem.clientWidth + 'px'; + $title.firstElementChild.firstElementChild.style.maxWidth = + $timelineItem.clientWidth + 'px'; + } + }); + }); + } + }; + + await web.whenReady(); + addTooltips(); + web.addDocumentObserver(addTooltips, [ + tableCellSelector, + timelineItemSelector, + `${timelineItemSelector} + div > :first-child`, + ]); +} diff --git a/src/mods/truncated-titles/mod.json b/src/mods/truncated-titles/mod.json new file mode 100644 index 0000000..eb2cf39 --- /dev/null +++ b/src/mods/truncated-titles/mod.json @@ -0,0 +1,33 @@ +{ + "name": "truncated titles", + "id": "1794c0bd-7b96-46ad-aa0b-fc4bd76fc7fb", + "version": "0.2.0", + "description": "see the full text of a truncated title on hover.", + "preview": "truncated-titles.jpg", + "tags": ["extension", "layout"], + "authors": [ + { + "name": "admiraldus", + "homepage": "https://github.com/admiraldus", + "avatar": "https://raw.githubusercontent.com/admiraldus/admiraldus/main/module.gif" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": {}, + "options": [ + { + "type": "toggle", + "key": "tables", + "label": "table titles", + "value": true + }, + { + "type": "toggle", + "key": "timelines", + "label": "timeline items", + "value": true + } + ] +} diff --git a/src/mods/truncated-titles/truncated-titles.jpg b/src/mods/truncated-titles/truncated-titles.jpg new file mode 100644 index 0000000..94a1cdb Binary files /dev/null and b/src/mods/truncated-titles/truncated-titles.jpg differ diff --git a/src/mods/tweaks/client.css b/src/mods/tweaks/client.css new file mode 100644 index 0000000..dac5ef4 --- /dev/null +++ b/src/mods/tweaks/client.css @@ -0,0 +1,132 @@ +/** + * notion-enhancer: tweaks + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2020 arecsu + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.enhancer--tweak-responsive_breakpoint + .notion-column_list-block + [style='display: flex;'] + > div { + width: 100% !important; +} +.enhancer--tweak-responsive_breakpoint .notion-column_list-block [style='display: flex;'] { + flex-direction: column !important; +} +.enhancer--tweak-responsive_breakpoint .notion-app-inner, +.enhancer--tweak-full_width_pages .notion-app-inner { + --theme--page-width: 100%; + --theme--page-padding: calc(48px + env(safe-area-inset-left)); +} + +.enhancer--tweak-normalise_table_scroll + .notion-frame + .notion-page-content + .notion-collection_view-block, +.enhancer--tweak-normalise_table_scroll .notion-peek-renderer .notion-collection_view-block, +.enhancer--tweak-normalise_table_scroll + .notion-page-template-modal + .notion-collection_view-block, +.enhancer--tweak-normalise_table_dscroll .notion-collection-view-body .notion-table-view { + width: 100% !important; + padding: 0 !important; +} +.enhancer--tweak-normalise_table_scroll + .notion-collection_view-block + > [contenteditable] + > .notion-scroller + > [class$='view'][style*='padding'], +.enhancer--tweak-normalise_table_scroll + .notion-collection_view-block + > :first-child[style*='padding-right'] { + padding: 1px !important; +} + +.enhancer--tweak-snappy_transitions * { + animation-duration: 0s !important; + transition-duration: 0s !important; +} +.enhancer--tweak-snappy_transitions .notion-selectable-halo { + opacity: 1 !important; +} + +.enhancer--tweak-hide_help .notion-help-button { + display: none !important; +} + +.enhancer--tweak-hide_slash_for_commands [contenteditable]:empty:after { + content: ' ' !important; +} + +.enhancer--tweak-thicker_bold .notion-page-content span[style*='font-weight:600'] { + font-weight: 700 !important; +} + +.enhancer--tweak-spaced_lines .notion-page-content .notion-selectable.notion-text-block { + line-height: 1.65 !important; + margin-top: 0.75em !important; +} + +.enhancer--tweak-condensed_bullets .notion-selectable.notion-bulleted_list-block { + margin-top: -1.5px !important; + margin-bottom: -1.5px !important; +} + +.enhancer--tweak-bracketed_links .notion-link-token span { + border-bottom: none !important; +} +.enhancer--tweak-bracketed_links .notion-link-token:before { + content: '[['; + opacity: 0.7; + transition: opacity 100ms ease-in; +} +.enhancer--tweak-bracketed_links .notion-link-token:after { + content: ']]'; + opacity: 0.7; + transition: opacity 100ms ease-in; +} +.enhancer--tweak-bracketed_links .notion-link-token:hover::before, +.enhancer--tweak-bracketed_links .notion-link-token:hover::after { + opacity: 1; +} + +.enhancer--tweak-accented_links .notion-link-token { + color: var(--theme--accent_blue) !important; +} +.enhancer--tweak-accented_links .notion-link-token span[style*='border-bottom:0.05em'] { + opacity: 1 !important; + border-color: var(--theme--accent_blue) !important; +} + +.enhancer--tweak-quotation_marks + .notion-quote-block + [style*='border-left: 3px solid currentcolor;'] { + position: relative; + padding-left: 24px !important; + padding-right: 18px !important; +} +.enhancer--tweak-quotation_marks .notion-quote-block [placeholder='Empty quote']::before, +.enhancer--tweak-quotation_marks .notion-quote-block [placeholder='Empty quote']::after { + font-family: Georgia, serif; + font-size: 24px; + font-weight: bold; + position: absolute; +} +.enhancer--tweak-quotation_marks .notion-quote-block [placeholder='Empty quote']::before { + content: '\201C'; + left: 8px; + top: -2px; +} +.enhancer--tweak-quotation_marks .notion-quote-block [placeholder='Empty quote']::after { + content: '\201D'; + right: 2px; + bottom: -2px; +} + +.enhancer--tweak-img_alignment-left .notion-image-block { + align-self: start !important; +} +.enhancer--tweak-img_alignment-right .notion-image-block { + align-self: end !important; +} diff --git a/src/mods/tweaks/client.mjs b/src/mods/tweaks/client.mjs new file mode 100644 index 0000000..66a2660 --- /dev/null +++ b/src/mods/tweaks/client.mjs @@ -0,0 +1,55 @@ +/** + * notion-enhancer: tweaks + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web }, db) { + const cssInsert = await db.get(['insert.css']); + if (cssInsert?.filename) { + document.head.append( + web.html`` + ); + } + + const responsiveBreakpointPx = +(await db.get(['tweak.responsive_breakpoint_px'])), + responsiveBreakpointPercent = + screen.width * 0.01 * (await db.get(['tweak.responsive_breakpoint_percent'])), + addResponsiveBreakpoint = () => { + document.body.classList.remove('enhancer--tweak-responsive_breakpoint'); + if ( + window.innerWidth <= responsiveBreakpointPx || + window.innerWidth <= responsiveBreakpointPercent + ) { + document.body.classList.add('enhancer--tweak-responsive_breakpoint'); + } + }; + window.addEventListener('resize', addResponsiveBreakpoint); + addResponsiveBreakpoint(); + + const tweaks = [ + 'full_width_pages', + 'normalise_table_scroll', + 'hide_help', + 'hide_slash_for_commands', + 'snappy_transitions', + 'thicker_bold', + 'spaced_lines', + 'condensed_bullets', + 'bracketed_links', + 'accented_links', + 'quotation_marks', + ]; + for (const tweak of tweaks) { + if (await db.get([`tweak.${tweak}`])) { + document.body.classList.add(`enhancer--tweak-${tweak}`); + } + } + + const imgAlignment = await db.get(['tweak.img_alignment']); + if (imgAlignment !== 'center') { + document.body.classList.add(`enhancer--tweak-img_alignment-${imgAlignment}`); + } +} diff --git a/src/mods/tweaks/mod.json b/src/mods/tweaks/mod.json new file mode 100644 index 0000000..b57e912 --- /dev/null +++ b/src/mods/tweaks/mod.json @@ -0,0 +1,124 @@ +{ + "name": "tweaks", + "id": "5174a483-c88d-4bf8-a95f-35cd330b76e2", + "version": "0.2.0", + "description": "common style/layout changes and custom code insertion. check out the [tweaks page](https://notion-enhancer.github.io/advanced/tweaks) for more.", + "tags": ["extension", "customisation"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "css": { + "client": ["client.css"] + }, + "js": { + "client": ["client.mjs"] + }, + "options": [ + { + "type": "file", + "key": "insert.css", + "label": "css insert", + "tooltip": "**upload a css file that will be applied to the notion client**", + "extensions": [".css"] + }, + { + "type": "number", + "key": "tweak.responsive_breakpoint_px", + "label": "responsive columns breakpoint (px)", + "tooltip": "the **width in pixels below which in-page columns are resized** to appear full-width to reduce content squishing", + "value": 600 + }, + { + "type": "number", + "key": "tweak.responsive_breakpoint_percent", + "label": "responsive columns breakpoint (%)", + "tooltip": "the **percentage of the screen below which in-page columns are resized to appear full-width** to reduce content squishing", + "value": 30 + }, + { + "type": "toggle", + "key": "tweak.full_width_pages", + "label": "full width pages", + "tooltip": "**decreases padding so every page appears full width**", + "value": false + }, + { + "type": "toggle", + "key": "tweak.normalise_table_scroll", + "label": "wrap tables to page width", + "tooltip": "**force horizontally scrollable tables to respect the width and padding of a page when they overflow**", + "value": true + }, + { + "type": "toggle", + "key": "tweak.snappy_transitions", + "label": "snappy transitions", + "tooltip": "enabling this **eliminates css animation time**, but will not prevent motion e.g. the sidebar popping out", + "value": false + }, + { + "type": "toggle", + "key": "tweak.hide_help", + "label": "hide help button", + "value": false + }, + { + "type": "toggle", + "key": "tweak.hide_slash_for_commands", + "label": "hide \"Type '/' for commands\"", + "value": false + }, + { + "type": "toggle", + "key": "tweak.thicker_bold", + "label": "thicker bold text", + "value": true + }, + { + "type": "toggle", + "key": "tweak.spaced_lines", + "label": "readable line spacing", + "tooltip": "**greater line spacing between text blocks**", + "value": false + }, + { + "type": "toggle", + "key": "tweak.condensed_bullets", + "label": "condense bullet points", + "tooltip": "**tighter line spacing between bullet point blocks**", + "value": false + }, + { + "type": "toggle", + "key": "tweak.bracketed_links", + "label": "bracketed links", + "tooltip": "**render links surrounded with [[brackets]] instead of __underlined__**", + "value": false + }, + { + "type": "toggle", + "key": "tweak.accented_links", + "label": "accented links", + "tooltip": "**links are shown by default with notion's blue colour or a theme's equivalent**", + "value": false + }, + { + "type": "toggle", + "key": "tweak.quotation_marks", + "label": "quote block quotation marks", + "tooltip": "**wrap quote block content in large, decorative serif quotation marks**", + "value": false + }, + { + "type": "select", + "key": "tweak.img_alignment", + "label": "image alignment", + "values": ["center", "left", "right"] + } + ] +} diff --git a/src/mods/view-scale/client.css b/src/mods/view-scale/client.css new file mode 100644 index 0000000..652cad1 --- /dev/null +++ b/src/mods/view-scale/client.css @@ -0,0 +1,90 @@ +/** + * notion-enhancer: view scale + * (c) 2021 SP12893678 (https://sp12893678.tk/) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +.view_scale--container { + border: 1px solid var(--theme--ui_divider); + border-radius: 9999px !important; + margin: 0 4px; + padding: 0 5px 0 4px; + align-items: center; + justify-content: center; + display: flex; +} + +.view_scale--slider { + appearance: none; + outline: 0; + border: 0px; + overflow: hidden; + width: 150px; + height: 20px; + margin: auto 4px auto 0; + cursor: ew-resize; + border-radius: 9999px; +} +.view_scale--slider::-webkit-slider-runnable-track { + appearance: none; + height: 20px; + background-color: var(--theme--ui_toggle-off); +} +.view_scale--slider::-webkit-slider-thumb { + appearance: none; + position: relative; + width: 20px; + height: 20px; + border-radius: 9999px; + border: 0px; + background: var(--theme--ui_toggle-on); + box-shadow: -100px 0 0 90px var(--theme--ui_toggle-on), + inset 0 0 0 20px var(--theme--ui_toggle-on); + transition: 0.2s; +} +.view_scale--slider:active::-webkit-slider-thumb { + background: var(--theme--ui_toggle-feature); + box-shadow: -100px 0 0 90px var(--theme--ui_toggle-on), + inset 0 0 0 2px var(--theme--ui_toggle-on); +} + +.view_scale--counter { + font-size: 14px; + margin: auto 4px auto 0; + width: 5ch; + text-align: right; +} + +.view_scale--button { + user-select: none; + transition: background 20ms ease-in 0s; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + border-radius: 9999px; + height: 20px; + width: 20px; + padding: 0 0.25px 0 0; + + margin-left: 2px; + border: none; + background: transparent; + font-size: 18px; +} +.view_scale--button:focus, +.view_scale--button:hover { + background: var(--theme--ui_interactive-hover); +} +.view_scale--button:active { + background: var(--theme--ui_interactive-active); +} +.view_scale--button svg { + display: block; + width: 14px; + height: 14px; + fill: var(--theme--icon); + color: var(--theme--icon); +} diff --git a/src/mods/view-scale/client.mjs b/src/mods/view-scale/client.mjs new file mode 100644 index 0000000..95ae64c --- /dev/null +++ b/src/mods/view-scale/client.mjs @@ -0,0 +1,87 @@ +/** + * notion-enhancer: view scale + * (c) 2021 SP12893678 (https://sp12893678.tk/) + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +export default async function ({ electron, web, components }, db) { + let zoomFactor = (await db.get(['default_zoom'])) / 100, + updateScale = () => {}; + electron.webFrame.setZoomFactor(zoomFactor); + + const zoomOffset = (await db.get(['offset'])) / 100, + zoomMin = 0.5, + zoomMax = 2, + getZoomFactor = () => electron.webFrame.getZoomFactor(), + setZoomFactor = (zoomFactor) => electron.webFrame.setZoomFactor(zoomFactor), + zoomPlus = (multiplier = 1) => { + zoomFactor = Math.min(getZoomFactor() + zoomOffset * multiplier, zoomMax); + setZoomFactor(zoomFactor); + updateScale(); + }, + zoomMinus = (multiplier = 1) => { + zoomFactor = Math.max(getZoomFactor() - zoomOffset * multiplier, zoomMin); + setZoomFactor(zoomFactor); + updateScale(); + }; + + const mousewheelModifier = await db.get(['mousewheel']); + if (mousewheelModifier !== '-- none --') { + const mousewheelModifierKey = { + Control: 'ctrlKey', + Alt: 'altKey', + Command: 'metaKey', + Shift: 'shiftKey', + }[mousewheelModifier]; + document.addEventListener('wheel', (event) => { + if (event[mousewheelModifierKey] && event.deltaY < 0) zoomPlus(); + if (event[mousewheelModifierKey] && event.deltaY > 0) zoomMinus(); + }); + } + + const showVisualSlider = await db.get(['ui']); + if (showVisualSlider) { + const topbarActionsSelector = + '.notion-topbar-action-buttons > div[style="display: flex;"]'; + await web.whenReady([topbarActionsSelector]); + + const $topbarActions = document.querySelector(topbarActionsSelector), + $scaleContainer = web.html`
`, + $scaleSlider = web.html``, + $scaleCounter = web.html`100%`, + $scalePlus = web.html``, + $scaleMinus = web.html``; + components.addTooltip($scalePlus, '**Zoom into the window**'); + components.addTooltip($scaleMinus, '**Zoom out of the window**'); + updateScale = () => { + if (getZoomFactor() !== zoomFactor) zoomFactor = getZoomFactor(); + $scaleSlider.value = Math.round(zoomFactor * 100); + $scaleCounter.innerHTML = Math.round(zoomFactor * 100) + '%'; + }; + updateScale(); + + $scaleSlider.addEventListener('input', () => { + zoomFactor = $scaleSlider.value / 100; + $scaleCounter.innerHTML = Math.round(zoomFactor * 100) + '%'; + }); + $scaleSlider.addEventListener('change', () => setZoomFactor(zoomFactor)); + $scalePlus.addEventListener('click', () => zoomPlus()); + $scaleMinus.addEventListener('click', () => zoomMinus()); + + $topbarActions.prepend( + web.render($scaleContainer, $scaleSlider, $scaleCounter, $scalePlus, $scaleMinus) + ); + + web.addHotkeyListener(['Ctrl', '+'], updateScale); + web.addHotkeyListener(['Ctrl', '-'], updateScale); + web.addHotkeyListener(['Ctrl', '0'], updateScale); + web.addHotkeyListener(['Command', '+'], updateScale); + web.addHotkeyListener(['Command', '-'], updateScale); + web.addHotkeyListener(['Command', '0'], updateScale); + } +} diff --git a/src/mods/view-scale/mod.json b/src/mods/view-scale/mod.json new file mode 100644 index 0000000..0a9059c --- /dev/null +++ b/src/mods/view-scale/mod.json @@ -0,0 +1,48 @@ +{ + "name": "view scale", + "id": "e71ce1e0-024c-435e-a25e-7dd50448d1df", + "environments": ["linux", "win32", "darwin"], + "version": "0.1.0", + "description": "zoom in/out of the notion window with the mousewheel or a visual slider (`ctrl/cmd +/-` are available in-app by default).", + "preview": "view-scale.jpg", + "tags": ["extension", "app"], + "authors": [ + { + "name": "SP12893678", + "homepage": "https://sp12893678.tk/", + "avatar": "https://sp12893678.tk/img/avatar.jpg" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [ + { + "type": "number", + "key": "offset", + "label": "scale +/- offset (%)", + "value": 10 + }, + { + "type": "number", + "key": "default_zoom", + "label": "default scale (%)", + "value": 100 + }, + { + "type": "toggle", + "key": "ui", + "label": "visual slider", + "value": true + }, + { + "type": "select", + "key": "mousewheel", + "label": "mousewheel scaling keyboard modifier", + "values": ["Control", "Alt", "Command", "Shift", "-- none --"] + } + ] +} diff --git a/src/mods/view-scale/view-scale.jpg b/src/mods/view-scale/view-scale.jpg new file mode 100644 index 0000000..f7ee516 Binary files /dev/null and b/src/mods/view-scale/view-scale.jpg differ diff --git a/src/mods/weekly-view/client.mjs b/src/mods/weekly-view/client.mjs new file mode 100644 index 0000000..426f5ba --- /dev/null +++ b/src/mods/weekly-view/client.mjs @@ -0,0 +1,71 @@ +/** + * notion-enhancer: weekly view + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +'use strict'; + +export default async function ({ web }, db) { + const pageSelector = '.notion-page-content', + calendarSelector = '.notion-calendar-view', + viewSelector = + '.notion-page-content > .notion-selectable.notion-collection_view-block', + viewControlSelector = ':scope>div>div>div>div>div', + todaySelector = '.notion-calendar-view-day[style*="background"]', + weekSelector = '[style^="position: relative; display: flex; height: "]', + toolbarBtnSelector = + '.notion-calendar-view > :first-child > :first-child > :first-child > :nth-last-child(2)'; + + const transformCalendarView = () => { + const $page = document.querySelector(pageSelector); + document.querySelectorAll(viewSelector).forEach(async ($view) => { + let currentText; + // Get view controls children nodes, convert to array, filter out non-text + const $viewNodes = [] + .slice.call($view.querySelector(viewControlSelector).children) + .filter(node => node.tagName.toLowerCase().match(/(div|span)/g)); + + // Find current view by analyzing children (which changes on viewport) + if ($viewNodes.length === 1) + { + // Mobile: Simple dropdown button (like legacy), text is current view + currentText = $viewNodes[0].innerText.toLowerCase(); + } else { + // Wide/Desktop: Tabs listed, current view indicated by border style + currentText = $viewNodes + // Find selected view by border style (possibly fragile) + .filter(($e) => $e.children[0].style.borderBottomWidth.toString() === '2px')[0] + .innerText.toLowerCase(); + } + + if (currentText !== 'weekly') return; + + const $calendar = $view.parentElement.parentElement.parentElement.parentElement; + if (!$calendar.querySelector(todaySelector)) { + $calendar.querySelector(toolbarBtnSelector).click(); + } + await new Promise((res, rej) => requestAnimationFrame(res)); + if ($page) { + for (const $week of $calendar.querySelectorAll(weekSelector)) { + if (!$week.querySelector(todaySelector)) { + $week.style.height = 0; + $week.style.visibility = 'hidden'; + } + } + } else { + const $weekContainer = $calendar.querySelector(weekSelector).parentElement; + for (const $week of $calendar.querySelectorAll(weekSelector)) { + if ($week.querySelector(todaySelector)) { + $weekContainer.style.maxHeight = $week.style.height; + break; + } else { + $week.style.height = '0'; + $week.style.visibility = 'hidden'; + } + } + } + }); + }; + web.addDocumentObserver(transformCalendarView, [calendarSelector]); +} diff --git a/src/mods/weekly-view/mod.json b/src/mods/weekly-view/mod.json new file mode 100644 index 0000000..6128450 --- /dev/null +++ b/src/mods/weekly-view/mod.json @@ -0,0 +1,21 @@ +{ + "name": "weekly view", + "id": "4c7acaea-6596-4590-85e5-8ac5a1455e8f", + "version": "0.6.0", + "description": "calendar views named \"weekly\" will show only the current week.", + "preview": "weekly-view.jpg", + "tags": ["extension", "layout"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": {}, + "options": [] +} diff --git a/src/mods/weekly-view/weekly-view.jpg b/src/mods/weekly-view/weekly-view.jpg new file mode 100644 index 0000000..345d33d Binary files /dev/null and b/src/mods/weekly-view/weekly-view.jpg differ diff --git a/src/mods/word-counter/client.css b/src/mods/word-counter/client.css new file mode 100644 index 0000000..bd44781 --- /dev/null +++ b/src/mods/word-counter/client.css @@ -0,0 +1,40 @@ +/** + * notion-enhancer: word counter + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +#word-counter--notice { + color: var(--theme--text_secondary); + font-size: 14px; + margin-top: 0; +} + +.word-counter--stat { + display: block; + background: var(--theme--ui_interactive-hover); + color: var(--theme--text); + font-size: 14px; + line-height: 1.2; + border: 1px solid transparent; + border-radius: 3px; + padding: 6px 8px; + cursor: pointer; + user-select: none; +} +.word-counter--stat:focus, +.word-counter--stat:hover { + background: transparent; + border: 1px solid var(--theme--ui_interactive-hover); +} +.word-counter--stat:active { + background: var(--theme--ui_interactive-active); +} + +.word-counter--stat svg { + display: inline-block; + height: 1em; + width: 1em; + margin: 0 0.4em -2px 0; + color: var(--theme--icon_secondary); +} diff --git a/src/mods/word-counter/client.mjs b/src/mods/word-counter/client.mjs new file mode 100644 index 0000000..30722d3 --- /dev/null +++ b/src/mods/word-counter/client.mjs @@ -0,0 +1,108 @@ +/** + * notion-enhancer: word counter + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +const humanTime = (mins) => { + let readable = ''; + if (1 <= mins) { + readable += `${Math.floor(mins)} min`; + if (2 <= mins) readable += 's'; + } + const secs = Math.round((mins % 1) * 60); + if (1 <= secs) { + if (1 <= mins) readable += ' '; + readable += `${secs} sec`; + if (2 <= secs) readable += 's'; + } + return readable; +}; + +export default async function ({ web, components }, db) { + const dbNoticeText = 'Open a page to see its word count.', + pageNoticeText = 'Click a stat to copy it.', + $notice = web.html`

${dbNoticeText}

`; + + const $wordCount = web.html`12`, + $characterCount = web.html`12`, + $sentenceCount = web.html`12`, + $blockCount = web.html`12`, + $readingTime = web.html`10 mins`, + $readingTooltip = web.html`${await components.feather('info')}`, + $speakingTime = web.html`18 secs`, + $speakingTooltip = web.html`${await components.feather('info')}`, + $statList = web.render( + web.html`
`, + web.render(web.html`

`, $wordCount, ' words'), + web.render(web.html`

`, $characterCount, ' characters'), + web.render(web.html`

`, $sentenceCount, ' sentences'), + web.render(web.html`

`, $blockCount, ' blocks'), + web.render( + web.html`

`, + $readingTooltip, + $readingTime, + ' reading time' + ), + web.render( + web.html`

`, + $speakingTooltip, + $speakingTime, + ' speaking time' + ) + ); + $statList.querySelectorAll('.word-counter--stat').forEach(($stat) => { + $stat.addEventListener('click', () => web.copyToClipboard($stat.innerText)); + }); + components.addTooltip($readingTooltip, '**~ 275 wpm**', { offsetDirection: 'left' }); + components.addTooltip($speakingTooltip, '**~ 180 wpm**', { offsetDirection: 'left' }); + + let viewFocused = false, + $page; + await components.addPanelView({ + id: 'b99deb52-6955-43d2-a53b-a31540cd19a5', + icon: await components.feather('type'), + title: 'Word Counter', + $content: web.render(web.html`
`, $notice, $statList), + onFocus: () => { + viewFocused = true; + $page = document.getElementsByClassName('notion-page-content')[0]; + updateStats(); + }, + onBlur: () => { + viewFocused = false; + }, + }); + + function updateStats() { + if (!$page) return; + const words = $page.innerText.split(/[^\w]+/).length; + $wordCount.innerText = words; + $characterCount.innerText = $page.innerText.length; + $sentenceCount.innerText = $page.innerText.split('.').length; + $blockCount.innerText = $page.querySelectorAll('[data-block-id]').length; + $readingTime.innerText = humanTime(words / 275); + $speakingTime.innerText = humanTime(words / 180); + } + const pageObserver = () => { + if (!viewFocused) return; + if (document.contains($page)) { + updateStats(); + } else { + $page = document.getElementsByClassName('notion-page-content')[0]; + if ($page) { + $notice.innerText = pageNoticeText; + $statList.style.display = ''; + updateStats(); + } else { + $notice.innerText = dbNoticeText; + $statList.style.display = 'none'; + } + } + }; + web.addDocumentObserver(pageObserver, [ + '.notion-page-content', + '.notion-collection_view_page-block', + ]); + pageObserver(); +} diff --git a/src/mods/word-counter/mod.json b/src/mods/word-counter/mod.json new file mode 100644 index 0000000..ad52d9f --- /dev/null +++ b/src/mods/word-counter/mod.json @@ -0,0 +1,23 @@ +{ + "name": "word counter", + "id": "b99deb52-6955-43d2-a53b-a31540cd19a5", + "version": "0.3.0", + "description": "view word/character/sentence/block count & speaking/reading times in the side panel.", + "preview": "word-counter.jpg", + "tags": ["extension", "panel"], + "authors": [ + { + "name": "dragonwocky", + "email": "thedragonring.bod@gmail.com", + "homepage": "https://dragonwocky.me/", + "avatar": "https://dragonwocky.me/avatar.jpg" + } + ], + "js": { + "client": ["client.mjs"] + }, + "css": { + "client": ["client.css"] + }, + "options": [] +} diff --git a/src/mods/word-counter/word-counter.jpg b/src/mods/word-counter/word-counter.jpg new file mode 100644 index 0000000..43978fc Binary files /dev/null and b/src/mods/word-counter/word-counter.jpg differ diff --git a/yarn.lock b/yarn.lock index a5954d6..497b305 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,154 +1,235 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! +__metadata: + version: 6 + cacheKey: 8 -"@types/glob@^7.1.1": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" - integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== +"@electron/asar@npm:^3.2.2": + version: 3.2.2 + resolution: "@electron/asar@npm:3.2.2" dependencies: - "@types/minimatch" "*" - "@types/node" "*" + "@types/glob": ^7.1.1 + chromium-pickle-js: ^0.2.0 + commander: ^5.0.0 + glob: ^7.1.6 + minimatch: ^3.0.4 + dependenciesMeta: + "@types/glob": + optional: true + bin: + asar: bin/asar.js + checksum: 38a3b4a47180f2033a599421175f03706941ba05a32591a639127f6374e0007c6a7c8bde550129de394f4072a0bf39c24aea202540fe1faba6d74b4181c007a8 + languageName: node + linkType: hard -"@types/minimatch@*": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" - integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== - -"@types/node@*": - version "16.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== +"@types/glob@npm:^7.1.1": + version: 7.2.0 + resolution: "@types/glob@npm:7.2.0" dependencies: - color-convert "^2.0.1" + "@types/minimatch": "*" + "@types/node": "*" + checksum: 6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19 + languageName: node + linkType: hard -asar@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/asar/-/asar-3.1.0.tgz#70b0509449fe3daccc63beb4d3c7d2e24d3c6473" - integrity sha512-vyxPxP5arcAqN4F/ebHd/HhwnAiZtwhglvdmc7BR2f0ywbVNTOpSeyhLDbGXtE/y58hv1oC75TaNIXutnsOZsQ== +"@types/minimatch@npm:*": + version: 5.1.2 + resolution: "@types/minimatch@npm:5.1.2" + checksum: 0391a282860c7cb6fe262c12b99564732401bdaa5e395bee9ca323c312c1a0f45efbf34dce974682036e857db59a5c9b1da522f3d6055aeead7097264c8705a8 + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 18.11.11 + resolution: "@types/node@npm:18.11.11" + checksum: c4b1176a8f1714a3ee3fc2a5e1d568b0cd50209000282db5c68154b3c975952928dbb834ef3a0ce55bd7b345ae29f2cbf4a34635a070294d135a24254231386a + languageName: node + linkType: hard + +"ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" dependencies: - chromium-pickle-js "^0.2.0" - commander "^5.0.0" - glob "^7.1.6" - minimatch "^3.0.4" - optionalDependencies: - "@types/glob" "^7.1.1" + color-convert: ^2.0.1 + checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 + languageName: node + linkType: hard -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +"arg@npm:^5.0.2": + version: 5.0.2 + resolution: "arg@npm:5.0.2" + checksum: 6c69ada1a9943d332d9e5382393e897c500908d91d5cb735a01120d5f71daf1b339b7b8980cbeaba8fd1afc68e658a739746179e4315a26e8a28951ff9930078 + languageName: node + linkType: hard -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" + balanced-match: ^1.0.0 + concat-map: 0.0.1 + checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 + languageName: node + linkType: hard -chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== +"chalk-template@npm:^0.4.0": + version: 0.4.0 + resolution: "chalk-template@npm:0.4.0" dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" + chalk: ^4.1.2 + checksum: 6c706802a79a7963cbce18f022b046fe86e438a67843151868852f80ea7346e975a6a9749991601e7e5d3b6a6c4852a04c53dc966a9a3d04031bd0e0ed53c819 + languageName: node + linkType: hard -chromium-pickle-js@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" - integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU= - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== +"chalk@npm:^4.1.2": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" dependencies: - color-name "~1.1.4" + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc + languageName: node + linkType: hard -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +"chromium-pickle-js@npm:^0.2.0": + version: 0.2.0 + resolution: "chromium-pickle-js@npm:0.2.0" + checksum: 5ccacc538b0a1ecf3484c8fb3327eae129ceee858db0f64eb0a5ff87bda096a418d0d3e6f6e0967c6334d336a2c7463f7b683ec0e1cafbe736907fa2ee2f58ca + languageName: node + linkType: hard -commander@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" + color-name: ~1.1.4 + checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 + languageName: node + linkType: hard -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= +"commander@npm:^5.0.0": + version: 5.1.0 + resolution: "commander@npm:5.1.0" + checksum: 0b7fec1712fbcc6230fcb161d8d73b4730fa91a21dc089515489402ad78810547683f058e2a9835929c212fead1d6a6ade70db28bbb03edbc2829a9ab7d69447 + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 + languageName: node + linkType: hard + +"glob@npm:^7.1.6": + version: 7.2.3 + resolution: "glob@npm:7.2.3" dependencies: - once "^1.3.0" - wrappy "1" + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.1.1 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 + languageName: node + linkType: hard -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad + languageName: node + linkType: hard -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" dependencies: - brace-expansion "^1.1.7" + once: ^1.3.0 + wrappy: 1 + checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd + languageName: node + linkType: hard -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= +"inherits@npm:2": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 + languageName: node + linkType: hard + +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" dependencies: - wrappy "1" + brace-expansion: ^1.1.7 + checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a + languageName: node + linkType: hard -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== +"notion-enhancer@workspace:.": + version: 0.0.0-use.local + resolution: "notion-enhancer@workspace:." dependencies: - has-flag "^4.0.0" + "@electron/asar": ^3.2.2 + arg: ^5.0.2 + chalk-template: ^0.4.0 + bin: + notion-enhancer: bin.mjs + languageName: unknown + linkType: soft -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +"once@npm:^1.3.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: 1 + checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: ^4.0.0 + checksum: 3dda818de06ebbe5b9653e07842d9479f3555ebc77e9a0280caf5a14fb877ffee9ed57007c3b78f5a6324b8dbeec648d9e97a24e2ed9fdb81ddc69ea07100f4a + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard