Commit Graph

62 Commits

Author SHA1 Message Date
Gigi
0fa5ac536b feat: add three-dot menu to articles and enhance highlight menus
- Add three-dot menu button at end of articles (before Mark as Read)
- Right-aligned menu with two options:
  - Open on Nostr (using nostr gateway/portal)
  - Open with Native App (using nostr: URI scheme)
- Add 'Open with Native App' option to highlight card menus
- Menu only appears for nostr-native articles (kind:30023)
- Styled consistently with highlight card menus
- Click outside to close menu functionality
2025-10-13 17:03:00 +02:00
Gigi
2de72b73c1 feat: add Prism.js syntax highlighting for code blocks
- Install prismjs and rehype-prism-plus packages
- Integrate rehype-prism plugin into ReactMarkdown
- Use prism-tomorrow dark theme for syntax highlighting
- Enhanced code block styling with better padding and borders
- Inline code now has distinct styling from code blocks
- Monospace font for all code (Monaco, Menlo, Consolas)
- Improved readability with proper line-height and spacing
2025-10-13 16:55:06 +02:00
Gigi
03e7484e71 fix: preserve reading font settings in markdown images
- Remove inline styles from custom image component
- Let CSS inheritance handle font and styling properly
- Images now respect user's reading font and size settings
2025-10-13 16:35:11 +02:00
Gigi
d9fd4ec286 feat: enable inline image rendering in nostr-native blog posts
- Install rehype-raw plugin for HTML support in ReactMarkdown
- Configure ReactMarkdown to parse and render HTML img tags
- Add responsive image styling with max-width and auto height
- Images now render inline in nostr-native blog posts with proper styling
2025-10-13 16:34:08 +02:00
Gigi
326d571871 fix(content): include currentArticle in useEffect deps to satisfy lint 2025-10-13 16:27:18 +02:00
Gigi
7b0bd7077c feat: use faBooks icon for Mark as Read button 2025-10-13 12:29:23 +02:00
Gigi
6c0a2439ad feat: instant mark-as-read with checkmark animation and read status checking
- Add functions to check if article/URL was already marked as read via NIP-25 reactions
- Make mark-as-read action instant with fire-and-forget publishing
- Add checkmark icon animation when marking as read
- Display read status on load by querying kind:7 (nostr events) and kind:17 (websites) reactions
- Add green styling for already-read state
- Button shows checkmark and is disabled when article is already marked as read
2025-10-13 10:17:16 +02:00
Gigi
1d90333803 feat: apply highlight group color to highlight count indicator
- Update ReaderHeader to receive highlights and highlightVisibility props
- Calculate dominant color based on visible highlight groups
- Apply color priority: nostrverse > friends > mine
- Highlight count indicator now reflects the active group colors
2025-10-12 23:47:05 +02:00
Gigi
096478bcec feat: add author info card for nostr-native articles
- Create AuthorCard component showing profile picture and bio
- Display author card after mark as read button
- Only shown for nostr-native articles (not external URLs)
- Fetch author profile data using applesauce ProfileModel
- Card displays author name, avatar, and bio (truncated to 3 lines)
- Responsive design with smaller avatar on mobile
- Elegant card styling matching app design system

Author information helps readers learn more about article authors
directly within the reading experience.
2025-10-11 08:55:37 +01:00
Gigi
6a6b8c4fad feat: add mark as read button for articles
- Create reactionService for handling kind:7 and kind:17 reactions
- Add mark as read button at the end of articles (📚 emoji)
- Use kind:7 reaction for nostr-native articles (/a/ paths)
- Use kind:17 reaction for external websites (/r/ paths)
- Pass activeAccount and currentArticle props through component tree
- Add responsive styling for mark as read button
- Button shows loading state while creating reaction
- Only visible when user is logged in

Implements NIP-25 (kind:7 reactions) and NIP-25 (kind:17 website reactions).
Users can now mark articles as read, creating a permanent record on nostr.
2025-10-11 08:34:36 +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
d529d83eb8 fix: add touch event support for highlight creation on mobile 2025-10-10 21:24:46 +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
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
fea425b5d0 feat: display article summary in header
- Add summary field to ReadableContent interface
- Pass summary through ContentPanel to ReaderHeader
- Display summary below title in both overlay and standard layouts
- Style summary with reading font for consistency
- Summary appears in white with shadow in image overlays
- Summary appears in gray (#aaa) in standard headers
- Enhances article preview and reading experience
2025-10-08 12:35:05 +01:00
Gigi
0c3e697df6 fix(reader): wire preview ref to markdown conversion hook
- Update useMarkdownToHTML to return {renderedHtml, previewRef}
- Use returned previewRef in ContentPanel hidden markdown preview
- Resolves article markdown not rendering instantly
2025-10-07 22:16:31 +01:00
Gigi
ac71d0b5a4 refactor(content): extract content rendering hooks
- Create useMarkdownToHTML hook for markdown conversion
- Create useHighlightedContent hook for highlight processing
- Create useHighlightInteractions hook for highlight interactions
- Reduce ContentPanel.tsx from 294 lines to 159 lines
2025-10-07 21:52:05 +01:00
Gigi
d9db10fd70 fix: improve highlight rendering pipeline with comprehensive debugging
- Add extensive logging to track highlight rendering through entire pipeline
- Fix markdown rendering to wait for HTML conversion before displaying
- Prevent fallback to non-highlighted markdown during initial render
- Add debugging to URL filtering to identify matching issues
- Add logging to highlight application to track matching success/failures
- Ensure highlights are always applied when content is ready
- Show mini loading spinner while markdown is being converted

This will help diagnose and fix cases where highlights aren't showing up.
2025-10-06 19:56:20 +01:00
Gigi
2f2e19fdf9 fix: move FAB to Bookmarks component for proper floating
Move HighlightButton outside .pane.main container by rendering it in Bookmarks component. This bypasses the CSS contain property that was preventing position:fixed from working properly. FAB now floats correctly in bottom-right corner.
2025-10-05 23:14:24 +01:00
Gigi
ce99600aa9 fix: move FAB outside reader container for proper viewport-fixed positioning
The CSS contain property on .reader was creating a new containing block that broke position:fixed. Moving FAB outside allows it to float properly.
2025-10-05 23:11:52 +01:00
Gigi
cc68e67726 refactor: change highlight button to FAB style
- Replace floating popup button with persistent FAB in bottom-right corner
- Button always visible but disabled when no text is selected
- Uses user's highlight color from settings
- Visual feedback: scales up and becomes opaque when text is selected
- Follows Google apps design pattern for floating action buttons
2025-10-05 23:09:36 +01:00
Gigi
290d9303b5 feat: add simple highlight creation feature
- Create HighlightButton component that appears on text selection
- Add highlightCreationService using EventFactory and HighlightBlueprint
- Integrate highlight button into ContentPanel with text selection detection
- Update Bookmarks to pass required props and refresh highlights after creation
- Publish highlights to NIP-84 relays automatically
- Only show button when user is logged in
2025-10-05 23:03:23 +01:00
Gigi
9252078fb7 refactor: rename 'underlines' to 'highlights' throughout codebase 2025-10-05 22:52:42 +01:00
Gigi
a8e48ba280 feat: sync highlight level toggles between sidebar and main article text 2025-10-05 22:49:07 +01:00
Gigi
ce2ccd54b3 fix(lint): resolve all linting and TypeScript errors 2025-10-05 22:12:07 +01:00
Gigi
e710391962 style(ui): replace all loading text with spinners per fontawesome rule 2025-10-05 22:02:01 +01:00
Gigi
d3ad08dd61 refactor(reader): extract ReaderHeader to keep ContentPanel concise (<210 lines) 2025-10-05 21:46:31 +01:00
Gigi
d148433fcc fix(content): render markdown immediately while computing highlights; prevent initial login refresh from overwriting article highlights 2025-10-05 21:45:47 +01:00
Gigi
8d7b853e75 fix: ensure highlights always render on markdown content
- Add logic to wait for HTML conversion when highlights need to be applied
- Prevent rendering plain markdown when highlights are pending
- Show ReactMarkdown fallback only when no highlights need to be applied
- Fixes default article highlights not showing
2025-10-05 21:15:30 +01:00
Gigi
65e7709c63 fix: remove Highlights title and count from panel, fix markdown rendering
- Remove 'Highlights' text and count number to save space in panel
- Fix markdown rendering fallback to always show content when finalHtml is not ready
- Simplify render logic by removing highlight count condition that prevented content display
2025-10-05 20:17:23 +01:00
Gigi
7f95eae405 fix: ensure highlights are shown for markdown content
- Only show raw ReactMarkdown when there are no highlights
- Wait for finalHtml (with highlights) when highlights are present
- Prevents highlights from being bypassed during markdown conversion
2025-10-05 20:01:41 +01:00
Gigi
8e0970b717 fix: show markdown content immediately when finalHtml is empty
- Render markdown directly with ReactMarkdown when finalHtml is not ready yet
- Prevents empty content display while markdown is being converted to HTML
- Fixes issue where default article text doesn't show
2025-10-05 19:06:53 +01:00
Gigi
320e7f000a fix: prevent 'No readable content' flash for markdown articles
- Check for markdown/html existence before checking finalHtml
- Show empty container while markdown is being converted to HTML
- Fixes issue where nostr blog posts briefly showed error message
2025-10-05 13:34:38 +01:00
Gigi
832740fb59 fix: enable highlights display and scroll-to for markdown content
- Convert markdown to HTML before applying highlights
- Use hidden ReactMarkdown preview to render markdown
- Apply highlights to rendered HTML for both HTML and markdown content
- Fix scroll-to-highlight functionality for nostr blog posts (kind:30023)
- Ensure highlight marks are properly injected into markdown-rendered content
2025-10-05 13:28:49 +01:00
Gigi
e8f44986da feat: display article hero images in bookmark views and reader
- Add image prop to ContentPanel to display hero images
- Extract image tag from kind:30023 bookmark tags
- Display article images in Card, Large, and Compact views
- Show hero image at top of article reader view
- Add CSS styling for article-hero-image and reader-hero-image
- Article images clickable to open article in reader
- Per NIP-23: image tag contains header/preview image URL
2025-10-05 08:22:46 +01:00
Gigi
07a5826774 refactor: extract components to keep files under 210 lines
- Extract ColorPicker component from Settings
- Extract FontSelector component from Settings
- Move hexToRgb helper to colorHelpers utils
- Export HIGHLIGHT_COLORS constant from colorHelpers
- Settings.tsx now 209 lines (was 242)
- ContentPanel.tsx now 197 lines (was 204)

Keeps code DRY and improves maintainability
2025-10-05 04:17:03 +01:00
Gigi
e4b6d1a122 feat: add configurable highlight colors
- Add highlightColor setting with 6 preset colors (yellow, orange, pink, green, blue, purple)
- Implement color picker UI with square color swatches
- Use CSS variables to dynamically apply highlight colors
- Add hex to RGB conversion for color transparency support
- Update both marker and underline styles to use selected color
2025-10-05 04:12:31 +01:00
Gigi
b59a295ad3 feat: add highlight style setting (marker & underline) 2025-10-05 04:08:58 +01:00
Gigi
a95c0ed3ff refactor: reduce file sizes to meet 210 line limit
- Extract URL normalization to urlHelpers utility (DRY)
- Condense Settings.tsx from 212 to 190 lines
  - Inline IconButton props on single lines
  - Shorten preview text
- Condense ContentPanel.tsx from 223 to 190 lines
  - Extract filterHighlightsByUrl function
  - Remove unnecessary logic
- All files now under 210 line limit
- All lint and type checks pass
2025-10-05 04:02:49 +01:00
Gigi
188147d057 fix: update originalHtmlRef when content changes
- Remove faulty conditional that prevented HTML ref from updating
- Now properly stores fresh content when switching articles
- Fixes issue where articles weren't switching properly
2025-10-05 03:42:28 +01:00
Gigi
91fe1711cd fix: move readingStats hook before early returns
- Fixes React Hooks order violation
- All hooks must be called unconditionally in the same order
- Moved readingStats useMemo before the conditional returns
- Resolves 'Rendered more hooks than during the previous render' error
2025-10-05 03:38:42 +01:00
Gigi
cc0b27f7cd fix: replace custom reading time with reading-time-estimator package
- Remove custom readingTime.ts implementation
- Install reading-time-estimator package (browser-compatible)
- Update ContentPanel to use named import from reading-time-estimator
- Fixes browser compatibility issues with reading-time package
- All linting and type checks pass
2025-10-05 03:35:22 +01:00
Gigi
2719ad3602 feat: add reading time estimate to articles
- Install reading-time package
- Calculate reading time from article content (html or markdown)
- Display reading time with clock icon in article header
- Strip HTML tags for accurate word count
- Style reading-time indicator similar to highlights
- Shows estimated reading time (e.g., '5 min read')
2025-10-05 03:28:36 +01:00
Gigi
9a1efd5b18 fix: prevent duplicate highlight application
- Store original HTML in ref to prevent re-highlighting already highlighted content
- Separate highlight application from click handler attachment effects
- Remove onHighlightClick from highlight application dependencies
- Remove verbose console logging for cleaner code
- Highlights now apply correctly without stacking on top of each other
2025-10-05 03:09:59 +01:00
Gigi
5f8d4b2c47 fix: delay pulse animation to let scroll complete first
- Add 500ms delay before starting pulse animation
- Prevents pulse from starting while element is still scrolling
- Creates better visual flow: scroll → pause → pulse
- Makes the highlight easier to track with your eyes

The pulse now starts after the smooth scroll completes,
making it much clearer which highlight you jumped to.
2025-10-05 02:10:17 +01:00
Gigi
a4c15ecc0e feat: add pulsing animation when scrolling to highlight
- Replace brightness change with subtle pulse animation
- Pulse twice over 1.5 seconds with scale and glow effects
- Scale slightly (1.02x) and increase shadow glow
- More elegant visual feedback than color change
- Easier to spot without being jarring

The highlight now pulses twice when clicked from the
sidebar, making it easy to see where you've jumped to.
2025-10-05 02:08:51 +01:00
Gigi
967aac49ef fix: scroll to highlight in article when clicking sidebar item
- Add selectedHighlightId prop to ContentPanel
- Add useEffect to watch for selectedHighlightId changes
- Find and scroll to the corresponding mark element
- Temporarily brighten the highlight for visual feedback
- Pass selectedHighlightId from Bookmarks to ContentPanel

Now clicking a highlight in the sidebar properly scrolls
to and highlights the text in the article view.
2025-10-05 02:07:05 +01:00
Gigi
05636046a8 feat: add click-to-scroll for highlights
- Clicking a highlight in the main text scrolls to it in the sidebar
- Selected highlight is visually highlighted with border and shadow
- Add selectedHighlightId state management in Bookmarks component
- Add click handlers to mark elements in ContentPanel
- Add isSelected prop to HighlightItem with scroll-into-view
- Add CSS styles for selected highlight state
- Set cursor to pointer on clickable highlights

Users can now click on highlighted text to jump to the corresponding
highlight in the right sidebar for easy navigation.
2025-10-04 22:21:43 +01:00
Gigi
e98dc1c5da fix: resolve all linting and type errors
- Remove unused applyHighlightsToText import from ContentPanel
- Replace while(true) with proper condition in findHighlightMatches
- Remove unused match parameter from replaceTextWithMark function

All ESLint and TypeScript checks now pass with no errors.
2025-10-04 22:13:31 +01:00