Spaces:
Sleeping
Sleeping
Download static/text_editor.js from elhamb/yoloe_explorer: direct link, hf CLI and curl.
- Browser
- Download file 4.98 kB
-
https://huggingface.co/spaces/elhamb/yoloe_explorer/resolve/main/static/text_editor.js
- Command line
-
hf download hf://spaces/elhamb/yoloe_explorer/static/text_editor.js
-
curl -L -o text_editor.js https://huggingface.co/spaces/elhamb/yoloe_explorer/resolve/main/static/text_editor.js
4.98 kB
| ; | |
| (() => { | |
| 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;} | |
| }); | |
| })(); | |