diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..74fc694 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI + +on: + pull_request: + push: + branches-ignore: [main] + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + + - run: npm ci + - run: npm run check + - run: npm run build diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..84b0fbb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Brandon Deonarine + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index cbfeef6..3c2f40f 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ https://m3rcu3y.github.io/Flow-Reader/ ## Run Locally -Prerequisites: Node.js +Prerequisite: Node.js 24 or newer. ```bash npm install @@ -44,10 +44,15 @@ Open the local URL printed by Vite (usually `http://localhost:3000`). ```bash npm run check +npm run build ``` `npm run check` runs TypeScript (`tsc --noEmit`) and the Vitest suite. Production builds are handled by the GitHub Pages workflow on `main`. +## License + +Flow Reader is licensed under the [MIT License](LICENSE). + ## Notes - The library, preferences, and themes use local browser storage. - “Clear Data” removes Flow Reader data from this device. diff --git a/docs/UX_AUDIT.md b/docs/UX_AUDIT.md new file mode 100644 index 0000000..1d781c1 --- /dev/null +++ b/docs/UX_AUDIT.md @@ -0,0 +1,113 @@ +# Flow Reader UX and Product Audit + +This audit accompanies the `agent/flow-reader-product-polish` branch. It separates changes that are safe to land as product polish from larger architectural work that should be handled deliberately. + +## What felt unreliable + +Flow Reader already has a surprisingly broad feature set, but several details made the product feel less trustworthy than the underlying functionality deserved: + +- The landing screen had weak visual hierarchy, so the primary action competed with demo, help, URL import, and file import controls. +- The library looked like a utility drawer rather than a durable reading workspace. +- Some interaction affordances were hover-first. Selecting a library entry relied on a clickable list item and the delete control was mostly hidden until hover. +- The reader mode switch looked polished but only partially implemented the expected keyboard tab pattern. +- Product styling used many local utility-class combinations without a compact surface vocabulary, which made related controls feel slightly inconsistent. +- The repository had deployment checks on `main`, but no equivalent non-deploying CI gate for feature branches. +- Build configuration still contained unused Gemini environment-variable plumbing even though the app does not use Gemini. + +## Changes in this branch + +### Product presentation + +- Deepened the dark visual system with more deliberate surfaces, borders, elevation, and focus states. +- Reworked the idle backdrop into a subtle reading-space atmosphere using a grid, orbit, grain, and restrained warm light. +- Gave the landing experience stronger editorial hierarchy and a dedicated glass reading dock. +- Refined the Flow Reader mark while keeping the existing identity and warm accent. +- Improved mobile spacing and reduced-motion behavior. + +### Library + +- Converted reading selection into a real keyboard-focusable button instead of a clickable list item. +- Kept delete as an independent control with an explicit accessible label. +- Made deletion discoverable on touch devices instead of depending on hover. +- Clarified word counts, progress, source metadata, and previous-session information. + +### Reader controls + +- Added roving tab focus to the reader mode switch. +- Added Arrow Left, Arrow Right, Home, and End keyboard behavior. +- Unified the switch with the updated surface language. + +### Reliability and build hygiene + +- Added CI for pull requests and non-main branches. It runs `npm ci`, `npm run check`, and `npm run build` without deploying. +- Removed stale Gemini build-time definitions. +- Enabled PWA cleanup of outdated caches and immediate client claim behavior. +- Improved metadata and mobile app-shell hints in `index.html`. + +## Recommended follow-up work + +### Priority 0: Make first-load/offline behavior more self-contained + +The app currently loads Tailwind's browser runtime, Google Fonts, and PDF.js from external CDNs. The PWA can cache those resources after they have loaded, but a fresh installation still depends on those networks. + +Move Tailwind to the normal Vite/PostCSS build pipeline, bundle PDF.js through npm, and consider either self-hosting fonts or using a robust local fallback stack. This reduces first-load failure modes, makes the PWA claim more trustworthy, and removes runtime styling compilation. + +### Priority 1: Split the two oversized orchestration components + +`src/App.tsx` currently owns reader state, session state, mobile chrome visibility, global listeners, persistence coordination, sprint behavior, overlays, and layout. `src/components/TextInput.tsx` owns multiple import engines plus several UI flows. + +Good extraction targets: + +- `useReadingSession` +- `useReaderChrome` +- `useActiveBook` +- `useBionicHotspots` +- `useFileImport` +- `useUrlImport` +- `PdfPasswordDialog` +- `ImportProgress` +- `FullscreenEditor` + +The goal is not abstraction for its own sake. The goal is to make individual user flows testable without loading the entire application shell. + +### Priority 1: Clarify URL-import privacy + +The local library is private to the device, but URL import intentionally sends the entered URL to a public text extractor. The existing disclosure is good, but the distinction should be even clearer because “local-first” can otherwise be interpreted as “nothing ever leaves this device.” + +Recommended product wording should distinguish: + +- pasted/file content: processed locally, except OCR dependencies already loaded by the app; +- URL import: the URL is sent to the extractor, then the resulting text is stored locally. + +A “Paste locally instead” route should remain prominent whenever URL extraction fails. + +### Priority 1: Add browser-level regression tests + +The existing service tests are useful, but the riskiest behavior now lives in browser interactions: mode switching, resume prompts, Bionic scroll persistence, mobile drawers, import status, localStorage restoration, and keyboard shortcuts. + +Add a small Playwright suite covering: + +1. Paste text → start → play/pause → seek → exit → resume. +2. Switch RSVP / RSVP+ / Bionic and persist the mode. +3. Add/delete a library item with keyboard navigation. +4. Mobile drawer and landing layout at a narrow viewport. +5. URL-import blocked/fallback state using a mocked response. +6. PWA production build smoke test. + +### Priority 2: Replace browser-native confirmation dialogs + +`confirm()` is currently used for destructive data clearing and large-PDF continuation. App-native dialogs would feel more coherent, provide better explanation, and give more control over focus management. + +### Priority 2: Improve long-library scalability + +The localStorage model is reasonable for a personal reader, but importing many large documents will eventually make synchronous serialization and full-list rendering noticeable. A future storage migration to IndexedDB should be considered before adding cloud sync or very large libraries. + +## Suggested sequence + +1. Land this visual/accessibility/CI pass after branch checks are green. +2. Bundle runtime CDN dependencies. +3. Split `App.tsx` and `TextInput.tsx` along the flow boundaries above. +4. Add Playwright coverage while those boundaries are fresh. +5. Revisit storage only if the product is going to support genuinely large libraries or sync. + +The reader engine itself does not need to be rewritten as part of this cleanup. The RSVP timing hook and service boundaries are already better isolated than the app shell, so the highest-return work is around orchestration, first-load reliability, accessibility, and product trust. diff --git a/index.html b/index.html index f28779a..e68da06 100644 --- a/index.html +++ b/index.html @@ -3,16 +3,22 @@
+ + + + + +