mirror of
https://github.com/dergigi/boris.git
synced 2026-02-16 12:34:41 +01:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f2b70779b | ||
|
|
cc9cc47b51 | ||
|
|
a19cb8b6dc | ||
|
|
c564d1608b | ||
|
|
c146a8f7ec | ||
|
|
48cde27a5b | ||
|
|
fdf0644bbb | ||
|
|
ec7371c43b | ||
|
|
35204ee400 | ||
|
|
d1031b3342 | ||
|
|
db67e94b9e | ||
|
|
a0e5ba3a63 | ||
|
|
f3f80449a6 | ||
|
|
bd0b4e848f | ||
|
|
4f5ba99214 | ||
|
|
aab67d8375 | ||
|
|
dbc0a48194 | ||
|
|
6a84646b0b | ||
|
|
e921967082 | ||
|
|
ec34bc3d04 | ||
|
|
96ce12b952 | ||
|
|
1066c43d6c |
184
CHANGELOG.md
184
CHANGELOG.md
@@ -7,9 +7,72 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Tailwind CSS integration with preflight enabled
|
||||
- Reading position tracking with visual progress indicator
|
||||
- Document-level scrolling with sticky sidebars on desktop
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactored layout system to use document scroll instead of pane scroll
|
||||
- Migrated reading progress indicator to Tailwind utilities
|
||||
- Simplified global CSS to work with Tailwind preflight
|
||||
- Added CSS variables for user-settable theme colors
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reading position indicator now always visible at bottom of screen
|
||||
- Progress tracking now accurately reflects reading position
|
||||
- Scroll behavior consistent across desktop and mobile
|
||||
|
||||
## [0.5.7] - 2025-01-14
|
||||
|
||||
### Added
|
||||
|
||||
- Vimeo video metadata extraction support
|
||||
- YouTube video metadata extraction with title, description, and captions
|
||||
- Responsive video player with aspect ratio support
|
||||
- Thumbnail images in compact view
|
||||
- URL routing for /me page tabs
|
||||
- Bookmark navigation in reading list
|
||||
- Video duration display for video URLs
|
||||
- Three-dot menu for videos with open/native/copy/share actions
|
||||
- External video embedding in reader using react-player
|
||||
- Video detection for Vimeo, Dailymotion, and other platforms
|
||||
|
||||
### Changed
|
||||
|
||||
- Enhanced borders for reading list cards
|
||||
- Reading list tab colored blue to match bookmarks icon
|
||||
- Left-aligned text in reading list elements
|
||||
- Increased spacing between mobile buttons and profile element
|
||||
- Main pane now full width when displaying videos
|
||||
- Video container breaks out of reader padding for full width
|
||||
- Simplified video container layout
|
||||
|
||||
### Fixed
|
||||
|
||||
- Video player edge-to-edge display with negative margins
|
||||
- Prevent profile element from bleeding off screen on mobile
|
||||
- Resolved TypeScript errors in youtube-meta.ts
|
||||
- Improved type safety in youtube-meta handler
|
||||
- More lenient YouTube description extraction
|
||||
- Corrected setTimeout ref type in Settings
|
||||
- Proper react-player responsive pattern implementation
|
||||
- Removed unused getIconForUrlType in CompactView
|
||||
|
||||
### Style
|
||||
|
||||
- Hide tab counts on mobile for /me page
|
||||
- Remove max-width on main pane, constrain reader instead
|
||||
- Full width layout for videos
|
||||
- Reader-video specific styles
|
||||
|
||||
## [0.5.6] - 2025-10-13
|
||||
|
||||
### Added
|
||||
|
||||
- Three-dot menu for articles and enhanced highlight menus
|
||||
- Prism.js syntax highlighting for code blocks
|
||||
- Inline image rendering in nostr-native blog posts
|
||||
@@ -17,23 +80,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Caching on `/me` page for faster loading
|
||||
|
||||
### Changed
|
||||
|
||||
- Reading List on `/me` now uses the same components as the bookmarks sidebar
|
||||
- Improve bookmarks sidebar visual design
|
||||
- Make article menu button more subtle by removing border
|
||||
|
||||
### Fixed
|
||||
|
||||
- Use round checkmark icon (faCheckCircle) for Mark as Read button
|
||||
- Remove extra horizontal divider above article menu
|
||||
- Ensure code blocks consistently use monospace fonts
|
||||
- Preserve reading font settings in markdown images
|
||||
|
||||
### Style
|
||||
|
||||
- Remove horizontal divider above Mark as Read button
|
||||
- Remove horizontal divider below article menu button
|
||||
|
||||
## [0.5.5] - 2025-01-27
|
||||
|
||||
### Added
|
||||
|
||||
- `/me` page with tabbed layout featuring Highlights, Reading List, and Library tabs
|
||||
- Two-pane layout for `/me` page with article sources and highlights
|
||||
- Custom FontAwesome Pro books icon for Archive tab
|
||||
@@ -41,6 +108,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Instant mark-as-read functionality with checkmark animation and read status checking
|
||||
|
||||
### Changed
|
||||
|
||||
- Rename Library tab to Archive
|
||||
- Move highlight timestamp to top-right corner of cards
|
||||
- Replace username with AuthorCard component on `/me` page
|
||||
@@ -50,6 +118,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Make quote icon a CompactButton in top-left corner
|
||||
|
||||
### Fixed
|
||||
|
||||
- Include currentArticle in useEffect deps to satisfy lint
|
||||
- Deduplicate article events in library to prevent showing duplicates
|
||||
- Remove incorrect useSettings hook usage in Me component
|
||||
@@ -61,6 +130,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Position relay indicator in bottom-left corner to prevent overlap with author
|
||||
|
||||
### Style
|
||||
|
||||
- Match `/me` profile card width to highlight cards
|
||||
- Improve Me page mobile tabs and avoid overlap with sidebar buttons
|
||||
- Reduce margins/paddings to make highlight cards more compact
|
||||
@@ -75,6 +145,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.5.4] - 2025-10-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactor CSS into modular structure
|
||||
- Split 3600+ line monolithic `index.css` into organized modules
|
||||
- Created `src/styles/` directory with base, layout, components, and utils subdirectories
|
||||
@@ -83,11 +154,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- No functional changes to styling
|
||||
|
||||
### Fixed
|
||||
|
||||
- Mobile button positioning now uses safe area insets for symmetrical layout on notched devices
|
||||
|
||||
## [0.5.3] - 2025-10-13
|
||||
|
||||
### Changed
|
||||
|
||||
- Relay status indicator is now more compact
|
||||
- Smaller padding and font sizes on desktop
|
||||
- Auto-collapsed on mobile (icon-only by default, tap to expand)
|
||||
@@ -95,6 +168,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Hides when scrolling down, shows when scrolling up (consistent with other mobile controls)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Invalid bookmarks without IDs no longer appear in bookmark list
|
||||
- Previously showed as "Now" timestamp with no content
|
||||
- Bookmarks without valid IDs are now filtered out entirely
|
||||
@@ -104,12 +178,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.5.2] - 2025-10-12
|
||||
|
||||
### Added
|
||||
|
||||
- Three-dot menu to highlight cards for more compact UI
|
||||
- Combines "Open on Nostr" and "Delete" actions into dropdown menu
|
||||
- Uses horizontal ellipsis icon (⋯)
|
||||
- Click-outside functionality to close menu
|
||||
|
||||
### Changed
|
||||
|
||||
- Switch Nostr gateway from njump.me/search.dergigi.com to ants.sh
|
||||
- Centralized gateway URLs in config file
|
||||
- All profile and event links now use ants.sh
|
||||
@@ -118,34 +194,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- "Open on Nostr" now links to the highlight event itself instead of the article
|
||||
|
||||
### Fixed
|
||||
|
||||
- Gateway URL routing for ants.sh requirements (/p/ for profiles, /e/ for events)
|
||||
- Linting errors in HighlightItem component
|
||||
|
||||
## [0.5.1] - 2025-10-12
|
||||
|
||||
### Added
|
||||
|
||||
- Highlight color customization to UI elements
|
||||
- Apply user's "my highlights" color to highlight creation buttons
|
||||
- Apply highlight group colors to highlight count indicators
|
||||
- Apply "my highlights" color to collapsed highlights panel button
|
||||
|
||||
### Fixed
|
||||
|
||||
- Highlight count indicator styling to match reading-time element
|
||||
- Brightness and border styling for highlight count indicator
|
||||
- User highlight color now applies to both marker and arrow icons
|
||||
- Highlight group color properly applied to count indicator background
|
||||
|
||||
### Removed
|
||||
|
||||
- MOBILE_IMPLEMENTATION.md documentation file
|
||||
|
||||
## [0.5.0] - 2025-10-12
|
||||
|
||||
### Added
|
||||
|
||||
- Upgrade to full PWA with `vite-plugin-pwa`
|
||||
- Replace placeholder icons with branded favicons
|
||||
- Author info card for nostr-native articles
|
||||
|
||||
### Changed
|
||||
|
||||
- Explore: shrink refresh spinner footprint; inline-sized loading row
|
||||
- Explore: preserve posts across navigations; seed from cache; merge streamed and final results
|
||||
- Explore: keep posts visible during refresh; inline spinner; no list wipe
|
||||
@@ -155,12 +237,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Lint/TypeScript: satisfy react-hooks dependencies; fix worker typings; clear ESLint/TS issues
|
||||
|
||||
### Fixed
|
||||
|
||||
- Highlights: merge remote results after local for article/url
|
||||
- Explore: always query remote relays after local; stream merge into UI
|
||||
- Improve mobile touch targets for highlight icons
|
||||
- Color `/me` highlights with "my highlights" color setting
|
||||
|
||||
### Performance
|
||||
|
||||
- Local-first then remote follow-up across services (titles, bookmarks, highlights)
|
||||
- Run local and remote fetches concurrently; stream and dedupe results
|
||||
- Stream contacts and early posts from local; merge remote later
|
||||
@@ -168,6 +252,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Stream results to UI; display cached/local immediately (articles, highlights, explore)
|
||||
|
||||
### Documentation
|
||||
|
||||
- PWA implementation summary and launch checklist updates
|
||||
- Update docs to reflect branded icons and final steps
|
||||
- Remove temporary PWA launch checklist and implementation summary
|
||||
@@ -175,6 +260,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.4.3] - 2025-10-11
|
||||
|
||||
### Added
|
||||
|
||||
- Mark as read functionality for articles (NIP-25)
|
||||
- Button at the end of each article to mark as read with 📚 emoji
|
||||
- Creates kind:7 reactions for nostr-native articles (`/a/` paths)
|
||||
@@ -199,6 +285,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Prevents accidental destructive actions
|
||||
|
||||
### Changed
|
||||
|
||||
- Relay status indicator on mobile now displays in compact mode
|
||||
- Shows only airplane icon by default (44x44px touch target)
|
||||
- Tap to expand for full connection details
|
||||
@@ -209,6 +296,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.4.2] - 2025-10-11
|
||||
|
||||
### Added
|
||||
|
||||
- NIP-19 identifier resolution in article content (NIP-19, NIP-27)
|
||||
- Support for `nostr:npub1...`, `nostr:note1...`, `nostr:nprofile1...`, `nostr:nevent1...`, `nostr:naddr1...`
|
||||
- Converts nostr: URIs to clickable links with human-readable labels
|
||||
@@ -223,6 +311,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Configurable threshold and enable/disable options
|
||||
|
||||
### Changed
|
||||
|
||||
- Article references (`naddr`) now link internally to `/a/{naddr}` instead of external njump.me
|
||||
- Sidebar auto-closes on mobile when navigating to content via routes
|
||||
- Handles clicking on blog posts in Explore view
|
||||
@@ -231,6 +320,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Article title resolution fetches titles in parallel for better performance
|
||||
|
||||
### Fixed
|
||||
|
||||
- Mobile button scroll detection now correctly monitors main pane element
|
||||
- Previously monitored window scroll which didn't work on mobile
|
||||
- Content scrolls within `.pane.main` div on mobile devices
|
||||
@@ -243,11 +333,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.4.1] - 2025-10-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Long article summaries overlapping with hero image content on mobile devices
|
||||
- Article summary now moves below hero image on mobile when longer than 150 characters
|
||||
- Article summary line clamp reduced from 3 to 2 lines on mobile for better space utilization
|
||||
|
||||
### Changed
|
||||
|
||||
- Hero image rendering on mobile now uses zoom-to-fit approach with viewport-based sizing
|
||||
- Hero image height on mobile set to 50vh (constrained between 280px-400px)
|
||||
- Improved image cropping with center positioning for better visual presentation
|
||||
@@ -256,6 +348,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.4.0] - 2025-10-10
|
||||
|
||||
### Added
|
||||
|
||||
- Mobile-responsive design with overlay sidebar drawer
|
||||
- Media query hooks for responsive behavior (`useIsMobile`, `useIsTablet`, `useIsCoarsePointer`)
|
||||
- Auto-collapse sidebar setting for mobile devices
|
||||
@@ -270,6 +363,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Mobile highlights panel as overlay with toggle button
|
||||
|
||||
### Changed
|
||||
|
||||
- Sidebar now displays as overlay drawer on mobile (≤768px)
|
||||
- Highlights panel hidden on mobile for better content focus
|
||||
- Sidebar auto-closes when selecting content on mobile
|
||||
@@ -277,6 +371,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Replace hamburger icon with bookmark icon on mobile
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ensure bookmarks container fills mobile sidepane properly
|
||||
- Restore desktop grid layout for highlights panel
|
||||
- Improve empty state and loading visibility in mobile sidepanes
|
||||
@@ -288,22 +383,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.3.8] - 2025-10-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Add vercel.json configuration to properly handle SPA routing on Vercel deployments (fixes 404 errors on page refresh)
|
||||
|
||||
## [0.3.7] - 2025-10-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Logout button functionality - now properly clears active account using clearActive() method
|
||||
|
||||
## [0.3.6] - 2025-10-10
|
||||
|
||||
### Added
|
||||
|
||||
- Compact date format for highlights (now, 5m, 3h, 2d, 1mo, 1y)
|
||||
- Ultra-compact date format for bookmarks sidebar
|
||||
- Encode event links as nevent/naddr per NIP-19 for better client compatibility
|
||||
- Render /explore within ThreePaneLayout to keep side panels visible
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remove incorrect padding-right from highlights container
|
||||
- Reduce font size of highlight metadata for cleaner look
|
||||
- Position highlight FAB button relative to article pane instead of viewport
|
||||
@@ -315,6 +414,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Change explore header icon from compass to newspaper
|
||||
|
||||
### Changed
|
||||
|
||||
- Make connecting notification more subtle with muted blue background
|
||||
- Update Boris pubkey for zap splits to npub19802see0gnk3vjlus0dnmfdagusqrtmsxpl5yfmkwn9uvnfnqylqduhr0x
|
||||
- Update domain references to read.withboris.com (URLs, SEO metadata, and documentation)
|
||||
@@ -322,20 +422,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.3.5] - 2025-10-09
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ensure connecting state shows for minimum 15 seconds to prevent premature offline display
|
||||
- Add Cloudflare Pages routing config for SPA paths
|
||||
|
||||
### Changed
|
||||
|
||||
- Extend connecting state duration and remove subtitle text for cleaner UI
|
||||
|
||||
## [0.3.4] - 2025-10-09
|
||||
|
||||
### Fixed
|
||||
|
||||
- Add p tag (author tag) to highlights of nostr-native content for proper attribution
|
||||
|
||||
## [0.3.3] - 2025-10-09
|
||||
|
||||
### Added
|
||||
|
||||
- Service Worker for robust offline image caching
|
||||
- /explore route to discover blog posts from friends on Nostr
|
||||
- Explore button (newspaper icon) in bookmarks header
|
||||
@@ -343,12 +447,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Last fetch time display with relative timestamps in bookmarks list
|
||||
|
||||
### Changed
|
||||
|
||||
- Simplify image caching to use Service Worker transparently
|
||||
- Move refresh button from top bar to end of bookmarks list
|
||||
- Make explore page article cards proper links (supports CMD+click to open in new tab)
|
||||
- Reorganize bookmarks UI for better UX
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improve image cache resilience for offline viewing and hard reloads
|
||||
- Correct TypeScript types for cache stats state
|
||||
- Resolve linter errors for unused parameters
|
||||
@@ -359,6 +465,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.3.0] - 2025-10-09
|
||||
|
||||
### Added
|
||||
|
||||
- Flight Mode with offline highlight creation and local relay support
|
||||
- Automatic offline sync - rebroadcast local events when back online
|
||||
- Relay indicator icon on highlight items showing sync status
|
||||
@@ -371,6 +478,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- 6th font size option for better UI scaling
|
||||
|
||||
### Fixed
|
||||
|
||||
- Highlight creation resilient to offline/flight mode
|
||||
- TypeScript type errors in offline sync
|
||||
- Relay indicator tooltip accuracy and reliability
|
||||
@@ -385,6 +493,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Include local relays in relay indicator tooltip
|
||||
|
||||
### Changed
|
||||
|
||||
- Rename 'Offline Mode' to 'Flight Mode' throughout UI
|
||||
- Move publication date to top-right corner with subtle border styling
|
||||
- Consolidate relay/status indicators into single unified icon
|
||||
@@ -398,6 +507,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Simplify rebroadcast settings UI with consistent checkbox style
|
||||
|
||||
### Performance
|
||||
|
||||
- Make highlight creation instant with non-blocking relay publish
|
||||
- Reduce relay status polling interval to 20 seconds
|
||||
- Show sync progress and hide indicator after successful sync
|
||||
@@ -405,6 +515,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.2.10] - 2025-10-09
|
||||
|
||||
### Added
|
||||
|
||||
- URL-based settings navigation with /settings route
|
||||
- Active zap split preset highlighting
|
||||
- Educational links about relays in reader view
|
||||
@@ -413,10 +524,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Relays section showing active and recently connected relays
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remove trailing slash from relay URLs
|
||||
- Constrain Reading Font dropdown width
|
||||
|
||||
### Changed
|
||||
|
||||
- Rename 'Default View Mode' to 'Default Bookmark View' in settings
|
||||
- Reorganize settings layout for better UX
|
||||
- Use sidebar-style colored buttons for highlight visibility
|
||||
@@ -425,26 +538,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.2.9] - 2025-10-09
|
||||
|
||||
### Fixed
|
||||
|
||||
- Deduplicate highlights in streaming callbacks
|
||||
|
||||
## [0.2.8] - 2025-10-09
|
||||
|
||||
### Added
|
||||
|
||||
- Display article summary in header
|
||||
- Overlay title and metadata on hero images
|
||||
- Apply reading font to article titles
|
||||
|
||||
### Fixed
|
||||
|
||||
- Pass article summary through to ReadableContent
|
||||
- Correct Jina AI Reader proxy URL format
|
||||
|
||||
### Changed
|
||||
|
||||
- Update homepage URL to read.withboris.com
|
||||
- Reorder toolbar buttons for better UX
|
||||
|
||||
## [0.2.7] - 2025-10-08
|
||||
|
||||
### Added
|
||||
|
||||
- Web bookmark creation (NIP-B0, kind:39701)
|
||||
- Tags support for web bookmarks per NIP-B0
|
||||
- Auto-fetch title and description when URL is pasted
|
||||
@@ -455,6 +573,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Respect existing zap tags in source content when creating highlights
|
||||
|
||||
### Fixed
|
||||
|
||||
- Revert to fetchReadableContent to avoid CORS issues
|
||||
- Improve modal spacing with proper box-sizing
|
||||
- Prevent sliders from jumping when resetting settings
|
||||
@@ -462,6 +581,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Correct type signature for addZapTags function
|
||||
|
||||
### Changed
|
||||
|
||||
- Reorder toolbar buttons for better UX
|
||||
- DRY up tag extraction with normalizeTags helper
|
||||
- Use url-metadata package for robust metadata extraction
|
||||
@@ -472,16 +592,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.2.6] - 2025-10-08
|
||||
|
||||
### Added
|
||||
|
||||
- Home button to bookmark bar
|
||||
- Configurable zap split for highlights on nostr-native content
|
||||
|
||||
## [0.2.5] - 2025-10-07
|
||||
|
||||
### Fixed
|
||||
|
||||
- Wire preview ref to markdown conversion hook
|
||||
- Add missing useEffect dependencies for article loading
|
||||
|
||||
### Changed
|
||||
|
||||
- DRY up highlight classification and URL normalization
|
||||
- Split highlighting utilities into modules
|
||||
- Extract highlights panel components
|
||||
@@ -493,14 +616,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.2.4] - 2025-10-07
|
||||
|
||||
### Added
|
||||
- Domain configuration for https://xn--bris-v0b.com/
|
||||
|
||||
- Domain configuration for <https://xn--bris-v0b.com/>
|
||||
- Public assets and deployment configuration
|
||||
- Hide bookmarks without content or URL
|
||||
|
||||
### Fixed
|
||||
|
||||
- Encode/decode URLs in /r/ route to preserve special characters
|
||||
|
||||
### Changed
|
||||
|
||||
- Cleanup after build fixes (remove shims, update locks)
|
||||
- Stop tracking node_modules/dist
|
||||
- Update dependencies and dedupe
|
||||
@@ -509,39 +635,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.2.3] - 2025-10-07
|
||||
|
||||
### Added
|
||||
|
||||
- Parse and display summary tag for nostr articles
|
||||
- Merge and flatten bookmarks from multiple lists
|
||||
- Update URL path when opening bookmarks from sidebar
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ensure bookmarks are sorted newest first after merging lists
|
||||
- Hide empty bookmarks without content
|
||||
- Remove encrypted cyphertext display from bookmark list
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove created date from bookmark list display
|
||||
|
||||
## [0.2.2] - 2025-10-06
|
||||
|
||||
### Added
|
||||
|
||||
- Support for web bookmarks (NIP-B0, kind:39701)
|
||||
- Default highlight visibility settings
|
||||
- Proxy.nostr-relay.app relay to configuration
|
||||
- Comprehensive logging to settings service
|
||||
|
||||
### Fixed
|
||||
|
||||
- Handle web bookmarks with URLs in d tag and prevent crash
|
||||
- Load settings from local cache first to eliminate FOUT
|
||||
- Ensure fonts are fully loaded before applying styles
|
||||
- Improve highlight rendering pipeline with comprehensive debugging
|
||||
|
||||
### Changed
|
||||
|
||||
- Use icon toggle buttons for highlight visibility settings
|
||||
- Change nostrverse icon from fa-globe to fa-network-wired
|
||||
|
||||
## [0.2.1] - 2025-10-05
|
||||
|
||||
### Added
|
||||
|
||||
- Local relay support and centralize relay configuration
|
||||
- Optimistic updates for highlight creation
|
||||
- Enable highlight creation from external URLs
|
||||
@@ -550,28 +683,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Boris branding to highlight alt tag
|
||||
|
||||
### Fixed
|
||||
|
||||
- Properly await account loading from localStorage on refresh
|
||||
- Add protected routes to prevent logout on page refresh
|
||||
- Use undo icon for reset to defaults button
|
||||
- Update local relay port to 10547
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove dedicated login page, handle login through main UI
|
||||
- Simplify to single RELAYS constant (DRY)
|
||||
|
||||
## [0.2.0] - 2025-10-05
|
||||
|
||||
### Added
|
||||
|
||||
- Simple highlight creation feature (FAB style)
|
||||
- Reset to defaults button in settings
|
||||
- Load and apply settings upon login
|
||||
|
||||
### Fixed
|
||||
|
||||
- Replace any types with proper NostrEvent types
|
||||
- Move FAB to Bookmarks component for proper floating
|
||||
- Highlight button positioning with scroll
|
||||
|
||||
### Changed
|
||||
|
||||
- Update color palette to include default friends/nostrverse colors
|
||||
- Show author name in highlight cards
|
||||
- Sync highlight level toggles between sidebar and main article text
|
||||
@@ -580,67 +718,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.1.11] - 2025-10-05
|
||||
|
||||
### Added
|
||||
|
||||
- Stream highlights progressively as they arrive from relays
|
||||
|
||||
### Fixed
|
||||
|
||||
- Display article immediately without waiting for highlights to load
|
||||
- Show highlights immediately when opening panel if already loaded
|
||||
- Prevent bookmark text from being cut off in compact view
|
||||
- Correct default highlight color for 'mine' to yellow (#ffff00)
|
||||
|
||||
### Changed
|
||||
|
||||
- Reduce padding between bookmark items and panel edge
|
||||
- Update default highlight colors to orange for friends and purple for nostrverse
|
||||
|
||||
## [0.1.10] - 2025-10-05
|
||||
|
||||
### Added
|
||||
|
||||
- Three-level highlight system (mine/friends/nostrverse)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ensure highlights always render on markdown content
|
||||
- Classify highlights before passing to ContentPanel
|
||||
- Position toggle buttons directly adjacent to main panel
|
||||
- Remove redundant setReaderLoading call in error handler
|
||||
|
||||
### Changed
|
||||
|
||||
- Always show friends and user highlight buttons
|
||||
- Remove Highlights title and count from panel
|
||||
|
||||
## [0.1.9] - 2025-10-05
|
||||
|
||||
### Fixed
|
||||
|
||||
- Show markdown content immediately when finalHtml is empty
|
||||
- Prevent highlight bleeding into sidebar
|
||||
|
||||
## [0.1.8] - 2025-10-05
|
||||
|
||||
### Fixed
|
||||
|
||||
- Prevent 'No readable content' flash for markdown articles
|
||||
- Enable highlights display and scroll-to for markdown content
|
||||
|
||||
### Added
|
||||
|
||||
- Persist accounts to localStorage
|
||||
|
||||
### Changed
|
||||
|
||||
- Simplify login by handling it directly in sidebar
|
||||
|
||||
## [0.1.7] - 2025-10-05
|
||||
|
||||
### Added
|
||||
|
||||
- Show highlights in article content and add mode toggle
|
||||
|
||||
### Fixed
|
||||
|
||||
- Show highlights for nostr articles by skipping URL filter
|
||||
- Refresh button now works without login for article highlights
|
||||
- Query highlights using both a-tag and e-tag
|
||||
|
||||
### Changed
|
||||
|
||||
- Keep Bookmarks.tsx under 210 lines by extracting logic
|
||||
|
||||
## [0.1.6] - 2025-10-03
|
||||
|
||||
### Added
|
||||
|
||||
- Native support for rendering Nostr long-form articles (NIP-23)
|
||||
- Display article titles for kind:30023 bookmarks
|
||||
- Enable clicking on kind:30023 articles to open in reader
|
||||
@@ -649,10 +801,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Highlight style setting (marker & underline)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Use bookmark pubkey for article author instead of tag lookup
|
||||
- Ensure highlight color CSS variable inherits from parent
|
||||
|
||||
### Changed
|
||||
|
||||
- Integrate long-form article rendering into existing reader view
|
||||
- Extract components to keep files under 210 lines
|
||||
- Make font size and color buttons match icon button size (33px)
|
||||
@@ -660,6 +814,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.1.5] - 2025-10-03
|
||||
|
||||
### Added
|
||||
|
||||
- Settings panel with NIP-78 storage
|
||||
- Auto-save for settings with toast notifications
|
||||
- Reading time estimate to articles
|
||||
@@ -669,12 +824,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Settings subscription to watch for Nostr updates
|
||||
|
||||
### Fixed
|
||||
|
||||
- Prevent settings from saving unnecessarily
|
||||
- Prevent save settings button from being cut off
|
||||
- Replace custom reading time with reading-time-estimator package
|
||||
- Update originalHtmlRef when content changes
|
||||
|
||||
### Changed
|
||||
|
||||
- Reduce file sizes to meet 210 line limit
|
||||
- Extract settings logic into custom hook
|
||||
- Consolidate settings initialization on login
|
||||
@@ -683,6 +840,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.1.4] - 2025-10-03
|
||||
|
||||
### Added
|
||||
|
||||
- Inline highlight annotations in content panel
|
||||
- NIP-84 highlights panel with three-pane layout
|
||||
- Toggle button to show/hide highlight underlines
|
||||
@@ -690,12 +848,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Pulsing animation when scrolling to highlight
|
||||
|
||||
### Fixed
|
||||
|
||||
- Apply highlights to markdown content as well as HTML
|
||||
- Use requestAnimationFrame for highlight DOM manipulation
|
||||
- Improve HTML highlight matching with DOM manipulation
|
||||
- Filter highlights panel to show only current article
|
||||
|
||||
### Changed
|
||||
|
||||
- Use applesauce helpers for highlight parsing
|
||||
- DRY up highlightMatching to stay under 210 lines
|
||||
- Change highlights to fluorescent marker style
|
||||
@@ -704,12 +864,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.1.3] - 2025-10-03
|
||||
|
||||
### Added
|
||||
|
||||
- View mode switching for bookmarks with compact list view
|
||||
- Large preview view mode
|
||||
- Image preview for large view cards
|
||||
- Hero images using free CORS proxy
|
||||
|
||||
### Changed
|
||||
|
||||
- Make entire compact list row clickable to open reader
|
||||
- Make card view timestamp clickable to open event
|
||||
- Enhance card view design with modern styling
|
||||
@@ -717,15 +879,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.1.2] - 2025-10-03
|
||||
|
||||
### Added
|
||||
|
||||
- Open bookmark URLs in reader instead of new window
|
||||
- localStorage caching for fetched articles
|
||||
- Collapsible bookmarks sidebar
|
||||
|
||||
### Fixed
|
||||
|
||||
- Make sidebar and reader scroll independently
|
||||
- Replace relative-time with date-fns for timestamp formatting
|
||||
|
||||
### Changed
|
||||
|
||||
- Display timestamps as relative time
|
||||
- Replace user text with profile image in sidebar header
|
||||
- Move user info and logout to sidebar header bar
|
||||
@@ -734,16 +899,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.1.1] - 2025-10-03
|
||||
|
||||
### Added
|
||||
|
||||
- Classify URLs by type and adjust action buttons
|
||||
- Collapse/expand functionality for bookmarks sidebar
|
||||
- IconButton component with square styling
|
||||
- Resolve nprofile/npub mentions to names in content
|
||||
|
||||
### Fixed
|
||||
|
||||
- Enforce 210-char truncation for both plain and parsed content
|
||||
- Use Rules of Hooks correctly
|
||||
|
||||
### Changed
|
||||
|
||||
- Use IconButton for all icon-only actions to enforce square sizing
|
||||
- Sort bookmarks by added_at (recently added first)
|
||||
- Make kind icon square to match IconButton sizing
|
||||
@@ -752,6 +920,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.1.0] - 2025-10-03
|
||||
|
||||
### Added
|
||||
|
||||
- Two-pane layout and content fetching pipeline
|
||||
- ContentPanel component to render readable HTML
|
||||
- Lightweight readability fetcher via r.jina.ai proxy
|
||||
@@ -761,11 +930,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- FontAwesome icons for event kinds
|
||||
|
||||
### Fixed
|
||||
|
||||
- Show bookmarked event author instead of list signer
|
||||
- Enable reactive profile fetch via address loader
|
||||
- Left-align content and constrain images in content panel
|
||||
|
||||
### Changed
|
||||
|
||||
- Resolve author names using applesauce ProfileModel
|
||||
- Propagate URL selection through BookmarkList to parent
|
||||
- Display URLs clearly in individual bookmarks
|
||||
@@ -773,16 +944,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.0.3] - 2025-10-02
|
||||
|
||||
### Added
|
||||
|
||||
- Manual decryption for unrecognized event kinds
|
||||
- Try NIP-44 then NIP-04 for manual decryption
|
||||
- Detailed debugging for decryption process
|
||||
- Support for hidden bookmarks decryption
|
||||
|
||||
### Fixed
|
||||
|
||||
- Surface manually decrypted hidden tags in UI
|
||||
- Dedupe individual bookmarks by id
|
||||
|
||||
### Changed
|
||||
|
||||
- Sort individual bookmarks by timestamp (newest first)
|
||||
- Increase bookmark loading timeout by 2x
|
||||
- Extract helpers and event processing
|
||||
@@ -790,6 +964,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.0.2] - 2025-10-02
|
||||
|
||||
### Added
|
||||
|
||||
- Fetch all NIP-51 events
|
||||
- Unlock private bookmarks via applesauce helpers
|
||||
- Copy-to-clipboard icons for event id and author pubkey
|
||||
@@ -797,12 +972,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Display content identically for private/public bookmarks
|
||||
|
||||
### Fixed
|
||||
|
||||
- Properly configure browser extension signer
|
||||
- Aggregate list(10003) + set(30001)
|
||||
- Handle applesauce bookmark structure correctly
|
||||
- Resolve loading state stuck issue
|
||||
|
||||
### Changed
|
||||
|
||||
- Change bookmarks display from grid to social feed list layout
|
||||
- Simplify bookmark service using applesauce helpers
|
||||
- Extract components and utilities to keep files under 210 lines
|
||||
@@ -810,6 +987,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [0.0.1] - 2025-10-02
|
||||
|
||||
### Added
|
||||
|
||||
- Initial release
|
||||
- Browser extension login support
|
||||
- NIP-51 bookmark fetching from nostr relays
|
||||
@@ -818,6 +996,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Basic UI with profile resolution
|
||||
|
||||
### Changed
|
||||
|
||||
- Migrate to applesauce-accounts for proper account management
|
||||
- Use proper applesauce-loaders for NIP-51 bookmark fetching
|
||||
- Optimize relay usage following applesauce-relay best practices
|
||||
@@ -835,8 +1014,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
[0.3.5]: https://github.com/dergigi/boris/compare/v0.3.4...v0.3.5
|
||||
[0.3.4]: https://github.com/dergigi/boris/compare/v0.3.3...v0.3.4
|
||||
[0.3.3]: https://github.com/dergigi/boris/compare/v0.3.2...v0.3.3
|
||||
[0.3.2]: https://github.com/dergigi/boris/compare/v0.3.1...v0.3.2
|
||||
[0.3.1]: https://github.com/dergigi/boris/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/dergigi/boris/compare/v0.2.10...v0.3.0
|
||||
[0.2.10]: https://github.com/dergigi/boris/compare/v0.2.9...v0.2.10
|
||||
[0.2.9]: https://github.com/dergigi/boris/compare/v0.2.8...v0.2.9
|
||||
@@ -864,4 +1041,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
[0.0.3]: https://github.com/dergigi/boris/compare/v0.0.2...v0.0.3
|
||||
[0.0.2]: https://github.com/dergigi/boris/compare/v0.0.1...v0.0.2
|
||||
[0.0.1]: https://github.com/dergigi/boris/releases/tag/v0.0.1
|
||||
|
||||
|
||||
188
TAILWIND_MIGRATION.md
Normal file
188
TAILWIND_MIGRATION.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# Tailwind CSS Migration Status
|
||||
|
||||
## ✅ Completed (Core Infrastructure)
|
||||
|
||||
### Phase 1: Setup & Foundation
|
||||
- [x] Install Tailwind CSS with PostCSS and Autoprefixer
|
||||
- [x] Configure `tailwind.config.js` with content globs and custom keyframes
|
||||
- [x] Create `src/styles/tailwind.css` with base/components/utilities
|
||||
- [x] Import Tailwind before existing CSS in `main.tsx`
|
||||
- [x] Enable Tailwind preflight (CSS reset)
|
||||
|
||||
### Phase 2: Base Styles Reconciliation
|
||||
- [x] Add CSS variables for user-settable theme colors
|
||||
- `--highlight-color-mine`, `--highlight-color-friends`, `--highlight-color-nostrverse`
|
||||
- `--reading-font`, `--reading-font-size`
|
||||
- [x] Simplify `global.css` to work with Tailwind preflight
|
||||
- [x] Remove redundant base styles handled by Tailwind
|
||||
- [x] Keep app-specific overrides (mobile sidebar lock, loading states)
|
||||
|
||||
### Phase 3: Layout System Refactor ⭐ **CRITICAL FIX**
|
||||
- [x] Switch from pane-scrolling to document-scrolling
|
||||
- [x] Make sidebars sticky on desktop (`position: sticky`)
|
||||
- [x] Update `app.css` to remove fixed container heights
|
||||
- [x] Update `ThreePaneLayout.tsx` to use window scroll
|
||||
- [x] Fix reading position tracking to work with document scroll
|
||||
- [x] Maintain mobile overlay behavior
|
||||
|
||||
### Phase 4: Component Migrations
|
||||
- [x] **ReadingProgressIndicator**: Full Tailwind conversion
|
||||
- Removed 80+ lines of CSS
|
||||
- Added shimmer animation to Tailwind config
|
||||
- Z-index layering maintained (1102)
|
||||
|
||||
- [x] **Mobile UI Elements**: Tailwind utilities
|
||||
- Mobile hamburger button
|
||||
- Mobile highlights button
|
||||
- Mobile backdrop
|
||||
- Removed 60+ lines of CSS
|
||||
|
||||
- [x] **App Container**: Tailwind utilities
|
||||
- Responsive padding (p-0 md:p-4)
|
||||
- Min-height viewport support
|
||||
|
||||
## 📊 Impact & Metrics
|
||||
|
||||
### Lines of CSS Removed
|
||||
- `global.css`: ~50 lines removed
|
||||
- `reader.css`: ~80 lines removed (progress indicator)
|
||||
- `app.css`: ~30 lines removed (mobile buttons/backdrop)
|
||||
- `sidebar.css`: ~30 lines removed (mobile hamburger)
|
||||
- **Total**: ~190 lines removed
|
||||
|
||||
### Key Achievements
|
||||
1. **Fixed Core Issue**: Reading position tracking now works correctly with document scroll
|
||||
2. **Tailwind Integration**: Fully functional with preflight enabled
|
||||
3. **No Breaking Changes**: All existing functionality preserved
|
||||
4. **Type Safety**: TypeScript checks passing
|
||||
5. **Lint Clean**: ESLint checks passing
|
||||
6. **Responsive**: Mobile/tablet/desktop layouts working
|
||||
|
||||
## 🔄 Remaining Work (Incremental)
|
||||
|
||||
The following migrations are **optional enhancements** that can be done as components are touched:
|
||||
|
||||
### High-Value Components
|
||||
- [ ] **ContentPanel** - Large component, high impact
|
||||
- Reader header, meta info, loading states
|
||||
- Mark as read button
|
||||
- Article/video menus
|
||||
|
||||
- [ ] **BookmarkList & BookmarkItem** - Core UI
|
||||
- Card layouts (compact/cards/large views)
|
||||
- Bookmark metadata display
|
||||
- Interactive states
|
||||
|
||||
- [ ] **HighlightsPanel** - Feature-rich
|
||||
- Header with toggles
|
||||
- Highlight items
|
||||
- Level-based styling
|
||||
|
||||
- [ ] **Settings Components** - Forms & controls
|
||||
- Color pickers
|
||||
- Font selectors
|
||||
- Toggle switches
|
||||
- Sliders
|
||||
|
||||
### CSS Files to Prune
|
||||
- `src/index.css` - Contains many inline bookmark/highlight styles (~3000+ lines)
|
||||
- `src/styles/components/cards.css` - Bookmark card styles
|
||||
- `src/styles/components/modals.css` - Modal dialogs
|
||||
- `src/styles/layout/highlights.css` - Highlight panel layout
|
||||
|
||||
## 🎯 Migration Strategy
|
||||
|
||||
### For New Components
|
||||
Use Tailwind utilities from the start. Reference:
|
||||
```tsx
|
||||
// Good: Tailwind utilities
|
||||
<div className="flex items-center gap-2 p-4 bg-gray-800 rounded-lg">
|
||||
|
||||
// Avoid: New CSS classes
|
||||
<div className="custom-component">
|
||||
```
|
||||
|
||||
### For Existing Components
|
||||
Migrate incrementally when touching files:
|
||||
1. Replace layout utilities (flex, grid, spacing, sizing)
|
||||
2. Replace color/background utilities
|
||||
3. Replace typography utilities
|
||||
4. Replace responsive variants
|
||||
5. Remove old CSS rules
|
||||
6. Keep file under 210 lines
|
||||
|
||||
### CSS Variable Usage
|
||||
Dynamic values should still use CSS variables or inline styles:
|
||||
```tsx
|
||||
// User-settable colors
|
||||
style={{ backgroundColor: settings.highlightColorMine }}
|
||||
|
||||
// Or reference CSS variable
|
||||
className="bg-[var(--highlight-color-mine)]"
|
||||
```
|
||||
|
||||
## 📝 Technical Notes
|
||||
|
||||
### Z-Index Layering
|
||||
- Mobile sidepanes: `z-[1001]`
|
||||
- Mobile backdrop: `z-[999]`
|
||||
- Progress indicator: `z-[1102]`
|
||||
- Mobile buttons: `z-[900]`
|
||||
- Relay status: `z-[999]`
|
||||
- Modals: `z-[10000]`
|
||||
|
||||
### Responsive Breakpoints
|
||||
- Mobile: `< 768px`
|
||||
- Tablet: `768px - 1024px`
|
||||
- Desktop: `> 1024px`
|
||||
|
||||
Use Tailwind: `md:` (768px), `lg:` (1024px)
|
||||
|
||||
### Safe Area Insets
|
||||
Mobile notch support:
|
||||
```tsx
|
||||
style={{
|
||||
top: 'calc(1rem + env(safe-area-inset-top))',
|
||||
left: 'calc(1rem + env(safe-area-inset-left))'
|
||||
}}
|
||||
```
|
||||
|
||||
### Custom Animations
|
||||
Add to `tailwind.config.js`:
|
||||
```js
|
||||
keyframes: {
|
||||
shimmer: {
|
||||
'0%': { transform: 'translateX(-100%)' },
|
||||
'100%': { transform: 'translateX(100%)' },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## ✅ Success Criteria Met
|
||||
|
||||
- [x] Tailwind CSS fully integrated and functional
|
||||
- [x] Document scrolling working correctly
|
||||
- [x] Reading position tracking accurate
|
||||
- [x] Progress indicator always visible
|
||||
- [x] No TypeScript errors
|
||||
- [x] No linting errors
|
||||
- [x] Mobile responsiveness maintained
|
||||
- [x] Theme colors (user settings) working
|
||||
- [x] All existing features functional
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
1. **Ship It**: Current state is production-ready
|
||||
2. **Incremental Migration**: Convert components as you touch them
|
||||
3. **Monitor**: Watch for any CSS conflicts
|
||||
4. **Cleanup**: Eventually remove unused CSS files
|
||||
5. **Document**: Update component docs with Tailwind patterns
|
||||
|
||||
---
|
||||
|
||||
**Status**: ✅ **CORE MIGRATION COMPLETE**
|
||||
**Date**: 2025-01-14
|
||||
**Commits**: 8 conventional commits
|
||||
**Lines Removed**: ~190 lines of CSS
|
||||
**Breaking Changes**: None
|
||||
|
||||
660
package-lock.json
generated
660
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "boris",
|
||||
"version": "0.5.6",
|
||||
"version": "0.5.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "boris",
|
||||
"version": "0.5.6",
|
||||
"version": "0.5.7",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^7.1.0",
|
||||
@@ -34,20 +34,37 @@
|
||||
"remark-gfm": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.14",
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||
"@typescript-eslint/parser": "^6.14.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.0.8",
|
||||
"vite-plugin-pwa": "^1.0.3",
|
||||
"workbox-window": "^7.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
@@ -3029,6 +3046,292 @@
|
||||
"string.prototype.matchall": "^4.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/node": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz",
|
||||
"integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.4",
|
||||
"enhanced-resolve": "^5.18.3",
|
||||
"jiti": "^2.6.0",
|
||||
"lightningcss": "1.30.1",
|
||||
"magic-string": "^0.30.19",
|
||||
"source-map-js": "^1.2.1",
|
||||
"tailwindcss": "4.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/node/node_modules/magic-string": {
|
||||
"version": "0.30.19",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
|
||||
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.14.tgz",
|
||||
"integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.4",
|
||||
"tar": "^7.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-android-arm64": "4.1.14",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.1.14",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.1.14",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.1.14",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.14",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.1.14",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.1.14",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.1.14",
|
||||
"@tailwindcss/oxide-wasm32-wasi": "4.1.14",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.14",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.14.tgz",
|
||||
"integrity": "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.14.tgz",
|
||||
"integrity": "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.14.tgz",
|
||||
"integrity": "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.14.tgz",
|
||||
"integrity": "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.14.tgz",
|
||||
"integrity": "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.14.tgz",
|
||||
"integrity": "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.14.tgz",
|
||||
"integrity": "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.14.tgz",
|
||||
"integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.14.tgz",
|
||||
"integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.14.tgz",
|
||||
"integrity": "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==",
|
||||
"bundleDependencies": [
|
||||
"@napi-rs/wasm-runtime",
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@tybys/wasm-util",
|
||||
"@emnapi/wasi-threads",
|
||||
"tslib"
|
||||
],
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.5.0",
|
||||
"@emnapi/runtime": "^1.5.0",
|
||||
"@emnapi/wasi-threads": "^1.1.0",
|
||||
"@napi-rs/wasm-runtime": "^1.0.5",
|
||||
"@tybys/wasm-util": "^0.10.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz",
|
||||
"integrity": "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.14.tgz",
|
||||
"integrity": "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.14.tgz",
|
||||
"integrity": "sha512-BdMjIxy7HUNThK87C7BC8I1rE8BVUsfNQSI5siQ4JK3iIa3w0XyVvVL9SXLWO//CtYTcp1v7zci0fYwJOjB+Zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"@tailwindcss/node": "4.1.14",
|
||||
"@tailwindcss/oxide": "4.1.14",
|
||||
"postcss": "^8.4.41",
|
||||
"tailwindcss": "4.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@treeee/youtube-caption-extractor": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@treeee/youtube-caption-extractor/-/youtube-caption-extractor-1.5.5.tgz",
|
||||
@@ -4128,6 +4431,44 @@
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.21",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
||||
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.24.4",
|
||||
"caniuse-lite": "^1.0.30001702",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"autoprefixer": "bin/autoprefixer"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
@@ -4952,6 +5293,20 @@
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.18.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
|
||||
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
@@ -5915,6 +6270,20 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "patreon",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
@@ -7196,6 +7565,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -7357,6 +7736,245 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
|
||||
"integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"lightningcss-darwin-arm64": "1.30.1",
|
||||
"lightningcss-darwin-x64": "1.30.1",
|
||||
"lightningcss-freebsd-x64": "1.30.1",
|
||||
"lightningcss-linux-arm-gnueabihf": "1.30.1",
|
||||
"lightningcss-linux-arm64-gnu": "1.30.1",
|
||||
"lightningcss-linux-arm64-musl": "1.30.1",
|
||||
"lightningcss-linux-x64-gnu": "1.30.1",
|
||||
"lightningcss-linux-x64-musl": "1.30.1",
|
||||
"lightningcss-win32-arm64-msvc": "1.30.1",
|
||||
"lightningcss-win32-x64-msvc": "1.30.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-arm64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
|
||||
"integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-x64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
|
||||
"integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-freebsd-x64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
|
||||
"integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
|
||||
"integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
|
||||
"integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-musl": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
|
||||
"integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-gnu": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
|
||||
"integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-musl": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
|
||||
"integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-arm64-msvc": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
|
||||
"integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-x64-msvc": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
|
||||
"integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/load-script": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz",
|
||||
@@ -8533,6 +9151,16 @@
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nostr-tools": {
|
||||
"version": "2.17.0",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.17.0.tgz",
|
||||
@@ -9010,6 +9638,13 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-value-parser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/postcss/node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
@@ -10369,6 +11004,27 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.14",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz",
|
||||
"integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
|
||||
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "boris",
|
||||
"version": "0.5.7",
|
||||
"version": "0.6.0",
|
||||
"description": "A minimal nostr client for bookmark management",
|
||||
"homepage": "https://read.withboris.com/",
|
||||
"type": "module",
|
||||
@@ -37,14 +37,18 @@
|
||||
"remark-gfm": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.14",
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||
"@typescript-eslint/parser": "^6.14.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.0.8",
|
||||
"vite-plugin-pwa": "^1.0.3",
|
||||
|
||||
7
postcss.config.js
Normal file
7
postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ function App() {
|
||||
<EventStoreProvider eventStore={eventStore}>
|
||||
<AccountsProvider manager={accountManager}>
|
||||
<BrowserRouter>
|
||||
<div className="app">
|
||||
<div className="min-h-screen p-0 md:p-4 max-w-none m-0 relative">
|
||||
<AppRoutes relayPool={relayPool} showToast={showToast} />
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
|
||||
@@ -33,6 +33,8 @@ import { faBooks } from '../icons/customIcons'
|
||||
import { extractYouTubeId, getYouTubeMeta } from '../services/youtubeMetaService'
|
||||
import { classifyUrl } from '../utils/helpers'
|
||||
import { buildNativeVideoUrl } from '../utils/videoHelpers'
|
||||
import { useReadingPosition } from '../hooks/useReadingPosition'
|
||||
import { ReadingProgressIndicator } from './ReadingProgressIndicator'
|
||||
|
||||
interface ContentPanelProps {
|
||||
loading: boolean
|
||||
@@ -115,6 +117,18 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
onClearSelection
|
||||
})
|
||||
|
||||
// Reading position tracking - only for text content, not videos
|
||||
const isTextContent = !loading && !!(markdown || html) && !selectedUrl?.includes('youtube') && !selectedUrl?.includes('vimeo')
|
||||
const { isReadingComplete, progressPercentage } = useReadingPosition({
|
||||
enabled: isTextContent,
|
||||
onReadingComplete: () => {
|
||||
// Optional: Auto-mark as read when reading is complete
|
||||
if (activeAccount && !isMarkedAsRead) {
|
||||
// Could trigger auto-mark as read here if desired
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Close menu when clicking outside
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
@@ -360,8 +374,18 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
const highlightRgb = hexToRgb(highlightColor)
|
||||
|
||||
return (
|
||||
<div className="reader" style={{ '--highlight-rgb': highlightRgb } as React.CSSProperties}>
|
||||
{/* Hidden markdown preview to convert markdown to HTML */}
|
||||
<>
|
||||
{/* Reading Progress Indicator - Outside reader for fixed positioning */}
|
||||
{isTextContent && (
|
||||
<ReadingProgressIndicator
|
||||
progress={progressPercentage}
|
||||
isComplete={isReadingComplete}
|
||||
showPercentage={true}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="reader" style={{ '--highlight-rgb': highlightRgb } as React.CSSProperties}>
|
||||
{/* Hidden markdown preview to convert markdown to HTML */}
|
||||
{markdown && (
|
||||
<div ref={markdownPreviewRef} style={{ display: 'none' }}>
|
||||
<ReactMarkdown
|
||||
@@ -567,7 +591,8 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
<p>No readable content found for this URL.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
41
src/components/ReadingProgressIndicator.tsx
Normal file
41
src/components/ReadingProgressIndicator.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react'
|
||||
|
||||
interface ReadingProgressIndicatorProps {
|
||||
progress: number // 0 to 100
|
||||
isComplete?: boolean
|
||||
showPercentage?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const ReadingProgressIndicator: React.FC<ReadingProgressIndicatorProps> = ({
|
||||
progress,
|
||||
isComplete = false,
|
||||
showPercentage = true,
|
||||
className = ''
|
||||
}) => {
|
||||
const clampedProgress = Math.min(100, Math.max(0, progress))
|
||||
|
||||
return (
|
||||
<div className={`fixed bottom-0 left-0 right-0 z-[1102] bg-[rgba(26,26,26,0.85)] backdrop-blur-sm px-3 py-1 flex items-center gap-2 transition-all duration-300 ${className}`}>
|
||||
<div className="flex-1 h-0.5 bg-white/10 rounded-full overflow-hidden relative">
|
||||
<div
|
||||
className={`h-full rounded-full transition-all duration-300 relative ${
|
||||
isComplete
|
||||
? 'bg-green-500'
|
||||
: 'bg-indigo-500'
|
||||
}`}
|
||||
style={{ width: `${clampedProgress}%` }}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-[shimmer_2s_infinite]" />
|
||||
</div>
|
||||
</div>
|
||||
{showPercentage && (
|
||||
<div className={`text-[0.625rem] font-normal min-w-[32px] text-right tabular-nums ${
|
||||
isComplete ? 'text-green-500' : 'text-gray-500'
|
||||
}`}>
|
||||
{isComplete ? '✓' : `${clampedProgress}%`}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -98,11 +98,10 @@ const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
|
||||
const mainPaneRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Detect scroll direction to hide/show mobile buttons
|
||||
// On mobile, scroll happens in the main pane, not on window
|
||||
// Now using window scroll (document scroll) instead of pane scroll
|
||||
const scrollDirection = useScrollDirection({
|
||||
threshold: 10,
|
||||
enabled: isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed,
|
||||
elementRef: mainPaneRef
|
||||
enabled: isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed
|
||||
})
|
||||
const showMobileButtons = scrollDirection !== 'down'
|
||||
|
||||
@@ -225,7 +224,15 @@ const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
|
||||
{/* Mobile bookmark button - only show when viewing article */}
|
||||
{isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed && (
|
||||
<button
|
||||
className={`mobile-hamburger-btn ${showMobileButtons ? 'visible' : 'hidden'}`}
|
||||
className={`fixed z-[900] bg-[#2a2a2a] border border-[#444] rounded-lg text-[#ddd] flex items-center justify-center transition-all duration-300 active:scale-95 md:hidden ${
|
||||
showMobileButtons ? 'opacity-100 visible' : 'opacity-0 invisible pointer-events-none'
|
||||
}`}
|
||||
style={{
|
||||
top: 'calc(1rem + env(safe-area-inset-top))',
|
||||
left: 'calc(1rem + env(safe-area-inset-left))',
|
||||
width: 'var(--min-touch-target)',
|
||||
height: 'var(--min-touch-target)'
|
||||
}}
|
||||
onClick={props.onToggleSidebar}
|
||||
aria-label="Open bookmarks"
|
||||
aria-expanded={props.isSidebarOpen}
|
||||
@@ -237,14 +244,20 @@ const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
|
||||
{/* Mobile highlights button - only show when viewing article */}
|
||||
{isMobile && !props.isSidebarOpen && props.isHighlightsCollapsed && (
|
||||
<button
|
||||
className={`mobile-highlights-btn ${showMobileButtons ? 'visible' : 'hidden'}`}
|
||||
onClick={props.onToggleHighlightsPanel}
|
||||
aria-label="Open highlights"
|
||||
aria-expanded={!props.isHighlightsCollapsed}
|
||||
className={`fixed z-[900] border border-[#444] rounded-lg flex items-center justify-center transition-all duration-300 active:scale-95 md:hidden ${
|
||||
showMobileButtons ? 'opacity-100 visible' : 'opacity-0 invisible pointer-events-none'
|
||||
}`}
|
||||
style={{
|
||||
top: 'calc(1rem + env(safe-area-inset-top))',
|
||||
right: 'calc(1rem + env(safe-area-inset-right))',
|
||||
width: 'var(--min-touch-target)',
|
||||
height: 'var(--min-touch-target)',
|
||||
backgroundColor: props.settings.highlightColorMine || '#ffff00',
|
||||
color: '#000'
|
||||
}}
|
||||
onClick={props.onToggleHighlightsPanel}
|
||||
aria-label="Open highlights"
|
||||
aria-expanded={!props.isHighlightsCollapsed}
|
||||
>
|
||||
<FontAwesomeIcon icon={faHighlighter} />
|
||||
</button>
|
||||
@@ -253,7 +266,9 @@ const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
|
||||
{/* Mobile backdrop */}
|
||||
{isMobile && (
|
||||
<div
|
||||
className={`mobile-sidebar-backdrop ${(props.isSidebarOpen || !props.isHighlightsCollapsed) ? 'visible' : ''}`}
|
||||
className={`fixed inset-0 bg-black/45 z-[999] transition-opacity duration-300 ${
|
||||
(props.isSidebarOpen || !props.isHighlightsCollapsed) ? 'block opacity-100' : 'hidden opacity-0'
|
||||
}`}
|
||||
onClick={handleBackdropClick}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
73
src/hooks/useReadingPosition.ts
Normal file
73
src/hooks/useReadingPosition.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
interface UseReadingPositionOptions {
|
||||
enabled?: boolean
|
||||
onPositionChange?: (position: number) => void
|
||||
onReadingComplete?: () => void
|
||||
readingCompleteThreshold?: number // Default 0.9 (90%)
|
||||
}
|
||||
|
||||
export const useReadingPosition = ({
|
||||
enabled = true,
|
||||
onPositionChange,
|
||||
onReadingComplete,
|
||||
readingCompleteThreshold = 0.9
|
||||
}: UseReadingPositionOptions = {}) => {
|
||||
const [position, setPosition] = useState(0)
|
||||
const [isReadingComplete, setIsReadingComplete] = useState(false)
|
||||
const hasTriggeredComplete = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) return
|
||||
|
||||
const handleScroll = () => {
|
||||
// Get the main content area (reader content)
|
||||
const readerContent = document.querySelector('.reader-html, .reader-markdown')
|
||||
if (!readerContent) return
|
||||
|
||||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
||||
const windowHeight = window.innerHeight
|
||||
const documentHeight = document.documentElement.scrollHeight
|
||||
|
||||
// Calculate position based on how much of the content has been scrolled through
|
||||
const scrollProgress = Math.min(scrollTop / (documentHeight - windowHeight), 1)
|
||||
const clampedProgress = Math.max(0, Math.min(1, scrollProgress))
|
||||
|
||||
setPosition(clampedProgress)
|
||||
onPositionChange?.(clampedProgress)
|
||||
|
||||
// Check if reading is complete
|
||||
if (clampedProgress >= readingCompleteThreshold && !hasTriggeredComplete.current) {
|
||||
setIsReadingComplete(true)
|
||||
hasTriggeredComplete.current = true
|
||||
onReadingComplete?.()
|
||||
}
|
||||
}
|
||||
|
||||
// Initial calculation
|
||||
handleScroll()
|
||||
|
||||
// Add scroll listener
|
||||
window.addEventListener('scroll', handleScroll, { passive: true })
|
||||
window.addEventListener('resize', handleScroll, { passive: true })
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
window.removeEventListener('resize', handleScroll)
|
||||
}
|
||||
}, [enabled, onPositionChange, onReadingComplete, readingCompleteThreshold])
|
||||
|
||||
// Reset reading complete state when enabled changes
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
setIsReadingComplete(false)
|
||||
hasTriggeredComplete.current = false
|
||||
}
|
||||
}, [enabled])
|
||||
|
||||
return {
|
||||
position,
|
||||
isReadingComplete,
|
||||
progressPercentage: Math.round(position * 100)
|
||||
}
|
||||
}
|
||||
3389
src/index.css
3389
src/index.css
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './styles/tailwind.css'
|
||||
import './index.css'
|
||||
|
||||
// Register Service Worker for PWA functionality
|
||||
|
||||
@@ -1,68 +1,26 @@
|
||||
/* Global element styles and app container */
|
||||
/* Global element styles and app container (Tailwind-compatible) */
|
||||
|
||||
/* Body - keep only app-specific overrides */
|
||||
body {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
overscroll-behavior: none;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* Use dynamic viewport height if supported */
|
||||
@supports (height: 100dvh) {
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
}
|
||||
}
|
||||
|
||||
body.mobile-sidebar-open {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#root {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#root {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.app {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.app header h1 {
|
||||
font-size: 2.5rem;
|
||||
margin: 0;
|
||||
color: #646cff;
|
||||
}
|
||||
|
||||
.app header p {
|
||||
margin: 0.5rem 0 0 0;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/* App loading states */
|
||||
.loading {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
|
||||
--reading-font: 'Source Serif 4', serif;
|
||||
--reading-font-size: 18px;
|
||||
|
||||
/* Highlight color variables (user-settable) */
|
||||
--highlight-color-mine: #ffff00;
|
||||
--highlight-color-friends: #f97316;
|
||||
--highlight-color-nostrverse: #9333ea;
|
||||
--highlight-color: #ffff00; /* Default highlight color */
|
||||
|
||||
/* Layout variables */
|
||||
--sidebar-width: 320px;
|
||||
--sidebar-collapsed-width: 64px;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
overflow: hidden;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 2rem; /* Add space for progress indicator */
|
||||
}
|
||||
|
||||
/* Video container - responsive wrapper following react-player docs */
|
||||
@@ -73,6 +74,15 @@
|
||||
.mark-as-read-btn:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||
.mark-as-read-btn svg { font-size: 1.1rem; }
|
||||
@media (max-width: 768px) {
|
||||
.reader {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
.mark-as-read-container { padding: 1.5rem 1rem; }
|
||||
.mark-as-read-btn { width: 100%; max-width: 300px; }
|
||||
}
|
||||
@@ -102,4 +112,6 @@
|
||||
.reader-header-overlay .reader-title { font-size: 1.5rem; line-height: 1.3; }
|
||||
}
|
||||
|
||||
/* Reading Progress Indicator - now using Tailwind utilities in component */
|
||||
|
||||
|
||||
|
||||
@@ -18,51 +18,69 @@
|
||||
|
||||
.two-pane.sidebar-collapsed { grid-template-columns: 60px 1fr; }
|
||||
|
||||
/* Three-pane layout */
|
||||
/* Three-pane layout - document scroll, sticky sidebars */
|
||||
.three-pane {
|
||||
display: grid;
|
||||
grid-template-columns: var(--sidebar-width) 1fr var(--highlights-width);
|
||||
column-gap: 0;
|
||||
height: calc(100vh - 2rem);
|
||||
transition: grid-template-columns 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@supports (height: 100dvh) {
|
||||
.three-pane { height: calc(100dvh - 2rem); }
|
||||
min-height: 100vh;
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.three-pane.sidebar-collapsed { grid-template-columns: var(--sidebar-collapsed-width) 1fr var(--highlights-width); }
|
||||
.three-pane.highlights-collapsed { grid-template-columns: var(--sidebar-width) 1fr var(--highlights-collapsed-width); }
|
||||
.three-pane.sidebar-collapsed.highlights-collapsed { grid-template-columns: var(--sidebar-collapsed-width) 1fr var(--highlights-collapsed-width); }
|
||||
|
||||
/* Mobile three-pane layout */
|
||||
@media (max-width: 768px) {
|
||||
.three-pane {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
/* Desktop: sticky sidebars, document scroll */
|
||||
@media (min-width: 769px) {
|
||||
.pane.sidebar {
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
max-height: calc(100vh - 2rem);
|
||||
overflow-y: auto;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.pane.main {
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--main-horizontal-padding);
|
||||
min-height: 100vh;
|
||||
overflow: visible !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.pane.highlights {
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
max-height: calc(100vh - 2rem);
|
||||
overflow-y: auto;
|
||||
align-self: start;
|
||||
}
|
||||
.three-pane.sidebar-collapsed,
|
||||
.three-pane.highlights-collapsed,
|
||||
.three-pane.sidebar-collapsed.highlights-collapsed { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.pane.sidebar { overflow-y: auto; height: 100%; }
|
||||
.pane.main {
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--main-horizontal-padding);
|
||||
overflow-x: hidden;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
/* Remove padding when sidebar is collapsed for zero gap */
|
||||
.three-pane.sidebar-collapsed .pane.main { padding-left: 0; }
|
||||
.three-pane.sidebar-collapsed.highlights-collapsed .pane.main { padding-left: 0; }
|
||||
.pane.highlights { overflow-y: auto; height: 100%; }
|
||||
|
||||
/* Mobile three-pane layout */
|
||||
@media (max-width: 768px) {
|
||||
.three-pane {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
.three-pane.sidebar-collapsed,
|
||||
.three-pane.highlights-collapsed,
|
||||
.three-pane.sidebar-collapsed.highlights-collapsed { grid-template-columns: 1fr; }
|
||||
|
||||
.pane.main {
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure panes are stacked in the correct order on desktop */
|
||||
@media (min-width: 769px) {
|
||||
@@ -102,43 +120,17 @@
|
||||
/* Highlights sidebar from right */
|
||||
.pane.highlights { right: 0; transform: translateX(100%); }
|
||||
.pane.highlights.mobile-open { transform: translateX(0); box-shadow: -4px 0 12px rgba(0, 0, 0, 0.5); }
|
||||
.pane.main { grid-column: 1; grid-row: 1; padding: 0.5rem; max-width: 100%; transition: opacity 0.2s ease; }
|
||||
.pane.main {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
padding: 0;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
/* Hide main content when sidepanes are open on mobile */
|
||||
.three-pane .pane.main.mobile-hidden { opacity: 0; pointer-events: none; }
|
||||
.mobile-sidebar-backdrop {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
z-index: 999; /* Below sidepanes */
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.mobile-sidebar-backdrop.visible { display: block; opacity: 1; }
|
||||
.mobile-highlights-btn {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: calc(1rem + env(safe-area-inset-top));
|
||||
right: calc(1rem + env(safe-area-inset-right));
|
||||
z-index: 900;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #444;
|
||||
border-radius: 8px;
|
||||
color: #ddd;
|
||||
width: var(--min-touch-target);
|
||||
height: var(--min-touch-target);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
transition: transform 0.2s ease, opacity 0.3s ease, visibility 0.3s ease;
|
||||
}
|
||||
.mobile-highlights-btn.hidden { opacity: 0; visibility: hidden; pointer-events: none; }
|
||||
.mobile-highlights-btn.visible { opacity: 1; visibility: visible; }
|
||||
@media (max-width: 768px) { .mobile-highlights-btn { display: flex; } }
|
||||
/* Mobile buttons and backdrop now use Tailwind utilities in component */
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -48,46 +48,13 @@
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.mobile-hamburger-btn {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: calc(1rem + env(safe-area-inset-top));
|
||||
left: calc(1rem + env(safe-area-inset-left));
|
||||
z-index: 900;
|
||||
background: #2a2a2a;
|
||||
border: 1px solid #444;
|
||||
border-radius: 8px;
|
||||
color: #ddd;
|
||||
width: var(--min-touch-target);
|
||||
height: var(--min-touch-target);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
transition: transform 0.2s ease, opacity 0.3s ease, visibility 0.3s ease;
|
||||
}
|
||||
|
||||
.mobile-hamburger-btn.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mobile-hamburger-btn.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.mobile-hamburger-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
/* Mobile hamburger button now uses Tailwind utilities in ThreePaneLayout */
|
||||
|
||||
.mobile-close-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.mobile-hamburger-btn { display: flex; }
|
||||
.sidebar-header-bar .toggle-sidebar-btn { display: none; }
|
||||
.mobile-close-btn { display: flex; }
|
||||
}
|
||||
|
||||
7
src/styles/tailwind.css
Normal file
7
src/styles/tailwind.css
Normal file
@@ -0,0 +1,7 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
184
src/styles/utils/legacy.css
Normal file
184
src/styles/utils/legacy.css
Normal file
@@ -0,0 +1,184 @@
|
||||
/* Legacy styles for bookmark debugging and nostr content parsing */
|
||||
|
||||
.user-info {
|
||||
margin: 0.5rem 0 0 0;
|
||||
color: #888;
|
||||
font-size: 0.9rem;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.bookmark-count {
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.event-link {
|
||||
color: #8ab4f8;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.event-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.bookmark-urls {
|
||||
margin: 0.75rem 0;
|
||||
}
|
||||
|
||||
.bookmark-url {
|
||||
display: block;
|
||||
margin: 0.25rem 0;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
word-break: break-all;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bookmark-url:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.url-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.read-inline-btn {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.read-inline-btn:hover {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.bookmark-events {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.bookmark-events h4 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.event-ids {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.event-id {
|
||||
background: #f5f5f5;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.more-events {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* Nostr content parsing styles */
|
||||
.parsed-content,
|
||||
.nostr-mention,
|
||||
.nostr-link,
|
||||
.nostr-uri-link {
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.parsed-content {
|
||||
margin: 1rem 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.nostr-mention,
|
||||
.nostr-uri-link {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
font-family: monospace;
|
||||
background: #f8f9fa;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.nostr-uri-link {
|
||||
font-size: 0.9em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.nostr-mention:hover,
|
||||
.nostr-uri-link:hover {
|
||||
background: #e9ecef;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.nostr-link {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nostr-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.logout-button {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.logout-button:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
/* Common state styles */
|
||||
.loading {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: #888;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
19
tailwind.config.js
Normal file
19
tailwind.config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
'./index.html',
|
||||
'./src/**/*.{ts,tsx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
keyframes: {
|
||||
shimmer: {
|
||||
'0%': { transform: 'translateX(-100%)' },
|
||||
'100%': { transform: 'translateX(100%)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user