mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-03 12:19:02 +00:00
feat: custom userscripts in-app and in-browser!
firefox support will involve some tweaking, manifest v3 not fully supported yet
This commit is contained in:
parent
607fcee4f8
commit
71f9ecc32b
@ -185,7 +185,7 @@ const renderMenu = async () => {
|
||||
const importApi = () => {
|
||||
return (_apiImport ??= (async () => {
|
||||
const api = globalThis.__enhancerApi;
|
||||
if (typeof api === "undefined") await import("../../common/system.js");
|
||||
if (typeof api === "undefined") await import("../../api/system.js");
|
||||
await import("../../load.mjs").then((i) => i.default);
|
||||
})());
|
||||
},
|
||||
|
@ -61,6 +61,13 @@
|
||||
"label": "Advanced",
|
||||
"_autoremoveIfSectionEmpty": false
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"label": "Custom JavaScript",
|
||||
"key": "customScript",
|
||||
"description": "Executes the uploaded userscript within Notion. Requires <a href='https://developer.chrome.com/docs/extensions/reference/api/userScripts#developer_mode_for_extension_users'>developer mode</a> to be enabled in your browser's extension settings to run in Chromium-based browsers.",
|
||||
"extensions": ["js"]
|
||||
},
|
||||
{
|
||||
"type": "toggle",
|
||||
"key": "developerMode",
|
||||
|
10
src/init.js
10
src/init.js
@ -19,7 +19,7 @@ if (isElectron()) {
|
||||
|
||||
const { enhancerUrl } = globalThis.__enhancerApi,
|
||||
{ getMods, isEnabled, modDatabase } = globalThis.__enhancerApi,
|
||||
API_LOADED = new Promise((res, rej) => {
|
||||
API_LOADED = new Promise((res) => {
|
||||
const onReady = globalThis.__enhancerReady;
|
||||
globalThis.__enhancerReady = () => (onReady?.(), res());
|
||||
});
|
||||
@ -33,12 +33,18 @@ if (isElectron()) {
|
||||
contextBridge.exposeInMainWorld("__getEnhancerApi", __getApi);
|
||||
|
||||
// load clientStyles, clientScripts
|
||||
document.addEventListener("readystatechange", () => {
|
||||
document.addEventListener("readystatechange", async () => {
|
||||
if (document.readyState !== "complete") return false;
|
||||
const $script = document.createElement("script");
|
||||
$script.type = "module";
|
||||
$script.src = enhancerUrl("load.mjs");
|
||||
document.head.append($script);
|
||||
|
||||
// register user-provided javascript for execution in-app
|
||||
const { webFrame } = require("electron"),
|
||||
db = await modDatabase("0f0bf8b6-eae6-4273-b307-8fc43f2ee082"),
|
||||
customScript = (await db.get("customScript"))?.content;
|
||||
if (customScript) webFrame.executeJavaScript(customScript);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ export default (async () => {
|
||||
// the dom must be re-imported
|
||||
|
||||
await Promise.all([
|
||||
IS_ELECTRON || import(enhancerUrl("common/registry.js")),
|
||||
IS_ELECTRON || import(enhancerUrl("api/registry.js")),
|
||||
import(enhancerUrl("api/interface.mjs")),
|
||||
import(enhancerUrl("api/state.js")),
|
||||
]);
|
||||
|
@ -19,15 +19,13 @@
|
||||
"permissions": [
|
||||
"tabs",
|
||||
"storage",
|
||||
"userScripts",
|
||||
"clipboardRead",
|
||||
"clipboardWrite",
|
||||
"unlimitedStorage"
|
||||
],
|
||||
"host_permissions": ["*://*.notion.so/*"],
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"matches": ["*://*.notion.so/*"],
|
||||
"resources": ["/*"]
|
||||
}
|
||||
{ "matches": ["*://*.notion.so/*"], "resources": ["/*"] }
|
||||
]
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ const initDatabase = async () => {
|
||||
value = JSON.parse(__statements.select.get(key)?.value);
|
||||
} catch {}
|
||||
} else value = (await chrome.storage.local.get([key]))[key];
|
||||
return value ?? args.fallbacks[args.key];
|
||||
return value ?? args.fallbacks?.[args.key];
|
||||
}
|
||||
case "set": {
|
||||
const key = namespaceify(args.key),
|
||||
@ -89,7 +89,6 @@ const initDatabase = async () => {
|
||||
? (__transactions.remove(keys), true)
|
||||
: chrome.storage.local.remove(keys);
|
||||
}
|
||||
|
||||
case "export": {
|
||||
// returns key/value pairs within scope w/out namespace
|
||||
// prefix e.g. to streamline importing from one profile and
|
||||
@ -149,6 +148,39 @@ if (IS_ELECTRON) {
|
||||
.forEach((tab) => chrome.tabs.reload(tab.id));
|
||||
};
|
||||
|
||||
const userScriptsAvailable = () => {
|
||||
// manifest v3 userscripts require developer mode to be
|
||||
// enabled in the browser's extension settings
|
||||
try {
|
||||
chrome.userScripts;
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
registerCustomScript = async () => {
|
||||
if (!userScriptsAvailable()) return;
|
||||
// enhancer apis are not available in the worker in-browser,
|
||||
// manual steps are required to get nested values from the db
|
||||
const key = "customScript",
|
||||
matches = ["*://*.notion.so/*"],
|
||||
coreId = "0f0bf8b6-eae6-4273-b307-8fc43f2ee082",
|
||||
profileId =
|
||||
(await queryDatabase([], "get", { key: "activeProfile" })) ??
|
||||
(await queryDatabase([], "get", { key: "profileIds" }))?.[0] ??
|
||||
"default",
|
||||
customScript = await queryDatabase([profileId, coreId], "get", { key }),
|
||||
existingScripts = await chrome.userScripts.getScripts({ ids: [key] }),
|
||||
code = customScript?.content || "";
|
||||
if (existingScripts[0]) {
|
||||
if (code === existingScripts[0]?.code) return;
|
||||
chrome.userScripts.update([{ id: key, matches, js: [{ code }] }]);
|
||||
} else if (code) {
|
||||
chrome.userScripts.register([{ id: key, matches, js: [{ code }] }]);
|
||||
}
|
||||
};
|
||||
registerCustomScript();
|
||||
|
||||
chrome.action.onClicked.addListener(openEnhancerMenu);
|
||||
// long-lived connection for rapid two-way messaging
|
||||
// b/w client and worker, primarily used for db wrapper:
|
||||
@ -163,6 +195,13 @@ if (IS_ELECTRON) {
|
||||
const { namespace, query, args } = message.data,
|
||||
res = await queryDatabase(namespace, query, args);
|
||||
if (invocation) port.postMessage({ invocation, message: res });
|
||||
// re-register userscript on updates:
|
||||
// profile change, db import, file upload, file deletion
|
||||
const customScriptChanged =
|
||||
query === "import" ||
|
||||
(query === "set" &&
|
||||
["activeProfile", "customScript"].includes(args.key));
|
||||
if (customScriptChanged) registerCustomScript();
|
||||
}
|
||||
if (message === "load-complete") {
|
||||
if (!openMenuInTabs.has(tabId)) return;
|
||||
|
Loading…
Reference in New Issue
Block a user