Download static/embed.js from vomebook/Hitokoto: direct link, hf CLI and curl.
- Browser
- Download file 5.52 kB
-
https://huggingface.co/spaces/vomebook/Hitokoto/resolve/main/static/embed.js
- Command line
-
hf download hf://spaces/vomebook/Hitokoto/static/embed.js
-
curl -L -o embed.js https://huggingface.co/spaces/vomebook/Hitokoto/resolve/main/static/embed.js
5.52 kB
| ; | |
| // Explicit CCRD pilot bridge. No URL text import, automatic synthesis or wildcard | |
| // postMessage. Parent identity and exact iframe Window are both mandatory. | |
| (() => { | |
| if (!embeddedReader) return; | |
| const PARENT = "https://vomebook-search.hf.space"; | |
| let channel = "", imported = null, replacement = null, modal = false; | |
| const title = document.createElement("p"); | |
| title.className = "note embed-source"; | |
| document.querySelector("main").prepend(title); | |
| const confirmation = document.createElement("div"); | |
| confirmation.className = "actions"; | |
| confirmation.hidden = true; | |
| const replace = document.createElement("button"), keep = document.createElement("button"); | |
| replace.type = keep.type = "button"; | |
| replace.id = "embed-replace"; keep.id = "embed-keep"; | |
| replace.textContent = "停止原朗读并替换文字"; keep.textContent = "保留原朗读"; | |
| confirmation.append(replace, keep); | |
| title.after(confirmation); | |
| function send(type, extra = {}) { | |
| if (channel) window.parent.postMessage({ type, channel, ...extra }, PARENT); | |
| } | |
| function validText(text, max, allowEmpty = false) { | |
| if (typeof text !== "string" || (!allowEmpty && !text.trim())) return false; | |
| let count = 0; | |
| for (const _ of text) if (++count > max) return false; | |
| return true; | |
| } | |
| function focusable() { | |
| return Array.from(document.querySelectorAll("button, a[href], input, select, textarea, audio[controls], [tabindex]:not([tabindex='-1'])")) | |
| .filter(node => !node.disabled && !node.hidden && node.getClientRects().length); | |
| } | |
| function load(data) { | |
| replacement = null; | |
| confirmation.hidden = true; | |
| stop({ message: "已载入 CCRD 正文;确认文字后点击合成才向语音 API 提交。" }); | |
| imported = { text: data.text, context_before: data.context_before, context_after: data.context_after }; | |
| bookmark = null; | |
| $("#remember").checked = false; | |
| $("#speech-text").value = data.text; | |
| $("#reader-view").hidden = true; | |
| $("#reader-source").replaceChildren(); | |
| $("#position").textContent = ""; | |
| $("#metrics").textContent = ""; | |
| $("#reading-progress").value = 0; | |
| title.textContent = "CCRD · " + data.title; | |
| controls(); | |
| send("ccrd-reader-loaded", { characters: Array.from(data.text).length }); | |
| } | |
| replace.addEventListener("click", () => { if (replacement) load(replacement); }); | |
| keep.addEventListener("click", () => { replacement = null; confirmation.hidden = true; send("ccrd-reader-rejected"); }); | |
| window.HitokotoEmbed = { | |
| context(plan, text) { | |
| if (!imported || text !== imported.text || !plan.length) return; | |
| plan[0].context_before = imported.context_before; | |
| plan[plan.length - 1].context_after = imported.context_after; | |
| }, | |
| }; | |
| window.addEventListener("message", (event) => { | |
| const data = event.data; | |
| if (event.origin !== PARENT || event.source !== window.parent || !data || | |
| typeof data.channel !== "string" || !/^[a-f0-9]{48}$/.test(data.channel)) return; | |
| if (data.type === "ccrd-reader-init" && data.version === 1) { | |
| if (channel && channel !== data.channel) return; | |
| channel = data.channel; | |
| modal = data.modal === true; | |
| send("ccrd-reader-ready", { version: 1, max_characters: ReaderPlan.maxText }); | |
| return; | |
| } | |
| if (!channel || data.channel !== channel) return; | |
| if (data.type === "ccrd-reader-import") { | |
| if (!validText(data.text, ReaderPlan.maxText) || !validText(data.title, 200, true) || | |
| !validText(data.context_before, 32, true) || !validText(data.context_after, 32, true)) { | |
| send("ccrd-reader-rejected"); return; | |
| } | |
| const payload = { text: data.text, title: data.title, context_before: data.context_before, context_after: data.context_after }; | |
| if (session) { | |
| // Native confirm() is restricted in cross-origin iframes in some | |
| // browsers. Keep this explicit user decision inside the visible UI. | |
| replacement = payload; | |
| confirmation.hidden = false; | |
| send("ccrd-reader-pending"); | |
| return; | |
| } | |
| load(payload); | |
| } else if (data.type === "ccrd-reader-theme" && typeof data.dark === "boolean") { | |
| document.documentElement.style.colorScheme = data.dark ? "dark" : "light"; | |
| } else if (data.type === "ccrd-reader-layout" && typeof data.modal === "boolean") { | |
| modal = data.modal; | |
| } else if (data.type === "ccrd-reader-focus" && modal && typeof data.last === "boolean") { | |
| const nodes = focusable(); | |
| (data.last ? nodes[nodes.length - 1] : nodes[0])?.focus(); | |
| } else if (data.type === "ccrd-reader-stop") { | |
| replacement = null; | |
| confirmation.hidden = true; | |
| stop({ message: "已停止,当前页面内可从进度继续" }); | |
| send("ccrd-reader-stopped"); | |
| } | |
| }); | |
| document.addEventListener("keydown", (event) => { | |
| if (event.key === "Escape" && event.target.tagName !== "SELECT") { | |
| event.preventDefault(); send("ccrd-reader-collapse"); | |
| } | |
| if (modal && event.key === "Tab") { | |
| const nodes = focusable(); | |
| if ((!event.shiftKey && event.target === nodes[nodes.length - 1]) || (event.shiftKey && event.target === nodes[0])) { | |
| event.preventDefault(); send("ccrd-reader-focus-out", { reverse: event.shiftKey }); | |
| } | |
| } | |
| }); | |
| $("#speech-text").value = ""; | |
| $("#remember").checked = false; | |
| title.textContent = "CCRD 朗读试点:请从搜索站载入选文或预览正文。仅在当前页面保留进度,不自动保存正文。"; | |
| })(); | |