- Add reading position loading to Explore component
- Add reading position loading to useBookmarksData hook
- Display progress bars in Explore tab blog posts
- Display progress bars in Bookmarks large preview view
- Progress shown as colored bar (green for completed, orange for in-progress)
- Only shown for kind:30023 articles with saved reading positions
- Requires syncReadingPosition setting to be enabled
- Add 5px threshold to detect when scrolled to bottom
- Set position to exactly 1.0 (100%) when within 5px of bottom
- Remove upper limit on saving positions (now saves 100% completion)
- Always save when reaching 100% completion (important milestone)
- Don't restore position for completed articles (100%), start from top
- Better handling of edge cases in position detection
- Matches ReadingProgressIndicator calculation logic
- Create readingPositionService.ts for save/load operations
- Add syncReadingPosition setting (opt-in via Settings > Layout & Behavior)
- Enhance useReadingPosition hook with auto-save (debounced 5s) and immediate save on navigation
- Integrate position restore in ContentPanel with smooth scroll to saved position
- Support both Nostr articles (naddr) and external URLs
- Reading positions stored privately to user's relays
- Auto-save excludes first 5% and last 5% of content to avoid noise
- Position automatically restored when returning to article
The issue was that useBookmarksData was fetching general highlights
whenever there was no naddr, which included external URL routes (/r/*).
This caused the URL-specific highlights loaded by useExternalUrlLoader
to be overwritten after a couple seconds.
Now we skip fetching general highlights when viewing external URLs,
letting useExternalUrlLoader manage those highlights instead.
- Improve error handling in fetchHighlightsForUrl to prevent silent failures
- Remove redundant setHighlights call that was overwriting streamed highlights
- Add logging to help diagnose highlight fetching issues
- Isolate rebroadcast errors so they don't break highlight display
- Add paragraphAlignment setting (left/justify) to UserSettings interface
- Add UI control with icon buttons in ReadingDisplaySettings
- Apply alignment via CSS variable to reader content and preview
- Default to left-aligned to maintain current behavior
- Keep headings always left-aligned for better readability
- Remove text-shadow from CSS for .publish-date-topright
- Remove shadowColor from useAdaptiveTextColor hook
- Only apply adaptive text color, no shadows or backgrounds
- Cleaner appearance with color-based readability only
- Increase shadow opacity from 0.5 to 0.8 for better readability
- Revert semi-transparent background approach per user feedback
- Keep debugging logs to diagnose color detection
- Remove incorrect await on synchronous getColor method
- Add console logging to debug color detection
- This should fix black-on-black readability issues
- Install fast-average-color library for image color detection
- Create useAdaptiveTextColor hook to analyze top-right image corner
- Update ReaderHeader to dynamically adjust date text/shadow colors
- Ensures publication date is readable on both light and dark backgrounds
- Wrapped toggleSidebar in useCallback to prevent function recreation on every render
- Updated route change effect to only close sidebar on actual pathname changes, not state changes
- Fixes issue where bookmarks sidebar wouldn't stay open on mobile PWA
- Replace @typescript-eslint/no-explicit-any with proper Filter type from nostr-tools/filter in dataFetch.ts and helpers.ts
- Replace @typescript-eslint/no-explicit-any with IAccount and AccountManager types from applesauce-accounts in hooks
- Replace @typescript-eslint/no-explicit-any with unknown type casts in App.tsx for keep-alive subscription
- Fix react-hooks/exhaustive-deps warnings by including all dependencies in useEffect hooks
- Remove unused _settings parameters in useImageCache.ts that were causing no-unused-vars warnings
- Remove unused imports (useRef, faExclamationCircle, getProfileUrl, Observable, UserSettings)
- Remove unused error state and setError calls in Explore and Me components
- Remove unused 'events' variable from exploreService and nostrverseService
- Remove unused '_relays' parameter from saveSettings
- Remove unused '_settings' parameter from publishEvent
- Update all callers of publishEvent and saveSettings to match new signatures
- Add eslint-disable comment for intentional dependency omission in Explore
- Update BookmarkList to use new pull-to-refresh library and RefreshIndicator
- All type checks and linting now pass
- Replace single 100ms delay with retry mechanism
- Try up to 20 times (2 seconds total) to find highlight mark element
- Fixes timing issue when content is still loading from explore page
- Mark elements need time to be rendered after article loads
- Retry every 100ms until element is found or max attempts reached
- Improves reliability of highlight scrolling from external navigation
- Update default from paper-white to sepia for warmer reading
- Midnight remains default for dark mode
- Sepia provides warm, eye-friendly tones for light mode
- Add darkColorTheme: black, midnight (default), charcoal
- Add lightColorTheme: paper-white (default), sepia, ivory
- Extend UserSettings with color theme fields
- Update ThemeSettings UI to show color options
- Add CSS variables for all color theme variants
- Sepia and Ivory have warm, reading-friendly palettes
- Black offers true black for OLED screens
- All color themes sync via Nostr (NIP-78)
- Define semantic color tokens (--color-bg, --color-text, etc.)
- Add .theme-dark, .theme-light, .theme-system CSS classes
- Create theme.ts utility for theme application
- Add early boot theme script to prevent FOUC
- Support system preference with live updates
- Add MutationObserver to detect when highlights are added/removed from DOM
- Use contentVersion state to trigger re-attachment of click handlers
- Add contentVersion dependency to scroll effect so it re-runs after DOM updates
- Add 100ms delay to scroll effect to ensure DOM is fully rendered
- Add warning log when mark element cannot be found
- Fixes issue where clicking highlights in sidebar wouldn't scroll after DOM changes
- Use flushSync to force React to render synchronously after highlight creation
- Eliminates render cycle delay for instant visual feedback
- Highlights now appear immediately in the text when created
- Add window.getSelection().removeAllRanges() after highlight creation
- Ensures DOM can update immediately without selection interference
- Improves perceived responsiveness of highlight creation
- Add getFilenameFromUrl helper to extract filename from URL
- Use filename as title when content loading fails (e.g., for video files)
- Decode URI component to handle special characters in filenames
- Improves UX for video and media file viewing
- Install position-indicator library for scroll position tracking
- Create useReadingPosition hook for position management
- Add ReadingProgressIndicator component with animated progress bar
- Integrate reading progress in ContentPanel for text content only
- Add CSS styles for fixed progress indicator with shimmer animation
- Track reading completion at 90% threshold
- Exclude video content from position tracking
- Add web app manifest with proper metadata and icon support
- Configure vite-plugin-pwa with injectManifest strategy
- Migrate service worker to Workbox with precaching and runtime caching
- Add runtime caching for cross-origin images (preserves existing behavior)
- Add runtime caching for cross-origin article HTML for offline reading
- Create PWA install hook and UI component in settings
- Add online/offline status monitoring and toast notifications
- Add service worker update notifications
- Add placeholder PWA icons (192x192, 512x512, maskable variants)
- Update HTML with manifest link and theme-color meta tag
- Preserve existing relay/airplane mode functionality (WebSockets not intercepted)
The app now passes PWA installability criteria while maintaining all existing
offline functionality. Icons should be replaced with proper branded designs.
- Remove unused React import from nostrUriResolver
- Add block scoping to switch case statements
- Add react-hooks plugin to eslint config
- Fix exhaustive-deps warnings in components
- Fix DecodeResult type to use ReturnType<typeof decode>
- Update dependency arrays to include all used values
- Add eslint-disable comment for intentional dependency omission
All linting warnings resolved. TypeScript type checking passes.
- Update useScrollDirection to accept elementRef parameter
- Detect scroll on main pane div instead of window
- Create mainPaneRef and attach to scrollable content area
- Fix issue where scroll events weren't detected on mobile
On mobile, content scrolls within .pane.main (overflow-y: auto) not on window.
Now buttons properly hide on scroll down and show on scroll up.
- Add articleTitleResolver service to fetch article titles from relays
- Extract naddr identifiers from markdown content
- Fetch article titles in parallel using relay pool
- Replace naddr references with actual article titles
- Fallback to identifier if title fetch fails
- Update markdown processing to be async for title resolution
- Pass relayPool through component tree to enable resolution
Example: nostr:naddr1... now shows as "My Article Title" instead of "article:identifier"
Improves readability by showing human-friendly article titles in cross-references
- Add nostrUriResolver utility to detect and replace nostr: URIs
- Support npub, note, nprofile, nevent, and naddr identifiers
- Convert nostr: URIs to clickable njump.me links
- Process markdown before rendering to handle nostr mentions
- Add CSS styling for nostr-uri-link class
- Implements NIP-19 and NIP-27 (nostr: URI scheme)
Nostr-native articles can now contain references like:
- nostr:npub1... → @npub1abc...
- nostr:note1... → note:note1abc...
- nostr:naddr1... → article:identifier
All identifiers become clickable links to njump.me
- Add useScrollDirection hook for scroll direction detection
- Hide bookmark and highlight buttons when scrolling down
- Show buttons again when scrolling up
- Smooth opacity transitions for better UX
- Only detect scroll when buttons are visible
- Improves mobile reading experience by maximizing content area
- Add eslint-disable comments for intentionally unused _settings parameters
- Parameters kept for API compatibility with existing code
- All linter and type checks now pass
- Move refresh button from top bar to end of bookmarks list
- Show relative time of last fetch next to refresh button
- Add 'Explore' button (fa-newspaper icon) to top bar that links to /explore
- Track lastFetchTime in useBookmarksData hook
- Better UX with explore more prominent and refresh less intrusive
- Remove complex Cache API management with blob URLs and metadata
- useImageCache now simply returns the URL (Service Worker handles caching)
- imageCacheService reduced to just stats and clear functions
- Service Worker automatically caches all images on fetch
- Much simpler, DRY code that 'just works' for offline mode
- Stats now read directly from Cache API instead of localStorage metadata
- Implement Service Worker to intercept and cache image requests
- Service Worker persists across hard reloads unlike Cache API alone
- Simplify useImageCache hook to work with Service Worker
- Images now work offline even after hard reload
- Service Worker handles transparent cache-first serving for images
- Clean up stale metadata when Cache API doesn't have cached data
- Handle online/offline state properly in image loading
- Show original URL when online, blob URL from cache when offline
- Prevent cache misses when browser clears Cache API on hard reload
BREAKING CHANGE: Image cache now uses Cache API instead of localStorage
Benefits:
- Support for actual 210MB cache size (localStorage limited to 5-10MB)
- Store native Response objects (no base64 overhead)
- Asynchronous, non-blocking operations
- Better suited for large binary blobs like images
- Can handle hundreds of MB to several GB
Changes:
- Rewrite imageCacheService to use Cache API for image storage
- Keep metadata in localStorage for LRU tracking (small footprint)
- Update useImageCache hook to handle async Cache API
- Add blob URL cleanup to prevent memory leaks
- Update clearImageCache to async function
The cache now works as advertised and won't hit quota limits.