From 0d0c89da6a9090f269ba530dfa27557e35dba963 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:02:48 -0400 Subject: [PATCH] =?UTF-8?q?book(G1):=20Reader=20Mode=20=E2=80=94=20full-sc?= =?UTF-8?q?reen=20page-flip=20viewer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full-screen reader launched from the Pages overview. Walks enumerateBookPhysicalPages and renders each via the existing export-canvas helpers (the same path the PDF pipeline uses) at ~144 DPI; pages cache in a session Map for instant revisits. Affordances: - Header: ☰ TOC toggle, book title, ✕ Close. - TOC sidebar: chapter headings + designed-page labels with current-page highlight; click jumps. Collapsible (closed by default; auto-closes on mobile-width jumps). - Stage: current page centered in the dark viewport. Click the left half to go back, right half to advance. Hover-revealed arrows on each side as a backup affordance. - Footer: "N / total" counter, progress bar, keyboard-hint line. Keyboard nav: → / Space / PageDown advance; ← / PageUp back; Home / End jump to first / last; Esc exits. Handler is registered on open and removed on close so the editor's shortcuts stay live behind the modal. Prefetch: after a page renders, the next two pages render in the background (best-effort, errors silenced) so forward flips feel instant once the user gets going. Premium / encrypted handling: the underlying renderers already decrypt via _decryptBookSidecarEvent and substitute tokens via bookTokenContext, so encrypted-private books and chapter-token masters Just Work in Reader Mode too. Co-Authored-By: Claude Opus 4.7 --- src/index.html | 279 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) diff --git a/src/index.html b/src/index.html index 9e8cf5e..8dc1eb9 100644 --- a/src/index.html +++ b/src/index.html @@ -1663,6 +1663,75 @@ .bookVisibilityChip.public { color: #15803d; border-color: #86efac; } .bookVisibilityChip.private { color: #6b21a8; border-color: #c084fc; background: #faf5ff; } .bookVisibilityChip:disabled { opacity: .5; cursor: not-allowed; } + /* Reader Mode — full-screen book viewer launched from the + Pages overview. Mirrors Presenter Mode's pattern for decks + but with a TOC sidebar instead of a slide strip. */ + #bookReaderOverlay { + position: fixed; inset: 0; z-index: 9999; + background: #1a1a1a; color: #fafafa; + display: none; flex-direction: column; + font-family: system-ui, -apple-system, sans-serif; + } + #bookReaderOverlay.open { display: flex; } + .bookReaderHeader { + display: flex; align-items: center; gap: 12px; + padding: 10px 14px; border-bottom: 1px solid #2a2a2a; + background: #0f0f0f; + } + .bookReaderTitle { flex: 1; font-size: .95rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .bookReaderTocToggle, .bookReaderCloseBtn { + background: transparent; color: #fafafa; border: 1px solid #444; + padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: .82rem; + } + .bookReaderTocToggle:hover, .bookReaderCloseBtn:hover { background: #2a2a2a; } + .bookReaderBody { + flex: 1; display: flex; min-height: 0; position: relative; + } + .bookReaderToc { + width: 0; overflow: hidden; transition: width .18s ease; + background: #0f0f0f; border-right: 1px solid #2a2a2a; + } + .bookReaderToc.open { width: 240px; overflow-y: auto; } + .bookReaderTocList { padding: 6px 0; } + .bookReaderTocItem { + padding: 6px 14px; font-size: .82rem; color: #c0c0c0; + cursor: pointer; border-left: 3px solid transparent; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + } + .bookReaderTocItem:hover { background: #1a1a1a; color: #fff; } + .bookReaderTocItem.current { background: #1a1a1a; color: #fff; border-left-color: var(--accent, #f59e0b); } + .bookReaderTocItem.chapterHead { color: #fde68a; font-weight: 600; padding-top: 10px; } + .bookReaderStage { + flex: 1; display: flex; align-items: center; justify-content: center; + position: relative; min-width: 0; cursor: pointer; + user-select: none; + } + .bookReaderPage { + max-width: 92%; max-height: 90%; + box-shadow: 0 8px 40px rgba(0,0,0,.6); + background: #fff; + } + .bookReaderPageWrap { + display: flex; align-items: center; justify-content: center; + width: 100%; height: 100%; + } + .bookReaderArrow { + position: absolute; top: 50%; transform: translateY(-50%); + width: 44px; height: 60px; display: flex; align-items: center; justify-content: center; + background: rgba(0,0,0,.4); border: none; color: #fafafa; + font-size: 1.6rem; cursor: pointer; opacity: 0; transition: opacity .12s; + } + .bookReaderStage:hover .bookReaderArrow { opacity: 1; } + .bookReaderArrow.prev { left: 14px; } + .bookReaderArrow.next { right: 14px; } + .bookReaderArrow:disabled { opacity: 0; pointer-events: none; } + .bookReaderFooter { + display: flex; align-items: center; gap: 14px; + padding: 8px 16px; background: #0f0f0f; border-top: 1px solid #2a2a2a; + font-size: .78rem; color: #c0c0c0; + } + .bookReaderProgress { flex: 1; height: 4px; background: #2a2a2a; border-radius: 2px; overflow: hidden; } + .bookReaderProgressFill { height: 100%; background: var(--accent, #f59e0b); width: 0%; transition: width .18s; } .bookStarterPanel { grid-column: 1 / -1; padding: 18px 22px; border: 1px dashed var(--line); border-radius: 8px; @@ -4445,6 +4514,7 @@ + @@ -5343,6 +5413,29 @@ + +