You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tab-switch auto-pause now shows an explanatory toast ("⏸ Paused — you switched tabs") and a resume nudge on return
Eye Focus mode now shows an exit hint ("Tap 👁 or Esc to exit") on enter, matching regular Focus mode behaviour
Burger menu coach mark after onboarding now persists until first content is loaded (was: auto-dismissed after 8s)
Added
Adaptive speed suggestion now persists as a tappable badge (⚡ N WPM) in the session strip until acted on or overridden by manual WPM change
Post-onboarding empty screen: Upload and Paste buttons highlighted with accent glow to guide first content load
WPM pill: faint "tap to set" micro-hint indicates the number is directly editable
Session strip: "↕ jump" affordance hint on the word-position button
Fine-tune settings: description line beneath each toggle/control explaining its effect
Onboarding demo: speed ramps from 150 WPM to 250 WPM over first 10 words (was: fixed 250 WPM cold start)
[2.4.0] — Gap fix · tagline · viewport nav · empty state · default text · session strip · footer
Fixed
Black gap below footer — added min-height: 100dvh to .appShell so the shell always fills the screen
Tagline breakpoint — .topBarTagline now hides at max-width: 340px (was 380px) so it stays visible on more phones
Word nav overlay hidden — .wordNavOverlay is now display: none; word count lives in the session strip in the controls dock
Page nav pill wider — .pagePillOverlay resized to 26px height with better padding, letter-spacing, and --text-muted colour for readability
Page nav buttons larger — .pageNavBtn is now 26×26px (was 22×22px)
Day-theme nav overrides — slightly more opaque backgrounds and explicit color: var(--text) on hover for contrast
Empty state spacious — .emptyState now uses gap: 1rem, padding: 2rem 1.5rem, transparent background; heading is 1.4rem with tracking and centred; emptyActionBtn uses --radius-lg and taller padding
Viewport expands when empty — .viewportEmpty modifier added; .readingMain:has(.viewportEmpty) stretches to fill available height
Session strip minimum 1% — pct now uses Math.max(1, ...) so a loaded document never shows "0%"
Large word-count truncation — documents ≥ 10 000 words show as e.g. "32.3k" in the session strip
Welcome text on first visit — WELCOME_TEXT loaded automatically when no history exists; returning users see their last state
Footer visual weight — .footer is opacity: 0.5 at rest, full opacity on hover
[2.3.0] — Top bar: tagline visible, ThemeToggle restored, help circle button, sign-in moved to burger Account section
Fixed
Tagline visibility — removed display: flex; flex-direction: column; align-items: flex-start from .topBarTitle; these flex properties made the title a column container that consumed all height, leaving zero space for the sibling tagline span
ThemeToggle restored — added ThemeToggle import and component back into topBarActions in App.tsx
Help circle button — added ? button to topBarActions wired to setShowHelp(true); restyled .helpBtn from 44px square to 36px circle (border-radius: 50%)
Sign-in moved to burger — unauthenticated UserAvatar now returns null (clean top bar); burger menu gains an Account section with "Sign in to sync reading" button (only when Supabase is configured) and name + sign-out when authenticated
Burger overlay (iOS portal fix) — wrapped {open && (...)} block in createPortal(…, document.body) in BurgerMenu.tsx; iOS Safari WebKit bug #224093 traps position:fixed descendants inside an ancestor with backdrop-filter — portaling to document.body escapes all ancestor compositing layers so the full-screen overlay now covers the entire viewport on iOS
Obsidian topBar — added [data-theme='obsidian'] .topBar override with rgba(28,28,28,0.95) background; the previous value rgba(10,10,10,0.80) was visually indistinguishable from the #000 page background; also added explicit midnight and warm overrides for completeness
contextStrip gap — removed padding-bottom: calc(0.5rem + 2.75rem) from .contextStrip; the 2.75rem was intended to prevent footer overlap but instead created a visible black gap below the collapsed preview panel
Burger menu overlay — removed backdrop-filter from .inner in Controls.module.css; iOS WebKit compositing bug caused the controls panel to paint above the burger menu fixed overlay regardless of z-index; replaced glass blur with solid --bg-panel background
Tagline contrast — .topBarTagline changed from var(--text-faint) to var(--text-muted) for all four themes (text-faint was near-invisible on day + obsidian themes); also removed leftover .topBarTitle::after CSS pseudo-element (now that TSX has the real span it was rendering the tagline twice)
Duplicate word count — .wordCountOverlay inside the reading viewport hidden (display: none) — session strip in the controls dock is the single source of truth for reading position
Changed
Sign in button — UserAvatar unauthenticated state replaced emoji 👤 placeholder with a proper "Sign in" ghost pill button that calls signInWithGoogle; styled with --border-input, --radius-full, hover/focus/active states
ContextPreview label — "Page Preview" → "Preview" (never wraps on any screen width)
[2.0.0] — Full UI/UX Facelift Part 2: viewport active glow, progress bar wired, top bar tagline TSX, controls 3-layer dock TSX, word-jump, help keyboard shortcut
Top bar — TSX restructure: <span className="topBarTitle"> replaced with <div className="topBarBrandText"> containing title + tagline "Read faster, Understand Better"; SyncStatusIndicator, ThemeToggle, and help ? button removed from topBarActions (theme accessible via BurgerMenu drawer; help opens via ? keyboard shortcut)
Controls dock — full 3-layer TSX restructure: Layer 1 = session strip (pct%, word position, reset icon); Layer 2 = action row (Upload+Paste cluster | circular play | Back+Next cluster); Layer 3 = WPM stepper pill; resetRow removed, merged into session strip
Controls context — currentWordIndex and goToWord added to context destructure
Added
Word jump — session strip pct% label is now a tappable button that opens an inline input; supports both word number (234) and percentage (47%) formats; focus auto-set on open
Reading progress bar — <div className="readingProgressBar"> wired in App.tsx with currentWordIndex / words.length width calculation
? keyboard shortcut — pressing ? toggles the HelpModal (replaces the removed top-bar ? button)
[1.9.0] — Full UI/UX Facelift Part 1: reading progress bar, glass top bar with tagline, controls v3 dock, burger menu z-index fix, ContextPreview glass card
Changed
Top bar — will-change: backdrop-filter for stable composite layer; opacity raised to 0.80; transition: opacity 0.15s ease; day-theme and @supports fallbacks updated
Top bar brand — tagline "Read faster, Understand Better" injected via CSS ::after (no TSX changes); topBarBrandText and topBarTagline classes added for Part 2 TSX update; title scaled to clamp(0.9rem, 2.5vw, 1.1rem) / weight 700
Top bar icon buttons — ghost border (transparent by default), text-faint color, simplified hover with state-hover bg; removed box-shadow and extra transitions
Controls dock — full v3 redesign: isolation: isolate removed from .controls (was causing burger menu backdrop to paint behind glass panels on WebKit/iOS Safari); .inner uses overflow: hidden and --shadow-md token with no z-index; new layer classes .sessionStrip, .btnCluster, .wpmRow ready for Part 2 TSX update; backward-compat .resetRow/.resetRowBtn preserved
Burger menu backdrop — z-index: 600 → 1200 (above all backdrop-filter compositing layers); top: env(safe-area-inset-top) → top: 0; added isolation: isolate and -webkit-backdrop-filter
.inner in Controls — grounding box-shadow: var(--shadow-sm) (structural panel)
.panel in BurgerMenu — box-shadow: var(--shadow-lg)
Changed
All raw 0.15s, 0.12s, 0.2s transition values in modified files replaced with design tokens (var(--transition-base), var(--transition-fast), var(--transition-slow))
.ctafont-weight changed from 700 → 600 (correct Inter weight for CTAs)
.titleInput transition now includes box-shadow for the focus ring
Known Issues
WPM stepper buttons (.wpmStepBtn) are 26×26 px — below the 44 px touch target minimum. To be addressed in a separate task.
Changed
Fine-tune menu reordered into 5 logical groups with thin dividers
Labels renamed to plain language (Reading anchor, Focus guides,
Dim side words, Dim level, Group into phrases, Side word size)
Side word controls hidden entirely when Words shown = 1
Context word size changed from checkbox to dropdown (Same as main /
Small / Medium / Normal / Large / X-Large / Huge)
Side word dimming now uses CSS opacity (not a separate color token);
--vp-text-peripheral tokens removed from all themes
Wizard restructured: 15 steps, new order (words → phrases → speed →
layout → word size → side word size → anchor → colour → guides →
dim → dim level → punctuation → long words → confirm)
Wizard auto-skips side-word steps when Words shown = 1
Wizard auto-skips ORP colour step when Reading anchor is off
Wizard auto-skips Dim level step when Dim side words is off
ZONE_EXPAND_GAP 30→40pt: captures labels adjacent to diagram anchor rows
X_SPREAD_MIN 60→75pt: eliminates marginal prose band overlap
[1.5.0]
Changed
Onboarding redesigned — 4 steps (was 5): Demo → Pick mode → Pick theme → Load content.
Burger menu callout step removed entirely.
Step 1 is now a vertical stack of 3 tappable mode tiles (Sprint / Focus / Flow) with emoji,
WPM range, description, and a "Recommended" badge on Focus.
Step 2 is a 2×2 theme grid (Midnight / Warm / Obsidian / Day) — better thumb reach than
the single row of 4. Step 3 keeps the original load-content cards unchanged.
Button layout fixed — Back is now a small chevron icon in a top nav row alongside the
progress dots and a Replay button (step 0 only). Primary action is full-width at the bottom
so it never moves between steps. Skip is a small text link below the primary button.
Post-onboarding empty state — the reading viewport now shows two large tappable cards
("Upload file" and "Paste text") when no document is loaded, replacing the plain text
placeholder. Cards show icons, labels, and format hints.
Post-onboarding coach mark — a single tooltip anchored to the burger menu appears 3 s
after onboarding completes ("Settings & history live here") and auto-dismisses after 5 s or
on any click. Replaces the previous scatter of help-button pulse, burger-button pulse, and
upload-button pulse.
Removed
Burger menu callout step from onboarding overlay.
pulseHelp, pulseBurger, pulseUpload, showPostOnboardingHint states and all related
CSS (helpBtnPulse, @keyframes helpPulse, postOnboardingHint, @keyframes hintBarFade).
pulseBurger prop from BurgerMenuProps.
pulseUpload prop from ControlsProps.
Added
Context word size toggle — new setting contextWordSameSize (default: true). When
enabled, context words render at the same font size as the main word. When disabled, they
use a smaller size (clamp(1.1rem, 5vw, 1.8rem)) to create visual hierarchy. Persisted
in fastread_context_same_size.
Dim amount slider — new setting contextWordOpacity (default: 0.65, range 0.20–1.00,
step 0.05). Controls the opacity of context words when peripheralFade is on. When
peripheralFade is off, context words always render at full opacity. Persisted in
fastread_context_opacity.
Wizard 1–5 word count — Step 1 of the Save Mode Wizard now offers all five window
sizes (1–5) instead of the previous 1–3.
Wizard Step 6: Context word size — new Yes/No step asking whether context words
should match the main word size.
Wizard Step 7: Dim amount — new stepper step to configure context word opacity,
applied when dimming is enabled.
Wizard keyboard navigation — press ←/→ or Enter to move between steps,
Y/N on Yes/No steps, 1–5 on the word-count step, Escape to close.
Wizard keyboard hint — small hint line below the progress bar shows available
keyboard shortcuts.
Burger menu closes on Resume — clicking "Resume" in the Session Analytics panel now
closes the burger menu before resuming the session.
Changed
getSlotOpacity signature updated: the windowSize parameter is replaced by
contextWordOpacity: number, which is used as the fade value when peripheralFade
is true (previously hard-coded to 0.45).
All three presets (speed, focus, read) updated with contextWordSameSize: true
and contextWordOpacity: 0.65.
[1.4.1]
Fixed
Eye focus button unclickable — the base .overlayBar rule carries
pointer-events: none (so the transparent overlay doesn't swallow word-area
taps). Child clusters (.pageNavOverlay, .wordNavOverlay) individually
restore pointer-events: all, but the .eyeBtn rule was missing the same
restoration. Added pointer-events: all to the base .eyeBtn rule in
ReaderViewport.module.css so the eye button is always clickable regardless
of eye-focus state.
[1.4.0]
Fixed
WPM resets to 238 on refresh — the adaptive speed system (finalizeSession)
was calling setWpm(newBaseline) which overwrote fastread_wpm in localStorage
with the adjusted value. On every subsequent refresh the app would initialise to
238 (250 × 0.95) instead of the user's saved preference. Fix: removed
setWpm(newBaseline) entirely — finalizeSession still runs to track rewinds
and store its baseline in fastread_adaptive_wpm, but the user's WPM preference
is never overwritten. Adaptive toast message updated to "Suggested speed for next
session" to match the new non-mutating behaviour.
Changed
WPM badge removed — the WPM number that appeared inside the reading area
during fullscreen focus mode has been removed. The Controls bar WPM stepper
remains unchanged. Removed .focusWpmBadge and .focusWpmUnit CSS classes.
Eye focus mode rearchitected — isFocusMode local state removed from
ReaderViewport; replaced with isEyeFocus prop driven from App. Eye focus
now borrows appShellFocused to hide the top bar and controls bar (same as the
existing maximize button), and hides page nav, word nav, source label, and focal
ticks within the viewport. The word display is completely unchanged in eye
focus — same size, same position, same color. Eye button lives inside the
overlayBar between the page nav and word nav clusters. Pressing Escape exits
eye focus mode.
v1.3.2 (in progress)
Added
Progress % in word panel — percentage now prepended before the "W" label
([XX%] W [current] / [total]). Calculated as
Math.round((currentWordIndex / totalWords) * 100) with a 0% guard when
no document is loaded. Uses muted --text-faint token so it recedes behind
the word count without a badge or border.
Source label — small, non-interactive overlay at the top-left of the reading
viewport showing the loaded filename (files) or session title / first line
(pasted text), truncated to 28 characters with a trailing ellipsis. Renders
null when no source is loaded. Hides in focus mode.
Paste text resume — pasted and URL-fetched content now reliably creates an
IndexedDB entry so sessions resume from the correct word position on next visit.
Fixed a pruning-race bug where the pruneTextCacheToNames call ran with a
stale records snapshot that did not yet include the newly created session,
causing the just-saved entry to be immediately deleted.
v1.3.1 (in progress)
Added
InputPanel now wires urlParser.ts for URL inputs (CORS-aware, honest error on blocked sites)
Reset to Beginning shows a 5s undo toast — position fully recoverable
Sign-in prompt fires only at ≥95% document completion, not on every pause
Post-onboarding: hint bar fades in above viewport + Upload button pulses for 4s
What's New converted from blocking modal to collapsible bottom banner
Burger menu pulse delayed 2s and extended to 6s post-onboarding
F key toggles focus mode
Focus mode entry/exit uses staggered fade transitions; "Esc or F to exit" hint fades in for 3s
WPM pill flashes accent briefly on every speed change
iOS back-swipe conflict fixed: left 20px excluded from swipe detection
Screen reader: aria-live="polite" region announces words at ≤300 WPM, silent above
Toasts include ARIA role="status" for screen reader announcement
Peripheral word contrast uses --vp-text-peripheral token (≥4.5:1 per theme) not opacity
HelpModal: F key + touch gesture documentation added
Adaptive speed adjustments now show a toast explaining the change
InputPanel: optional session title auto-populated from first sentence of pasted text
[1.3.0]
Fixed
Page Preview jitter eliminated: active word now uses threshold-based instant scroll
(snaps when past 75% of container) instead of competing smooth-scroll animations
Page Preview text reflow fixed: .word and .activeWord both use font-weight: 600;
active state is differentiated by color only, preventing width-change reflow
Top bar page number chip (topBarReadPos) removed — redundant with viewport overlay
and Page Preview header
Added
"↩ current" return button in Page Preview header — appears when user has browsed
away from the reading position (isDetached); clicking snaps view back to current
reading page and clears detached state
[1.0.6]
Changed
All control buttons unified into one visual system matching +/- style
Progress bar removed — word count and page pill remain
Info row and page navigation merged into one compact row
PLAY/PAUSE is visually primary — accent filled, slightly larger
RESET is visually muted — faint color, no accent hover
Icon + small label below on all action buttons
Controls area reorganised from 3 rows to 2 main rows (info + actions + slim WPM row)
[1.0.5]
Changed
ORP coloring is now a separate toggle from ORP alignment — "Highlight key letter"
ORP color picker replaced with 4 researched science-backed options per theme
All buttons and icon fills now use var(--color-accent) — theme-synchronized
"Words" select removed from burger menu display section (redundant with Custom tab)
Focus mode always enables focal line — removable only in Custom mode
DESIGN_SYSTEM.md updated: color rules, token audit, icon guidelines
[1.0.4]
Added
3 app themes: Midnight (default), Warm, Day
Theme switcher in burger menu (replaces day/night toggle)
Changed
Peripheral fade is now uniform across all context slots (0.45 when ON, 0.65 when OFF)