vomebook commited on
Commit
63e5da3
·
1 Parent(s): c547bd8

Improve Reader status and startup loading

Browse files
Files changed (3) hide show
  1. static/index.html +1 -0
  2. static/reader.css +4 -0
  3. static/reader.js +2 -0
static/index.html CHANGED
@@ -16,6 +16,7 @@
16
  <link rel="icon" type="image/png" sizes="512x512" href="/icons/icon-512.png">
17
  <link rel="icon" type="image/svg+xml" href="/icons/icon.svg">
18
  <script>fetch("/api/ping", { cache: "no-store" }).catch(() => {});</script>
 
19
  <link rel="stylesheet" href="/static/style.css">
20
  </head>
21
  <body class="dark">
 
16
  <link rel="icon" type="image/png" sizes="512x512" href="/icons/icon-512.png">
17
  <link rel="icon" type="image/svg+xml" href="/icons/icon.svg">
18
  <script>fetch("/api/ping", { cache: "no-store" }).catch(() => {});</script>
19
+ <link rel="preload" href="/api/reader-assets" as="fetch" crossorigin="anonymous">
20
  <link rel="stylesheet" href="/static/style.css">
21
  </head>
22
  <body class="dark">
static/reader.css CHANGED
@@ -26,6 +26,10 @@ body { margin: 0; height: 100vh; height: 100dvh; overflow: hidden; display: flex
26
  .reader-panel-tabs button[aria-selected="true"] { color: #e7e9eb; border-bottom-color: #8ab4f8; }
27
  .reader-panel-view { min-height: 0; flex: 1; overflow: auto; }
28
  .reader-progress-tools { flex: none; padding: 10px 12px 12px; border-top: 1px solid #34383d; background: inherit; }
 
 
 
 
29
  .reader-progress-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto auto; align-items: center; gap: 7px; min-width: 0; }
30
  .reader-progress-row input[type="range"] { min-width: 0; flex: 1; accent-color: #8ab4f8; }
31
  .reader-progress-percent { min-width: 42px; color: #aeb5ba; font-size: 12px; text-align: right; font-variant-numeric: tabular-nums; }
 
26
  .reader-panel-tabs button[aria-selected="true"] { color: #e7e9eb; border-bottom-color: #8ab4f8; }
27
  .reader-panel-view { min-height: 0; flex: 1; overflow: auto; }
28
  .reader-progress-tools { flex: none; padding: 10px 12px 12px; border-top: 1px solid #34383d; background: inherit; }
29
+ .reader-progress-summary { display: grid; justify-items: center; gap: 4px; padding: 2px 0 10px; text-align: center; }
30
+ .reader-progress-summary strong { font-size: 14px; }
31
+ .reader-progress-summary-value { color: #8ab4f8; font-size: 20px; font-variant-numeric: tabular-nums; }
32
+ .reader-progress-bookmark { padding: 2px 0; font-size: 12px; }
33
  .reader-progress-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto auto; align-items: center; gap: 7px; min-width: 0; }
34
  .reader-progress-row input[type="range"] { min-width: 0; flex: 1; accent-color: #8ab4f8; }
35
  .reader-progress-percent { min-width: 42px; color: #aeb5ba; font-size: 12px; text-align: right; font-variant-numeric: tabular-nums; }
static/reader.js CHANGED
@@ -955,6 +955,7 @@ function prepareDocument() {
955
  return Promise.resolve(null);
956
  }
957
  const progressTools = document.createElement("div"); progressTools.className = "reader-progress-tools"; progressTools.innerHTML = '<div class="reader-progress-row"><button class="reader-chapter-prev" type="button" aria-label="上一章" title="上一章"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m14 5-7 7 7 7"/></svg></button><input class="reader-progress-range" type="range" min="0" max="100" step="0.1" value="0" aria-label="阅读进度"><span class="reader-progress-percent">0.0%</span><button class="reader-progress-undo" type="button" aria-label="撤销进度调整" title="撤销进度调整"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7.2 8.3A6.5 6.5 0 1 1 6 16"/><path d="m7.2 8.3 3-.2"/><circle cx="12" cy="12" r="1.5" fill="currentColor" stroke="none"/></svg></button><button class="reader-chapter-next" type="button" aria-label="下一章" title="下一章"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m10 5 7 7-7 7"/></svg></button></div>'; document.querySelector("#history-panel").appendChild(progressTools);
 
958
  setInterval(() => { const tab = document.querySelector("#toc-tab"); if (tab && !tocEntries.length) { tab.hidden = false; tab.textContent = "阅读"; } }, 500);
959
  const progressRange = progressTools.querySelector(".reader-progress-range"), progressPercent = progressTools.querySelector(".reader-progress-percent"), progressUndo = progressTools.querySelector(".reader-progress-undo"), chapterPrev = progressTools.querySelector(".reader-chapter-prev"), chapterNext = progressTools.querySelector(".reader-chapter-next"); let previousProgressState = null, progressSeeking = false, epubSeekFrame = 0, epubSeekTarget = 0, epubSeekPromise = Promise.resolve();
960
  function updateProgressTools() { if (progressSeeking) return; const percent = readerProgressPercent(); progressRange.value = String(percent); progressPercent.textContent = `${percent.toFixed(1)}%`; const hasChapters = tocEntries.length > 0, hasPreviousChapter = hasChapters && currentChapterIndex > 0, hasNextChapter = hasChapters && currentChapterIndex >= 0 && currentChapterIndex < tocEntries.length - 1; chapterPrev.hidden = !hasChapters; chapterNext.hidden = !hasChapters; chapterPrev.disabled = !hasPreviousChapter; chapterNext.disabled = !hasNextChapter; progressUndo.hidden = !previousProgressState; }
@@ -970,6 +971,7 @@ fullSearchView.id = "full-search-view"; fullSearchView.className = "reader-panel
970
  fullSearchView.innerHTML = '<div class="full-search-bar"><input id="full-search-input" class="full-search-input" type="search" placeholder="搜索正文" aria-label="搜索正文"><button id="full-search-clear" class="icon-button" type="button" aria-label="清除全文搜索" title="清除">×</button></div><div id="full-search-status" class="full-search-status" role="status">输入关键词搜索正文</div><div class="full-search-nav"><button id="full-search-prev" type="button" disabled>上一个</button><button id="full-search-next" type="button" disabled>下一个</button></div><div id="full-search-results" class="full-search-results"></div>';
971
  document.querySelector("#history-panel").insertBefore(fullSearchView, document.querySelector(".reader-panel-tabs"));
972
  const fullSearchButton = document.createElement("button"); fullSearchButton.id = "full-search-toggle"; fullSearchButton.className = "text-action"; fullSearchButton.type = "button"; fullSearchButton.textContent = "全文搜索"; fullSearchButton.setAttribute("aria-label", "全文搜索"); document.querySelector("#history-panel > header").insertBefore(fullSearchButton, readerThemeToggle);
 
973
  let fullSearchResults = [], fullSearchIndex = -1, fullSearchGeneration = 0;
974
  const fullSearchInput = fullSearchView.querySelector("#full-search-input"), fullSearchStatus = fullSearchView.querySelector("#full-search-status"), fullSearchResultsNode = fullSearchView.querySelector("#full-search-results");
975
  function fullSearchEscape(value) { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }
 
955
  return Promise.resolve(null);
956
  }
957
  const progressTools = document.createElement("div"); progressTools.className = "reader-progress-tools"; progressTools.innerHTML = '<div class="reader-progress-row"><button class="reader-chapter-prev" type="button" aria-label="上一章" title="上一章"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m14 5-7 7 7 7"/></svg></button><input class="reader-progress-range" type="range" min="0" max="100" step="0.1" value="0" aria-label="阅读进度"><span class="reader-progress-percent">0.0%</span><button class="reader-progress-undo" type="button" aria-label="撤销进度调整" title="撤销进度调整"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7.2 8.3A6.5 6.5 0 1 1 6 16"/><path d="m7.2 8.3 3-.2"/><circle cx="12" cy="12" r="1.5" fill="currentColor" stroke="none"/></svg></button><button class="reader-chapter-next" type="button" aria-label="下一章" title="下一章"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m10 5 7 7-7 7"/></svg></button></div>'; document.querySelector("#history-panel").appendChild(progressTools);
958
+ const readingProgressSummary = document.createElement("div"); readingProgressSummary.className = "reader-progress-summary"; readingProgressSummary.innerHTML = '<strong>阅读状态</strong><span class="reader-progress-summary-value">0.0%</span><button class="reader-progress-bookmark text-action" type="button">在当前位置添加书签</button>'; progressTools.prepend(readingProgressSummary); readingProgressSummary.querySelector(".reader-progress-bookmark").addEventListener("click", () => bookmarkRibbon.click()); function updateReadingProgressSummary() { const noToc = !tocEntries.length && !mediaElement; readingProgressSummary.hidden = !noToc; if (noToc) readingProgressSummary.querySelector(".reader-progress-summary-value").textContent = `${readerProgressPercent().toFixed(1)}%`; } setInterval(updateReadingProgressSummary, 250); updateReadingProgressSummary();
959
  setInterval(() => { const tab = document.querySelector("#toc-tab"); if (tab && !tocEntries.length) { tab.hidden = false; tab.textContent = "阅读"; } }, 500);
960
  const progressRange = progressTools.querySelector(".reader-progress-range"), progressPercent = progressTools.querySelector(".reader-progress-percent"), progressUndo = progressTools.querySelector(".reader-progress-undo"), chapterPrev = progressTools.querySelector(".reader-chapter-prev"), chapterNext = progressTools.querySelector(".reader-chapter-next"); let previousProgressState = null, progressSeeking = false, epubSeekFrame = 0, epubSeekTarget = 0, epubSeekPromise = Promise.resolve();
961
  function updateProgressTools() { if (progressSeeking) return; const percent = readerProgressPercent(); progressRange.value = String(percent); progressPercent.textContent = `${percent.toFixed(1)}%`; const hasChapters = tocEntries.length > 0, hasPreviousChapter = hasChapters && currentChapterIndex > 0, hasNextChapter = hasChapters && currentChapterIndex >= 0 && currentChapterIndex < tocEntries.length - 1; chapterPrev.hidden = !hasChapters; chapterNext.hidden = !hasChapters; chapterPrev.disabled = !hasPreviousChapter; chapterNext.disabled = !hasNextChapter; progressUndo.hidden = !previousProgressState; }
 
971
  fullSearchView.innerHTML = '<div class="full-search-bar"><input id="full-search-input" class="full-search-input" type="search" placeholder="搜索正文" aria-label="搜索正文"><button id="full-search-clear" class="icon-button" type="button" aria-label="清除全文搜索" title="清除">×</button></div><div id="full-search-status" class="full-search-status" role="status">输入关键词搜索正文</div><div class="full-search-nav"><button id="full-search-prev" type="button" disabled>上一个</button><button id="full-search-next" type="button" disabled>下一个</button></div><div id="full-search-results" class="full-search-results"></div>';
972
  document.querySelector("#history-panel").insertBefore(fullSearchView, document.querySelector(".reader-panel-tabs"));
973
  const fullSearchButton = document.createElement("button"); fullSearchButton.id = "full-search-toggle"; fullSearchButton.className = "text-action"; fullSearchButton.type = "button"; fullSearchButton.textContent = "全文搜索"; fullSearchButton.setAttribute("aria-label", "全文搜索"); document.querySelector("#history-panel > header").insertBefore(fullSearchButton, readerThemeToggle);
974
+ fullSearchButton.hidden = !["pdf", "text", "markdown", "docx", "html", "epub"].includes(capability.mode);
975
  let fullSearchResults = [], fullSearchIndex = -1, fullSearchGeneration = 0;
976
  const fullSearchInput = fullSearchView.querySelector("#full-search-input"), fullSearchStatus = fullSearchView.querySelector("#full-search-status"), fullSearchResultsNode = fullSearchView.querySelector("#full-search-results");
977
  function fullSearchEscape(value) { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }