bugfix tab management for menu, transfer to mod folder, start menu mockup

This commit is contained in:
dragonwocky 2021-04-18 02:32:56 +10:00
parent 05760882b5
commit 5fe31ac703
17 changed files with 539 additions and 90 deletions

View File

@ -1,7 +0,0 @@
/*
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import(chrome.runtime.getURL('src/launcher.js')).then((launcher) => launcher.default());

View File

@ -5,9 +5,15 @@
*/
'use strict';
const ERROR = Symbol();
export const ERROR = Symbol();
export const env = {};
env.name = 'browser';
env.openEnhancerMenu = () => chrome.runtime.sendMessage({ type: 'enhancerMenu.open' });
export const web = {};
const web = {};
web.whenReady = (selectors = [], callback = () => {}) => {
return new Promise((res, rej) => {
function onLoad() {
@ -29,11 +35,20 @@ web.whenReady = (selectors = [], callback = () => {}) => {
} else onLoad();
});
};
/**
* @param {string} html
* @returns HTMLElement
*/
web.createElement = (html) => {
const template = document.createElement('template');
template.innerHTML = html.trim();
return template.content.firstElementChild;
};
/**
* @param {string} sheet
*/
web.loadStyleset = (sheet) => {
document.head.appendChild(
web.createElement(`<link rel="stylesheet" href="${chrome.runtime.getURL(sheet)}">`)
@ -41,25 +56,53 @@ web.loadStyleset = (sheet) => {
return true;
};
//
/**
* @param {array} keys
* @param {function} callback
*/
web.hotkeyListener = (keys, callback) => {
if (!web._hotkeyListener) {
web._hotkeys = [];
web._hotkeyListener = document.addEventListener('keyup', (event) => {
for (let hotkey of web._hotkeys) {
const matchesEvent = hotkey.keys.every((key) => {
const modifiers = {
altKey: 'alt',
ctrlKey: 'ctrl',
metaKey: 'meta',
shiftKey: 'shift',
};
for (let modifier in modifiers) {
if (key.toLowerCase() === modifiers[modifier] && event[modifier]) return true;
}
const pressedKeycode = [event.key.toLowerCase(), event.code.toLowerCase()];
if (pressedKeycode.includes(key.toLowerCase())) return true;
});
if (matchesEvent) hotkey.callback();
}
});
}
web._hotkeys.push({ keys, callback });
};
const fs = {};
export const fs = {};
/**
* @param {string} path
*/
fs.getJSON = (path) => fetch(chrome.runtime.getURL(path)).then((res) => res.json());
fs.getText = (path) => fetch(chrome.runtime.getURL(path)).then((res) => res.text());
fs.isFile = async (path) => {
try {
await fetch(chrome.runtime.getURL(`/repo/${path}`));
await fetch(chrome.runtime.getURL(`repo/${path}`));
return true;
} catch {
return false;
}
};
//
const regexers = {
export const regexers = {
uuid(str) {
const match = str.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
if (match && match.length) return true;
@ -92,9 +135,7 @@ const regexers = {
},
};
//
const registry = {};
export const registry = {};
registry.validate = async (mod, err, check) =>
Promise.all(
@ -135,7 +176,7 @@ registry.validate = async (mod, err, check) =>
).then((css) => {
if (css === ERROR) return ERROR;
if (!css) return undefined;
return ['frame', 'client', 'gui']
return ['frame', 'client', 'menu']
.filter((dest) => css[dest])
.map(async (dest) =>
check(`css.${dest}`, css[dest], Array.isArray(css[dest])).then((files) =>
@ -208,7 +249,7 @@ registry.validate = async (mod, err, check) =>
if (filepath === ERROR) return ERROR;
if (!filepath) return undefined;
try {
const options = await fs.getJSON(`/repo/${mod._dir}/${mod.options}`);
const options = await fs.getJSON(`repo/${mod._dir}/${mod.options}`);
// todo: validate options
} catch {
err(`invalid options ${filepath}`);
@ -220,10 +261,10 @@ registry.validate = async (mod, err, check) =>
registry.get = async (callback = () => {}) => {
registry._list = [];
if (!registry._errors) registry._errors = [];
for (const dir of await fs.getJSON('/repo/registry.json')) {
for (const dir of await fs.getJSON('repo/registry.json')) {
const err = (message) => [registry._errors.push({ source: dir, message }), ERROR][1];
try {
const mod = await fs.getJSON(`/repo/${dir}/mod.json`);
const mod = await fs.getJSON(`repo/${dir}/mod.json`);
mod._dir = dir;
mod.tags = mod.tags ?? [];
mod.css = mod.css ?? [];
@ -247,4 +288,36 @@ registry.errors = async (callback = () => {}) => {
return registry._errors;
};
export { web, fs, regexers, registry };
export const markdown = {};
markdown.simple = (string) =>
string
.split('\n')
.map((line) =>
line
.trim()
.replace(/\s+/g, ' ')
// > quote
.replace(/^>\s+(.+)$/g, '<blockquote>$1</blockquote>')
// ~~strikethrough~~
.replace(/([^\\])?~~((?:(?!~~).)*[^\\])~~/g, '$1<s>$2</s>')
// __underline__
.replace(/([^\\])?__((?:(?!__).)*[^\\])__/g, '$1<u>$2</u>')
// **bold**
.replace(/([^\\])?\*\*((?:(?!\*\*).)*[^\\])\*\*/g, '$1<b>$2</b>')
// *italic*
.replace(/([^\\])?\*([^*]*[^\\*])\*/g, '$1<i>$2</i>')
// _italic_
.replace(/([^\\])?_([^_]*[^\\_])_/g, '$1<i>$2</i>')
// `code`
.replace(/([^\\])?`([^`]*[^\\`])`/g, '$1<code>$2</code>')
// ![image_title](source)
.replace(
/([^\\])?\!\[([^\]]*[^\\\]]?)\]\(([^)]*[^\\)])\)/g,
`$1<img alt="$2" src="$3" onerror="this.remove()">`
)
// [link](destination)
.replace(/([^\\])?\[([^\]]*[^\\\]]?)\]\(([^)]*[^\\)])\)/g, '$1<a href="$3">$2</a>')
)
.map((line) => (line.startsWith('<blockquote>') ? line : `<p>${line}</p>`))
.join('');

20
extension/launcher.js Normal file
View File

@ -0,0 +1,20 @@
/*
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
import(chrome.runtime.getURL('helpers.js')).then(({ web, registry }) => {
web.whenReady([], async () => {
for (let mod of await registry.get()) {
for (let sheet of mod.css?.client || []) {
web.loadStyleset(`repo/${mod._dir}/${sheet}`);
}
for (let script of mod.js?.client || []) {
import(chrome.runtime.getURL(`repo/${mod._dir}/${script}`));
}
}
});
});

View File

@ -17,14 +17,14 @@
},
"web_accessible_resources": [
{
"resources": ["icons/*", "src/*", "repo/*"],
"resources": ["icons/*", "repo/*", "helpers.js"],
"matches": ["https://*.notion.so/*"]
}
],
"content_scripts": [
{
"matches": ["https://*.notion.so/*"],
"js": ["content-loader.js"]
"js": ["launcher.js"]
}
],
"host_permissions": ["https://*.notion.so/*"]

View File

@ -1,5 +1,5 @@
/*
* notion-enhancer
* notion-enhancer core: menu
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/

View File

@ -0,0 +1,21 @@
/*
* notion-enhancer core: menu
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
import { web, fs, env } from '../../helpers.js';
const sidebarSelector =
'#notion-app > div > div.notion-cursor-listener > div.notion-sidebar-container > div > div > div > div:nth-child(4)';
web.whenReady([sidebarSelector], async () => {
const enhancerIcon = await fs.getText('icons/colour.svg'),
enhancerSidebarElement = web.createElement(
`<div class="enhancer--sidebarMenuTrigger" role="button" tabindex="0"><div><div>${enhancerIcon}</div><div><div>notion-enhancer</div></div></div></div>`
);
enhancerSidebarElement.addEventListener('click', env.openEnhancerMenu);
document.querySelector(sidebarSelector).appendChild(enhancerSidebarElement);
});
web.hotkeyListener(['Ctrl', 'Alt', 'E'], env.openEnhancerMenu);

View File

@ -0,0 +1,147 @@
/*
* notion-enhancer core: menu
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
* {
box-sizing: border-box;
word-break: break-word;
text-size-adjust: 100%;
font-family: var(--theme--font_sans);
color: var(--theme--text);
}
body {
padding: 1.5rem;
background: var(--theme--sidebar);
}
header {
display: flex;
margin-bottom: 1.25rem;
}
header > * {
margin: 0 1.25rem 0 0;
font-size: 1.25rem;
}
header img {
height: 100%;
width: 24px;
}
header h1 a:not([data-active]) {
text-decoration: none;
}
main section[data-container='library'] {
display: grid;
grid-gap: 1.25em;
grid-template-columns: 1fr 1fr 1fr;
}
main section[data-container='library'] article {
border-radius: 5px;
box-shadow: rgb(0 0 0 / 10%) 0px 20px 25px -5px, rgb(0 0 0 / 4%) 0px 10px 10px -5px;
border: 1px solid var(--theme--divider);
background: var(--theme--page);
}
main section[data-container='library'] article > img {
max-width: 100%;
border-bottom: 1px solid var(--theme--divider);
}
main section[data-container='library'] article > div {
padding: 1em;
border-bottom: 1px solid var(--theme--divider);
}
.library--title {
margin: 0;
}
.library--version {
font-weight: normal;
font-size: 0.8rem;
border-radius: 5px;
padding: 0.125rem 0.25rem;
background: var(--theme--tag_default);
color: var(--theme--tag_default-text);
}
.library--toggle_label {
align-items: center;
display: flex;
cursor: pointer;
}
.library--toggle {
position: relative;
margin-left: auto;
}
.library--toggle {
width: 2rem;
height: 1.25rem;
display: block;
border-radius: 9999px;
background: var(--theme--toggle_off);
}
.library--toggle::after {
content: '';
transition: transform 200ms ease-out 0s, background 200ms ease-out 0s;
height: 0.85rem;
width: 0.8rem;
left: 0.18rem;
top: 0.175rem;
position: absolute;
border-radius: 9999px;
background: var(--theme--toggle_dot);
}
.library--toggle_label input[type='checkbox']:checked ~ .library--toggle {
background: var(--theme--toggle_on);
}
.library--toggle_label input[type='checkbox']:checked ~ .library--toggle::after {
transform: translateX(100%);
}
.library--toggle_label input[type='checkbox'] {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.library--description {
font-size: 0.8rem;
}
.library--tags,
.library--authors {
padding: 0;
list-style-type: none;
display: flex;
margin: 0.5em 0;
}
.library--tags li {
margin: 0 0.5rem 0 0;
opacity: 0.7;
}
.library--authors li {
margin: 0 0.5rem 0 0;
}
.library--authors a {
font-size: 0.8rem;
text-decoration: none;
border-radius: 5px;
padding: 0.125rem 0.25rem 0.25rem 0.25rem;
background: var(--theme--tag_default);
color: var(--theme--tag_default-text);
}
.library--authors img {
height: 1em;
width: 1em;
margin-bottom: 0.15625em;
display: inline-block;
vertical-align: middle;
border-radius: 50%;
object-fit: cover;
}
main section:not([data-active]) {
display: none;
}

View File

@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="en" class="notion-dark-theme">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>notion-enhancer</title>
</head>
<body>
<header>
<p><img width="24" src="../../icons/colour.svg" /></p>
<h1><a href="#" data-target="library" data-active>library</a></h1>
<h1><a href="#" data-target="alerts">alerts</a></h1>
<h1><a href="#" data-target="documentation">documentation</a></h1>
</header>
<main>
<section data-container="library" data-active>
<article>
<div>
<label for="library.enable--ID1" class="library--toggle_label">
<h2 class="library--title">
custom inserts <span class="library--version">v0.2.0</span>
</h2>
<input type="checkbox" id="library.enable--ID1" checked />
<span class="library--toggle"></span>
</label>
<ul class="library--tags">
<li>#core</li>
<li>#extension</li>
<li>#customisation</li>
</ul>
<p class="library--description">
link files for small client-side tweaks. (not sure how to do something? check out
the
<a
href="https://github.com/notion-enhancer/notion-enhancer/blob/master/TWEAKS.md"
>tweaks</a
>
collection.)
</p>
<ul class="library--authors">
<li>
<a href="https://dragonwocky.me"
><img src="https://dragonwocky.me/avatar.jpg" /> <span>dragonwocky</span></a
>
</li>
<li>
<a href="https://dragonwocky.me"
><img src="https://dragonwocky.me/avatar.jpg" /> <span>dragonwocky</span></a
>
</li>
</ul>
</div>
</article>
<article>
<img
alt=""
class="library--preview"
src="https://raw.githubusercontent.com/notion-enhancer/notion-enhancer/dev/notion-enhancer%20v0.10.0%20banner.jpg"
/>
<div>
<label for="library.enable--ID2" class="library--toggle_label">
<h2 class="library--title">
dark+ <span class="library--version">v0.2.0</span>
</h2>
<input type="checkbox" id="library.enable--ID2" />
<span class="library--toggle"></span>
</label>
<ul class="library--tags">
<li>#core</li>
<li>#theme</li>
<li>#dark</li>
</ul>
<p class="library--description">a vivid-colour near-black theme</p>
<ul class="library--authors">
<li>
<a href="https://dragonwocky.me"
><img src="https://dragonwocky.me/avatar.jpg" /> <span>dragonwocky</span></a
>
</li>
</ul>
</div>
</article>
<article>
<div>
<label for="library.enable--ID3" class="library--toggle_label">
<h2 class="library--title">
custom inserts <span class="library--version">v0.2.0</span>
</h2>
<input type="checkbox" id="library.enable--ID3" checked />
<span class="library--toggle"></span>
</label>
<ul class="library--tags">
<li>#core</li>
<li>#extension</li>
<li>#customisation</li>
</ul>
<p class="library--description">
link files for small client-side tweaks. (not sure how to do something? check out
the
<a
href="https://github.com/notion-enhancer/notion-enhancer/blob/master/TWEAKS.md"
>tweaks</a
>
collection.)
</p>
<ul class="library--authors">
<li>
<a href="https://dragonwocky.me"
><img src="https://dragonwocky.me/avatar.jpg" /> <span>dragonwocky</span></a
>
</li>
<li>
<a href="https://dragonwocky.me"
><img src="https://dragonwocky.me/avatar.jpg" /> <span>dragonwocky</span></a
>
</li>
</ul>
</div>
</article>
<article>
<div>
<label for="library.enable--ID4" class="library--toggle_label">
<h2 class="library--title">
custom inserts <span class="library--version">v0.2.0</span>
</h2>
<input type="checkbox" id="library.enable--ID4" />
<span class="library--toggle"></span>
</label>
<ul class="library--tags">
<li>#core</li>
<li>#extension</li>
<li>#customisation</li>
</ul>
<p class="library--description">
link files for small client-side tweaks. (not sure how to do something? check out
the
<a
href="https://github.com/notion-enhancer/notion-enhancer/blob/master/TWEAKS.md"
>tweaks</a
>
collection.)
</p>
<ul class="library--authors">
<li>
<a href="https://dragonwocky.me"
><img src="https://dragonwocky.me/avatar.jpg" /> <span>dragonwocky</span></a
>
</li>
</ul>
</div>
</article>
</section>
<section data-container="alerts"></section>
<section data-container="documentation">documentation</section>
</main>
<script src="./menu.js" type="module"></script>
</body>
</html>

View File

@ -0,0 +1,34 @@
/*
* notion-enhancer core: menu
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
import { web, fs, registry } from '../../helpers.js';
for (let mod of await registry.get()) {
for (let sheet of mod.css?.menu || []) {
web.loadStyleset(`repo/${mod._dir}/${sheet}`);
}
}
const tabs = ['library', 'alerts', 'documentation'].map((tab) => ({
title: document.querySelector(`header [data-target="${tab}"]`),
container: document.querySelector(`main [data-container="${tab}"]`),
}));
tabs.forEach((tab) => {
tab.title.addEventListener('click', (event) => {
tabs.forEach((_tab) => {
_tab.title.removeAttribute('data-active');
_tab.container.removeAttribute('data-active');
});
tab.title.dataset.active = true;
tab.container.dataset.active = true;
});
});
// registry.errors().then((err) => {
// document.querySelector('[data-section="alerts"]').innerHTML = JSON.stringify(err);
// });

View File

@ -0,0 +1,22 @@
{
"name": "menu",
"id": "a6621988-551d-495a-97d8-3c568bca2e9e",
"description": "the enhancer's graphical menu, related buttons and shortcuts.",
"version": "0.11.0",
"tags": ["core"],
"authors": [
{
"name": "dragonwocky",
"email": "thedragonring.bod@gmail.com",
"url": "https://dragonwocky.me/",
"icon": "https://dragonwocky.me/avatar.jpg"
}
],
"css": {
"client": ["client.css"],
"menu": ["menu.css"]
},
"js": {
"client": ["client.js"]
}
}

View File

@ -1 +1 @@
["theming@0f0bf8b6-eae6-4273-b307-8fc43f2ee082"]
["menu@a6621988-551d-495a-97d8-3c568bca2e9e", "theming@0f0bf8b6-eae6-4273-b307-8fc43f2ee082"]

View File

@ -1,5 +1,5 @@
/*
* notion-enhancer
* notion-enhancer core: theming
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2020 TarasokUA
* (c) 2020 Arecsu

View File

@ -3,7 +3,7 @@
"id": "0f0bf8b6-eae6-4273-b307-8fc43f2ee082",
"description": "the default theme variables, required by other themes & extensions.",
"version": "0.11.0",
"tags": ["core", "theme"],
"tags": ["core"],
"authors": [
{
"name": "dragonwocky",
@ -13,6 +13,7 @@
}
],
"css": {
"client": ["client.css"]
"client": ["client.css"],
"menu": ["variables.css"]
}
}

View File

@ -1,5 +1,5 @@
/*
* notion-enhancer
* notion-enhancer core: theming
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2020 TarasokUA
* (c) 2020 Arecsu

View File

@ -1,9 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>notion-enhancer</title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>

View File

@ -1,36 +0,0 @@
/*
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
import { web, fs, registry } from './helpers.js';
export default () => {
web.whenReady([], async () => {
web.loadStyleset('/src/client.css');
for (let mod of await registry.get()) {
for (let sheet of mod.css?.client || []) {
web.loadStyleset(`/repo/${mod._dir}/${sheet}`);
}
for (let script of mod.js?.client || []) {
import(chrome.runtime.getURL(`/repo/${mod._dir}/${script}`));
}
}
});
const sidebarSelector =
'#notion-app > div > div.notion-cursor-listener > div.notion-sidebar-container > div > div > div > div:nth-child(4)';
web.whenReady([sidebarSelector], async () => {
const enhancerIcon = await fs.getText('/icons/colour.svg'),
enhancerSidebarElement = web.createElement(
`<div class="enhancer--sidebarMenuTrigger" role="button" tabindex="0"><div><div>${enhancerIcon}</div><div><div>notion-enhancer</div></div></div></div>`
);
enhancerSidebarElement.addEventListener('click', (event) =>
chrome.runtime.sendMessage({ type: 'openEnhancerMenu' })
);
document.querySelector(sidebarSelector).appendChild(enhancerSidebarElement);
});
};

View File

@ -6,26 +6,50 @@
'use strict';
let _enhancerMenuTab;
async function openEnhancerMenu() {
if (!_enhancerMenuTab) {
_enhancerMenuTab = await new Promise((res, rej) => {
const enhancerMenu = {
_tab: {},
highlight() {
return new Promise((res, rej) =>
chrome.tabs.get(this._tab.id, async (tab) => {
if (chrome.runtime.lastError) {
chrome.tabs.highlight({ 'tabs': (await this.create()).index });
} else {
chrome.tabs.highlight({ 'tabs': tab.index });
}
res(this._tab);
})
);
},
create() {
return new Promise((res, rej) =>
chrome.tabs.create(
{
url: chrome.runtime.getURL('/src/gui.html'),
url: chrome.runtime.getURL(
'repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html'
),
},
res
);
});
}
chrome.tabs.highlight({ 'tabs': _enhancerMenuTab.index }, function () {});
}
chrome.action.onClicked.addListener(openEnhancerMenu);
(tab) => {
this._tab = tab;
res(this._tab);
}
)
);
},
async open() {
try {
await this.highlight();
} catch {
await this.create();
}
return this._tab;
},
};
chrome.action.onClicked.addListener(enhancerMenu.open);
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
switch (request.type) {
case 'openEnhancerMenu':
openEnhancerMenu();
case 'enhancerMenu.open':
enhancerMenu.open();
break;
}
return true;