All notable changes to this project will be documented in this file. See Conventional Commits for commit guidelines.
30.2.0 (2026-10-20)
- core: give a spine item's loaded document (689e50d)
- core: end a spine item's loaded document when it is destroyed (269edce)
- core: resolve a cfi only into its item's loaded document (9968556), closes #499
30.1.2 (2026-10-20)
- enhancer-search: fold case outside the BMP when matching (571a4a1)
- enhancer-search: match the search text literally (cc19223)
30.1.1 (2026-10-19)
Note: Version bump only for package @prose-reader/root
30.1.0 (2026-10-19)
30.0.0 (2026-10-19)
- enhancer-koreader:
reader.koreader.cfiToXPointer(cfi)is replaced byreader.koreader.cfiToProgress(cfi), which returns the same xpointer for an EPUB and a page number, such as "12", for a PDF or a comic archive.reader.navigation.goToXPointer(xpointer)is replaced byreader.navigation.goToKoreaderProgress(progress), and the{ type: "xpointer" }navigation target andXPointerNavigationTargetby{ type: "koreaderProgress" }andKoreaderProgressNavigationTarget. The form follows the manifest'smediaType: a book of unknown type,application/octet-stream, such as one opened from an archive with nofilename, noencodingFormatand noMETA-INF/container.xml, has no progress, nor has a plain ZIP the app does not give a comic's media type.
- enhancer-koreader: give the KOReader progress of any book (4c10a15), references #535
- koreader: tell the form of a book's progress (3c169c5)
- front: center the landing footer on a phone (f76f438)
- front: set the landing footer's name and license on one baseline (4b01cd2)
29.0.0 (2026-10-19)
- archive-reader:
getArchiveOpfInfo(archive), which returned the first.opfrecord as{ data, basePath }, is replaced by the asynchronousgetArchiveOpfRecord(archive), which returns the record of the package document the container file lists.readArchiveOpf(archive)still gives the parsed package document with itsbasePath.
- front: renovate the landing page, as a page of its own (afd7827)
- archive-reader: leave out a container file's elements of other namespaces (ddd22c4)
- archive-reader: read the package document the container file lists (1c7145a)
- front: center the mark on the name in the landing header and footer (8e477d9)
- front: draw the landing wordmark from the brand logo (323f278)
- front: show the reader setup without getResource on the landing page (2829e49)
28.2.0 (2026-10-19)
- streamer: make the archive key optional (e8fad96)
28.1.0 (2026-10-19)
- archive-reader: export the resolution of a book's references (6526b47)
- draw the prose mark, and show it in the readme (e372fc7)
- front: serve the prose mark as the favicon (0cd1d58)
- center the mark on the name in the wordmarks (dcfb409)
- streamer: find a resource's declared media type by its record (01ac289)
- streamer: rebase an EPUB manifest item as its spine item (dc51a27)
28.0.0 (2026-10-19)
- archive-reader:
getSpineItemFilesFromArchiveis removed from@prose-reader/archive-reader. Resolve the reading order withresolveArchiveReadingOrder(archive)and find each item's record withgetArchiveFileRecordByUri(archive, item.uri).
- archive-reader: find the nav document and the NCX by their record (cbb391b)
- archive-reader: keep a reference's query and fragment out of its path (939001f)
- archive-reader: resolve escaped dot segments as a browser does (07b9cd6)
- archive-reader: resolve the dot segments of toc links (7dbdae7)
- archive-reader: remove getSpineItemFilesFromArchive (a21d1fe)
27.0.0 (2026-10-19)
- streamer:
Manifest.spineItems[].containerUriis required. A manifest built by hand states the uri of the archive record each spine item shows. Manifests generated by the streamer carry it. A manifest saved before this release lacks it: generate it again.
- archive-reader: resolve an escaped href to the record it names (36886cf)
- enhancer-pdf: render the page each spine item names, past the tenth (8008a98)
- streamer: name each audiobook track after its own archive record (c3bfbd4)
26.0.0 (2026-10-19)
- streamer:
Manifest.mediaTypeis required. A manifest built by hand states the media type of the book it stands for,application/octet-streamwhen it is none. Manifests generated by the streamer carry it. A manifest saved before this release lacks it: generate it again.
- archive-reader: resolve the media type of the book file (cfd4a08)
- streamer: carry the book's media type in the manifest (a3aa009)
25.1.0 (2026-10-10)
- koreader: compute the digests KOReader files reading positions under (c550d66)
- shared: move the md5 out of @prose-reader/koreader (a7f37ee)
- start every watcher in start and start:lib (e527d17)
25.0.1 (2026-10-10)
- declare the MIT license in every published package (fefd424)
25.0.0 (2026-10-10)
- gestures: @prose-reader/enhancer-gestures requires gesturx 2,
and Node >=22.12 when loaded on Node. Apps on gesturx 1 upgrade it
alongside; its API is unchanged.
importand bundler builds are unaffected.
- gestures: require gesturx 2 (4697012)
24.0.0 (2026-09-27)
- core:
DocumentRendererno longer hassetDocumentContainer()ordetach(). A custom renderer returns its document's element fromonCreateDocument(), which becomesdocumentContainer, and no longer detaches it inonUnload():DocumentRendererremoves it from the dom onceonUnload()has run.
- core: let the renderer's lifecycle own its document container (45f80eb)
23.0.0 (2026-09-27)
- core:
SpineItemState.errorand a renderer'serrorare renamedloadError. - core:
SpineItemState.isLoadedandisErrorare replaced byloadStatus: DocumentLoadStatus,"idle" | "loading" | "loaded" | "unloading" | "error":isLoadedisloadStatus === "loaded"andisErrorisloadStatus === "error". A renderer'sstateis renamedloadStatus.SpineItemsObserver.itemLoad$,itemUnload$anditemLoadFailure$are replaced byitemLoadStatusChange$, emitting{ item, loadStatus }.SpineItem.loaded$andunloaded$, andDocumentRenderer.loaded$,unloaded$andisLoaded$, are removed: watchloadStatusinstead. - core:
SpineItemStateand a renderer's state are unions onloadStatus:loadErrorisundefinedunlessloadStatusiserror.
- core: deliver load status changes as they happen, and defer only the layout (73f3721)
- core: lay out after a load status reaches every subscriber, on load ends only (6a299d2)
- core: hold a load error only with the error status, and test the selection and writing mode (b4be71c)
- core: name the reason a load failed loadError, and what ends a load (002f4d8)
- core: one load status for a spine item, not two flags and three streams (a5656f8)
22.0.2 (2026-09-27)
- core: hold one document at a time in a renderer (bb20d76), closes #497
- core: release on destroy while the renderer's state is still observed (f74ff11)
22.0.1 (2026-09-27)
22.0.0 (2026-09-27)
- navigation:
ReadingPosition.stateis replaced byReadingPosition.status,"pending" | "success":"final"is"success", and"standIn"and"targetPlace"are"pending". A pending value stands in at its spine item's start whenreader.cfi.isRootCfisays its cfi names only the item. A navigation entry'sanchoris anInternalNavigationAnchor, which keepsstate. - core:
ReadingPosition.statusgains"error": the navigation's spine item failed to load, and the value stays until the next navigation.load()no longer retries a renderer or spine item in error: it loads again afterunload().isSettledpagination results can describe an item in error.
- core: end a navigation into a spine item that fails to load in error (0a4d495)
- navigation: say whether the reading position is found, not how much of it (ed9312b)
21.0.2 (2026-09-27)
- navigation: measure an element without text from the offset in it (1f49c1e)
- navigation: place the boundary after an element's last child where its contents end (12fe453)
21.0.1 (2026-09-27)
- core: keep a failed load in the renderer's state, not in its streams (16512cb)
- core: release what a failed load created only once (c2f5c30)
21.0.0 (2026-09-27)
- enhancer-koreader:
reader.navigation.readingPositionXPointer$is removed. ConvertreadingPosition$'scfiwithreader.koreader.cfiToXPointer, which returnsundefinedfor a place in a chapter that is not loaded, or a cfi naming nothing in the book.rxjsis no longer a peer dependency of@prose-reader/enhancer-koreader.
- enhancer-koreader: convert a cfi to an xpointer on demand, not the reading position as a stream (3c8d019)
- front: offer the spread mode's scopes in the demo, as the font size's (b4da63b)
- react-reader: manage the spread mode through props, as the font size (80f3b02)
- gestures: release the navigation however a pan that moved the page ends (e05f5b8)
- navigation: find the page holding text at the left edge of its document (e4d68fc)
- navigation: place a node that isn't rendered where the content after it is (6d5cbd5)
- navigation: release the lock of a pan stopped while it takes it (1ce57d0)
- react-reader: keep the app's setting value over the one the reader holds when met (d747b4e)
20.0.1 (2026-09-26)
- navigation: move the reading position once a locked navigation lands (6baa902), references #392 #392
20.0.0 (2026-09-26)
- navigation:
ReadingPosition.isFinalis replaced byReadingPosition.state,"standIn" | "targetPlace" | "final";isFinalisstate === "final". TheNavigationAnchortype is removed: a navigation entry'sanchoris aReadingPosition.
- navigation: give the reading position its state, not only whether it is final (9f6afe2)
19.0.0 (2026-09-26)
- navigation: a navigation to a cfi reports its chapter's start as the reading position until the chapter is loaded, rather than the cfi as asked. A cfi whose path leads to nothing in its chapter, or into a spine item without a document, is reported as the page it lands on.
18.0.0 (2026-09-26)
- navigation: ReadingPosition gains
isFinal, which code building one must set. readingPosition$ emits once more when a value becomes final, even with the same cfi and progress.
- navigation: say whether the reading position is final (49ed7f2)
- navigation: stop a selector waiting for a document its item never gets (e697b87)
17.0.4 (2026-09-26)
Note: Version bump only for package @prose-reader/root
17.0.3 (2026-09-26)
Note: Version bump only for package @prose-reader/root
17.0.2 (2026-09-26)
Note: Version bump only for package @prose-reader/root
17.0.1 (2026-09-26)
- cfi: read a text assertion spanning lines (0823177)
17.0.0 (2026-09-26)
- cfi: parse throws for strings it used to read in part, such as a cfi with text after its closing parenthesis or without one, or with a step or an offset missing its number.
- navigation: a navigation to a spine item the book does not have is ignored rather than clamped to its first or last item, and a navigation to an empty cfi is ignored rather than going to the first item.
- cfi: throw for a string that is only partly a cfi (a071e27)
- navigation: ignore a navigation whose target names nothing in the book (5203b99), closes #465
16.0.1 (2026-09-26)
- enhancer-audio: select the track a book opens on (5dba3ae)
16.0.0 (2026-09-26)
- navigation: reader.navigation.readingPosition$ emits { cfi, percentageEstimateOfBook } instead of the cfi, and the readingPosition of @prose-reader/react-native's ReaderState follows it.
- navigation: report how far into the book the reading position is (bd3971f)
- core: keep progression estimates within the book whatever the weights (fca46fa)
- enhancer-audio: fail a refused play instead of waiting for canplay (96f2dc0)
- metadata-fetcher-api: serve the playground from a checkout under a dot-directory (d55e3e8)
- navigation: take a target's progression from the page holding it (e3b5681)
15.0.1 (2026-09-26)
Note: Version bump only for package @prose-reader/root
15.0.0 (2026-09-26)
- core:
shouldUseSpreadModeForViewportis removed from@prose-reader/core. Callreader.viewport.wouldSpreadAt(size)instead, which reads the book and thespreadModesetting from the reader.Context.updateaccepts onlyrootElementandhasVerticalWriting.
- core: ask the reader about spreads instead of free functions (33d9b41)
- react-reader: add a layout dialog to the quick menu (8a3ce75), closes #424
- deps: declare the rxjs versions each package needs (1b31ae0)
- deps: raise the rxjs floor to 7.5.5, the first to export its types (eecd5af)
- enhancer-pdf,enhancer-audio: constrain options only to what they read (8b7bcc0)
14.1.0 (2026-09-25)
- enhancer-koreader: navigate to xpointers, and report the reading position as one (aea1d36)
- enhancer-koreader: follow the target option, and pin the reopen report (dd67cd6)
- enhancer-koreader: require the rxjs it is written against (2458bc0)
14.0.0 (2026-09-25)
- core: the
cfioption ofcreateReaderis removed. Passtarget: { type: "cfi", value: cfi }instead.ReaderLoadOptionsof@prose-reader/react-nativetakestargetin place ofcfithe same way.
- core: open the reader at any navigation target (370d3c2)
13.0.5 (2026-09-25)
- core: open at the initial cfi without reporting the start of the book (fd1ea00)
13.0.4 (2026-09-25)
Note: Version bump only for package @prose-reader/root
13.0.3 (2026-09-25)
- demo: declare haruko-comic.zip right to left (1f6b4e2)
- demo: declare sample.cbz left to right (c24b6f7), closes #449
- demo: label sous-le-vent as French and the webtoons as reflowable (bff329e)
13.0.2 (2026-09-25)
- gestures: make the drag listener the only thing stopping the browser's own drag (a9e23d7)
- gestures: stop the browser's own drag inside the book, so a moving press still pans or taps (80fb239)
13.0.1 (2026-09-25)
Note: Version bump only for package @prose-reader/root
13.0.0 (2026-09-25)
- navigation: the core
NavigationTargetno longer hasurl; the readercreateReaderreturns still takes it, through the url enhancer.nodeis a new target type. AftergoToUrlto an element, the reading position is that element's cfi rather than the first character of its page. A url outside the book is ignored instead of going to the first item.
- navigation: keep a selector waiting for its document without an anchor (f6a33d4)
- navigation: state what a selector may assume, and survive one that throws (7ac3133)
- navigation: move url navigation into a core enhancer, on a generic node target (e4b2a22)
12.0.0 (2026-09-24)
- navigation: UserNavigationEntry.direction is removed, and InternalNavigationEntry.directionFromLastNavigation no longer includes "anchor".
- core: keep a forwarded pointer event's button, pointer and modifier keys (aed8733), closes #240 #240
- gestures: require gesturx 1.11.1, where only the primary button starts a gesture (#439) (a162710)
11.0.0 (2026-09-24)
- navigation: ask for one typed target per navigation (#434)
10.0.0 (2026-09-24)
- layout: the
spreadModesetting is now"auto" | "always" | "never", defaulting to"auto", instead of a boolean."always"still shows no spread for a book whoserendition:spreadisnone, which the EPUB specification forbids, or whoserendition:flowisscrolled-continuous. ThecomputedSpreadModecomputed setting is removed: read what the reader shows fromreader.viewport.value.isSpread, or follow it withreader.viewport.watch("isSpread").shouldEnableSpreadModeForViewportandshouldUseComputedSpreadModeForViewportare replaced byshouldUseSpreadModeForViewport, which takes the setting. react-reader'swouldRotationUseComputedSpreadModeis renamedwouldRotationUseSpreadModeand takes the setting too.
- metadata-fetcher-api: cap concurrent lookups, leave rate limiting to the deployment (#425) (a0d78ea)
- layout: decide spread mode in the viewport's layout, so a resize that switches it lays out once (#398) (260c332)
9.0.0 (2026-09-24)
- react-native:
bridgeReader'screateReaderfactory receives{ manifest, cfi }instead of the manifest, andloadfromuseCreateReadertakes{ manifest, cfi? }instead of the manifest. - react-native:
pagination,contextandreadingPositionin the bridge state arenull, notundefined, until the reader of the book last loaded reports them. The web side reports throughreport(load, state)instead ofsetPaginationandsetContext.
- react-native: relay the reading position to native, and let load reopen at one (#422) (13fb0f9), closes #415
8.0.1 (2026-09-24)
Note: Version bump only for package @prose-reader/root
8.0.0 (2026-09-24)
- navigation:
paginationBeginCfiis removed from navigation entries and"pagination"fromtriggeredBy;navigation$emits every restoration;EnhancerPaginationIntois renamedEnhancerPaginationInfo.
- navigation: derive the reading position and resolve pagination on navigations only (#392) (5f34d62), closes #390
7.0.4 (2026-09-24)
7.0.3 (2026-09-24)
Note: Version bump only for package @prose-reader/root
7.0.2 (2026-09-24)
7.0.1 (2026-09-23)
- layout: stop laying out after every settings change, and lay out for the settings that move the pages (#387) (c1808cf)
7.0.0 (2026-09-23)
- navigation:
reader.navigation.state$no longer emitscanTurnLeftandcanTurnRight.
6.0.0 (2026-09-23)
- pagination:
EnhancerPaginationIntois renamedEnhancerPaginationInfo.ChapterInfois now recursive: the toc has no depth limit, and the builder already produced chains deeper than the four hand-unrolled levels the type described, through casts.
5.0.0 (2026-09-23)
- react-native: @prose-reader/react-native requires Expo SDK 57; its expo-file-system peer is ^57.0.0.
4.0.2 (2026-09-23)
Note: Version bump only for package @prose-reader/root
4.0.1 (2026-09-23)
Note: Version bump only for package @prose-reader/root
4.0.0 (2026-09-23)
- pagination:
PagesStategainslayoutRequest, which reachesreader.layout$values, andSpineLayout.layout$emits the number of the request it laid out for.
- pagination: keep settlement only while the layout is current and the page is ready (#379) (d4059dd), closes #369
3.0.0 (2026-09-23)
- pagination: anchor navigation on settled results and drop navigationId (#369)
2.0.3 (2026-09-22)
2.0.2 (2026-09-22)
2.0.1 (2026-09-22)
- build: annotate the exported types declaration emit cannot infer, and reattach the apps to the workspace (#371) (6091f84)
2.0.0 (2026-09-22)
- deps: consumers reaching these packages through
require()now need Node >=22.12, whererequire(esm)landed unflagged.importis unaffected on any Node that runs them, as is any bundler build. Documented on the streamer's Node page.
1.374.2 (2026-09-21)
Note: Version bump only for package @prose-reader/root
1.374.1 (2026-09-21)
Note: Version bump only for package @prose-reader/root