Download static/reader.html from VoiceOfML/Search: direct link, hf CLI and curl.
- Browser
- Download file 8.1 kB
-
https://huggingface.co/spaces/VoiceOfML/Search/resolve/main/static/reader.html
- Command line
-
hf download hf://spaces/VoiceOfML/Search/static/reader.html
-
curl -L -o reader.html https://huggingface.co/spaces/VoiceOfML/Search/resolve/main/static/reader.html
8.1 kB
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <meta | |
| http-equiv="Content-Security-Policy" | |
| content="default-src 'self'; base-uri 'self' blob:; script-src 'self' https://unpkg.com 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline' blob:; img-src 'self' data: blob: https:; media-src 'self' https://voiceofml-search.hf.space https://hf-mirror.com https://huggingface.co https://*.hf.co https://*.huggingface.co; connect-src 'self' blob: https://unpkg.com https://huggingface.co https://*.hf.co https://*.huggingface.co https://voiceofml-search.hf.space https://hf-mirror.com; frame-src 'self' blob:;" | |
| /> | |
| <title>VoiceOfML Reader</title> | |
| <script> | |
| // Start source lookup before the Reader module graph loads; consume it once. | |
| (() => { | |
| const params = new URLSearchParams(location.search), id = params.get("id"); | |
| if (params.get("url") || !/^[a-z0-9]{13}$/.test(id || "")) return; | |
| try { | |
| if (JSON.parse(sessionStorage.getItem(`reader-resolve:${id}`) || "null")?.url) return; | |
| } catch (_) {} | |
| const controller = new AbortController(); | |
| const timer = setTimeout(() => controller.abort(), 120000); | |
| const dispose = () => { | |
| clearTimeout(timer); | |
| controller.abort(); | |
| removeEventListener("pagehide", onPageHide); | |
| }; | |
| const onPageHide = (event) => { if (!event.persisted) dispose(); }; | |
| addEventListener("pagehide", onPageHide); | |
| const promise = fetch(`/api/reader-resolve?id=${encodeURIComponent(id)}`, { | |
| signal: controller.signal | |
| }).then(async (response) => { | |
| const data = response.ok ? await response.json() : null; | |
| if (!response.ok) await response.body?.cancel(); | |
| return { ok: response.ok, status: response.status, data }; | |
| }).finally(() => { | |
| clearTimeout(timer); | |
| removeEventListener("pagehide", onPageHide); | |
| }); | |
| promise.catch(() => {}); | |
| window.__VOICE_READER_RESOLVE__ = { id, promise, dispose }; | |
| })(); | |
| </script> | |
| <link rel="modulepreload" href="/static/reader.js?reader-v3" /> | |
| <style> | |
| #loading-status { | |
| display: none ; | |
| } | |
| </style> | |
| <link rel="preconnect" href="https://huggingface.co" /> | |
| <link rel="preconnect" href="https://voiceofml-search.hf.space" /> | |
| <link rel="stylesheet" href="/static/reader.css" /> | |
| <script defer src="/static/reader-security.js"></script> | |
| <script defer src="/static/reader-resources.js"></script> | |
| <script defer src="/static/reader-contract.js"></script> | |
| <script defer src="/static/reader-store.js"></script> | |
| </head> | |
| <body> | |
| <header class="reader-toolbar"> | |
| <button id="back" class="icon-button" type="button">←</button> | |
| <div class="reader-heading"> | |
| <strong id="title">在线阅读</strong> | |
| <button id="reader-path" class="reader-path" type="button" hidden></button> | |
| <span id="status">正在准备...</span> | |
| </div> | |
| <div class="reader-actions"> | |
| <span class="control-group page-controls"> | |
| <button id="page-prev" class="icon-button" type="button">‹</button> | |
| <label class="compact-input" | |
| ><input id="page-number" type="number" min="1" value="1" /><span id="page-total" | |
| >/ -</span | |
| ></label | |
| > | |
| <button id="page-next" class="icon-button" type="button">›</button> | |
| </span> | |
| <span class="control-group zoom-controls"> | |
| <button id="zoom-out" class="icon-button" type="button">−</button> | |
| <label class="compact-input" | |
| ><input id="zoom" type="number" min="25" max="400" step="10" value="100" /><span | |
| >%</span | |
| ></label | |
| > | |
| <button id="zoom-in" class="icon-button" type="button">+</button> | |
| </span> | |
| <a | |
| id="download" | |
| class="icon-button" | |
| aria-label="下载原文件" | |
| title="下载原文件" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| >⇩</a | |
| > | |
| <span id="download-feedback" role="status" hidden></span> | |
| </div> | |
| <button id="history" class="icon-button" type="button">☰</button> | |
| </header> | |
| <main id="viewport" class="reader-viewport"> | |
| <button id="bookmark-ribbon" class="bookmark-ribbon" type="button"></button> | |
| <div id="bookmark-popover" hidden> | |
| <span id="bookmark-prompt"></span> | |
| <div> | |
| <button id="bookmark-add" type="button">添加书签</button | |
| ><button id="bookmark-cancel" type="button">取消</button> | |
| </div> | |
| </div> | |
| <div id="loading-status" role="status">正在加载原文件...</div> | |
| <div id="content" class="reader-content"></div> | |
| </main> | |
| <aside id="history-panel" class="reader-panel" hidden> | |
| <header> | |
| <strong>阅读选项</strong> | |
| <button id="full-search-toggle" class="text-action" type="button">全文搜索</button> | |
| <button id="theme-toggle" class="theme-toggle" type="button" aria-label="切换主题"></button> | |
| <button id="history-close" class="icon-button" type="button">×</button> | |
| </header> | |
| <nav class="reader-panel-tabs" role="tablist" aria-label="阅读面板"> | |
| <button | |
| id="toc-tab" | |
| role="tab" | |
| aria-controls="toc-panel" | |
| aria-selected="false" | |
| data-panel="toc" | |
| hidden | |
| > | |
| 目录 | |
| </button> | |
| <button | |
| id="bookmarks-tab" | |
| role="tab" | |
| aria-controls="bookmarks-panel" | |
| aria-selected="false" | |
| data-panel="bookmarks" | |
| > | |
| 书签 | |
| </button> | |
| <button | |
| id="history-tab" | |
| role="tab" | |
| aria-controls="history-view" | |
| aria-selected="false" | |
| data-panel="history" | |
| > | |
| 历史 | |
| </button> | |
| </nav> | |
| <section | |
| id="toc-panel" | |
| class="reader-panel-view" | |
| role="tabpanel" | |
| aria-labelledby="toc-tab" | |
| data-panel-view="toc" | |
| hidden | |
| > | |
| <div class="panel-view-header"><strong>目录</strong></div> | |
| <div id="toc-list" class="panel-list"></div> | |
| </section> | |
| <section | |
| id="bookmarks-panel" | |
| class="reader-panel-view" | |
| role="tabpanel" | |
| aria-labelledby="bookmarks-tab" | |
| data-panel-view="bookmarks" | |
| hidden | |
| > | |
| <div class="panel-view-header"><strong>书签</strong></div> | |
| <div id="bookmarks-list" class="panel-list"></div> | |
| </section> | |
| <section | |
| id="history-view" | |
| class="reader-panel-view" | |
| role="tabpanel" | |
| aria-labelledby="history-tab" | |
| data-panel-view="history" | |
| hidden | |
| > | |
| <div class="panel-view-header"> | |
| <strong>阅读历史</strong | |
| ><span | |
| ><button id="history-clear" class="text-action" type="button">清空历史</button></span | |
| > | |
| </div> | |
| <div id="history-list" class="panel-list"></div> | |
| </section> | |
| </aside> | |
| <script> | |
| for (const panel of document.querySelectorAll(".reader-panel-view")) { | |
| const header = panel.querySelector(".panel-view-header"); | |
| const actions = | |
| panel.id === "history-view" ? header.querySelector("span") || header : header; | |
| const button = document.createElement("button"); | |
| button.className = "panel-search-toggle"; | |
| actions.appendChild(button); | |
| const input = document.createElement("input"); | |
| input.className = "panel-search"; | |
| input.setAttribute("aria-label", `搜索${header.querySelector("strong").textContent}`); | |
| input.hidden = true; | |
| panel.insertBefore(input, panel.querySelector(".panel-list")); | |
| } | |
| </script> | |
| <script type="module" src="/static/reader.js?reader-v3"></script> | |
| </body> | |
| </html> | |