mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-05 05:09:03 +00:00
merge v0.11.0 rework into dev
This commit is contained in:
commit
1a03c08b36
20
.github/workflows/submodules.yml
vendored
Normal file
20
.github/workflows/submodules.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
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
|
105
.gitignore
vendored
105
.gitignore
vendored
@ -1,104 +1 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
node_modules/*
|
16
.gitmodules
vendored
Normal file
16
.gitmodules
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
[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
|
107
CHANGELOG.md
107
CHANGELOG.md
@ -1,9 +1,102 @@
|
||||
# changelog
|
||||
|
||||
**potential future features (not confirmed)**
|
||||
### v0.11.0 (dev)
|
||||
|
||||
- [highlight/mark viewer](https://chrome.google.com/webstore/detail/notion%2B-mark-manager/hipgmnlpnimedfepbfbfiaobohhffcfc)
|
||||
- [advanced math editor](https://github.com/Manueloccorso/NotionMathEditor_BrowserExtension)
|
||||
a complete redesign & rewrite of the enhancer, with new features and a port to the browser as a chrome extension.
|
||||
|
||||
#### new
|
||||
|
||||
- cross-environment and properly documented api to replace helpers.
|
||||
- cross-environment mod loader structure.
|
||||
- "integrations", a category of mods that can access/use an unofficial notion api.
|
||||
- notifications sourced from an online endpoint for sending global user alerts.
|
||||
- simplify user installations by depending on the chrome web store and [notion-repackaged](https://github.com/notion-enhancer/notion-repackaged).
|
||||
- separate menu profiles for mod configurations.
|
||||
- a hotkey option type that allows typing in/pressing a hotkey to enter it, instead of typing.
|
||||
- a rainbow indentation lines style.
|
||||
- border & background style options for the code line numbers extension.
|
||||
- an icon sets option to encode images to data urls to prevent quality reduction.
|
||||
- customisation of integrated titlebar & always on top window buttons.
|
||||
- an open on startup option under the tray mod.
|
||||
- optional icon or title-only tab labels.
|
||||
- choice of tab layout styles: traditional tabbed, traditional, bubble and compact.
|
||||
- a hotkey for reopening closed tabs.
|
||||
- an option to remember last open tabs for a continue-where-you-left-off experience
|
||||
(recently active tabs are reopened after an app relaunch).
|
||||
|
||||
#### improved
|
||||
|
||||
- split the core mod into separate mods for specific features.
|
||||
- theming variables that are applied more specifically, less laggy, and less complicated.
|
||||
- merged bracketed-links into tweaks.
|
||||
- a redesigned menu with nicer ui, separate categories for mods and a sidebar for configuration.
|
||||
- simplified and smoothened the side panel + moved it to the core so any mod can hook into it.
|
||||
- font chooser option for heading fonts.
|
||||
- renamed "property-layout" to "collapsible properties", added per-page memory of collapse state.
|
||||
- chevron icon instead of arrow for scroll to top.
|
||||
- moved word counter to display in the side panel instead of within the page,
|
||||
implemented a more accurate word counter method.
|
||||
- the topbar icons extension defaults to the notion default topbar icons for comment/updates/favorite/more,
|
||||
but can revert them to text (it still adds a custom icon for the share button).
|
||||
- relative indenting in outliner.
|
||||
- rtl support for toggles, indentation lines, table of contents and databases + force inline math to ltr.
|
||||
- replaced the "truncated table titles" extension with a "truncated titles" extension
|
||||
with an option to truncate timeline item titles.
|
||||
- renamed "notion icons" to "icon sets" with new support for uploading/reusing custom icons
|
||||
directly within the icon picker.
|
||||
- moved the tray to its own configurable and enable/disable-able mod, with window management enhancements
|
||||
that follow more sensible defaults and work more reliably.
|
||||
- tabs will auto shrink/expand to take up available space instead of wrapping to a second line.
|
||||
- a visually revamped cli to more clearly and aesthetically communicate status and usage.
|
||||
- cli can now detect and apply to user-only installations on macOS.
|
||||
- a shortcut built into the cli to fix the "you do not have permission to open this app" error on macos.
|
||||
|
||||
#### removed
|
||||
|
||||
- integrated scrollbar tweak (notion now includes by default).
|
||||
- js insert. css insert moved to tweaks mod.
|
||||
- majority of layout and font size variables - better to leave former to notion and use `ctrl +`/`ctrl -` for latter.
|
||||
- the "panel sites" extension, due to it's limited/buggy functionality and incompatibility with reimplementation.
|
||||
|
||||
#### fixed
|
||||
|
||||
- bypass csp restrictions.
|
||||
- many. like many many. all the bugfixes. (mostly a side effect of completely rewriting everything,
|
||||
but reported extension-specific bugs were all intentionally fixed.)
|
||||
|
||||
#### themes
|
||||
|
||||
- "nord" = an arctic, north-bluish color palette.
|
||||
- "gruvbox light" = a sepia, 'retro groove' palette based on the vim theme of the same name.
|
||||
- "gruvbox dark" = a gray, 'retro groove' palette based on the vim theme of the same name.
|
||||
- "light+" = a simple white theme that brightens coloured text and blocks,
|
||||
with configurable accents (formerly littlepig light).
|
||||
- "playful purple" = a purple-shaded theme with bright highlights (formerly littlepig dark and gameish).
|
||||
- "pinky boom" = pinkify your life.
|
||||
|
||||
#### extensions
|
||||
|
||||
- "calendar scroll" = add a button to jump down to the current week in fullpage/infinite-scroll calendars.
|
||||
- "global block links" = easily copy the global link of a page or block.
|
||||
- "collapsible headers" = adds toggles to collapse header sections of pages.
|
||||
- "simpler databases" = adds a menu to inline databases to toggle ui elements.
|
||||
- "view scale" = zoom in/out of the notion window with the mousewheel or a visual slider (`ctrl/cmd +/-` are available in-app by default).
|
||||
|
||||
#### tweaks
|
||||
|
||||
- wrap tables to page width. - hide "Type '/' for commands".
|
||||
- quote block quotation marks.
|
||||
- responsive columns breakpoint (%).
|
||||
- accented links.
|
||||
- full width pages.
|
||||
- image alignment (center/left/right).
|
||||
|
||||
#### integrations
|
||||
|
||||
- "quick note" = 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).
|
||||
|
||||
**below this point the enhancer was desktop-only. in v0.11.0 it was been ported to also**
|
||||
**run as a chrome extension. changes made to both are indicated above.**
|
||||
|
||||
### v0.10.2 (2020-12-05)
|
||||
|
||||
@ -271,7 +364,7 @@ complete rewrite with node.js.
|
||||
- bugfix: odd mix of `\\` and `/` being used for windows filepaths.
|
||||
- bugfix: app no longer crashes when sidebar is toggled.
|
||||
|
||||
> 📥 [notion-enhancer.v0.7.0.zip](https://github.com/notion-enhancer/notion-enhancer/archive/v0.7.0.zip)
|
||||
> 📥 [notion-enhancer.v0.7.0.zip](https://github.com/notion-enhancer/desktop/archive/v0.7.0.zip)
|
||||
|
||||
### v0.6.0 (2020-06-30)
|
||||
|
||||
@ -285,7 +378,7 @@ complete rewrite with node.js.
|
||||
- improved: more obviously visible drag area.
|
||||
- bugfix: specify UTF-8 encoding to prevent multibyte/gbk codec errors (thanks to [@etnperlong](https://github.com/etnperlong)).
|
||||
|
||||
> 📥 [notion-enhancer.v0.6.0.zip](https://github.com/notion-enhancer/notion-enhancer/archive/v0.6.0.zip)
|
||||
> 📥 [notion-enhancer.v0.6.0.zip](https://github.com/notion-enhancer/desktop/archive/v0.6.0.zip)
|
||||
|
||||
### v0.5.0 (2020-05-23)
|
||||
|
||||
@ -297,12 +390,12 @@ complete rewrite with node.js.
|
||||
improved: scrollbar colours that fit better with notion's theming.
|
||||
- bugfix: un-break having multiple notion windows open.
|
||||
|
||||
> 📥 [notion-enhancer.v0.5.0.zip](https://github.com/notion-enhancer/notion-enhancer/archive/v0.5.0.zip)
|
||||
> 📥 [notion-enhancer.v0.5.0.zip](https://github.com/notion-enhancer/desktop/archive/v0.5.0.zip)
|
||||
|
||||
**development here taken over by [@dragonwocky](https://github.com/dragonwocky).**
|
||||
|
||||
**the ~~crossed out~~ features below are no longer features included by default,**
|
||||
**but can still easily be added as [custom tweaks](TWEAKS.md).**
|
||||
**but can still easily be added as [custom tweaks](https://github.com/notion-enhancer/tweaks).**
|
||||
|
||||
### v0.4.1 (2020-02-13)
|
||||
|
||||
|
3
LICENSE
3
LICENSE
@ -1,7 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 TarasokUA
|
||||
Copyright (c) 2020 dragonwocky
|
||||
Copyright (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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
|
||||
|
153
bin.mjs
Normal file
153
bin.mjs
Normal file
@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import os from 'os';
|
||||
|
||||
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';
|
||||
import sign from './pkg/sign.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'],
|
||||
['sign', '[macos only] fix the "you do not have permission to open this app" error'],
|
||||
],
|
||||
options: [
|
||||
['-y, --yes', 'skip prompts'],
|
||||
['-n, --no', 'skip prompts'],
|
||||
['-d, --dev', 'show detailed error messages (for debug purposes)'],
|
||||
[
|
||||
'--path=</path/to/notion/resources>',
|
||||
'provide a file location to enhance (otherwise auto-picked)',
|
||||
],
|
||||
['--no-backup', 'skip backup (faster enhancement, but disables removal)'],
|
||||
['-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('');
|
||||
}
|
||||
if (i !== stack.length - 1) {
|
||||
strs.push('\n');
|
||||
tags.push('');
|
||||
}
|
||||
}
|
||||
log(strs, ...tags);
|
||||
} else {
|
||||
log`{bold.red Error:} ${err.message} {grey (run with -d for more information)}`;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const notionPath = opts.get('path') || findNotion();
|
||||
|
||||
switch (args()[0]) {
|
||||
case 'apply': {
|
||||
log`{bold.rgb(245,245,245) [NOTION-ENHANCER] APPLY}`;
|
||||
const res = await apply(notionPath, {
|
||||
overwritePrevious: promptRes,
|
||||
takeBackup: opts.get('no-backup') ? false : true,
|
||||
applyDevPatch: opts.get('dev-patch') ? true : false,
|
||||
});
|
||||
if (res) {
|
||||
log`{bold.rgb(245,245,245) SUCCESS} {green ✔}`;
|
||||
} else log`{bold.rgb(245,245,245) CANCELLED} {red ✘}`;
|
||||
break;
|
||||
}
|
||||
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');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'sign': {
|
||||
log`{bold.rgb(245,245,245) [NOTION-ENHANCER] SIGN}`;
|
||||
const res = await sign(notionPath);
|
||||
if (res) {
|
||||
log`{bold.rgb(245,245,245) SUCCESS} {green ✔}`;
|
||||
} else log`{bold.rgb(245,245,245) CANCELLED} {red ✘}`;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
displayHelp();
|
||||
}
|
||||
} catch (err) {
|
||||
if (lastSpinner) lastSpinner.stop();
|
||||
handleError(err);
|
||||
process.exit(1);
|
||||
}
|
1
insert/api
Submodule
1
insert/api
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit cf0c26434f8085823d1add0390befbb899866423
|
34
insert/client.mjs
Normal file
34
insert/client.mjs
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
(async () => {
|
||||
const page = location.pathname.split(/[/-]/g).reverse()[0].length === 32,
|
||||
whitelisted = ['/', '/onboarding'].includes(location.pathname),
|
||||
signedIn = localStorage['LRU:KeyValueStore2:current-user-id'];
|
||||
|
||||
if (page || (whitelisted && signedIn)) {
|
||||
const api = await import('./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.error('[notion-enhancer] registry errors:');
|
||||
console.table(errors);
|
||||
}
|
||||
}
|
||||
})();
|
1
insert/dep
Submodule
1
insert/dep
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 65ae1944a75b9525ee79610586438facf14d8531
|
146
insert/electronApi.cjs
Normal file
146
insert/electronApi.cjs
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const os = require('os'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
_cacheFile = path.resolve(`${os.homedir()}/.notion-enhancer`),
|
||||
_fsQueue = new Set(),
|
||||
_onDbChangeListeners = [];
|
||||
|
||||
// handle leftover cache from prev versions
|
||||
if (fs.existsSync(_cacheFile) && fs.lstatSync(_cacheFile).isDirectory()) {
|
||||
fs.rmdirSync(_cacheFile);
|
||||
}
|
||||
|
||||
const isRenderer = process && process.type === 'renderer';
|
||||
|
||||
const getCache = async () => {
|
||||
try {
|
||||
return fs.readFileSync(_cacheFile);
|
||||
} catch (err) {
|
||||
await new Promise((res, rej) => setTimeout(res, 50));
|
||||
return getCache();
|
||||
}
|
||||
},
|
||||
getData = async () => {
|
||||
if (!fs.existsSync(_cacheFile)) {
|
||||
fs.writeFileSync(_cacheFile, '{}', 'utf8');
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
return JSON.parse(await getCache());
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
saveData = (data) => fs.writeFileSync(_cacheFile, JSON.stringify(data));
|
||||
|
||||
const db = {
|
||||
get: async (path, fallback = undefined) => {
|
||||
if (!path.length) return fallback;
|
||||
while (_fsQueue.size) await new Promise(requestIdleCallback);
|
||||
const values = await getData();
|
||||
let value = values;
|
||||
while (path.length) {
|
||||
if (value === undefined) {
|
||||
value = fallback;
|
||||
break;
|
||||
}
|
||||
value = value[path.shift()];
|
||||
}
|
||||
return value ?? fallback;
|
||||
},
|
||||
set: async (path, value) => {
|
||||
if (!path.length) return undefined;
|
||||
while (_fsQueue.size) await new Promise(requestIdleCallback);
|
||||
const op = Symbol();
|
||||
_fsQueue.add(op);
|
||||
const pathClone = [...path],
|
||||
values = await getData();
|
||||
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];
|
||||
}
|
||||
saveData(values);
|
||||
_onDbChangeListeners.forEach((listener) => listener({ path: pathClone, new: value, old }));
|
||||
_fsQueue.delete(op);
|
||||
return value;
|
||||
},
|
||||
addChangeListener: (callback) => {
|
||||
_onDbChangeListeners.push(callback);
|
||||
},
|
||||
removeChangeListener: (callback) => {
|
||||
_onDbChangeListeners = _onDbChangeListeners.filter((listener) => listener !== callback);
|
||||
},
|
||||
};
|
||||
|
||||
const ipcRenderer = {
|
||||
sendMessage: (channel, data = undefined, namespace = 'notion-enhancer') => {
|
||||
const { ipcRenderer } = require('electron');
|
||||
channel = namespace ? `${namespace}:${channel}` : channel;
|
||||
ipcRenderer.send(channel, data);
|
||||
},
|
||||
sendMessageToHost: (channel, data = undefined, namespace = 'notion-enhancer') => {
|
||||
const { ipcRenderer } = require('electron');
|
||||
channel = namespace ? `${namespace}:${channel}` : channel;
|
||||
ipcRenderer.sendToHost(channel, data);
|
||||
},
|
||||
onMessage: (channel, callback, namespace = 'notion-enhancer') => {
|
||||
const { ipcRenderer } = require('electron');
|
||||
channel = namespace ? `${namespace}:${channel}` : channel;
|
||||
ipcRenderer.on(channel, callback);
|
||||
},
|
||||
};
|
||||
|
||||
globalThis.__enhancerElectronApi = {
|
||||
platform: process.platform,
|
||||
version: require('notion-enhancer/package.json').version,
|
||||
db,
|
||||
|
||||
browser: isRenderer ? require('electron').remote.getCurrentWindow() : {},
|
||||
webFrame: isRenderer ? require('electron').webFrame : {},
|
||||
notionRequire: (path) => require(`../../${path}`),
|
||||
notionPath: (path) => require('path').resolve(`${__dirname}/../../${path}`),
|
||||
nodeRequire: (path) => require(path),
|
||||
|
||||
focusMenu: () => {
|
||||
if (isRenderer) return ipcRenderer.sendMessage('focusMenu');
|
||||
const { focusMenu } = require('notion-enhancer/worker.cjs');
|
||||
return focusMenu();
|
||||
},
|
||||
focusNotion: () => {
|
||||
if (isRenderer) return ipcRenderer.sendMessage('focusNotion');
|
||||
const { focusNotion } = require('notion-enhancer/worker.cjs');
|
||||
return focusNotion();
|
||||
},
|
||||
reload: () => {
|
||||
if (isRenderer) return ipcRenderer.sendMessage('reload');
|
||||
const { reload } = require('notion-enhancer/worker.cjs');
|
||||
return reload();
|
||||
},
|
||||
|
||||
getNotionWindows: () => {
|
||||
const { getNotionWindows } = require('notion-enhancer/worker.cjs');
|
||||
return getNotionWindows();
|
||||
},
|
||||
getFocusedNotionWindow: () => {
|
||||
const { getFocusedNotionWindow } = require('notion-enhancer/worker.cjs');
|
||||
return getFocusedNotionWindow();
|
||||
},
|
||||
|
||||
ipcRenderer,
|
||||
};
|
44
insert/env/env.mjs
vendored
Normal file
44
insert/env/env.mjs
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* notion-enhancer: api
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* environment-specific methods and constants
|
||||
* @module notion-enhancer/api/env
|
||||
*/
|
||||
|
||||
/**
|
||||
* the environment/platform name code is currently being executed in
|
||||
* @constant
|
||||
* @type {string}
|
||||
*/
|
||||
export const name = globalThis.__enhancerElectronApi.platform;
|
||||
|
||||
/**
|
||||
* the current version of the enhancer
|
||||
* @constant
|
||||
* @type {string}
|
||||
*/
|
||||
export const version = globalThis.__enhancerElectronApi.version;
|
||||
|
||||
/**
|
||||
* open the enhancer's menu
|
||||
* @type {function}
|
||||
*/
|
||||
export const focusMenu = globalThis.__enhancerElectronApi.focusMenu;
|
||||
|
||||
/**
|
||||
* focus an active notion tab
|
||||
* @type {function}
|
||||
*/
|
||||
export const focusNotion = globalThis.__enhancerElectronApi.focusNotion;
|
||||
|
||||
/**
|
||||
* reload all notion and enhancer menu tabs to apply changes
|
||||
* @type {function}
|
||||
*/
|
||||
export const reload = globalThis.__enhancerElectronApi.reload;
|
80
insert/env/fs.mjs
vendored
Normal file
80
insert/env/fs.mjs
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* notion-enhancer: api
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* environment-specific file reading
|
||||
* @module notion-enhancer/api/fs
|
||||
*/
|
||||
|
||||
/**
|
||||
* get an absolute path to files within notion
|
||||
* @param {string} path - relative to the root notion/resources/app/ e.g. renderer/search.js
|
||||
* @runtime electron
|
||||
*/
|
||||
export const notionPath = globalThis.__enhancerElectronApi.notionPath;
|
||||
|
||||
/**
|
||||
* 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 = (path) => `notion://www.notion.so/__notion-enhancer/${path}`;
|
||||
|
||||
/**
|
||||
* 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 = {}) => {
|
||||
if (path.startsWith('http')) return fetch(path, opts).then((res) => res.json());
|
||||
try {
|
||||
return globalThis.__enhancerElectronApi.nodeRequire(`notion-enhancer/${path}`);
|
||||
} catch (err) {
|
||||
return fetch(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 = {}) => {
|
||||
if (path.startsWith('http')) return fetch(path, opts).then((res) => res.text());
|
||||
try {
|
||||
const fs = globalThis.__enhancerElectronApi.nodeRequire('fs');
|
||||
return fs.readFileSync(notionPath(`notion-enhancer/${path}`));
|
||||
} catch (err) {
|
||||
return fetch(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 {
|
||||
const fs = globalThis.__enhancerElectronApi.nodeRequire('fs');
|
||||
if (path.startsWith('http')) {
|
||||
await fetch(path);
|
||||
} else {
|
||||
try {
|
||||
fs.existsSync(notionPath(`notion-enhancer/${path}`));
|
||||
} catch (err) {
|
||||
await fetch(localPath(path));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
72
insert/env/storage.mjs
vendored
Normal file
72
insert/env/storage.mjs
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* notion-enhancer: api
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* environment-specific data persistence
|
||||
* @module notion-enhancer/api/storage
|
||||
*/
|
||||
|
||||
/**
|
||||
* get persisted data
|
||||
* @param {array<string>} path - the path of keys to the value being fetched
|
||||
* @param {*} [fallback] - a default value if the path is not matched
|
||||
* @returns {Promise} value ?? fallback
|
||||
*/
|
||||
export const get = (path, fallback = undefined) => {
|
||||
return globalThis.__enhancerElectronApi.db.get(path, fallback);
|
||||
};
|
||||
|
||||
/**
|
||||
* persist data
|
||||
* @param {array<string>} path - the path of keys to the value being set
|
||||
* @param {*} value - the data to save
|
||||
* @returns {Promise} resolves when data has been saved
|
||||
*/
|
||||
export const set = (path, value) => {
|
||||
return globalThis.__enhancerElectronApi.db.set(path, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* create a wrapper for accessing a partition of the storage
|
||||
* @param {array<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) => {
|
||||
return globalThis.__enhancerElectronApi.db.addChangeListener(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* remove a listener added with storage.addChangeListener
|
||||
* @param {onStorageChangeCallback} callback
|
||||
*/
|
||||
export const removeChangeListener = (callback) => {
|
||||
return globalThis.__enhancerElectronApi.db.removeChangeListener(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
|
||||
*/
|
28
insert/frame.mjs
Normal file
28
insert/frame.mjs
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
(async () => {
|
||||
const api = await import('./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?.frame || []) {
|
||||
web.loadStylesheet(`repo/${mod._dir}/${sheet}`);
|
||||
}
|
||||
for (let script of mod.js?.frame || []) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
})();
|
46
insert/init.cjs
Normal file
46
insert/init.cjs
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = async function (target, __exports, __eval) {
|
||||
require('notion-enhancer/electronApi.cjs');
|
||||
const api = require('notion-enhancer/api/index.cjs'),
|
||||
{ registry } = api;
|
||||
|
||||
if (target === 'renderer/index') {
|
||||
document.addEventListener('readystatechange', (event) => {
|
||||
if (document.readyState !== 'complete') return false;
|
||||
const script = document.createElement('script');
|
||||
script.type = 'module';
|
||||
script.src = api.fs.localPath('frame.mjs');
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
if (target === 'renderer/preload') {
|
||||
document.addEventListener('readystatechange', (event) => {
|
||||
if (document.readyState !== 'complete') return false;
|
||||
const script = document.createElement('script');
|
||||
script.type = 'module';
|
||||
script.src = api.fs.localPath('client.mjs');
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
if (target === 'main/main') {
|
||||
const { app } = require('electron');
|
||||
app.whenReady().then(require('notion-enhancer/worker.cjs').listen);
|
||||
}
|
||||
|
||||
for (const mod of await registry.list((mod) => registry.enabled(mod.id))) {
|
||||
for (const { source, target: scriptTarget } of (mod.js ? mod.js.electron : []) || []) {
|
||||
if (`${target}.js` !== scriptTarget) continue;
|
||||
const script = require(`notion-enhancer/repo/${mod._dir}/${source}`);
|
||||
script(api, await registry.db(mod.id), __exports, __eval);
|
||||
}
|
||||
}
|
||||
};
|
1
insert/media
Submodule
1
insert/media
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 5472e23e983ab893b72af6493bbf582017c182be
|
1
insert/repo
Submodule
1
insert/repo
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 635b0815f0bcdf0afceb6200110f634ab7e6bd89
|
104
insert/worker.cjs
Normal file
104
insert/worker.cjs
Normal file
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
module.exports = {};
|
||||
|
||||
const onMessage = (id, callback) => {
|
||||
const { ipcMain } = require('electron');
|
||||
ipcMain.on(`notion-enhancer:${id}`, callback);
|
||||
};
|
||||
|
||||
let enhancerMenu;
|
||||
module.exports.focusMenu = async () => {
|
||||
if (enhancerMenu) return enhancerMenu.show();
|
||||
|
||||
const { fs } = require('notion-enhancer/api/index.cjs'),
|
||||
{ app, session, BrowserWindow } = require('electron'),
|
||||
windowState = require('electron-window-state')({
|
||||
file: 'enhancer-menu-window-state.json',
|
||||
defaultWidth: 1250,
|
||||
defaultHeight: 850,
|
||||
}),
|
||||
{ registry } = require('notion-enhancer/api/index.cjs'),
|
||||
integratedTitlebar = await registry.enabled('a5658d03-21c6-4088-bade-fa4780459133');
|
||||
|
||||
enhancerMenu = new BrowserWindow({
|
||||
show: true,
|
||||
frame: !integratedTitlebar,
|
||||
titleBarStyle: 'hiddenInset',
|
||||
x: windowState.x,
|
||||
y: windowState.y,
|
||||
width: windowState.width,
|
||||
height: windowState.height,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
session: session.fromPartition('persist:notion'),
|
||||
preload: require('path').resolve(`${__dirname}/electronApi.cjs`),
|
||||
},
|
||||
});
|
||||
enhancerMenu.loadURL(fs.localPath('repo/menu/menu.html'));
|
||||
windowState.manage(enhancerMenu);
|
||||
|
||||
let appQuit = false;
|
||||
app.once('before-quit', () => {
|
||||
appQuit = true;
|
||||
});
|
||||
|
||||
// handle opening external links
|
||||
// must have target="_blank"
|
||||
enhancerMenu.webContents.on('new-window', (e, url) => {
|
||||
e.preventDefault();
|
||||
require('electron').shell.openExternal(url);
|
||||
});
|
||||
|
||||
const trayID = 'f96f4a73-21af-4e3f-a68f-ab4976b020da',
|
||||
runInBackground =
|
||||
(await registry.enabled(trayID)) &&
|
||||
(await (await registry.db(trayID)).get(['run_in_background']));
|
||||
enhancerMenu.on('close', (e) => {
|
||||
const isLastWindow = BrowserWindow.getAllWindows().length === 1;
|
||||
if (!appQuit && isLastWindow && runInBackground) {
|
||||
enhancerMenu.hide();
|
||||
e.preventDefault();
|
||||
} else enhancerMenu = null;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.getNotionWindows = () => {
|
||||
const { BrowserWindow } = require('electron'),
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
if (enhancerMenu) return windows.filter((win) => win.id !== enhancerMenu.id);
|
||||
return windows;
|
||||
};
|
||||
|
||||
module.exports.getFocusedNotionWindow = () => {
|
||||
const { BrowserWindow } = require('electron'),
|
||||
focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
if (enhancerMenu && focusedWindow && focusedWindow.id === enhancerMenu.id) return null;
|
||||
return focusedWindow;
|
||||
};
|
||||
|
||||
module.exports.focusNotion = () => {
|
||||
const api = require('notion-enhancer/api/index.cjs'),
|
||||
{ createWindow } = api.electron.notionRequire('main/createWindow.js');
|
||||
let window = module.exports.getFocusedNotionWindow() || module.exports.getNotionWindows()[0];
|
||||
if (!window) window = createWindow('/');
|
||||
window.show();
|
||||
};
|
||||
|
||||
module.exports.reload = () => {
|
||||
const { app } = require('electron');
|
||||
app.relaunch({ args: process.argv.slice(1).filter((arg) => arg !== '--startup') });
|
||||
app.quit();
|
||||
};
|
||||
|
||||
module.exports.listen = () => {
|
||||
onMessage('focusMenu', module.exports.focusMenu);
|
||||
onMessage('focusNotion', module.exports.focusNotion);
|
||||
onMessage('reload', module.exports.reload);
|
||||
};
|
53
package.json
53
package.json
@ -1,42 +1,47 @@
|
||||
{
|
||||
"name": "notion-enhancer",
|
||||
"version": "0.10.2",
|
||||
"version": "0.11.0-dev",
|
||||
"author": "dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)",
|
||||
"description": "an enhancer/customiser for the all-in-one productivity workspace notion.so",
|
||||
"main": "index.js",
|
||||
"homepage": "https://github.com/notion-enhancer/desktop",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"notion-enhancer": "bin.js"
|
||||
"notion-enhancer": "bin.mjs"
|
||||
},
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=16.x.x"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"no test specified\"",
|
||||
"postinstall": "node bin.js apply -y",
|
||||
"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/notion-enhancer.git"
|
||||
"url": "git+https://github.com/notion-enhancer/desktop.git"
|
||||
},
|
||||
"keywords": [
|
||||
"notion",
|
||||
"productivity",
|
||||
"mod",
|
||||
"loader",
|
||||
"enhancer",
|
||||
"hack",
|
||||
"macOS",
|
||||
"windows",
|
||||
"linux"
|
||||
"macos",
|
||||
"linux",
|
||||
"productivity",
|
||||
"hack",
|
||||
"extensions",
|
||||
"themes",
|
||||
"integrations",
|
||||
"mod",
|
||||
"mods",
|
||||
"mod-loader",
|
||||
"enhancer",
|
||||
"notion",
|
||||
"notion-enhancer"
|
||||
],
|
||||
"author": "dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/notion-enhancer/notion-enhancer/issues"
|
||||
},
|
||||
"homepage": "https://dragonwocky.me/notion-enhancer",
|
||||
"dependencies": {
|
||||
"asar": "^3.0.3",
|
||||
"cac": "^6.5.12",
|
||||
"fs-extra": "^9.0.1",
|
||||
"keyboardevent-from-electron-accelerator": "^2.0.0",
|
||||
"readdir-enhanced": "^6.0.3"
|
||||
"url": "https://github.com/notion-enhancer/desktop/issues"
|
||||
}
|
||||
}
|
||||
|
98
pkg/apply.mjs
Normal file
98
pkg/apply.mjs
Normal file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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, takeBackup = true, applyDevPatch = false } = {}
|
||||
) {
|
||||
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 (!applyDevPatch) {
|
||||
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),
|
||||
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;
|
||||
}
|
63
pkg/check.mjs
Normal file
63
pkg/check.mjs
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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;
|
||||
}
|
138
pkg/cli.mjs
Normal file
138
pkg/cli.mjs
Normal file
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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} <command> [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)} : ${cmd[1]}`).join('\n');
|
||||
options = options.map((opt) => ` ${opt[0].padEnd(optPad)} : ${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}`;
|
||||
};
|
110
pkg/helpers.mjs
Normal file
110
pkg/helpers.mjs
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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;
|
||||
};
|
48
pkg/remove.mjs
Normal file
48
pkg/remove.mjs
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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;
|
||||
}
|
23
pkg/replacers/main/main.mjs
Normal file
23
pkg/replacers/main/main.mjs
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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;
|
||||
}
|
45
pkg/replacers/main/schemeHandler.mjs
Normal file
45
pkg/replacers/main/schemeHandler.mjs
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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;
|
||||
}
|
22
pkg/replacers/main/systemMenu.mjs
Normal file
22
pkg/replacers/main/systemMenu.mjs
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (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;
|
||||
}
|
28
pkg/sign.mjs
Normal file
28
pkg/sign.mjs
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* notion-enhancer
|
||||
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
||||
* (https://notion-enhancer.github.io/) under the MIT license
|
||||
*/
|
||||
|
||||
import { log } from './cli.mjs';
|
||||
import { findNotion } from './helpers.mjs';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
import check from './check.mjs';
|
||||
|
||||
export default async function (notionFolder = findNotion()) {
|
||||
const status = check(notionFolder);
|
||||
if (process.platform === 'darwin') {
|
||||
log` {grey * app re-signing is only available on macos: exiting}`;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status.code > 1 && status.executable) {
|
||||
log` {grey * installing xcode cli tools}`;
|
||||
execSync('xcode-select --install');
|
||||
log` {grey * codesigning app directory}`;
|
||||
execSync(`codesign --force --deep --sign - ${status.installation}`);
|
||||
} else log` {grey * enhancements not found: skipping}`;
|
||||
|
||||
return true;
|
||||
}
|
139
yarn.lock
139
yarn.lock
@ -2,42 +2,35 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@jsdevtools/file-path-filter@^3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@jsdevtools/file-path-filter/-/file-path-filter-3.0.2.tgz#22a0b544b8471fafd8da87c471a92bc778ab75f1"
|
||||
integrity sha512-+SbZG6stIE/nRF2PpRnubtuzhh4pouDsk/hEWwM5mKsSKlFfr4ziAE5VMogGG/K++i9NHbUTxxW0y4vdM678ew==
|
||||
dependencies:
|
||||
glob-to-regexp "^0.4.1"
|
||||
|
||||
"@jsdevtools/readdir-enhanced@6.0.4":
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@jsdevtools/readdir-enhanced/-/readdir-enhanced-6.0.4.tgz#077749dac62cefd01453cd5af1084586c635a358"
|
||||
integrity sha512-I6D6Omu6C7XWHzvlVbXeCS0FSxYYQ13XzdrFuo1K30unnRSpdt9AxY2KyJZbYJyfI2uNNidqDkG9/K/y699AjA==
|
||||
dependencies:
|
||||
"@jsdevtools/file-path-filter" "^3.0.2"
|
||||
|
||||
"@types/glob@^7.1.1":
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
|
||||
integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
||||
integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
|
||||
dependencies:
|
||||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/minimatch@*":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||
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 "14.0.26"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.26.tgz#22a3b8a46510da8944b67bfc27df02c34a35331c"
|
||||
integrity sha512-W+fpe5s91FBGE0pEa0lnqGLL4USgpLgs4nokw16SrBBco/gQxuua7KnArSEOd5iaMqbbSHV10vUDkJYJJqpXKA==
|
||||
version "16.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
|
||||
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
|
||||
|
||||
asar@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/asar/-/asar-3.0.3.tgz#1fef03c2d6d2de0cbad138788e4f7ae03b129c7b"
|
||||
integrity sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw==
|
||||
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==
|
||||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
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==
|
||||
dependencies:
|
||||
chromium-pickle-js "^0.2.0"
|
||||
commander "^5.0.0"
|
||||
@ -46,15 +39,10 @@ asar@^3.0.3:
|
||||
optionalDependencies:
|
||||
"@types/glob" "^7.1.1"
|
||||
|
||||
at-least-node@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
@ -64,16 +52,31 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
cac@^6.5.12:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/cac/-/cac-6.6.1.tgz#3dde3f6943f45d42a56729ea3573c08b3e7b6a6d"
|
||||
integrity sha512-uhki4T3Ax68hw7Dufi0bATVAF8ayBSwOKUEJHjObPrUN4tlQ8Lf7oljpTje/mArLxYN0D743c2zJt4C1bVTCqg==
|
||||
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==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
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==
|
||||
dependencies:
|
||||
color-name "~1.1.4"
|
||||
|
||||
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==
|
||||
|
||||
commander@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||
@ -84,30 +87,15 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
fs-extra@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc"
|
||||
integrity sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==
|
||||
dependencies:
|
||||
at-least-node "^1.0.0"
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^1.0.0"
|
||||
|
||||
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-to-regexp@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||
|
||||
glob@^7.1.6:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
@ -116,10 +104,10 @@ glob@^7.1.6:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
||||
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==
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
@ -134,20 +122,6 @@ inherits@2:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
jsonfile@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179"
|
||||
integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==
|
||||
dependencies:
|
||||
universalify "^1.0.0"
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
keyboardevent-from-electron-accelerator@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/keyboardevent-from-electron-accelerator/-/keyboardevent-from-electron-accelerator-2.0.0.tgz#ace21b1aa4e47148815d160057f9edb66567c50c"
|
||||
integrity sha512-iQcmNA0M4ETMNi0kG/q0h/43wZk7rMeKYrXP7sqKIJbHkTU8Koowgzv+ieR/vWJbOwxx5nDC3UnudZ0aLSu4VA==
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
@ -167,17 +141,12 @@ path-is-absolute@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
readdir-enhanced@^6.0.3:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/readdir-enhanced/-/readdir-enhanced-6.0.4.tgz#71186776390bd1cf33b7c1451924ffaced7db184"
|
||||
integrity sha512-MWY048D/nEpHwqdnsBiUxpqjJPkEw2i2RmY5gM2Gadn0rkHS/DhUBqrYTkOqKHF4RoUlYZZ8GnP4ymlRGuo30A==
|
||||
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==
|
||||
dependencies:
|
||||
"@jsdevtools/readdir-enhanced" "6.0.4"
|
||||
|
||||
universalify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"
|
||||
integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==
|
||||
has-flag "^4.0.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
|
Loading…
Reference in New Issue
Block a user