notion-enhancer/api/index.cjs

1043 lines
37 KiB
JavaScript

var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
__markAsModule(target);
for (var name3 in all)
__defProp(target, name3, { get: all[name3], enumerable: true });
};
// insert/api/index.mjs
__export(exports, {
components: () => components_exports,
electron: () => electron_exports,
env: () => env_exports,
fmt: () => fmt_exports,
fs: () => fs_exports,
registry: () => registry_exports,
storage: () => storage_exports,
web: () => web_exports
});
// insert/api/env.mjs
var env_exports = {};
__export(env_exports, {
focusMenu: () => focusMenu2,
focusNotion: () => focusNotion2,
name: () => name2,
notionRequire: () => notionRequire2,
reload: () => reload2,
version: () => version2
});
// insert/env/env.mjs
"use strict";
var name = globalThis.__enhancerElectronApi.platform;
var version = globalThis.__enhancerElectronApi.version;
var focusMenu = globalThis.__enhancerElectronApi.focusMenu;
var focusNotion = globalThis.__enhancerElectronApi.focusNotion;
var reload = globalThis.__enhancerElectronApi.reload;
var notionRequire = globalThis.__enhancerElectronApi.notionRequire;
// insert/api/env.mjs
"use strict";
var name2 = name;
var version2 = version;
var focusMenu2 = focusMenu;
var focusNotion2 = focusNotion;
var reload2 = reload;
var notionRequire2 = notionRequire;
// insert/api/fs.mjs
var fs_exports = {};
__export(fs_exports, {
getJSON: () => getJSON2,
getText: () => getText2,
isFile: () => isFile2,
localPath: () => localPath2
});
// insert/env/fs.mjs
"use strict";
var localPath = (path) => `notion://www.notion.so/__notion-enhancer/${path}`;
var 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());
}
};
var getText = (path, opts = {}) => {
if (path.startsWith("http"))
return fetch(path, opts).then((res) => res.text());
try {
const fs2 = globalThis.__enhancerElectronApi.nodeRequire("fs"), { resolve: resolvePath } = globalThis.__enhancerElectronApi.nodeRequire("path");
return fs2.readFileSync(resolvePath(`${__dirname}/../../${path}`));
} catch (err) {
return fetch(localPath(path), opts).then((res) => res.text());
}
};
var isFile = async (path) => {
try {
const fs2 = globalThis.__enhancerElectronApi.nodeRequire("fs"), { resolve: resolvePath } = globalThis.__enhancerElectronApi.nodeRequire("path");
if (path.startsWith("http")) {
await fetch(path);
} else {
try {
fs2.existsSync(resolvePath(`${__dirname}/../../${path}`));
} catch (err) {
await fetch(localPath(path));
}
}
return true;
} catch {
return false;
}
};
// insert/api/fs.mjs
"use strict";
var localPath2 = localPath;
var getJSON2 = getJSON;
var getText2 = getText;
var isFile2 = isFile;
// insert/api/storage.mjs
var storage_exports = {};
__export(storage_exports, {
addChangeListener: () => addChangeListener2,
db: () => db2,
get: () => get2,
removeChangeListener: () => removeChangeListener2,
set: () => set2
});
// insert/env/storage.mjs
"use strict";
var get = (path, fallback = void 0) => {
return globalThis.__enhancerElectronApi.db.get(path, fallback);
};
var set = (path, value) => {
return globalThis.__enhancerElectronApi.db.set(path, value);
};
var db = (namespace, getFunc = get, setFunc = set) => {
if (typeof namespace === "string")
namespace = [namespace];
return {
get: (path = [], fallback = void 0) => getFunc([...namespace, ...path], fallback),
set: (path, value) => setFunc([...namespace, ...path], value)
};
};
var addChangeListener = (callback) => {
return globalThis.__enhancerElectronApi.db.addChangeListener(callback);
};
var removeChangeListener = (callback) => {
return globalThis.__enhancerElectronApi.db.removeChangeListener(callback);
};
// insert/api/storage.mjs
"use strict";
var get2 = get;
var set2 = set;
var db2 = db;
var addChangeListener2 = addChangeListener;
var removeChangeListener2 = removeChangeListener;
// insert/api/electron.mjs
var electron_exports = {};
__export(electron_exports, {
browser: () => browser,
onMessage: () => onMessage,
sendMessage: () => sendMessage,
sendMessageToHost: () => sendMessageToHost,
webFrame: () => webFrame
});
"use strict";
var browser = globalThis.__enhancerElectronApi?.browser;
var webFrame = globalThis.__enhancerElectronApi?.webFrame;
var sendMessage = globalThis.__enhancerElectronApi?.ipcRenderer?.sendMessage;
var sendMessageToHost = globalThis.__enhancerElectronApi?.ipcRenderer?.sendMessageToHost;
var onMessage = globalThis.__enhancerElectronApi?.ipcRenderer?.onMessage;
// insert/api/fmt.mjs
var fmt_exports = {};
__export(fmt_exports, {
is: () => is,
rgbContrast: () => rgbContrast,
rgbLogShade: () => rgbLogShade,
slugger: () => slugger,
uuidv4: () => uuidv4
});
"use strict";
var slugger = (heading, slugs = new Set()) => {
heading = heading.replace(/\s/g, "-").replace(/[^A-Za-z0-9-_]/g, "").toLowerCase();
let i = 0, slug = heading;
while (slugs.has(slug)) {
i++;
slug = `${heading}-${i}`;
}
return slug;
};
var uuidv4 = () => {
if (crypto?.randomUUID)
return crypto.randomUUID();
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
};
var rgbLogShade = (p, c) => {
var i = parseInt, r = Math.round, [a, b, c, d] = c.split(","), P = p < 0, t = P ? 0 : p * 255 ** 2, P = P ? 1 + p : 1 - p;
return "rgb" + (d ? "a(" : "(") + r((P * i(a[3] == "a" ? a.slice(5) : a.slice(4)) ** 2 + t) ** 0.5) + "," + r((P * i(b) ** 2 + t) ** 0.5) + "," + r((P * i(c) ** 2 + t) ** 0.5) + (d ? "," + d : ")");
};
var rgbContrast = (r, g, b) => {
return Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)) > 165.75 ? "rgb(0,0,0)" : "rgb(255,255,255)";
};
var patterns = {
alphanumeric: /^[\w\.-]+$/,
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
semver: /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/i,
email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i,
url: /^[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,64}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/i,
color: /^(?:#|0x)(?:[a-f0-9]{3}|[a-f0-9]{6})\b|(?:rgb|hsl)a?\([^\)]*\)$/i
};
function test(str, pattern) {
const match = str.match(pattern);
return !!(match && match.length);
}
var is = async (value, type, { extension = "" } = {}) => {
extension = !value || !value.endsWith || value.endsWith(extension);
if (Array.isArray(type)) {
return type.includes(value);
}
switch (type) {
case "array":
return Array.isArray(value);
case "object":
return value && typeof value === "object" && !Array.isArray(value);
case "undefined":
case "boolean":
case "number":
return typeof value === type && extension;
case "string":
return typeof value === type && extension;
case "alphanumeric":
case "uuid":
case "semver":
case "email":
case "url":
case "color":
return typeof value === "string" && test(value, patterns[type]) && extension;
case "file":
return typeof value === "string" && value && await fs_exports.isFile(value) && extension;
}
return false;
};
// insert/api/registry.mjs
var registry_exports = {};
__export(registry_exports, {
core: () => core,
db: () => db3,
enabled: () => enabled,
errors: () => errors,
get: () => get3,
list: () => list,
optionDefault: () => optionDefault,
optionTypes: () => optionTypes,
profileDB: () => profileDB,
profileName: () => profileName,
supportedEnvs: () => supportedEnvs
});
// insert/api/registry-validation.mjs
"use strict";
var check = async (mod, key, value, types, {
extension = "",
error = `invalid ${key} (${extension ? `${extension} ` : ""}${types}): ${JSON.stringify(value)}`,
optional = false
} = {}) => {
let test2;
for (const type of Array.isArray(types) ? [types] : types.split("|")) {
if (type === "file") {
test2 = value && !value.startsWith("http") ? await fmt_exports.is(`repo/${mod._dir}/${value}`, type, { extension }) : false;
} else
test2 = await fmt_exports.is(value, type, { extension });
if (test2)
break;
}
if (!test2) {
if (optional && await fmt_exports.is(value, "undefined"))
return true;
if (error)
mod._err(error);
return false;
}
return true;
};
var validateEnvironments = async (mod) => {
mod.environments = mod.environments ?? registry_exports.supportedEnvs;
const isArray = await check(mod, "environments", mod.environments, "array");
if (!isArray)
return false;
return mod.environments.map((tag) => check(mod, "environments.env", tag, registry_exports.supportedEnvs));
};
var validateTags = async (mod) => {
const isArray = await check(mod, "tags", mod.tags, "array");
if (!isArray)
return false;
const categoryTags = ["core", "extension", "theme", "integration"], containsCategory = mod.tags.filter((tag) => categoryTags.includes(tag)).length;
if (!containsCategory) {
mod._err(`invalid tags (must contain at least one of 'core', 'extension', 'theme' or 'integration'):
${JSON.stringify(mod.tags)}`);
return false;
}
const isTheme = mod.tags.includes("theme"), hasThemeMode = mod.tags.includes("light") || mod.tags.includes("dark"), isBothThemeModes = mod.tags.includes("light") && mod.tags.includes("dark");
if (isTheme && (!hasThemeMode || isBothThemeModes)) {
mod._err(`invalid tags (themes must be either 'light' or 'dark', not neither or both):
${JSON.stringify(mod.tags)}`);
return false;
}
return mod.tags.map((tag) => check(mod, "tags.tag", tag, "string"));
};
var validateAuthors = async (mod) => {
const isArray = await check(mod, "authors", mod.authors, "array");
if (!isArray)
return false;
return mod.authors.map((author) => [
check(mod, "authors.author.name", author.name, "string"),
check(mod, "authors.author.email", author.email, "email", { optional: true }),
check(mod, "authors.author.homepage", author.homepage, "url"),
check(mod, "authors.author.avatar", author.avatar, "url")
]);
};
var validateCSS = async (mod) => {
const isArray = await check(mod, "css", mod.css, "object");
if (!isArray)
return false;
const tests = [];
for (let dest of ["frame", "client", "menu"]) {
if (!mod.css[dest])
continue;
let test2 = await check(mod, `css.${dest}`, mod.css[dest], "array");
if (test2) {
test2 = mod.css[dest].map((file) => check(mod, `css.${dest}.file`, file, "file", { extension: ".css" }));
}
tests.push(test2);
}
return tests;
};
var validateJS = async (mod) => {
const isArray = await check(mod, "js", mod.js, "object");
if (!isArray)
return false;
const tests = [];
for (let dest of ["frame", "client", "menu"]) {
if (!mod.js[dest])
continue;
let test2 = await check(mod, `js.${dest}`, mod.js[dest], "array");
if (test2) {
test2 = mod.js[dest].map((file) => check(mod, `js.${dest}.file`, file, "file", { extension: ".mjs" }));
}
tests.push(test2);
}
if (mod.js.electron) {
const isArray2 = await check(mod, "js.electron", mod.js.electron, "array");
if (isArray2) {
for (const file of mod.js.electron) {
const isObject = await check(mod, "js.electron.file", file, "object");
if (!isObject) {
tests.push(false);
continue;
}
tests.push([
check(mod, "js.electron.file.source", file.source, "file", {
extension: ".cjs"
}),
check(mod, "js.electron.file.target", file.target, "string", {
extension: ".js"
})
]);
}
} else
tests.push(false);
}
return tests;
};
var validateOptions = async (mod) => {
const isArray = await check(mod, "options", mod.options, "array");
if (!isArray)
return false;
const tests = [];
for (const option of mod.options) {
const key = "options.option", optTypeValid = await check(mod, `${key}.type`, option.type, registry_exports.optionTypes);
if (!optTypeValid) {
tests.push(false);
continue;
}
option.environments = option.environments ?? registry_exports.supportedEnvs;
tests.push([
check(mod, `${key}.key`, option.key, "alphanumeric"),
check(mod, `${key}.label`, option.label, "string"),
check(mod, `${key}.tooltip`, option.tooltip, "string", {
optional: true
}),
check(mod, `${key}.environments`, option.environments, "array").then((isArray2) => {
if (!isArray2)
return false;
return option.environments.map((environment) => check(mod, `${key}.environments.env`, environment, registry_exports.supportedEnvs));
})
]);
switch (option.type) {
case "toggle":
tests.push(check(mod, `${key}.value`, option.value, "boolean"));
break;
case "select": {
let test2 = await check(mod, `${key}.values`, option.values, "array");
if (test2) {
test2 = option.values.map((value) => check(mod, `${key}.values.value`, value, "string"));
}
tests.push(test2);
break;
}
case "text":
case "hotkey":
tests.push(check(mod, `${key}.value`, option.value, "string"));
break;
case "number":
case "color":
tests.push(check(mod, `${key}.value`, option.value, option.type));
break;
case "file": {
let test2 = await check(mod, `${key}.extensions`, option.extensions, "array");
if (test2) {
test2 = option.extensions.map((ext) => check(mod, `${key}.extensions.extension`, ext, "string"));
}
tests.push(test2);
break;
}
}
}
return tests;
};
async function validate(mod) {
let conditions = [
check(mod, "name", mod.name, "string"),
check(mod, "id", mod.id, "uuid"),
check(mod, "version", mod.version, "semver"),
validateEnvironments(mod),
check(mod, "description", mod.description, "string"),
check(mod, "preview", mod.preview, "file|url", { optional: true }),
validateTags(mod),
validateAuthors(mod),
validateCSS(mod),
validateJS(mod),
validateOptions(mod)
];
do {
conditions = await Promise.all(conditions.flat(Infinity));
} while (conditions.some((condition) => Array.isArray(condition)));
return conditions.every((passed) => passed);
}
// insert/api/registry.mjs
"use strict";
var core = [
"a6621988-551d-495a-97d8-3c568bca2e9e",
"0f0bf8b6-eae6-4273-b307-8fc43f2ee082",
"36a2ffc9-27ff-480e-84a7-c7700a7d232d"
];
var supportedEnvs = ["linux", "win32", "darwin", "extension"];
var optionTypes = ["toggle", "select", "text", "number", "color", "file", "hotkey"];
var profileName = async () => storage_exports.get(["currentprofile"], "default");
var profileDB = async () => storage_exports.db(["profiles", await profileName()]);
var _list;
var _errors = [];
var list = async (filter = (mod) => true) => {
if (!_list) {
_list = new Promise(async (res, rej) => {
const passed = [];
for (const dir of await fs_exports.getJSON("repo/registry.json")) {
try {
const mod = {
...await fs_exports.getJSON(`repo/${dir}/mod.json`),
_dir: dir,
_err: (message) => _errors.push({ source: dir, message })
};
if (await validate(mod))
passed.push(mod);
} catch {
_errors.push({ source: dir, message: "invalid mod.json" });
}
}
res(passed);
});
}
const filtered = [];
for (const mod of await _list)
if (await filter(mod))
filtered.push(mod);
return filtered;
};
var errors = async () => {
await list();
return _errors;
};
var get3 = async (id) => {
return (await list((mod) => mod.id === id))[0];
};
var enabled = async (id) => {
const mod = await get3(id);
if (!mod.environments.includes(env_exports.name))
return false;
if (core.includes(id))
return true;
return (await profileDB()).get(["_mods", id], false);
};
var optionDefault = async (id, key) => {
const mod = await get3(id), opt = mod.options.find((opt2) => opt2.key === key);
if (!opt)
return void 0;
switch (opt.type) {
case "toggle":
case "text":
case "number":
case "color":
case "hotkey":
return opt.value;
case "select":
return opt.values[0];
case "file":
return void 0;
}
};
var db3 = async (id) => {
const db5 = await profileDB();
return storage_exports.db([id], async (path, fallback = void 0) => {
if (typeof path === "string")
path = [path];
if (path.length === 2) {
fallback = await optionDefault(id, path[1]) ?? fallback;
}
return db5.get(path, fallback);
}, db5.set);
};
// insert/api/web.mjs
var web_exports = {};
__export(web_exports, {
addDocumentObserver: () => addDocumentObserver,
addHotkeyListener: () => addHotkeyListener,
copyToClipboard: () => copyToClipboard,
empty: () => empty,
escape: () => escape,
html: () => html,
loadStylesheet: () => loadStylesheet,
queryParams: () => queryParams,
raw: () => raw,
readFromClipboard: () => readFromClipboard,
removeDocumentObserver: () => removeDocumentObserver,
removeHotkeyListener: () => removeHotkeyListener,
render: () => render,
whenReady: () => whenReady
});
"use strict";
var _hotkeyListenersActivated = false;
var _hotkeyEventListeners = [];
var _documentObserver;
var _documentObserverListeners = [];
var _documentObserverEvents = [];
var whenReady = (selectors = []) => {
return new Promise((res, rej) => {
function onLoad() {
let isReadyInt;
isReadyInt = setInterval(isReadyTest, 100);
function isReadyTest() {
if (selectors.every((selector) => document.querySelector(selector))) {
clearInterval(isReadyInt);
res(true);
}
}
isReadyTest();
}
if (document.readyState !== "complete") {
document.addEventListener("readystatechange", (event) => {
if (document.readyState === "complete")
onLoad();
});
} else
onLoad();
});
};
var queryParams = () => new URLSearchParams(window.location.search);
var escape = (str) => str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/'/g, "&#39;").replace(/"/g, "&quot;").replace(/\\/g, "&#x5C;");
var raw = (str, ...templates) => {
const html2 = str.map((chunk) => chunk + (["string", "number"].includes(typeof templates[0]) ? templates.shift() : escape(JSON.stringify(templates.shift(), null, 2) ?? ""))).join("");
return html2.includes("<pre") ? html2.trim() : html2.split(/\n/).map((line) => line.trim()).filter((line) => line.length).join(" ");
};
var html = (str, ...templates) => {
const $fragment = document.createRange().createContextualFragment(raw(str, ...templates));
return $fragment.children.length === 1 ? $fragment.children[0] : $fragment.children;
};
var render = ($container, ...$elems) => {
$elems = $elems.map(($elem) => $elem instanceof HTMLCollection ? [...$elem] : $elem).flat(Infinity).filter(($elem) => $elem);
$container.append(...$elems);
return $container;
};
var empty = ($container) => {
while ($container.firstChild && $container.removeChild($container.firstChild))
;
return $container;
};
var loadStylesheet = (path) => {
const $stylesheet4 = html`<link
rel="stylesheet"
href="${path.startsWith("https://") ? path : fs_exports.localPath(path)}"
/>`;
render(document.head, $stylesheet4);
return $stylesheet4;
};
var copyToClipboard = async (str) => {
try {
await navigator.clipboard.writeText(str);
} catch {
const $el = document.createElement("textarea");
$el.value = str;
$el.setAttribute("readonly", "");
$el.style.position = "absolute";
$el.style.left = "-9999px";
document.body.appendChild($el);
$el.select();
document.execCommand("copy");
document.body.removeChild($el);
}
};
var readFromClipboard = () => {
return navigator.clipboard.readText();
};
var triggerHotkeyListener = (event, hotkey) => {
const inInput = document.activeElement.nodeName === "INPUT" && !hotkey.listenInInput;
if (inInput)
return;
const pressed = hotkey.keys.every((key) => {
key = key.toLowerCase();
const modifiers = {
metaKey: ["meta", "os", "win", "cmd", "command"],
ctrlKey: ["ctrl", "control"],
shiftKey: ["shift"],
altKey: ["alt"]
};
for (const modifier in modifiers) {
const pressed2 = modifiers[modifier].includes(key) && event[modifier];
if (pressed2)
return true;
}
if (key === "space")
key = " ";
if (key === "plus")
key = "+";
if (key === event.key.toLowerCase())
return true;
});
if (pressed)
hotkey.callback(event);
};
var addHotkeyListener = (keys, callback, { listenInInput = false, keydown = false } = {}) => {
if (typeof keys === "string")
keys = keys.split("+");
_hotkeyEventListeners.push({ keys, callback, listenInInput, keydown });
if (!_hotkeyListenersActivated) {
_hotkeyListenersActivated = true;
document.addEventListener("keyup", (event) => {
for (const hotkey of _hotkeyEventListeners.filter(({ keydown: keydown2 }) => !keydown2)) {
triggerHotkeyListener(event, hotkey);
}
});
document.addEventListener("keydown", (event) => {
for (const hotkey of _hotkeyEventListeners.filter(({ keydown: keydown2 }) => keydown2)) {
triggerHotkeyListener(event, hotkey);
}
});
}
};
var removeHotkeyListener = (callback) => {
_hotkeyEventListeners = _hotkeyEventListeners.filter((listener) => listener.callback !== callback);
};
var addDocumentObserver = (callback, selectors = []) => {
if (!_documentObserver) {
const handle = (queue) => {
while (queue.length) {
const event = queue.shift(), matchesAddedNode = ($node, selector) => $node instanceof Element && ($node.matches(selector) || $node.matches(`${selector} *`) || $node.querySelector(selector)), matchesTarget = (selector) => event.target.matches(selector) || event.target.matches(`${selector} *`) || [...event.addedNodes].some(($node) => matchesAddedNode($node, selector));
for (const listener of _documentObserverListeners) {
if (!listener.selectors.length || listener.selectors.some(matchesTarget)) {
listener.callback(event);
}
}
}
};
_documentObserver = new MutationObserver((list2, observer) => {
if (!_documentObserverEvents.length)
requestIdleCallback(() => handle(_documentObserverEvents));
_documentObserverEvents.push(...list2);
});
_documentObserver.observe(document.body, {
childList: true,
subtree: true,
attributes: true
});
}
_documentObserverListeners.push({ callback, selectors });
};
var removeDocumentObserver = (callback) => {
_documentObserverListeners = _documentObserverListeners.filter((listener) => listener.callback !== callback);
};
// insert/api/components/index.mjs
var components_exports = {};
__export(components_exports, {
addCornerAction: () => addCornerAction,
addPanelView: () => addPanelView,
addTooltip: () => addTooltip,
feather: () => feather
});
// insert/api/components/tooltip.mjs
"use strict";
var $stylesheet;
var _$tooltip;
var countLines = ($el) => [...$el.getClientRects()].reduce((prev, val) => prev.some((p) => p.y === val.y) ? prev : [...prev, val], []).length;
var position = async ($ref, offsetDirection, maxLines) => {
_$tooltip.style.top = `0px`;
_$tooltip.style.left = `0px`;
const rect = $ref.getBoundingClientRect(), { offsetWidth, offsetHeight } = _$tooltip, pad = 6;
let x = rect.x, y = Math.floor(rect.y);
if (["top", "bottom"].includes(offsetDirection)) {
if (offsetDirection === "top")
y -= offsetHeight + pad;
if (offsetDirection === "bottom")
y += rect.height + pad;
x -= offsetWidth / 2 - rect.width / 2;
_$tooltip.style.left = `${x}px`;
_$tooltip.style.top = `${y}px`;
const testLines = () => countLines(_$tooltip.firstElementChild) > maxLines, padEdgesX = testLines();
while (testLines()) {
_$tooltip.style.left = `${window.innerWidth - x > x ? x++ : x--}px`;
}
if (padEdgesX) {
x += window.innerWidth - x > x ? pad : -pad;
_$tooltip.style.left = `${x}px`;
}
}
if (["left", "right"].includes(offsetDirection)) {
y -= offsetHeight / 2 - rect.height / 2;
if (offsetDirection === "left")
x -= offsetWidth + pad;
if (offsetDirection === "right")
x += rect.width + pad;
_$tooltip.style.left = `${x}px`;
_$tooltip.style.top = `${y}px`;
}
return true;
};
var addTooltip = async ($ref, $content, { delay = 100, offsetDirection = "bottom", maxLines = 1 } = {}) => {
if (!$stylesheet) {
$stylesheet = web_exports.loadStylesheet("api/components/tooltip.css");
_$tooltip = web_exports.html`<div id="enhancer--tooltip"></div>`;
web_exports.render(document.body, _$tooltip);
}
if (!globalThis.markdownit)
await import(fs_exports.localPath("dep/markdown-it.min.js"));
const md = markdownit({ linkify: true });
if (!($content instanceof Element))
$content = web_exports.html`<div style="display:inline">
${$content.split("\n").map((text) => md.renderInline(text)).join("<br>")}
</div>`;
let displayDelay;
$ref.addEventListener("mouseover", async (event) => {
if (!displayDelay) {
displayDelay = setTimeout(async () => {
if ($ref.matches(":hover")) {
if (_$tooltip.style.display !== "block") {
_$tooltip.style.display = "block";
web_exports.render(web_exports.empty(_$tooltip), $content);
position($ref, offsetDirection, maxLines);
await _$tooltip.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 65 }).finished;
}
}
displayDelay = void 0;
}, delay);
}
});
$ref.addEventListener("mouseout", async (event) => {
displayDelay = void 0;
if (_$tooltip.style.display === "block" && !$ref.matches(":hover")) {
await _$tooltip.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 65 }).finished;
_$tooltip.style.display = "";
}
});
};
// insert/api/components/feather.mjs
"use strict";
var _$iconSheet;
var feather = async (name3, attrs = {}) => {
if (!_$iconSheet) {
_$iconSheet = web_exports.html`${await fs_exports.getText("dep/feather-sprite.svg")}`;
}
attrs.style = ((attrs.style ? attrs.style + ";" : "") + "stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;fill:none;").trim();
attrs.viewBox = "0 0 24 24";
return `<svg ${Object.entries(attrs).map(([key, val]) => `${web_exports.escape(key)}="${web_exports.escape(val)}"`).join(" ")}>${_$iconSheet.getElementById(name3)?.innerHTML}</svg>`;
};
// insert/api/components/panel.mjs
"use strict";
var _views = [];
var svgExpand = web_exports.raw`<svg viewBox="-1 -1 9 11">
<path d="M 3.5 0L 3.98809 -0.569442L 3.5 -0.987808L 3.01191 -0.569442L 3.5 0ZM 3.5 9L 3.01191
9.56944L 3.5 9.98781L 3.98809 9.56944L 3.5 9ZM 0.488094 3.56944L 3.98809 0.569442L 3.01191
-0.569442L -0.488094 2.43056L 0.488094 3.56944ZM 3.01191 0.569442L 6.51191 3.56944L 7.48809
2.43056L 3.98809 -0.569442L 3.01191 0.569442ZM -0.488094 6.56944L 3.01191 9.56944L 3.98809
8.43056L 0.488094 5.43056L -0.488094 6.56944ZM 3.98809 9.56944L 7.48809 6.56944L 6.51191
5.43056L 3.01191 8.43056L 3.98809 9.56944Z"></path>
</svg>`;
var $stylesheet2;
var db4;
var $notionFrame;
var $notionRightSidebar;
var $panel;
var $hoverTrigger;
var $resizeHandle;
var dragStartX;
var dragStartWidth;
var dragEventsFired;
var panelWidth;
var $notionApp;
var $pinnedToggle;
var $panelTitle;
var $header;
var $panelContent;
var $switcher;
var $switcherTrigger;
var $switcherOverlayContainer;
var panelPinnedAttr = "data-enhancer-panel-pinned";
var isPinned = () => $panel.hasAttribute(panelPinnedAttr);
var togglePanel = () => {
const $elems = [$notionFrame, $notionRightSidebar, $hoverTrigger, $panel].filter(($el) => $el);
if (isPinned()) {
closeSwitcher();
for (const $elem of $elems)
$elem.removeAttribute(panelPinnedAttr);
} else {
for (const $elem of $elems)
$elem.setAttribute(panelPinnedAttr, "true");
}
db4.set(["panel.pinned"], isPinned());
};
var updateWidth = async () => {
document.documentElement.style.setProperty("--component--panel-width", panelWidth + "px");
db4.set(["panel.width"], panelWidth);
};
var resizeDrag = (event) => {
event.preventDefault();
dragEventsFired = true;
panelWidth = dragStartWidth + (dragStartX - event.clientX);
if (panelWidth < 190)
panelWidth = 190;
if (panelWidth > 480)
panelWidth = 480;
$panel.style.width = panelWidth + "px";
$hoverTrigger.style.width = panelWidth + "px";
$notionFrame.style.paddingRight = panelWidth + "px";
if ($notionRightSidebar)
$notionRightSidebar.style.right = panelWidth + "px";
};
var resizeEnd = (event) => {
$panel.style.width = "";
$hoverTrigger.style.width = "";
$notionFrame.style.paddingRight = "";
if ($notionRightSidebar)
$notionRightSidebar.style.right = "";
updateWidth();
$resizeHandle.style.cursor = "";
document.body.removeEventListener("mousemove", resizeDrag);
document.body.removeEventListener("mouseup", resizeEnd);
};
var resizeStart = (event) => {
dragStartX = event.clientX;
dragStartWidth = panelWidth;
$resizeHandle.style.cursor = "auto";
document.body.addEventListener("mousemove", resizeDrag);
document.body.addEventListener("mouseup", resizeEnd);
};
var isSwitcherOpen = () => document.body.contains($switcher);
var openSwitcher = () => {
if (!isPinned())
return togglePanel();
web_exports.render($notionApp, $switcherOverlayContainer);
web_exports.empty($switcher);
for (const view of _views) {
const open = $panelTitle.contains(view.$title), $item = web_exports.render(web_exports.html`<div class="enhancer--panel-switcher-item" tabindex="0" ${open ? "data-open" : ""}></div>`, web_exports.render(web_exports.html`<span class="enhancer--panel-view-title"></span>`, view.$icon.cloneNode(true), view.$title.cloneNode(true)));
$item.addEventListener("click", () => {
renderView(view);
db4.set(["panel.open"], view.id);
});
web_exports.render($switcher, $item);
}
const rect = $header.getBoundingClientRect();
web_exports.render(web_exports.empty($switcherOverlayContainer), web_exports.render(web_exports.html`<div style="position: fixed; top: ${rect.top}px; left: ${rect.left}px;
width: ${rect.width}px; height: ${rect.height}px;"></div>`, web_exports.render(web_exports.html`<div style="position: relative; top: 100%; pointer-events: auto;"></div>`, $switcher)));
$switcher.querySelector("[data-open]").focus();
$switcher.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 200 });
document.addEventListener("keydown", switcherKeyListeners);
};
var closeSwitcher = () => {
document.removeEventListener("keydown", switcherKeyListeners);
$switcher.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 200 }).onfinish = () => $switcherOverlayContainer.remove();
};
var switcherKeyListeners = (event) => {
if (isSwitcherOpen()) {
switch (event.key) {
case "Escape":
closeSwitcher();
event.stopPropagation();
break;
case "Enter":
document.activeElement.click();
event.stopPropagation();
break;
case "ArrowUp":
const $prev = event.target.previousElementSibling;
($prev || event.target.parentElement.lastElementChild).focus();
event.stopPropagation();
break;
case "ArrowDown":
const $next = event.target.nextElementSibling;
($next || event.target.parentElement.firstElementChild).focus();
event.stopPropagation();
break;
}
}
};
var renderView = (view) => {
const prevView = _views.find(({ $content }) => document.contains($content));
web_exports.render(web_exports.empty($panelTitle), web_exports.render(web_exports.html`<span class="enhancer--panel-view-title"></span>`, view.$icon, view.$title));
view.onFocus();
web_exports.render(web_exports.empty($panelContent), view.$content);
if (prevView)
prevView.onBlur();
};
async function createPanel() {
await web_exports.whenReady([".notion-frame"]);
$notionFrame = document.querySelector(".notion-frame");
$panel = web_exports.html`<div id="enhancer--panel"></div>`;
$hoverTrigger = web_exports.html`<div id="enhancer--panel-hover-trigger"></div>`;
$resizeHandle = web_exports.html`<div id="enhancer--panel-resize"><div></div></div>`;
$panelTitle = web_exports.html`<div id="enhancer--panel-header-title"></div>`;
$header = web_exports.render(web_exports.html`<div id="enhancer--panel-header"></div>`, $panelTitle);
$panelContent = web_exports.html`<div id="enhancer--panel-content"></div>`;
$switcher = web_exports.html`<div id="enhancer--panel-switcher"></div>`;
$switcherTrigger = web_exports.html`<div id="enhancer--panel-header-switcher" tabindex="0">
${svgExpand}
</div>`;
$switcherOverlayContainer = web_exports.html`<div id="enhancer--panel-switcher-overlay-container"></div>`;
const notionRightSidebarSelector = '.notion-cursor-listener > div[style*="flex-end"]', detectRightSidebar = () => {
if (!document.contains($notionRightSidebar)) {
$notionRightSidebar = document.querySelector(notionRightSidebarSelector);
if (isPinned() && $notionRightSidebar) {
$notionRightSidebar.setAttribute(panelPinnedAttr, "true");
}
}
};
$notionRightSidebar = document.querySelector(notionRightSidebarSelector);
web_exports.addDocumentObserver(detectRightSidebar, [notionRightSidebarSelector]);
if (await db4.get(["panel.pinned"]))
togglePanel();
web_exports.addHotkeyListener(await db4.get(["panel.hotkey"]), togglePanel);
$pinnedToggle.addEventListener("click", (event) => {
event.stopPropagation();
togglePanel();
});
web_exports.render($panel, web_exports.render($header, $panelTitle, $switcherTrigger, $pinnedToggle), $panelContent, $resizeHandle);
await enablePanelResize();
await createViews();
const cursorListenerSelector = '.notion-cursor-listener > .notion-sidebar-container ~ [style^="position: absolute"]';
await web_exports.whenReady([cursorListenerSelector]);
document.querySelector(cursorListenerSelector).before($hoverTrigger, $panel);
}
async function enablePanelResize() {
panelWidth = await db4.get(["panel.width"], 240);
updateWidth();
$resizeHandle.addEventListener("mousedown", resizeStart);
$resizeHandle.addEventListener("click", () => {
if (dragEventsFired) {
dragEventsFired = false;
} else
togglePanel();
});
}
async function createViews() {
$notionApp = document.querySelector(".notion-app-inner");
$header.addEventListener("click", openSwitcher);
$switcherTrigger.addEventListener("click", openSwitcher);
$switcherOverlayContainer.addEventListener("click", closeSwitcher);
}
var addPanelView = async ({
id,
icon,
title,
$content,
onFocus = () => {
},
onBlur = () => {
}
}) => {
if (!$stylesheet2) {
$stylesheet2 = web_exports.loadStylesheet("api/components/panel.css");
}
if (!db4)
db4 = await registry_exports.db("36a2ffc9-27ff-480e-84a7-c7700a7d232d");
if (!$pinnedToggle) {
$pinnedToggle = web_exports.html`<div id="enhancer--panel-header-toggle" tabindex="0"><div>
${await components_exports.feather("chevrons-right")}
</div></div>`;
}
const view = {
id,
$icon: web_exports.render(web_exports.html`<span class="enhancer--panel-view-title-icon"></span>`, icon instanceof Element ? icon : web_exports.html`${icon}`),
$title: web_exports.render(web_exports.html`<span class="enhancer--panel-view-title-text"></span>`, title),
$content,
onFocus,
onBlur
};
_views.push(view);
if (_views.length === 1)
await createPanel();
if (_views.length === 1 || await db4.get(["panel.open"]) === id)
renderView(view);
};
// insert/api/components/corner-action.mjs
"use strict";
var $stylesheet3;
var $cornerButtonsContainer;
var addCornerAction = async (icon, listener) => {
if (!$stylesheet3) {
$stylesheet3 = web_exports.loadStylesheet("api/components/corner-action.css");
$cornerButtonsContainer = web_exports.html`<div id="enhancer--corner-actions"></div>`;
}
await web_exports.whenReady([".notion-help-button"]);
const $helpButton = document.querySelector(".notion-help-button"), $onboardingButton = document.querySelector(".onboarding-checklist-button");
if ($onboardingButton)
$cornerButtonsContainer.prepend($onboardingButton);
$cornerButtonsContainer.prepend($helpButton);
web_exports.render(document.querySelector(".notion-app-inner > .notion-cursor-listener"), $cornerButtonsContainer);
const $actionButton = web_exports.html`<div class="enhancer--corner-action-button">${icon}</div>`;
$actionButton.addEventListener("click", listener);
web_exports.render($cornerButtonsContainer, $actionButton);
return $actionButton;
};
// insert/api/components/index.mjs
"use strict";
// insert/api/index.mjs
"use strict";