mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
* Resume EPUBs and comic books where the reader left off The EPUB viewer (epub.html) accepts `?cfi=` and posts its location and progress to the page that embeds it on every page turn. Progress comes from the chapter and the page within it, rather than book.locations.generate(), which stalls a Raspberry Pi on a large book. A search result's `loc` still wins over the saved position. The comic viewer opens at the saved page and saves each page shown. Both resume on the doc page and in the file preview. The resume point is decided once per opened file, so saves made while reading do not change the EPUB viewer's URL and reload it. The doc page tracks only the FileGroup's primary file; another format beside it is not tracked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * EPUB/comic progress: never save a place the server would discard The server treats a position at the start (the cover, under 1%) as not started, and replaces the saved place with progress 0. Both viewers reported the place they opened on, so opening a finished book un-finished it, and peeking at the cover erased the reader's page. - The comic viewer does not save the page it opens on, nor the cover; a peek at the cover keeps the last page saved. - The EPUB viewer posts nothing at the start of the book. - Progress before the end is kept between 1% and 94% (resumableProgress), so early pages of a long book and the last pages before the end keep their position. Only the last page (comics) or the end (EPUB) is finished. - Non-finite EPUB progress is ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
54 lines
2.5 KiB
JavaScript
54 lines
2.5 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
/*
|
|
* The document page's embedded viewer, and which of them is withheld in a filtering theme.
|
|
*
|
|
* This is the surface that measured 55% non-red pixels in night mode: a PDF is drawn by the
|
|
* browser in its own out-of-process viewer, which the theme's SVG filter never reaches, so it
|
|
* arrives in full color however the iframe is styled. The EPUB viewer is a document of our own
|
|
* and tints correctly, so gating it would hide something for no reason.
|
|
*
|
|
* Read off the source. DocPage renders only after `useDoc` resolves a file group and reaches
|
|
* the embed through several branches of file selection; a render test here would assert more
|
|
* about the mocks than about the wiring. ui.test.js covers what MediaGate itself does, and
|
|
* FilePreview.test.js covers the same contract on the other embed surface.
|
|
*/
|
|
|
|
const source = fs.readFileSync(path.join(__dirname, 'Docs.js'), 'utf8');
|
|
|
|
describe('DocPage embed gating (source contract)', () => {
|
|
it('wraps the embedded viewer in a MediaGate', () => {
|
|
// The iframe must not be reachable without passing through the gate. The gate's only child is the
|
|
// viewer frame, and the frame is just the iframe.
|
|
const embed = source.match(/<MediaGate[^>]*>\s*<DocViewerFrame/);
|
|
expect(embed).not.toBeNull();
|
|
|
|
const frame = source.match(/function DocViewerFrame[\s\S]*?\n}\n/);
|
|
expect(frame).not.toBeNull();
|
|
expect(frame[0]).toMatch(/return <iframe/);
|
|
expect(source.match(/<iframe/g)).toHaveLength(1);
|
|
});
|
|
|
|
it('gates on the live filter state, not on the theme name', () => {
|
|
/*
|
|
* `mediaFilterEnabled` rather than `theme === 'night'`: a reader who turns night's
|
|
* filter off has said they do not want this, and amber with its filter on has the same
|
|
* unreachable PDF and does.
|
|
*/
|
|
const gate = source.match(/<MediaGate[^>]*gated=\{([^}]*)\}/);
|
|
|
|
expect(gate).not.toBeNull();
|
|
expect(gate[1]).toContain('mediaFilterEnabled');
|
|
expect(gate[1]).toContain('isPdf');
|
|
// The theme's NAME must not appear in the condition.
|
|
expect(gate[1]).not.toMatch(/theme\s*===/);
|
|
});
|
|
|
|
it('remounts the gate per document, so revealing one does not reveal the next', () => {
|
|
// Selecting a different file reuses the same fiber otherwise, carrying `revealed` over.
|
|
const gate = source.match(/<MediaGate([^>]*)>/);
|
|
|
|
expect(gate[1]).toMatch(/key=\{/);
|
|
});
|
|
});
|