vomebook commited on
Commit
3e4fad5
·
verified ·
1 Parent(s): 5667bfe

Fix Reader zoom and result layout

Browse files
Files changed (3) hide show
  1. static/reader.css +6 -5
  2. static/reader.js +6 -1
  3. static/style.css +23 -0
static/reader.css CHANGED
@@ -23,19 +23,20 @@ body { margin: 0; height: 100vh; height: 100dvh; overflow: hidden; display: flex
23
  .reader-viewport { min-height: 0; flex: 1; overflow: auto; background: #121416; }
24
  .loading-status { display: none; color: #aeb5ba; font-size: 13px; text-align: center; padding: 12px; }
25
  .loading-status[hidden] { display: none; }
26
- .reader-content { --reader-zoom: 1; width: min(calc(100% * var(--reader-zoom)), calc(1100px * var(--reader-zoom))); margin: 0 auto; padding: 20px; }
27
  .reader-page { position: relative; margin: 0 auto 18px; background: white; box-shadow: 0 2px 14px #0008; min-height: 160px; }
 
28
  .reader-page canvas { display: block; width: 100%; height: auto; }
29
  .reader-page canvas { position: relative; opacity: 0; transition: opacity 120ms ease; }
30
  .reader-page canvas.ready { opacity: 1; }
31
- .reader-image { display: block; max-width: 100%; height: auto; margin: 0 auto; background: #fff; }
32
- .reader-text { margin: 0 auto; max-width: 76ch; white-space: pre-wrap; overflow-wrap: anywhere; font: 17px/1.8 ui-monospace, monospace; color: #e7e9eb; }
33
- .reader-markdown { margin: 0 auto; max-width: 76ch; font-size: 17px; line-height: 1.75; }
34
  .reader-markdown img { max-width: 100%; }
35
  .reader-markdown a { color: #72a7df; }
36
  .reader-error { margin: 20vh auto; max-width: 560px; color: #d7dadd; text-align: center; line-height: 1.6; }
37
  .epub-frame { width: 100%; height: calc(100vh - 96px); background: white; }
38
  .docx-body { overflow-x: auto; color: #111; }
39
- .docx-body .reader-docx-wrapper { background: transparent; padding: 0; }
40
  .docx-body .reader-docx-wrapper > section.reader-docx { margin: 0 auto 18px; box-shadow: 0 2px 14px #0008; }
41
  @media (max-width: 600px) { .reader-toolbar { min-height: 36px; gap: 1px; padding: 2px 5px; } .reader-content { padding: 10px; } .reader-heading { display: none; } .reader-actions { flex: 1; justify-content: flex-end; gap: 1px; min-width: 0; } .reader-actions .control-group { gap: 0; } .reader-actions .icon-button { width: 26px; } .reader-actions .text-button { height: 30px; padding: 0 3px; } .loading-status { display: block; padding: 10px 6px 0; } .icon-button { width: 26px; height: 30px; flex: none; } .compact-input { height: 30px; padding-right: 3px; font-size: 11px; } .compact-input input { width: 24px; height: 28px; } .zoom-controls .compact-input input { width: 32px; } .history-panel { top: 36px; } }
 
23
  .reader-viewport { min-height: 0; flex: 1; overflow: auto; background: #121416; }
24
  .loading-status { display: none; color: #aeb5ba; font-size: 13px; text-align: center; padding: 12px; }
25
  .loading-status[hidden] { display: none; }
26
+ .reader-content { --reader-zoom: 1; width: min(100%, 1100px); margin: 0 auto; padding: 20px; }
27
  .reader-page { position: relative; margin: 0 auto 18px; background: white; box-shadow: 0 2px 14px #0008; min-height: 160px; }
28
+ .reader-content[data-mode="pdf"] .reader-page { width: calc(100% * var(--reader-zoom)); }
29
  .reader-page canvas { display: block; width: 100%; height: auto; }
30
  .reader-page canvas { position: relative; opacity: 0; transition: opacity 120ms ease; }
31
  .reader-page canvas.ready { opacity: 1; }
32
+ .reader-image { display: block; width: calc(100% * var(--reader-zoom)); max-width: none; height: auto; margin: 0 auto; background: #fff; }
33
+ .reader-text { margin: 0 auto; max-width: 76ch; white-space: pre-wrap; overflow-wrap: anywhere; font: calc(17px * var(--reader-zoom))/1.8 ui-monospace, monospace; color: #e7e9eb; }
34
+ .reader-markdown { margin: 0 auto; max-width: 76ch; font-size: calc(17px * var(--reader-zoom)); line-height: 1.75; }
35
  .reader-markdown img { max-width: 100%; }
36
  .reader-markdown a { color: #72a7df; }
37
  .reader-error { margin: 20vh auto; max-width: 560px; color: #d7dadd; text-align: center; line-height: 1.6; }
38
  .epub-frame { width: 100%; height: calc(100vh - 96px); background: white; }
39
  .docx-body { overflow-x: auto; color: #111; }
40
+ .docx-body .reader-docx-wrapper { background: transparent; padding: 0; zoom: var(--reader-zoom); }
41
  .docx-body .reader-docx-wrapper > section.reader-docx { margin: 0 auto 18px; box-shadow: 0 2px 14px #0008; }
42
  @media (max-width: 600px) { .reader-toolbar { min-height: 36px; gap: 1px; padding: 2px 5px; } .reader-content { padding: 10px; } .reader-heading { display: none; } .reader-actions { flex: 1; justify-content: flex-end; gap: 1px; min-width: 0; } .reader-actions .control-group { gap: 0; } .reader-actions .icon-button { width: 26px; } .reader-actions .text-button { height: 30px; padding: 0 3px; } .loading-status { display: block; padding: 10px 6px 0; } .icon-button { width: 26px; height: 30px; flex: none; } .compact-input { height: 30px; padding-right: 3px; font-size: 11px; } .compact-input input { width: 24px; height: 28px; } .zoom-controls .compact-input input { width: 32px; } .history-panel { top: 36px; } }
static/reader.js CHANGED
@@ -35,6 +35,7 @@ const downloadUrl = params.get("download") || sourceUrl;
35
  const extension = (params.get("ext") || "").toLowerCase();
36
  const capability = VoiceOfMLReader.capability(extension);
37
  const content = document.querySelector("#content");
 
38
  const status = document.querySelector("#status");
39
  const loadingStatus = document.querySelector("#loading-status");
40
  const title = params.get("title") || "在线阅读";
@@ -87,10 +88,13 @@ document.querySelector("#back").addEventListener("click", () => {
87
  });
88
  function setZoom(percent, persist = true) {
89
  const normalized = VoiceOfMLReader.clampNumber(percent, 50, 250, 100);
 
90
  zoom = normalized / 100;
91
  content.style.setProperty("--reader-zoom", String(zoom));
92
  zoomInput.value = String(normalized);
 
93
  if (pdfDocument) rerenderVisiblePdfPages();
 
94
  if (persist) scheduleSave();
95
  }
96
  for (const [id, delta] of [["#zoom-out", -10], ["#zoom-in", 10]]) {
@@ -222,7 +226,7 @@ async function renderPdfShell(shell, force = false, priority = false) {
222
  try {
223
  const page = await pdfDocument.getPage(Number(shell.dataset.page));
224
  const base = page.getViewport({ scale: 1 });
225
- const scale = Math.min(3, Math.max(0.5, content.clientWidth / base.width));
226
  const rendered = page.getViewport({ scale });
227
  const canvas = shell.querySelector("canvas");
228
  canvas.width = rendered.width; canvas.height = rendered.height;
@@ -377,6 +381,7 @@ async function renderEpub(prepared) {
377
  }
378
  async function displayEpub(url, frame) {
379
  const book = ePub(url); epubRendition = book.renderTo(frame, { width: "100%", height: "100%", spread: "none", flow: "scrolled-doc" });
 
380
  epubRendition.on("relocated", (location) => { epubLocation = location && location.start ? location.start.cfi : ""; scheduleSave(); });
381
  const restoredLocation = restoredEntry && restoredEntry.epubLocation;
382
  try { await epubRendition.display(restoredLocation || undefined); }
 
35
  const extension = (params.get("ext") || "").toLowerCase();
36
  const capability = VoiceOfMLReader.capability(extension);
37
  const content = document.querySelector("#content");
38
+ content.dataset.mode = capability.mode || "unsupported";
39
  const status = document.querySelector("#status");
40
  const loadingStatus = document.querySelector("#loading-status");
41
  const title = params.get("title") || "在线阅读";
 
88
  });
89
  function setZoom(percent, persist = true) {
90
  const normalized = VoiceOfMLReader.clampNumber(percent, 50, 250, 100);
91
+ const horizontalCenter = viewport.scrollWidth ? (viewport.scrollLeft + viewport.clientWidth / 2) / viewport.scrollWidth : 0;
92
  zoom = normalized / 100;
93
  content.style.setProperty("--reader-zoom", String(zoom));
94
  zoomInput.value = String(normalized);
95
+ if (epubRendition) epubRendition.themes.fontSize(`${normalized}%`);
96
  if (pdfDocument) rerenderVisiblePdfPages();
97
+ viewport.scrollLeft = horizontalCenter * viewport.scrollWidth - viewport.clientWidth / 2;
98
  if (persist) scheduleSave();
99
  }
100
  for (const [id, delta] of [["#zoom-out", -10], ["#zoom-in", 10]]) {
 
226
  try {
227
  const page = await pdfDocument.getPage(Number(shell.dataset.page));
228
  const base = page.getViewport({ scale: 1 });
229
+ const scale = Math.min(3, Math.max(0.5, shell.clientWidth / base.width));
230
  const rendered = page.getViewport({ scale });
231
  const canvas = shell.querySelector("canvas");
232
  canvas.width = rendered.width; canvas.height = rendered.height;
 
381
  }
382
  async function displayEpub(url, frame) {
383
  const book = ePub(url); epubRendition = book.renderTo(frame, { width: "100%", height: "100%", spread: "none", flow: "scrolled-doc" });
384
+ epubRendition.themes.fontSize(`${Math.round(zoom * 100)}%`);
385
  epubRendition.on("relocated", (location) => { epubLocation = location && location.start ? location.start.cfi : ""; scheduleSave(); });
386
  const restoredLocation = restoredEntry && restoredEntry.epubLocation;
387
  try { await epubRendition.display(restoredLocation || undefined); }
static/style.css CHANGED
@@ -944,19 +944,42 @@ body.mobile .multi-toggle { padding: 1px 4px; font-size: 11px; }
944
  align-items: flex-start;
945
  }
946
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
947
  .result-action-btn {
 
 
 
 
948
  font-size: 11px;
 
949
  padding: 3px 8px;
950
  border-radius: var(--radius-sm);
951
  color: var(--on-surface-variant);
952
  border: 1px solid var(--outline-variant);
953
  white-space: nowrap;
 
954
  transition: all var(--transition);
955
  }
956
 
957
  .result-action-btn:hover, .result-action-btn:focus-visible {
958
  background: var(--surface-variant);
959
  border-color: var(--outline);
 
960
  }
961
 
962
  .result-action-btn.primary {
 
944
  align-items: flex-start;
945
  }
946
 
947
+ body:not(.mobile) .result-item {
948
+ display: grid;
949
+ grid-template-columns: auto auto minmax(0, 1fr) auto;
950
+ grid-template-rows: auto auto auto;
951
+ column-gap: 10px;
952
+ row-gap: 3px;
953
+ }
954
+
955
+ body:not(.mobile) .result-checkbox { grid-column: 1; grid-row: 1 / 4; align-self: start; margin-top: 4px; }
956
+ body:not(.mobile) .result-file-icon { grid-column: 2; grid-row: 1 / 4; }
957
+ body:not(.mobile) .result-info { display: contents; }
958
+ body:not(.mobile) .result-title { grid-column: 3; grid-row: 1; min-width: 0; }
959
+ body:not(.mobile) .result-actions { grid-column: 4; grid-row: 1; justify-content: flex-end; }
960
+ body:not(.mobile) .result-path { grid-column: 3 / 5; grid-row: 2; margin-top: 0; min-width: 0; }
961
+ body:not(.mobile) .result-meta { grid-column: 3 / 5; grid-row: 3; margin-top: 0; min-width: 0; }
962
+
963
  .result-action-btn {
964
+ height: 24px;
965
+ display: inline-flex;
966
+ align-items: center;
967
+ justify-content: center;
968
  font-size: 11px;
969
+ line-height: 1;
970
  padding: 3px 8px;
971
  border-radius: var(--radius-sm);
972
  color: var(--on-surface-variant);
973
  border: 1px solid var(--outline-variant);
974
  white-space: nowrap;
975
+ text-decoration: none;
976
  transition: all var(--transition);
977
  }
978
 
979
  .result-action-btn:hover, .result-action-btn:focus-visible {
980
  background: var(--surface-variant);
981
  border-color: var(--outline);
982
+ text-decoration: none;
983
  }
984
 
985
  .result-action-btn.primary {