yoloe_explorer / static /text_editor.js
elhamb's picture
Upload folder using huggingface_hub
6f1464a verified
Raw History Blame Contribute Delete
4.98 kB
"use strict";
(() => {
const editable = "h1,h2,h3,p,strong,summary,label,button,small,figcaption,svg text";
const protectedAreas = "#run,#matching-query-form input,#encoder-values,#encoder-phrase,#results,#bridge-product,#bridge-adjustment,#bridge-score,#matching-detail,#bridge-calculation,#bridge-prompt,#bridge-region,#sigmoid-chart,#scene-panel .scene-footer,#status,#filename,#runtime,#matching-query-status,.architecture-input,select,option";
let edits = {}, enabled = false, selected = null, selectedKey = null;
function keyFor(node) {
const parts = [];
for (let current = node; current && current !== document.body; current = current.parentElement) {
if (current.id) {parts.unshift(`#${CSS.escape(current.id)}`); break;}
const siblings = [...current.parentElement.children].filter(sibling => sibling.tagName === current.tagName);
parts.unshift(`${current.localName}:nth-of-type(${siblings.indexOf(current) + 1})`);
}
let context = "";
if (["view-title", "view-copy", "view-reading"].includes(node.id)) context = document.querySelector("[data-view][aria-selected='true']")?.dataset.view || "matching";
if (node.id.startsWith("architecture-description-")) context = document.querySelector("[data-architecture-component].described")?.dataset.componentName || "default";
return `${parts.join(" > ")}||${context}`;
}
function targets() {
return [...document.querySelectorAll(editable)].filter(node => !node.closest("#text-editor") && !node.closest(protectedAreas) && !node.querySelector("input,select,canvas,svg,img") && !node.querySelector(editable));
}
function applyEdits() {
for (const node of targets()) {
const key = keyFor(node);
if (Object.hasOwn(edits, key) && node.textContent !== edits[key]) node.textContent = edits[key];
}
}
// Saved copy also applies when the editing controls are disabled.
fetch("/api/text-edits").then(response => {
if (!response.ok) throw new Error("Could not load saved text.");
return response.json();
}).then(value => {edits = value; applyEdits();}).catch(error => {if (message) message.textContent = error.message;});
const observer = new MutationObserver(applyEdits);
observer.observe(document.querySelector("main"), {childList:true, subtree:true, characterData:true});
let message = null;
if (document.getElementById("text-editor-script").dataset.enabled !== "true") return;
const panel = document.createElement("aside");
panel.id = "text-editor";
panel.innerHTML = '<div class="editor-toolbar"><strong>Text editor (temporary)</strong><button type="button" id="edit-toggle">Edit text</button></div><div id="editor-fields" hidden><p>Click the text you want to change. Turn editing off to use the app.</p><textarea id="edit-value" aria-label="Replacement text" rows="5" maxlength="6000"></textarea><button type="button" id="edit-save" disabled>Save text</button><button type="button" id="edit-cancel">Cancel</button></div><p id="edit-message" role="status"></p>';
document.body.append(panel);
const toggle = document.getElementById("edit-toggle"), fields = document.getElementById("editor-fields"), input = document.getElementById("edit-value"), save = document.getElementById("edit-save");
message = document.getElementById("edit-message");
function clearSelection() {selected?.classList.remove("text-edit-selected"); selected = null; selectedKey = null; save.disabled = true; input.value = "";}
toggle.addEventListener("click", () => {
enabled = !enabled; fields.hidden = !enabled;
document.body.classList.toggle("editing-copy", enabled);
toggle.textContent = enabled ? "Done editing" : "Edit text";
if (!enabled) clearSelection();
});
document.addEventListener("click", event => {
if (!enabled || event.target.closest("#text-editor")) return;
const node = event.target.closest(editable);
if (!node || !targets().includes(node)) return;
event.preventDefault(); event.stopImmediatePropagation();
clearSelection(); selected = node; selectedKey = keyFor(node);
node.classList.add("text-edit-selected"); input.value = node.textContent;
save.disabled = false; input.focus(); message.textContent = "";
}, true);
document.getElementById("edit-cancel").addEventListener("click", clearSelection);
save.addEventListener("click", async () => {
if (!selectedKey) return;
const key = selectedKey, text = input.value;
save.disabled = true;
try {
const response = await fetch("/api/text-edits", {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({key, text})});
const data = await response.json();
if (!response.ok) throw new Error(data.error || "Text could not be saved.");
edits[key] = text; applyEdits(); message.textContent = "Saved to text_edits.json. Your text will stay after refresh.";
} catch (error) {message.textContent = error.message;}
finally {save.disabled = !selected;}
});
})();