All checks were successful
Build and Release Sanctum / Build App (push) Successful in 1m55s
688 lines
25 KiB
JavaScript
688 lines
25 KiB
JavaScript
(function () {
|
|
|
|
if (window.__AVIA_LOCAL_PLUGINS_LOADED__) return;
|
|
window.__AVIA_LOCAL_PLUGINS_LOADED__ = true;
|
|
|
|
const STORAGE_KEY = "avia_local_plugins";
|
|
const BUILTIN_SEED = Array.isArray(window.__SANCTUM_BUILTIN_LOCAL_PLUGINS__)
|
|
? window.__SANCTUM_BUILTIN_LOCAL_PLUGINS__
|
|
: [];
|
|
|
|
const runningLocalPlugins = {};
|
|
const localPluginErrors = {};
|
|
|
|
const getLocalPlugins = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
|
|
const setLocalPlugins = (data) => localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
|
|
function upsertBuiltinLocalPlugins() {
|
|
if (!BUILTIN_SEED.length) return;
|
|
|
|
const plugins = getLocalPlugins();
|
|
let dirty = false;
|
|
|
|
for (const builtin of BUILTIN_SEED) {
|
|
const next = {
|
|
id: builtin.id,
|
|
name: builtin.name,
|
|
code: builtin.code || "",
|
|
enabled: true,
|
|
locked: true,
|
|
builtin: true,
|
|
};
|
|
|
|
const existingIndex = plugins.findIndex((plugin) =>
|
|
plugin.id === next.id || plugin.name === next.name
|
|
);
|
|
|
|
if (existingIndex >= 0) {
|
|
const current = plugins[existingIndex];
|
|
const merged = {
|
|
...current,
|
|
...next,
|
|
enabled: true,
|
|
locked: true,
|
|
builtin: true,
|
|
};
|
|
|
|
if (
|
|
JSON.stringify(current) !== JSON.stringify(merged)
|
|
) {
|
|
plugins[existingIndex] = merged;
|
|
dirty = true;
|
|
}
|
|
} else {
|
|
plugins.push(next);
|
|
dirty = true;
|
|
}
|
|
}
|
|
|
|
if (dirty) setLocalPlugins(plugins);
|
|
}
|
|
|
|
function preloadMonaco() {
|
|
return new Promise(resolve => {
|
|
if (window.monaco) return resolve();
|
|
const loader = document.createElement("script");
|
|
loader.src = "https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs/loader.js";
|
|
loader.onload = function () {
|
|
require.config({ paths: { vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs" } });
|
|
require(["vs/editor/editor.main"], () => resolve());
|
|
};
|
|
document.head.appendChild(loader);
|
|
});
|
|
}
|
|
|
|
function runLocalPlugin(plugin) {
|
|
stopLocalPlugin(plugin);
|
|
try {
|
|
const script = document.createElement("script");
|
|
script.textContent = plugin.code || "";
|
|
script.dataset.localPluginId = plugin.id;
|
|
document.body.appendChild(script);
|
|
runningLocalPlugins[plugin.id] = script;
|
|
delete localPluginErrors[plugin.id];
|
|
} catch (e) {
|
|
localPluginErrors[plugin.id] = true;
|
|
}
|
|
renderLocalPanel();
|
|
}
|
|
|
|
function stopLocalPlugin(plugin) {
|
|
const script = runningLocalPlugins[plugin.id];
|
|
if (!script) return;
|
|
script.remove();
|
|
delete runningLocalPlugins[plugin.id];
|
|
delete localPluginErrors[plugin.id];
|
|
renderLocalPanel();
|
|
}
|
|
|
|
async function openEditorPanel(plugin, onSave) {
|
|
await preloadMonaco();
|
|
|
|
const existing = document.getElementById("avia-local-editor-panel");
|
|
if (existing) existing.remove();
|
|
|
|
const panel = document.createElement("div");
|
|
panel.id = "avia-local-editor-panel";
|
|
Object.assign(panel.style, {
|
|
position: "fixed",
|
|
bottom: "24px",
|
|
left: "24px",
|
|
width: "680px",
|
|
height: "460px",
|
|
background: "var(--md-sys-color-surface, #1e1e1e)",
|
|
borderRadius: "16px",
|
|
boxShadow: "0 8px 28px rgba(0,0,0,0.35)",
|
|
zIndex: "9999999",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
overflow: "hidden",
|
|
border: "1px solid rgba(255,255,255,0.08)",
|
|
backdropFilter: "blur(12px)"
|
|
});
|
|
|
|
const header = document.createElement("div");
|
|
header.textContent = `Editing: ${plugin.name}`;
|
|
Object.assign(header.style, {
|
|
padding: "14px 16px",
|
|
fontWeight: "600",
|
|
fontSize: "14px",
|
|
background: "var(--md-sys-color-surface-container, rgba(255,255,255,0.04))",
|
|
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
|
cursor: "move",
|
|
color: "#fff",
|
|
flex: "0 0 auto"
|
|
});
|
|
|
|
const closeBtn = document.createElement("div");
|
|
closeBtn.textContent = "✕";
|
|
Object.assign(closeBtn.style, {
|
|
position: "absolute",
|
|
top: "12px",
|
|
right: "16px",
|
|
cursor: "pointer",
|
|
opacity: "0.7",
|
|
color: "#fff",
|
|
zIndex: "1"
|
|
});
|
|
closeBtn.onmouseenter = () => closeBtn.style.opacity = "1";
|
|
closeBtn.onmouseleave = () => closeBtn.style.opacity = "0.7";
|
|
closeBtn.onclick = () => panel.remove();
|
|
|
|
const toolbar = document.createElement("div");
|
|
Object.assign(toolbar.style, {
|
|
padding: "8px 16px",
|
|
display: "flex",
|
|
gap: "8px",
|
|
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
|
flex: "0 0 auto"
|
|
});
|
|
|
|
const saveBtn = document.createElement("button");
|
|
saveBtn.textContent = "💾 Save";
|
|
styleEditorBtn(saveBtn, "#2d6a4f");
|
|
|
|
const saveRunBtn = document.createElement("button");
|
|
saveRunBtn.textContent = "▶ Save & Run";
|
|
styleEditorBtn(saveRunBtn, "#1b4332");
|
|
|
|
toolbar.appendChild(saveBtn);
|
|
toolbar.appendChild(saveRunBtn);
|
|
|
|
const editorContainer = document.createElement("div");
|
|
editorContainer.style.flex = "1";
|
|
|
|
panel.appendChild(header);
|
|
panel.appendChild(closeBtn);
|
|
panel.appendChild(toolbar);
|
|
panel.appendChild(editorContainer);
|
|
document.body.appendChild(panel);
|
|
|
|
const editor = monaco.editor.create(editorContainer, {
|
|
value: plugin.code || "// Write your plugin code here\n",
|
|
language: "javascript",
|
|
theme: "vs-dark",
|
|
automaticLayout: true,
|
|
minimap: { enabled: false },
|
|
fontSize: 13,
|
|
scrollBeyondLastLine: false,
|
|
wordWrap: "on"
|
|
});
|
|
|
|
saveBtn.onclick = () => {
|
|
onSave(editor.getValue(), false);
|
|
saveBtn.textContent = "✓ Saved";
|
|
setTimeout(() => saveBtn.textContent = "💾 Save", 1200);
|
|
};
|
|
|
|
saveRunBtn.onclick = () => {
|
|
onSave(editor.getValue(), true);
|
|
saveRunBtn.textContent = "✓ Ran!";
|
|
setTimeout(() => saveRunBtn.textContent = "▶ Save & Run", 1200);
|
|
};
|
|
|
|
enableEditorDrag(panel, header);
|
|
}
|
|
|
|
function styleEditorBtn(btn, bg) {
|
|
Object.assign(btn.style, {
|
|
padding: "5px 14px",
|
|
borderRadius: "8px",
|
|
border: "none",
|
|
background: bg || "rgba(255,255,255,0.1)",
|
|
color: "#fff",
|
|
cursor: "pointer",
|
|
fontSize: "12px",
|
|
fontWeight: "500"
|
|
});
|
|
btn.onmouseenter = () => btn.style.opacity = "0.8";
|
|
btn.onmouseleave = () => btn.style.opacity = "1";
|
|
}
|
|
|
|
function enableEditorDrag(panel, handle) {
|
|
let isDragging = false, offsetX, offsetY;
|
|
handle.addEventListener("mousedown", e => {
|
|
isDragging = true;
|
|
offsetX = e.clientX - panel.offsetLeft;
|
|
offsetY = e.clientY - panel.offsetTop;
|
|
document.body.style.userSelect = "none";
|
|
});
|
|
document.addEventListener("mouseup", () => {
|
|
isDragging = false;
|
|
document.body.style.userSelect = "";
|
|
});
|
|
document.addEventListener("mousemove", e => {
|
|
if (!isDragging) return;
|
|
panel.style.left = (e.clientX - offsetX) + "px";
|
|
panel.style.top = (e.clientY - offsetY) + "px";
|
|
panel.style.right = "auto";
|
|
panel.style.bottom = "auto";
|
|
});
|
|
}
|
|
|
|
function toggleLocalPanel() {
|
|
let panel = document.getElementById("avia-local-plugins-panel");
|
|
if (panel) {
|
|
panel.style.display = panel.style.display === "none" ? "flex" : "none";
|
|
return;
|
|
}
|
|
|
|
panel = document.createElement("div");
|
|
panel.id = "avia-local-plugins-panel";
|
|
Object.assign(panel.style, {
|
|
position: "fixed",
|
|
bottom: "24px",
|
|
right: "560px",
|
|
width: "520px",
|
|
height: "460px",
|
|
background: "var(--md-sys-color-surface, #1e1e1e)",
|
|
color: "var(--md-sys-color-on-surface, #fff)",
|
|
borderRadius: "16px",
|
|
boxShadow: "0 8px 28px rgba(0,0,0,0.35)",
|
|
zIndex: "999999",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
overflow: "hidden",
|
|
border: "1px solid rgba(255,255,255,0.08)",
|
|
backdropFilter: "blur(12px)"
|
|
});
|
|
|
|
const header = document.createElement("div");
|
|
header.textContent = "Local Plugins";
|
|
Object.assign(header.style, {
|
|
padding: "14px 16px",
|
|
fontWeight: "600",
|
|
fontSize: "14px",
|
|
background: "var(--md-sys-color-surface-container, rgba(255,255,255,0.04))",
|
|
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
|
cursor: "move"
|
|
});
|
|
|
|
const closeBtn = document.createElement("div");
|
|
closeBtn.textContent = "✕";
|
|
Object.assign(closeBtn.style, {
|
|
position: "absolute",
|
|
top: "12px",
|
|
right: "16px",
|
|
cursor: "pointer",
|
|
opacity: "0.7"
|
|
});
|
|
closeBtn.onclick = () => panel.style.display = "none";
|
|
|
|
const controlsBar = document.createElement("div");
|
|
Object.assign(controlsBar.style, {
|
|
padding: "12px 16px",
|
|
display: "flex",
|
|
gap: "8px",
|
|
alignItems: "center",
|
|
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
|
flex: "0 0 auto"
|
|
});
|
|
|
|
const nameInput = document.createElement("input");
|
|
nameInput.placeholder = "Plugin name";
|
|
styleLocalInput(nameInput);
|
|
nameInput.style.flex = "1";
|
|
|
|
const addBtn = document.createElement("button");
|
|
addBtn.textContent = "+ New";
|
|
styleLocalBtn(addBtn);
|
|
addBtn.onclick = () => {
|
|
const name = nameInput.value.trim();
|
|
if (!name) return;
|
|
const plugins = getLocalPlugins();
|
|
const newPlugin = {
|
|
id: "local_" + Date.now(),
|
|
name,
|
|
code: "// " + name + "\n",
|
|
enabled: false
|
|
};
|
|
plugins.push(newPlugin);
|
|
setLocalPlugins(plugins);
|
|
nameInput.value = "";
|
|
renderLocalPanel();
|
|
};
|
|
|
|
const importBtn = document.createElement("button");
|
|
importBtn.textContent = "Import";
|
|
styleLocalBtn(importBtn, "#2d6a4f");
|
|
importBtn.onmouseenter = () => importBtn.style.opacity = "0.75";
|
|
importBtn.onmouseleave = () => importBtn.style.opacity = "1";
|
|
|
|
const fileInput = document.createElement("input");
|
|
fileInput.type = "file";
|
|
fileInput.accept = ".js";
|
|
fileInput.multiple = true;
|
|
fileInput.style.display = "none";
|
|
|
|
importBtn.onclick = () => fileInput.click();
|
|
|
|
fileInput.onchange = async () => {
|
|
const files = [...fileInput.files];
|
|
if (!files.length) return;
|
|
|
|
const plugins = getLocalPlugins();
|
|
|
|
for (const file of files) {
|
|
const text = await file.text();
|
|
const name = file.name.replace(/\.js$/i, "");
|
|
plugins.push({
|
|
id: "local_" + Date.now() + "_" + Math.random(),
|
|
name,
|
|
code: text,
|
|
enabled: false
|
|
});
|
|
}
|
|
|
|
setLocalPlugins(plugins);
|
|
fileInput.value = "";
|
|
renderLocalPanel();
|
|
};
|
|
|
|
controlsBar.appendChild(nameInput);
|
|
controlsBar.appendChild(addBtn);
|
|
controlsBar.appendChild(importBtn);
|
|
controlsBar.appendChild(fileInput);
|
|
|
|
const content = document.createElement("div");
|
|
content.id = "avia-local-plugins-content";
|
|
Object.assign(content.style, {
|
|
flex: "1",
|
|
overflow: "auto",
|
|
padding: "16px"
|
|
});
|
|
|
|
panel.appendChild(header);
|
|
panel.appendChild(closeBtn);
|
|
panel.appendChild(controlsBar);
|
|
panel.appendChild(content);
|
|
document.body.appendChild(panel);
|
|
|
|
const dropOverlay = document.createElement("div");
|
|
dropOverlay.textContent = "Import JS files";
|
|
Object.assign(dropOverlay.style, {
|
|
position: "absolute",
|
|
inset: "0",
|
|
background: "rgba(0,0,0,0.6)",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
fontSize: "18px",
|
|
fontWeight: "600",
|
|
color: "#fff",
|
|
opacity: "0",
|
|
pointerEvents: "none",
|
|
transition: "opacity 0.15s ease",
|
|
borderRadius: "16px"
|
|
});
|
|
panel.appendChild(dropOverlay);
|
|
|
|
let dragDepth = 0;
|
|
|
|
panel.addEventListener("dragenter", e => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
dragDepth++;
|
|
dropOverlay.style.opacity = "1";
|
|
panel.style.border = "1px dashed rgba(255,255,255,0.4)";
|
|
});
|
|
|
|
panel.addEventListener("dragover", e => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
});
|
|
|
|
panel.addEventListener("dragleave", e => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
dragDepth--;
|
|
if (dragDepth <= 0) {
|
|
dropOverlay.style.opacity = "0";
|
|
panel.style.border = "1px solid rgba(255,255,255,0.08)";
|
|
dragDepth = 0;
|
|
}
|
|
});
|
|
|
|
panel.addEventListener("drop", async e => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
dropOverlay.style.opacity = "0";
|
|
panel.style.border = "1px solid rgba(255,255,255,0.08)";
|
|
dragDepth = 0;
|
|
|
|
const files = [...e.dataTransfer.files].filter(f => f.name.endsWith(".js"));
|
|
if (!files.length) return;
|
|
|
|
const plugins = getLocalPlugins();
|
|
|
|
for (const file of files) {
|
|
const text = await file.text();
|
|
const name = file.name.replace(/\.js$/i, "");
|
|
plugins.push({
|
|
id: "local_" + Date.now() + "_" + Math.random(),
|
|
name,
|
|
code: text,
|
|
enabled: false
|
|
});
|
|
}
|
|
|
|
setLocalPlugins(plugins);
|
|
renderLocalPanel();
|
|
});
|
|
|
|
let isDragging = false, offsetX, offsetY;
|
|
header.addEventListener("mousedown", e => {
|
|
isDragging = true;
|
|
offsetX = e.clientX - panel.offsetLeft;
|
|
offsetY = e.clientY - panel.offsetTop;
|
|
});
|
|
document.addEventListener("mouseup", () => isDragging = false);
|
|
document.addEventListener("mousemove", e => {
|
|
if (!isDragging) return;
|
|
panel.style.left = (e.clientX - offsetX) + "px";
|
|
panel.style.top = (e.clientY - offsetY) + "px";
|
|
panel.style.right = "auto";
|
|
panel.style.bottom = "auto";
|
|
});
|
|
|
|
renderLocalPanel();
|
|
}
|
|
|
|
function renderLocalPanel() {
|
|
const content = document.getElementById("avia-local-plugins-content");
|
|
if (!content) return;
|
|
content.innerHTML = "";
|
|
const plugins = getLocalPlugins();
|
|
|
|
if (plugins.length === 0) {
|
|
const empty = document.createElement("div");
|
|
empty.textContent = "No local plugins yet. Add one above.";
|
|
empty.style.opacity = "0.4";
|
|
empty.style.fontSize = "13px";
|
|
content.appendChild(empty);
|
|
return;
|
|
}
|
|
|
|
plugins.forEach((plugin, index) => {
|
|
const isRunning = !!runningLocalPlugins[plugin.id];
|
|
const hasError = !!localPluginErrors[plugin.id];
|
|
const isBuiltin = !!plugin.locked || !!plugin.builtin;
|
|
|
|
const row = document.createElement("div");
|
|
Object.assign(row.style, {
|
|
display: "flex",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
marginBottom: "12px",
|
|
padding: "10px 12px",
|
|
borderRadius: "10px",
|
|
background: "rgba(255,255,255,0.04)",
|
|
border: "1px solid rgba(255,255,255,0.06)"
|
|
});
|
|
|
|
const left = document.createElement("div");
|
|
Object.assign(left.style, { display: "flex", alignItems: "center", gap: "10px" });
|
|
|
|
const statusDot = document.createElement("div");
|
|
Object.assign(statusDot.style, { width: "10px", height: "10px", borderRadius: "50%", flexShrink: "0" });
|
|
if (hasError) {
|
|
statusDot.style.background = "#ff4d4d";
|
|
statusDot.style.boxShadow = "0 0 6px #ff4d4d";
|
|
} else if (isRunning) {
|
|
statusDot.style.background = "#4dff88";
|
|
statusDot.style.boxShadow = "0 0 6px #4dff88";
|
|
} else {
|
|
statusDot.style.background = "#777";
|
|
}
|
|
|
|
const name = document.createElement("div");
|
|
name.textContent = plugin.name;
|
|
name.style.fontSize = "13px";
|
|
|
|
left.appendChild(statusDot);
|
|
left.appendChild(name);
|
|
|
|
if (isBuiltin) {
|
|
const badge = document.createElement("div");
|
|
badge.textContent = "Built-in";
|
|
Object.assign(badge.style, {
|
|
fontSize: "10px",
|
|
padding: "2px 7px",
|
|
borderRadius: "999px",
|
|
background: "rgba(120,170,255,0.16)",
|
|
color: "#a9c4ff",
|
|
border: "1px solid rgba(120,170,255,0.28)",
|
|
marginLeft: "2px",
|
|
textTransform: "uppercase",
|
|
letterSpacing: "0.06em",
|
|
});
|
|
left.appendChild(badge);
|
|
}
|
|
|
|
const controls = document.createElement("div");
|
|
Object.assign(controls.style, { display: "flex", gap: "6px" });
|
|
|
|
if (!isBuiltin) {
|
|
const editBtn = document.createElement("button");
|
|
editBtn.textContent = "✏ Edit";
|
|
styleLocalBtn(editBtn, "rgba(100,140,255,0.2)");
|
|
editBtn.onclick = () => {
|
|
openEditorPanel(plugin, (newCode, andRun) => {
|
|
const all = getLocalPlugins();
|
|
const target = all.find(p => p.id === plugin.id);
|
|
if (target) {
|
|
target.code = newCode;
|
|
plugin.code = newCode;
|
|
setLocalPlugins(all);
|
|
}
|
|
if (andRun) {
|
|
plugin.enabled = true;
|
|
if (target) target.enabled = true;
|
|
setLocalPlugins(getLocalPlugins().map(p => p.id === plugin.id ? { ...p, code: newCode, enabled: true } : p));
|
|
runLocalPlugin(plugin);
|
|
}
|
|
renderLocalPanel();
|
|
});
|
|
};
|
|
|
|
const toggleBtn = document.createElement("button");
|
|
toggleBtn.textContent = plugin.enabled ? "Disable" : "Enable";
|
|
styleLocalBtn(toggleBtn);
|
|
toggleBtn.onclick = () => {
|
|
const all = getLocalPlugins();
|
|
const target = all.find(p => p.id === plugin.id);
|
|
if (!target) return;
|
|
target.enabled = !target.enabled;
|
|
plugin.enabled = target.enabled;
|
|
setLocalPlugins(all);
|
|
if (target.enabled) runLocalPlugin(plugin);
|
|
else stopLocalPlugin(plugin);
|
|
renderLocalPanel();
|
|
};
|
|
|
|
const removeBtn = document.createElement("button");
|
|
removeBtn.textContent = "✕";
|
|
styleLocalBtn(removeBtn, "rgba(255,80,80,0.15)");
|
|
removeBtn.onclick = () => {
|
|
stopLocalPlugin(plugin);
|
|
const editorPanel = document.getElementById("avia-local-editor-panel");
|
|
if (editorPanel) editorPanel.remove();
|
|
const all = getLocalPlugins();
|
|
all.splice(all.findIndex(p => p.id === plugin.id), 1);
|
|
setLocalPlugins(all);
|
|
renderLocalPanel();
|
|
};
|
|
|
|
controls.appendChild(editBtn);
|
|
controls.appendChild(toggleBtn);
|
|
controls.appendChild(removeBtn);
|
|
}
|
|
row.appendChild(left);
|
|
row.appendChild(controls);
|
|
content.appendChild(row);
|
|
});
|
|
}
|
|
|
|
function styleLocalInput(input) {
|
|
Object.assign(input.style, {
|
|
padding: "6px 8px",
|
|
borderRadius: "8px",
|
|
border: "1px solid rgba(255,255,255,0.1)",
|
|
background: "rgba(255,255,255,0.05)",
|
|
color: "#fff",
|
|
fontSize: "13px"
|
|
});
|
|
}
|
|
|
|
function styleLocalBtn(btn, bg) {
|
|
Object.assign(btn.style, {
|
|
padding: "5px 12px",
|
|
borderRadius: "8px",
|
|
border: "none",
|
|
background: bg || "rgba(255,255,255,0.08)",
|
|
color: "#fff",
|
|
cursor: "pointer",
|
|
fontSize: "12px",
|
|
whiteSpace: "nowrap"
|
|
});
|
|
btn.onmouseenter = () => btn.style.opacity = "0.75";
|
|
btn.onmouseleave = () => btn.style.opacity = "1";
|
|
}
|
|
|
|
function injectLocalButton() {
|
|
if (document.getElementById("avia-local-plugins-btn")) return;
|
|
const appearanceBtn = [...document.querySelectorAll("a")]
|
|
.find(a => a.textContent.trim() === "Appearance");
|
|
if (!appearanceBtn) return;
|
|
|
|
const aviaPluginsBtn = document.getElementById("stoat-fake-plugins");
|
|
if (!aviaPluginsBtn) return;
|
|
|
|
const localBtn = appearanceBtn.cloneNode(true);
|
|
localBtn.id = "avia-local-plugins-btn";
|
|
|
|
const textNode = [...localBtn.querySelectorAll("div")]
|
|
.find(d => d.children.length === 0 && d.textContent.trim() === "Appearance");
|
|
if (textNode) textNode.textContent = "(Sanctum) Local Plugins";
|
|
|
|
const oldSvg = localBtn.querySelector("svg");
|
|
if (oldSvg) oldSvg.remove();
|
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
const svg = document.createElementNS(svgNS, "svg");
|
|
svg.setAttribute("viewBox", "0 0 24 24");
|
|
svg.setAttribute("width", "20");
|
|
svg.setAttribute("height", "20");
|
|
svg.setAttribute("fill", "currentColor");
|
|
svg.style.marginRight = "8px";
|
|
const path = document.createElementNS(svgNS, "path");
|
|
|
|
path.setAttribute("d", "M20.5 11H19V7a2 2 0 00-2-2h-4V3.5a2.5 2.5 0 00-5 0V5H4a2 2 0 00-2 2v3.8h1.5c1.5 0 2.7 1.2 2.7 2.7S5 16.2 3.5 16.2H2V20a2 2 0 002 2h3.8v-1.5c0-1.5 1.2-2.7 2.7-2.7s2.7 1.2 2.7 2.7V22H17a2 2 0 002-2v-4h1.5a2.5 2.5 0 000-5z");
|
|
svg.appendChild(path);
|
|
localBtn.insertBefore(svg, localBtn.firstChild);
|
|
|
|
localBtn.addEventListener("click", toggleLocalPanel);
|
|
aviaPluginsBtn.parentElement.insertBefore(localBtn, aviaPluginsBtn.nextSibling);
|
|
}
|
|
|
|
function waitForBody(callback) {
|
|
if (document.body) callback();
|
|
else new MutationObserver((obs) => {
|
|
if (document.body) { obs.disconnect(); callback(); }
|
|
}).observe(document.documentElement, { childList: true });
|
|
}
|
|
|
|
waitForBody(() => {
|
|
const observer = new MutationObserver(() => injectLocalButton());
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
injectLocalButton();
|
|
});
|
|
|
|
upsertBuiltinLocalPlugins();
|
|
getLocalPlugins().forEach(plugin => {
|
|
if (plugin.enabled) runLocalPlugin(plugin);
|
|
});
|
|
|
|
preloadMonaco();
|
|
|
|
})();
|