Search / static /reader.html
vomebook
feat: support additional Reader formats
1787788
Raw History Blame Contribute Delete
8.1 kB
<!doctype html>
<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 !important;
}
</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">&#8592;</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">&#8249;</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">&#8250;</button>
</span>
<span class="control-group zoom-controls">
<button id="zoom-out" class="icon-button" type="button">&#8722;</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"
>&#8681;</a
>
<span id="download-feedback" role="status" hidden></span>
</div>
<button id="history" class="icon-button" type="button">&#9776;</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">&times;</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>