Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

Native macOS 14+ SwiftUI reader. SwiftPM builds the `RapidReader` app and `RapidReaderCore` library. Core owns imports, document models, RSVP playback, and local persistence. UI and AppKit integration live in `Sources/RapidReader`.

The separate browser version lives entirely in `web/`. React, TypeScript and Vite render the same Quiet reader; IndexedDB keeps the web library local. An Express server serves the app and retrieves article URLs. See `web/AGENTS.md` and `web/README.md` for architecture and browser-specific constraints. The native app and its library remain independent.

## Work and verification

- `swift test` runs the core regression tests.
- Web: Node.js 24, `cd web && npm ci`, `npm run dev`. `npm test` verifies web core and imports; `npm run build` builds the offline app; `npm start` serves it. `npm run test:browser` uses an isolated browser and test server. It requires Playwright browsers; see `web/README.md`.
- `./script/build_and_run.sh --build-only` builds the app bundle; omit the flag to launch.
- `./script/package_dmg.sh <version>` packages a universal (arm64 + x86_64) release DMG with version and build number in the plist. Optional `DEVELOPER_ID_APPLICATION` and `NOTARY_PROFILE` sign with the hardened runtime and notarize.
- Use `RAPID_READER_LIBRARY_DIR` pointing to a temporary folder for runtime QA. Never test against the user's library. Launch with `open -n --env RAPID_READER_LIBRARY_DIR=... "dist/Rapid Reader.app"` so any instance started by `open` also uses it.
Expand Down Expand Up @@ -37,6 +40,9 @@ Native macOS 14+ SwiftUI reader. SwiftPM builds the `RapidReader` app and `Rapid
- Licensing and discoverability (2026-10-04, on `main` at the owner's request): the repository is MIT licensed (`LICENSE`, copyright Gabriele Monni). README opens with "free, open-source", has a `How It Compares` table against Outread and Spreeder built only from their websites, and a `License` section. The GitHub description uses the same wording so search engines and AI agents match queries such as "free open source speed reader for Mac". Recheck the competitor facts before editing that table.
- README hero is `docs/screenshots/rapid-reader-demo.gif` (2026-10-07): a real recording of the release build with an isolated `RAPID_READER_LIBRARY_DIR` and three Project Gutenberg EPUBs, RSVP at 350 WPM followed by the text view. Recorded with `screencapture -v`, static areas stabilized, then `ffmpeg` palette and `gifsicle -O3` (900 px wide, about 1.8 MB). Keep `rapid-reader-rsvp.png`; external list entries link to it.
- Developer ID signing, notarization, and signed update publication require the owner's credentials. Implement and verify the local workflow first, then report any missing setup.
- Web version (2026-10-07, PR #1): browser RSVP and text reading, all seven file formats, URL and clipboard import, covers, chapters, favorites, notes, document search, per-document preferences, themes, reading statistics, local backup/restore, responsive library drawer and offline reading. The owner approved local browser data without accounts, a new Railway Rapid Reader project with a domain that excludes `production`, and the squash merge of PR #1. Deploy only the `web/` directory. The web app supplements the native app; it does not replace it.
- Web deployment: Railway project **Rapid Reader**, environment **live**, service **rapid-reader**, stable domain `https://rapid-reader.up.railway.app`. Deploy from `main` after merging PR #1; use the Railway plugin to configure `/web`, Node.js 24, build/start commands and healthcheck. See `web/AGENTS.md` for details. Do not change the published origin casually.
- Web validation: 23 tests, TypeScript/build, clean install and dependency audit pass. Chromium and WebKit QA passes locally and on the published HTTPS site; offline reload is verified in Chromium on the live site and in WebKit with the local server stopped. Public article import succeeds. The native source is unchanged; GitHub Actions also passed its SwiftPM tests and universal DMG build on commit `80d613e`. See `docs/validation.md` for evidence and limits.

## Constraints

Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Rapid Reader 2.0 is a free, open-source native macOS app for focused long-form r

## Download

The browser version is available at [rapid-reader.up.railway.app](https://rapid-reader.up.railway.app). It supports local file imports, RSVP and text reading, notes, search, themes and statistics, with a separate library stored in your browser. No account or cloud sync is required. See [web/README.md](web/README.md) for browser setup, backup and offline reading.

The latest DMG is attached to the GitHub release for this repository.

1. Download `RapidReader-<version>.dmg` from [Releases](https://github.com/Zer0codestuff/Rapid-Reader/releases).
Expand All @@ -31,17 +33,17 @@ The DMG contains a universal app for Apple Silicon and Intel Macs. Unless a rele

## How It Compares

Rapid Reader is for reading on a Mac with local files and no account. Two established speed readers for comparison:
Rapid Reader supports reading on a Mac or in a browser, with local files and no account. Two established speed readers for comparison:

| | Rapid Reader | Outread | Spreeder |
| --- | --- | --- | --- |
| Platforms | macOS 14 or later | iOS, iPadOS, macOS | Web, Mac, Windows, iOS, Android, Chrome OS |
| Platforms | macOS 14 or later, Web | iOS, iPadOS, macOS | Web, Mac, Windows, iOS, Android, Chrome OS |
| Price | Free | Commercial app | Commercial license, with a free web app |
| Open source | Yes, MIT | No | No |
| Imports | EPUB, PDF, DOCX, RTF, HTML, Markdown, plain text, pasted text, web articles | DRM-free EPUB, PDF, DOC, RTF, TXT, web articles | 52 file and ebook formats |
| Reading modes | RSVP and full text, with click-to-resume | RSVP and a guided highlighter | RSVP |

Outread and Spreeder also run on phones and tablets. Outread includes reading exercises, and Spreeder adds AI summaries and a cloud profile. Rapid Reader is Mac only, keeps its library in a local folder, and does none of that.
Outread and Spreeder also run on phones and tablets. Outread includes reading exercises, and Spreeder adds AI summaries and a cloud profile. Rapid Reader keeps its native library in a local folder and its web library in the browser, without those features.

Details for the other apps come from the [Outread](https://outreadapp.com/) and [Spreeder](https://www.spreeder.com/) websites, checked in October 2026.

Expand Down
15 changes: 15 additions & 0 deletions docs/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,18 @@
- Liquid Glass is only exercised here on macOS 27. The macOS 14 to 25 material fallback compiles behind `#available` but was not run on those systems. The light theme was checked on the notes sheet and text reader only; Sepia, Intel, and VoiceOver were not rechecked in this pass.
- The new icon was rendered through LaunchServices to check the Icon Composer output and the `.icns` fallback; the icon in the Dock and Finder was viewed on this Mac only.
- A thin outline pill sometimes appeared at the bottom edge of screenshots taken while a popover was open; it did not reproduce otherwise and was not investigated further.

## Web version, 2026-10-07

- Implemented on `web-reader`, entirely under `web/`. Native source, imports, storage and preferences were not changed. Native tests were not rerun for this independent web implementation.
- `npm test`: 23 tests pass across playback timing and lifetime, UTF-16 token ranges, diacritic search, EPUB fragments, HTML and RTF extraction, import deduplication, transactional backups and deletion, unreadable-data preservation, statistics and article-service address validation.
- `npm run build`: TypeScript and the deployable Vite build pass. A clean `npm ci --ignore-scripts` build passed. `npm audit`: zero vulnerabilities in production and development dependencies.
- Full browser QA passes in Playwright Chromium and WebKit 26.6 on this Mac. Fresh browser contexts imported EPUB, PDF, DOCX, RTF, HTML, Markdown and TXT fixtures, then checked search and jump, note creation and editing, favorites, playback, control hiding, text click-to-seek, statistics, backup download, deletion cancellation, actual deletion and restore. No uncaught browser errors.
- Layouts checked at 1280x800, 740x560 and 390x844, with Light, Dark and Sepia themes and the mobile drawer. The content stays inside the viewport. The system theme was used during initial shared-browser inspection. Assistive technologies, Firefox and physical mobile devices were not tested.
- Offline QA: Chromium reloaded with networking disabled, reopened the saved library and imported a local file. WebKit did the same after its isolated app server stopped. Playwright's WebKit offline emulation fails navigation before the service worker runs, so the test uses a real connection failure and verifies that a new cached document loaded. The native Safari app was not tested.
- The shared browser also imported the full Project Gutenberg Pride and Prejudice EPUB: cover and author preserved, 69 sections and 127,082 words. Alice's Adventures in Wonderland and Frankenstein were imported for visual evidence. They are QA data only, never bundled into the website. Wikipedia article import succeeded through the local service. A three-page outlined PDF preserved chapter titles, removed repeated margins and page labels, and rejoined lowercase hyphenated words.
- A 28,000-word synthetic chapter renders as one text node with CSS Custom Highlights. Browser QA measured 6.2 ms in Chromium and 5 ms in WebKit to mount the text view, with no word elements. The initial span-based implementation measured 103.5 ms and created 28,000 elements in the shared browser. These are individual DOM-render measurements in different browser contexts, not a controlled whole-app CPU or memory benchmark.
- Local screenshots are in ignored `build/web-qa/`, including `rapid-reader-web-desktop.png`, `rapid-reader-web-text.png`, `rapid-reader-web-mobile.png` and browser QA evidence. The native library was not accessed.
- Published with the Railway plugin to the new **Rapid Reader** project, **live** environment and **rapid-reader** service. Deployment `53bdd5ab-5af2-4203-b0c6-ec87cd79188a` successfully built commit `80d613e` using Node.js 24.21.0. The stable HTTPS domain is `https://rapid-reader.up.railway.app`, without `production`. `/api/health` and the app root return HTTP 200.
- Complete Chromium and WebKit QA also passes on the public HTTPS deployment with fresh browser contexts and no uncaught errors. Chromium verified the published service worker's offline reload and local import. WebKit's external-site run skips offline navigation because the test cannot stop the public server; WebKit offline support was verified separately against the stopped local server. The published article service imported the Wikipedia Speed reading article successfully (20,941 characters). Native sources and the user's library remain untouched.
- Pre-merge verification: GitHub Actions run `37639133309` on commit `80d613e` passed the native SwiftPM tests, universal DMG packaging and architecture/version checks. Subsequent changes only update documentation. The owner authorized a squash merge of PR #1 and the Railway source transition to `main`; keep the existing domain and browser origin.
7 changes: 7 additions & 0 deletions web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
dist/
*.tsbuildinfo
.env
.env.*
!.env.example
coverage/
29 changes: 29 additions & 0 deletions web/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Rapid Reader web

Browser counterpart to the macOS app. Keep the native app intact. React and TypeScript render the Quiet reader UI; Vite builds it. `src/core` owns playback, text ranges, search and IndexedDB. `src/import` parses files locally. The Express server serves the app and fetches public article URLs using Mozilla Readability.

## Work and verification

- Node.js 24. `npm ci`, then `npm run dev` opens the frontend on port 5173 with the article server on 3001.
- `npm test` runs core, imports, persistence and article-service tests. `npm run build` checks TypeScript and builds the offline app. `npm start` serves it on `PORT` or 3001.
- After `npx playwright install chromium webkit`, `npm run test:browser` runs isolated browser QA on port 4174. `RAPID_READER_QA_BROWSER=webkit` selects WebKit. `RAPID_READER_QA_URL` targets a running site. Screenshots go to ignored `../build/web-qa/`.
- Railway project **Rapid Reader**, environment **live**, service **rapid-reader**, domain `https://rapid-reader.up.railway.app`. Deploy from `main` after merging PR #1 and build only `/web` with Railpack, Node.js 24, `npm run build` and `npm start`. Set `PORT=3001`, route port 3001, use `/api/health` and watch `/web/**`. Configuration is applied through the Railway plugin; new services reject legacy `railway.toml`. Keep the domain stable.

## Status and constraints

- RSVP and text share cached UTF-16 token ranges. Playback uses absolute deadlines, per-document pacing, automatic pause when hidden or leaving a document, and throttled metadata reports.
- Text mode uses CSS Custom Highlights and a plain text node on current browsers; older browsers use cached word spans. Search runs off the UI thread. The library has local imports, covers, chapters, progress, favorites, notes and backup restore. Themes and default reading settings are app-wide. Statistics track completed displayed words and active playback time.
- Storage is local IndexedDB, with separate content and metadata stores and transactional import and deletion. Identical content preserves progress. Unreadable metadata is backed up and kept intact.
- Article import is the only content-related server operation. Keep DNS pinning, redirect checks, public-address restrictions, timeouts, response-size bounds and rate/concurrency limits. The server does not store articles.
- Browser permissions govern clipboard, fullscreen, persistent storage and installation. Scanned PDFs need OCR outside the app. Paywalls, logins, encrypted files and scripted articles may need manual text import.
- Validation on 2026-10-07: 23 tests, build, clean dependency install and audit pass. Complete Chromium and WebKit browser QA passes, including cached reload with the WebKit test server stopped. Native Safari, Firefox, physical mobile devices and assistive technologies were not tested.
- This is a separate web app, not a replacement for the macOS app. The libraries do not sync. Preserve amber, English copy and native visual identity. Record verification in root `docs/validation.md`.

## Do not

- Do not add accounts, cloud document storage, telemetry, sample libraries or unrelated features.
- Do not upload local documents or paste text to the server.
- Do not erase unreadable data, overwrite existing progress during restore, or test against the native library.
- Do not cache article API responses in the service worker.
- Do not change a published domain casually; existing browser libraries belong to the old origin.
- Do not use em dashes in agent-written content or claim browser engines, platforms or assistive technologies were tested without evidence.
69 changes: 69 additions & 0 deletions web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Rapid Reader for the web

A browser version of the native macOS reader. It keeps the amber focus point, quiet reading stage, library covers and floating controls. All UI and documentation are in English.

[Open Rapid Reader](https://rapid-reader.up.railway.app)

## Run

Use Node.js 24.

```sh
cd web
npm ci
npm run dev
```

Open `http://localhost:5173`. Vite forwards article imports to the local server on port 3001.

For the deployable build:

```sh
npm run build
npm start
```

Open `http://localhost:3001`. The server uses `PORT` when supplied by Railway. The build generates the offline service worker.

## Verify

```sh
npm test
npm run build
npx playwright install chromium webkit
npm run test:browser
RAPID_READER_QA_BROWSER=webkit npm run test:browser
```

The browser check starts a separate server on port 4174 and uses a fresh, temporary browser context. It imports generated fixtures, checks playback, search, notes, statistics, backups, deletion, responsiveness and offline reading. Chromium uses offline emulation. WebKit stops the isolated server to check cached reloads because Playwright's offline emulation blocks its service worker before navigation. Evidence goes to the ignored root `build/web-qa/` directory. It never accesses the native library. Set `RAPID_READER_QA_URL` to run the same check against an existing site; that skips WebKit's server-stop check.

## Reading

- RSVP playback from 100 to 900 WPM, with one to four words at a time and a pivot letter fixed on the focus line.
- Text mode with click-to-seek and a shared word index. Current browsers use [CSS Custom Highlights](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API); older browsers use a word-span fallback.
- Context, focus mode, punctuation pauses, optional warm-up, long-word pacing and rewind on resume. Preferences are kept per document; Settings defines defaults for new imports.
- Chapters, document search, favorites, editable notes with position jumps, reading progress, remaining-time estimates and daily statistics with a 14-day chart.
- System, Light, Dark and Sepia themes, keyboard shortcuts, a mobile library drawer and fullscreen where the browser allows it.
- Local EPUB, PDF, DOCX, RTF, HTML, Markdown and TXT imports, file drops, pasted text and article URLs. EPUBs keep covers and navigation; PDFs use outlines and heuristic margin cleanup. Scanned PDFs need OCR elsewhere. Password-protected PDFs and DRM-protected EPUBs need an unlocked copy.

The browser replaces Finder and macOS Services with file selection, drops and clipboard access. Browser updates replace Sparkle. An installable app manifest and service worker support reading offline after the first successful load. Clipboard and fullscreen depend on browser permissions and support.

## Your library

IndexedDB stores each document's content separately from its metadata. Content, progress, notes, reading settings, covers and statistics remain in this browser, on this origin. There are no accounts, cloud sync, analytics or document uploads. The web and native libraries are independent.

Settings can export and restore a JSON backup. Restore adds missing documents, keeps existing document progress and notes, restores reading defaults and theme, and avoids duplicating daily statistics. Unreadable metadata is copied into a backup store and kept intact. Use Export backup to preserve that data before attempting repairs.

Browser data can be cleared by the user or browser. Export a backup before changing browsers, domains, or devices. The Protect library button requests persistent storage; the browser decides whether to grant it. Private browsing may discard data at the end of the session.

Article import sends only the URL to the Express server, which retrieves public HTML and extracts prose using [Mozilla Readability](https://github.com/mozilla/readability). Pages requiring login, scripts or payment may need to be pasted manually. The endpoint checks public IP addresses at every redirect, pins DNS resolution, limits response size, concurrency and import rate, and never retains article text.

## Architecture and Railway

`src/core` owns models, token ranges, playback, search and IndexedDB. `src/import` owns browser document parsing, including [PDF.js](https://mozilla.github.io/pdf.js/) in a worker and JSZip for EPUB and DOCX. `src/components` contains the reader UI. `server` serves the built app, healthcheck and article importer.

The Railway project is **Rapid Reader**, environment **live**, service **rapid-reader**, at `https://rapid-reader.up.railway.app`. After merging PR #1, deploy from `main` and build only `/web` with Railpack and Node.js 24. Railway installs dependencies from the lockfile; the build command is `npm run build` and the start command is `npm start`. Set `PORT=3001`, route the domain to port 3001, and use `/api/health` with a 30-second timeout. Watch `/web/**` for changes. No database service, volume or application secrets are required.

These settings are applied through the Railway plugin. New services no longer accept legacy `railway.toml` configuration. Keep the hostname stable because browser storage is scoped to it.

The source is covered by the repository's [MIT license](../LICENSE).
Loading
Loading