Commit Graph

75 Commits

Author SHA1 Message Date
Gigi
423ebb403f fix: add retry mechanism for scroll-to-highlight in article content
- 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
2025-10-14 11:24:35 +02:00
Gigi
689963c041 refactor(theme): change default light theme to sepia
- 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
2025-10-14 10:00:21 +02:00
Gigi
129aced1a2 feat(theme): add color theme variants for light and dark modes
- 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)
2025-10-14 09:39:13 +02:00
Gigi
994d834a0b feat(theme): add CSS variable tokens and theme classes
- 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
2025-10-14 09:11:38 +02:00
Gigi
876ecc808d feat: add pull-to-refresh for mobile on all scrollable views
- Create reusable usePullToRefresh hook with touch gesture detection
- Add PullToRefreshIndicator component with visual feedback
- Implement pull-to-refresh on HighlightsPanel (right sidebar)
- Implement pull-to-refresh on Explore page
- Implement pull-to-refresh on Me pages (all tabs)
- Implement pull-to-refresh on BookmarkList (left sidebar)
- Only activates on touch devices for mobile-first experience
- Shows rotating arrow icon that becomes refresh spinner
- Displays contextual messages (pull/release/refreshing)
- Integrates with existing refresh handlers and loading states
2025-10-14 00:47:48 +02:00
Gigi
eadab9a37f fix(highlights): restore scroll-to-highlight functionality with MutationObserver
- 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
2025-10-14 00:19:45 +02:00
Gigi
c7c82954ad feat(highlights): force synchronous render for immediate highlight display
- 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
2025-10-14 00:03:56 +02:00
Gigi
3b639e2783 feat(highlights): clear browser selection immediately after creating highlight
- Add window.getSelection().removeAllRanges() after highlight creation
- Ensures DOM can update immediately without selection interference
- Improves perceived responsiveness of highlight creation
2025-10-14 00:03:19 +02:00
Gigi
0a4bc2cfbb fix(ui): show filename for videos instead of 'Error Loading Content'
- 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
2025-10-13 23:48:11 +02:00
Gigi
1dd2e1dc38 refactor: switch to brighter yellow-300 for highlight defaults and add semantic color aliases 2025-10-13 23:19:33 +02:00
Gigi
e667cf05c2 refactor: update default highlight color to yellow-400 in useSettings 2025-10-13 23:18:13 +02:00
Gigi
e921967082 fix: move progress indicator outside reader and fix position tracking
- Move ReadingProgressIndicator outside reader div for true fixed positioning
- Replace position-indicator library with custom scroll tracking
- Track document scroll position instead of content scroll
- Remove unused position-indicator dependency
- Ensure progress indicator is always visible and shows correct percentage
2025-10-13 21:04:39 +02:00
Gigi
96ce12b952 feat: add reading position tracking with visual progress indicator
- 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
2025-10-13 21:01:44 +02:00
Gigi
b3fc9bb5c3 ux(bookmarks): avoid clearing list when no new events; decouple refetch from route changes 2025-10-12 23:26:18 +02:00
Gigi
68c9623c35 ux(bookmarks): keep list visible during refresh; show spinner only; no wipe 2025-10-12 23:22:53 +02:00
Gigi
e1420140d1 chore(lint): fix eslint and typescript issues; no rule changes 2025-10-12 22:55:46 +02:00
Gigi
ca95d6c7f4 perf(ui): stream results to UI; show cached/local immediately (articles, highlights, explore) 2025-10-12 22:33:46 +02:00
Gigi
418bcb0295 feat(pwa): upgrade to full PWA with vite-plugin-pwa
- 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.
2025-10-11 20:41:49 +01:00
Gigi
a0e30aa197 fix: resolve all linting and type errors
- 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.
2025-10-11 03:23:38 +01:00
Gigi
3a8203d26e fix: mobile button scroll detection on main pane element
- 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.
2025-10-11 01:48:45 +01:00
Gigi
ffe848883e feat: resolve and display article titles for naddr references
- 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
2025-10-11 01:47:11 +01:00
Gigi
8a69d5bc6b feat: resolve NIP-19 identifiers in article content
- 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
2025-10-11 01:42:03 +01:00
Gigi
6783ff23f9 feat: auto-hide mobile buttons on scroll down
- 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
2025-10-11 01:39:24 +01:00
Gigi
d529d83eb8 fix: add touch event support for highlight creation on mobile 2025-10-10 21:24:46 +01:00
Gigi
124d399d1f feat: add mobile sidebar state management to useBookmarksUI 2025-10-10 16:56:19 +01:00
Gigi
e22cf71b15 feat: add media query hooks for responsive design 2025-10-10 16:55:53 +01:00
Gigi
b852dad243 fix: resolve linter errors for unused parameters
- Add eslint-disable comments for intentionally unused _settings parameters
- Parameters kept for API compatibility with existing code
- All linter and type checks now pass
2025-10-09 18:31:08 +01:00
Gigi
1552a5f106 feat: reorganize bookmarks UI - add explore button and move refresh
- 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
2025-10-09 18:29:41 +01:00
Gigi
2e96f93d81 refactor: simplify image caching to use Service Worker only
- 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
2025-10-09 18:24:22 +01:00
Gigi
1e8182d984 feat: add Service Worker for robust offline image caching
- 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
2025-10-09 18:17:27 +01:00
Gigi
b20a67d4d0 fix: improve image cache resilience for offline viewing
- 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
2025-10-09 18:15:30 +01:00
Gigi
d6be6f364b refactor: migrate image cache from localStorage to Cache API
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.
2025-10-09 17:48:59 +01:00
Gigi
43d5554c0c feat: change default image cache size to 210MB
Increase default cache size from 50MB to 210MB for better offline experience
2025-10-09 17:31:53 +01:00
Gigi
507288f51c feat: add image caching for offline mode
- Add imageCacheService with localStorage-based image caching and LRU eviction
- Create useImageCache hook for React components to fetch and cache images
- Integrate image caching with article service to cache cover images on load
- Add image cache settings (enable/disable, size limit) to user settings
- Update ReaderHeader to use cached images for article covers
- Update BookmarkViews (CardView, LargeView) to use cached images
- Add image cache configuration UI in OfflineModeSettings with:
  - Toggle to enable/disable image caching
  - Slider to set cache size limit (10-200 MB)
  - Display current cache stats (size and image count)
  - Clear cache button

Images are cached in localStorage for offline viewing, with a configurable
size limit (default 50MB). LRU eviction ensures cache stays within limits.
2025-10-09 17:23:31 +01:00
Gigi
4e3bb36ea5 perf: reduce relay status polling interval to 20 seconds
Change relay status polling from 5s (default) and 2s (Settings/Indicator) to 20s across the board to reduce CPU usage and network requests
2025-10-09 16:23:59 +01:00
Gigi
3c1114ad21 fix: resolve TypeScript type errors in offline sync
- Import IAccount from applesauce-accounts (not applesauce-core)
- Remove unused account parameter from syncLocalEventsToRemote
- Fix undefined vs null type mismatch in Bookmarks component
- All linter and type checks now pass cleanly
2025-10-09 15:52:34 +01:00
Gigi
ca35e4e7cc fix: plane icon now shows for offline-created highlights
- Add useEffect to watch highlight.isOfflineCreated prop changes
- State now updates when prop changes (not just on initial mount)
- Add isOfflineCreated to console log for easier debugging
- Fixes issue where plane icon wouldn't appear for new offline highlights

The bug was that showOfflineIndicator state was only set once during
component initialization. If the highlight prop didn't have isOfflineCreated
set at that moment, the icon would never appear even if the prop changed later.
2025-10-09 14:05:58 +01:00
Gigi
d294287c64 refactor: use applesauce EventStore for offline event management
Major improvements:
- Store highlights in EventStore immediately when created
- Query EventStore instead of local relays for offline sync
- Pass eventStore to highlight creation service and hooks
- Simplified offline sync: no more relay queries, just EventStore lookups
- More efficient and reliable offline event tracking
- Better integration with applesauce architecture

Benefits:
- Faster sync (no relay queries needed)
- More reliable (events always in EventStore)
- Cleaner code (leveraging applesauce patterns)
- Better separation of concerns
2025-10-09 13:54:47 +01:00
Gigi
4224c989c6 fix: improve offline sync with better tracking and logging
- Track events explicitly when created in offline mode
- Mark highlights as offline-created when isLocalOnly is true
- Add extensive debug logging throughout sync process
- Increase query timeout from 5s to 10s for better reliability
- Add 2-second delay before syncing to allow relays to connect
- Log relay state transitions and event counts
- Log each event received during sync query
- Should help diagnose and fix offline sync issues
2025-10-09 13:49:35 +01:00
Gigi
3330f22f82 fix: clean up sync state tracking in offline sync service
- Remove duplicate state tracking from service
- State transition detection now fully handled by hook
- Fix remaining syncState reference bug
- Simplify sync lock mechanism
2025-10-09 13:38:23 +01:00
Gigi
450776f9d0 feat: automatic offline sync - rebroadcast local events when back online
- Create offlineSyncService to sync local-only events to remote relays
- Create useOfflineSync hook to detect online/offline transitions
- When user comes back online (remote relays connect), automatically:
  - Query local relays for user's events from last 24 hours
  - Rebroadcast highlights and bookmarks to remote relays
- Integrate sync into Bookmarks component
- Enables seamless offline workflow:
  - User can work offline with local relays
  - Events are automatically synced when connection restored
  - No manual intervention required
2025-10-09 13:37:33 +01:00
Gigi
19595d19ca feat: improve font size scale and default
- Change font sizes from [14,16,18,20,22,24] to [16,18,21,24,28,32]
- Larger sizes now more spread out (28px and 32px)
- Set default font size to 21px instead of 18px
- Better progression for reading comfort
2025-10-09 13:19:07 +01:00
Gigi
b055294afc feat: add relay rebroadcast settings for caching and propagation
- Add two new settings:
  - Use local relay(s) as cache (default: enabled)
  - Rebroadcast events to all relays (default: disabled)
- Create rebroadcastService to handle rebroadcasting events
- Hook into article, bookmark, and highlight fetching services
- Automatically rebroadcast fetched events based on settings:
  - Articles when opened
  - Bookmarks when fetched
  - Highlights when fetched
- Add RelayRebroadcastSettings component with plane/globe icons
- Benefits:
  - Local caching for offline access
  - Content propagation across nostr network
  - User control over bandwidth usage
2025-10-09 13:01:38 +01:00
Gigi
bdab9c06e4 fix: make highlight creation resilient to offline/flight mode
- Wrap relay publish in try-catch to handle failures gracefully
- Attempt to publish to local relay even when no relays are connected
- Always return highlight object even if publish fails completely
- Add detailed logging to track publish status and failures
- Mark highlights as local-only when publish fails or only local relays available
- Ensure UI always displays newly created highlights immediately
2025-10-09 12:45:51 +01:00
Gigi
6636d540aa feat: add offline highlight creation with local relay tracking
- Add relay tracking to Highlight type (publishedRelays, isLocalOnly fields)
- Create utility functions to identify local relays (localhost/127.0.0.1)
- Update highlight creation service to track which relays received the event
- Detect when highlights are only on local relays and mark accordingly
- Add visual indicator in UI for local-only highlights with amber badge
- Enable immediate display of highlights created offline
- Ensure highlights work even when only local relay is available
2025-10-09 12:40:04 +01:00
Gigi
39d0147cfa feat(routing): add /settings route and URL-based settings navigation
- Add /settings route to router
- Derive showSettings from location.pathname instead of state
- Update onOpenSettings to navigate to /settings
- Update onCloseSettings to navigate back to previous location
- Track previous location to restore context when closing settings
- Remove showSettings state from useBookmarksUI hook
2025-10-09 12:27:43 +01:00
Gigi
48a9919db8 feat(reader): display article publication date
- Add published field to ReadableContent interface
- Pass published date from article loader through component chain
- Display formatted publication date in ReaderHeader with calendar icon
- Format date as 'MMMM d, yyyy' using date-fns
2025-10-09 12:15:28 +01:00
Gigi
facdd36145 feat(settings): add Relays section showing active and recently connected relays
- Add relayStatusService to track relay connections with 20-minute history
- Add useRelayStatus hook for polling relay status updates
- Create RelaySettings component to display active and recent relays
- Update Settings and ThreePaneLayout to integrate relay status display
- Shows relay connection status with visual indicators and timestamps
2025-10-09 12:09:53 +01:00
Gigi
22a02d228d fix: deduplicate highlights in streaming callbacks
- Replace array accumulation with Map keyed by highlight ID
- Prevents duplicate highlights when same event arrives from multiple relays
- Fix applied in useArticleLoader and useBookmarksData hooks
- Only updates UI when new unique highlight arrives
- Resolves issue where highlights appeared twice in sidebar
2025-10-08 12:55:27 +01:00
Gigi
d642c87527 fix: pass article summary through to ReadableContent
- Add summary field when converting ArticleContent to ReadableContent
- Fix contentLoader.ts to include article.summary
- Fix useArticleLoader.ts to include article.summary
- Article summaries now properly display in reader header
- Resolves missing summary display for kind:30023 articles
2025-10-08 12:41:03 +01:00