Commit Graph

35 Commits

Author SHA1 Message Date
Alejandro
ee2b62f2d6 feat: enhance login options with read-only and nsec support (#126)
* feat: enhance login options with read-only and nsec support

- Add read-only login mode supporting:
  - npub (bech32 public key)
  - nprofile (bech32 profile with relay hints)
  - hex public key
  - NIP-05 addresses (user@domain.com)

- Add private key (nsec) login with security warning
  - Supports nsec1... format
  - Supports 64-char hex private key
  - Shows prominent security warning about localStorage storage

- Reorganize user menu to show login before theme option

- Use ReadonlyAccount from applesauce-accounts for read-only mode
- Use PrivateKeyAccount from applesauce-accounts for nsec login
- Update LoginDialog with 4 tabs: Extension, Read-Only, Private Key, Remote
- All account types properly registered via registerCommonAccountTypes()

Technical notes:
- ReadonlySigner throws errors on sign/encrypt operations
- Existing components naturally handle accounts without signing capability
- Hub/ActionRunner already syncs with account signers automatically

* feat: add generate identity button to login dialog

- Add "Generate Identity" button above login tabs
- Uses Wand2 icon from lucide-react
- Creates new key pair using PrivateKeyAccount.generateNew()
- Automatically stores nsec in localStorage and sets as active account
- Provides quick onboarding for new users without external wallet setup

* feat: add useAccount hook for signing capability detection

Created a centralized hook to check account signing capabilities and
refactored components to distinguish between signing and read-only operations.

New hook (src/hooks/useAccount.ts):
- Returns account, pubkey, canSign, signer, isLoggedIn
- Detects ReadonlyAccount vs signing accounts
- Provides clear API for checking signing capability

Refactored components:
- ChatViewer: Use canSign for message composer, replying, actions
  - Show "Sign in to send messages" for read-only accounts
  - Disable message input for accounts without signing
- SpellDialog: Use canSign for publishing spells
  - Show clear warning for read-only accounts
  - Updated error messages to mention read-only limitation
- useEmojiSearch: Use pubkey for loading custom emoji lists
  - Works correctly with both signing and read-only accounts

Benefits:
- Clear separation between read (pubkey) and write (canSign, signer) operations
- Read-only accounts can browse, view profiles, load data
- Signing operations properly disabled for read-only accounts
- Consistent pattern across the codebase for account checks
- Better UX with specific messages about account capabilities

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-17 21:14:44 +01:00
Alejandro
16764e1aca Display user's blossom servers in menu (#90)
* feat: Display blossom servers in user menu with caching

Implements caching and display of user's blossom server lists (kind 10063) in the user menu dropdown.

**Key Changes:**

1. **Database Schema (db.ts)**:
   - Added `CachedBlossomServerList` interface
   - Added `blossomServers` table to Dexie v15
   - Indexed by pubkey and updatedAt for efficient querying

2. **Caching Service (blossom-server-cache.ts)**:
   - Dual-layer caching: LRU memory cache (100 entries) + Dexie persistent cache
   - 24-hour TTL to reduce network requests
   - Auto-caches kind:10063 events from EventStore
   - Mirrors relay-list-cache pattern for consistency

3. **Sync Hooks**:
   - `useBlossomServerCacheSync`: Subscribes to EventStore for auto-caching kind:10063 events
   - `useAccountSync`: Extended to fetch and watch active account's blossom servers

4. **State Management**:
   - Added `blossomServers` field to `activeAccount` in GrimoireState
   - Created `setActiveAccountBlossomServers` pure function and callback
   - Reactive updates when user's server list changes

5. **User Menu UI (user-menu.tsx)**:
   - Added blossom servers section with HardDrive icon
   - Shows server count badge (e.g., "3 servers")
   - Clickable links open servers in new tabs
   - Displays only when user has configured servers

**Architecture:**
- Follows existing relay list caching pattern for consistency
- Reactive: UI auto-updates when kind:10063 events arrive
- Incremental sync: Fetches on login, caches for 24h
- Performance: Memory cache for <1ms lookups, Dexie for persistence

**Testing:**
- Build: ✓ No TypeScript errors
- Tests: ✓ All 838 tests passing

* feat: Open blossom server file lists directly from menus

**User Menu & Profile Viewer Improvements:**

1. **Enhanced Click Behavior**:
   - Clicking a blossom server now opens the file list for that server
   - Shows blobs uploaded by the user (user menu) or profile owner (profile viewer)
   - Pre-selects the clicked server in the dropdown

2. **UX Improvements**:
   - Removed server count from user menu label (cleaner UI)
   - Added `cursor-crosshair` to blossom server items (consistent with other clickable items)
   - Removed external link icon (not opening external URL anymore)

3. **Technical Changes**:
   - Updated `ListBlobsView` to accept optional `serverUrl` prop for pre-selection
   - User menu: Opens `blossom list` with `serverUrl` for active user
   - Profile viewer: Opens `blossom list` with both `pubkey` and `serverUrl`

**Flow:**
- User menu → Click server → Opens files for active user on that server
- Profile viewer → Click server → Opens files for viewed user on that server

* fix: Properly fetch blossom servers for any profile view

**Problem:**
Blossom servers were only visible for the logged-in user's profile,
not for other users' profiles being viewed.

**Solution:**
Enhanced ProfileViewer blossom server fetching with multi-layer approach:

1. **Cache-first loading**: Check blossomServerCache for instant display
2. **EventStore check**: Use existing cached event if available
3. **Reactive subscription**: Subscribe to EventStore for real-time updates
4. **Network fetch**: Use addressLoader to fetch latest from relays
5. **Auto-caching**: Update cache when new events arrive

**Benefits:**
- Blossom servers now display for ANY user's profile
- Instant display from cache (< 1ms)
- Reactive updates when data changes
- Proper cache hydration for future visits
- Consistent with relay list fetching pattern

**Technical:**
- Imported and integrated blossomServerCache service
- Added cache check before network fetch
- Separated EventStore subscription from network fetch
- Added cache updates on event arrival

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-14 14:52:16 +01:00
Alejandro
9ef1fefd3d feat: BLOSSOM (#75)
* Add Blossom blob storage integration

- Add blossom-client-sdk dependency for blob storage operations
- Create blossom.ts service with upload, list, check, mirror, delete primitives
- Add kind 10063 server list fetching and parsing
- Create blossom-parser.ts for command argument parsing with subcommands
- Add BLOSSOM command to man.ts with subcommands:
  - servers: Show configured Blossom servers
  - check: Check server health
  - upload: Upload files to user's servers
  - list: List blobs for a user
  - mirror: Mirror blobs between servers
  - delete: Delete blobs from servers
- Create BlossomViewer component with views for each subcommand
- Wire up BlossomViewer in WindowRenderer
- Add Blossom servers dropdown to ProfileViewer header
- Upload primitives can be called programmatically for use in other components

* Enhance Blossom viewer with server selection and blob details

- Add server selection checkboxes to upload view for choosing target servers
- Add BlobDetailView with media preview (image/video/audio) and metadata display
- Add 'blob' subcommand to view individual blob details
- Remove unused 'check' subcommand

* Add Blossom upload dialog with chat integration

- Create BlossomUploadDialog component with file picker, server selection, and preview
- Create useBlossomUpload hook for easy integration in any component
- Add insertText method to MentionEditor for programmatic text insertion
- Integrate upload button (paperclip icon) in chat composer
- Supports image, video, and audio uploads with drag-and-drop

* Add rich blob attachments with imeta tags for chat

- Add BlobAttachment TipTap extension with inline preview (thumbnail for images, icons for video/audio)
- Store full blob metadata (sha256, url, mimeType, size, server) in editor nodes
- Convert blob nodes to URLs in content with NIP-92 imeta tags when sending
- Add insertBlob method to MentionEditor for programmatic blob insertion
- Update NIP-29 and NIP-53 adapters to include imeta tags with blob metadata
- Pass blob attachments through entire send flow (editor -> ChatViewer -> adapter)

* Add fallback public Blossom servers for users without server list

- Add well-known public servers as fallbacks (blossom.primal.net, nostr.download, files.v0l.io)
- Use fallbacks when user has no kind 10063 server list configured
- Show "Public Servers" label with Globe icon when using fallbacks
- Inform user that no server list was found
- Select first fallback server by default (vs all user servers)

* Fix: Don't show fallback servers when not logged in

Blossom uploads require signed auth events, so users must be logged in.
The 'Account required' message is already shown in this case.

* Remove files.v0l.io from fallback servers

* Add rich renderer for kind 10063 Blossom server list

- Create BlossomServerListRenderer.tsx with feed and detail views
- Show user's configured Blossom servers with clickable links
- Clicking a server opens the Blossom window with server info
- Register renderers for kind 10063 (BUD-03)
- Fix lint error by renaming useFallbackServers to applyFallbackServers

* Add individual server view and NIP-05 support for blossom commands

- Add 'server' subcommand to view info about a specific Blossom server
- Update BlossomServerListRenderer to open server view on click
- Make blossom parser async to support NIP-05 resolution in 'list' command
- Add kind 10063 (Blossom Server List) to EVENT_KINDS constants with BUD-03 reference
- Update command examples with NIP-05 identifier support

* Add comprehensive tests for blossom-parser

- 34 test cases covering all subcommands (servers, server, upload, list, blob, mirror, delete)
- Tests for NIP-05 resolution, npub/nprofile decoding, $me alias
- Tests for error handling and input validation
- Tests for case insensitivity and command aliases (ls, view, rm)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-13 17:16:31 +01:00
Alejandro
2bad592a3a feat: emoji autocompletion (#54)
* feat: add NIP-30 emoji autocompletion to editor

Implement emoji autocomplete triggered by `:` in the MentionEditor:

- EmojiSearchService: flexsearch-based indexing for emoji shortcodes
- useEmojiSearch hook: loads Unicode emojis + user's custom emoji (kind 10030/30030)
- EmojiSuggestionList: grid-based suggestion UI with keyboard nav
- Update MentionEditor with second Mention extension for emoji
- Serialize emoji as `:shortcode:` format with NIP-30 emoji tags
- Update chat adapters to include emoji tags in messages

Sources:
- Unicode: ~300 common emojis with shortcodes
- Custom: user's emoji list (kind 10030) and referenced sets (kind 30030)
- Context: emoji tags from events being replied to

* feat: add rich emoji preview in editor

Emoji inserted via the autocomplete now display as actual images/characters
instead of :shortcode: text:

- Custom emoji: renders as inline <img> with proper sizing
- Unicode emoji: renders as text with emoji font sizing
- Both show :shortcode: on hover via title attribute

CSS styles ensure proper vertical alignment with surrounding text.

* fix: store emoji url and source attributes in node schema

The TipTap Mention extension only defines `id` and `label` by default.
Added `addAttributes()` to EmojiMention extension to also store `url`
and `source` attributes, fixing emoji tags not being included in sent
messages.

* fix: improve emoji node rendering in editor

- Remove redundant renderLabel (nodeView handles display)
- Add renderText for proper clipboard behavior
- Make nodeView more robust with null checks
- Add fallback to shortcode if image fails to load
- Unicode emoji shows character, custom shows image

* fix: serialize unicode emoji as actual characters, not shortcodes

When sending messages:
- Unicode emoji (😄, 🔥) → outputs 😄, 🔥 (the actual character)
- Custom emoji (:pepe:) → outputs :pepe: with emoji tag for rendering

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-12 11:30:52 +01:00
Alejandro Gómez
0b20a628e2 feat: add mention editor and NIP-29 chat enhancements
Implements rich text editing with profile mentions, NIP-29 system messages,
day markers, and naddr support for a more complete chat experience.

Editor Features:
- TipTap-based rich text editor with @mention autocomplete
- FlexSearch-powered profile search (case-insensitive)
- Converts mentions to nostr:npub URIs on submission
- Keyboard navigation (Arrow keys, Enter, Escape)
- Fixed Enter key and Send button submission

NIP-29 Chat Improvements:
- System messages for join/leave events (kinds 9000, 9001, 9021, 9022)
- Styled system messages aligned left with muted text
- Shows "joined" instead of "was added" for consistency
- Accepts kind 39000 naddr (group metadata addresses)
- Day markers between messages from different days
- Day markers use locale-aware formatting (short month, no year)

Components:
- src/components/editor/MentionEditor.tsx - TipTap editor with mention support
- src/components/editor/ProfileSuggestionList.tsx - Autocomplete dropdown
- src/services/profile-search.ts - FlexSearch service for profile indexing
- src/hooks/useProfileSearch.ts - React hook for profile search

Dependencies:
- @tiptap/react, @tiptap/starter-kit, @tiptap/extension-mention
- @tiptap/extension-placeholder, @tiptap/suggestion
- flexsearch@0.7.43, tippy.js@6.3.7

Tests:
- Added 6 new tests for naddr parsing in NIP-29 adapter
- All 710 tests passing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-12 10:26:38 +01:00
Alejandro
b2b398b9fb docs: add applesauce v5 upgrade plan (#39)
* docs: add applesauce v5 upgrade plan

Comprehensive migration plan covering:
- Package updates (add applesauce-common, update to v5)
- EventFactory import migration (applesauce-factory → applesauce-core)
- Unified event loader setup
- ActionHub → ActionRunner migration
- useObservableMemo → use$ hook migration
- New features: casting system, encrypted content caching
- Documentation and skills updates needed

* feat: upgrade applesauce libraries to v5

Major upgrade from applesauce v4 to v5 with breaking changes:

Package updates:
- applesauce-core: ^4.0.0 → ^5.0.0
- applesauce-actions: ^4.0.0 → ^5.0.0
- applesauce-loaders: ^4.0.0 → ^5.0.0
- applesauce-react: ^4.0.0 → ^5.0.0
- applesauce-relay: ^4.0.0 → ^5.0.0
- applesauce-signers: ^4.0.0 → ^5.0.0
- applesauce-accounts: ^4.0.0 → ^5.0.0
- Added new applesauce-common: ^5.0.0 package

API migrations:
- EventFactory: applesauce-factory → applesauce-core/event-factory
- ActionHub → ActionRunner with async function pattern (not generators)
- useObservableMemo → use$ hook across all components
- Helper imports: article, highlight, threading, zap, comment, lists
  moved from applesauce-core to applesauce-common
- parseCoordinate → parseReplaceableAddress
- Subscription options: retries → reconnect
- getEventPointerFromETag now returns null instead of throwing

New features:
- Unified event loader via createEventLoaderForStore
- Updated loaders.ts to use v5 unified loader pattern

Documentation:
- Updated CLAUDE.md with v5 patterns and migration notes
- Updated applesauce-core skill for v5 changes
- Created new applesauce-common skill

Test fixes:
- Updated publish-spellbook.test.ts for v5 ActionRunner pattern
- Updated publish-spell.test.ts with eventStore mock
- Updated relay-selection.test.ts with valid test events
- Updated loaders.test.ts with valid 64-char hex event IDs
- Added createEventLoaderForStore mock

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 14:54:21 +01:00
Alejandro
32d394b398 feat: add preview routes for Nostr identifiers (npub, nevent, note, naddr) (#33)
* feat: add preview routes for Nostr identifiers (npub, nevent, note, naddr)

This commit adds dedicated preview routes for Nostr identifiers at the root level:
- /npub... - Shows a single profile view for npub identifiers
- /nevent... - Shows a single event detail view for nevent identifiers
- /note... - Shows a single event detail view for note identifiers
- /naddr... - Redirects spellbooks (kind 30777) to /:actor/:identifier route

Key changes:
- Created PreviewProfilePage component for npub identifiers
- Created PreviewEventPage component for nevent/note identifiers
- Created PreviewAddressPage component for naddr redirects
- Added hideBottomBar prop to AppShell to hide tabs in preview mode
- Added routes to root.tsx for all identifier types

Preview pages don't show bottom tabs and don't affect user's workspace layout.

* chore: update package-lock.json

* refactor: create reusable useNip19Decode hook and improve preview pages

This commit makes the preview pages production-ready by:

1. Created useNip19Decode hook (src/hooks/useNip19Decode.ts):
   - Reusable hook for decoding NIP-19 identifiers (npub, note, nevent, naddr, nprofile)
   - Type-safe with discriminated union for decoded entities
   - Comprehensive error handling with retry functionality
   - Loading states and error messages
   - Well-documented with JSDoc comments and usage examples

2. Comprehensive test coverage (src/hooks/useNip19Decode.test.ts):
   - 11 tests covering all entity types (npub, note, nevent, naddr)
   - Tests for error handling (missing identifier, invalid format, corrupted bech32)
   - Tests for retry functionality and state changes
   - Uses jsdom environment for React hook testing
   - All tests passing ✓

3. Refactored preview pages to use the hook:
   - PreviewProfilePage: Simplified from 80 to 81 lines with cleaner logic
   - PreviewEventPage: Improved type safety and error handling
   - PreviewAddressPage: Better separation of concerns
   - All pages now have consistent error handling and retry functionality
   - Better user experience with improved error messages

4. Dependencies added:
   - @testing-library/react for React hook testing
   - @testing-library/dom for DOM testing utilities
   - jsdom and happy-dom for browser environment simulation in tests

Benefits:
- Code deduplication: Preview pages share decoding logic
- Type safety: Discriminated union prevents type errors
- Testability: Hook can be tested independently
- Maintainability: Single source of truth for NIP-19 decoding
- User experience: Consistent error handling and retry across all preview pages
- Production-ready: Comprehensive tests and error handling

* refactor: simplify useNip19Decode to synchronous with memoization

NIP-19 decoding is synchronous - removed unnecessary async complexity:

Hook changes (src/hooks/useNip19Decode.ts):
- Removed loading states (isLoading, setIsLoading)
- Removed retry functionality (unnecessary for sync operations)
- Now uses useMemo for efficient memoization
- Returns { decoded, error } instead of { decoded, isLoading, error, retry }
- Same string always yields same result (memoized)
- Went from ~120 lines to ~115 lines, but much simpler

Preview page changes:
- Removed loading spinners and states
- Removed retry buttons
- Simplified error handling
- Cleaner, more readable code
- PreviewProfilePage: 55 lines (down from 81)
- PreviewEventPage: 83 lines (down from 105)
- PreviewAddressPage: 83 lines (down from 117)

Test changes (src/hooks/useNip19Decode.test.ts):
- Removed waitFor and async/await (not needed for sync)
- Tests run faster (39ms vs 77ms - 49% improvement)
- Added memoization tests to verify caching works
- Simplified from 11 async tests to 11 sync tests
- All 11 tests passing ✓

Benefits:
- Simpler mental model: decode happens instantly
- Better performance: no state updates, just memoization
- Easier to test: synchronous tests are simpler
- More correct: matches the actual synchronous nature of nip19.decode()
- Less code: removed ~150 lines of unnecessary complexity

* feat: show detail view for all addressable events in naddr preview

Previously, PreviewAddressPage only handled spellbooks (kind 30777) and
showed errors for other addressable events. Now:

- Spellbooks (kind 30777): Redirect to /:actor/:identifier (existing behavior)
- All other addressable events: Show in EventDetailViewer

This enables previewing any addressable event (long-form articles, live
events, community posts, etc.) via naddr links.

Changes:
- Import EventDetailViewer
- Removed error state for non-spellbook kinds
- Show EventDetailViewer with AddressPointer for all other kinds
- Simplified from 83 lines to 77 lines

* fix: correct route patterns for NIP-19 identifier previews

The previous route patterns (/npub:identifier) conflicted with the catch-all
/:actor/:identifier route and didn't properly match NIP-19 identifiers.

Fixed by:
1. Using wildcard routes with correct prefixes:
   - /npub1* (not /npub:identifier)
   - /nevent1* (not /nevent:identifier)
   - /note1* (not /note:identifier)
   - /naddr1* (not /naddr:identifier)

2. Updated preview components to use params['*'] for wildcard capture:
   - Reconstruct full identifier as prefix + captured part
   - e.g., npub1 + params['*'] = npub107jk7htfv...

This ensures routes properly match before the catch-all /:actor/:identifier
route and correctly capture the full bech32-encoded identifier.

Test URL: /npub107jk7htfv243u0x5ynn43scq9wrxtaasmrwwa8lfu2ydwag6cx2quqncxg

* style: apply prettier formatting

* fix: use loader-based routing for NIP-19 identifiers in React Router v7

Previous attempts using wildcard routes didn't work properly in React Router v7.

Solution:
- Single /:identifier route with a loader that validates NIP-19 prefixes
- Loader throws 404 if identifier doesn't start with npub1/note1/nevent1/naddr1
- Created Nip19PreviewRouter component that routes to correct preview page
- Routes are properly ordered: /:identifier before /:actor/:identifier catch-all

This ensures /npub107jk... routes to profile preview, not spellbook route.

Benefits:
- Simpler routing configuration (1 route vs 4 duplicate routes)
- Proper validation via loader
- Clean separation of concerns with router component
- Works correctly in React Router v7

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 20:12:48 +01:00
Claude
af8cf427d6 fix: implement per-relay EOSE detection by subscribing to relays individually
**CRITICAL FIX for EOSE detection:**

**The Problem:**
- Used pool.subscription(relays, filters) which creates a RelayGroup
- RelayGroup tracks per-relay EOSE internally but only emits ONE "EOSE" when ALL relays finish
- This caused:
  1. EOSE indicators taking forever to appear (waiting for slowest relay)
  2. REQ stuck in LOADING state when fast relays finish but slow relays never do
  3. No way to show per-relay EOSE status accurately

**The Solution:**
Subscribe to each relay individually using pool.relay(url).subscription():
- Each relay subscription emits its own EOSE immediately when that relay finishes
- We track per-relay EOSE in relayStates map with accurate timing
- Overall EOSE is derived when ALL relays reach terminal state (eose/error/disconnected)
- EOSE indicators now appear immediately as each relay finishes

**Technical Details:**
- Changed from: pool.subscription(relays, filters)
- Changed to: relays.map(url => pool.relay(url).subscription(filters))
- Added eoseReceivedRef to track overall EOSE in closures
- Mark specific relay as EOSE when that relay emits "EOSE"
- Calculate overall EOSE when all relays in terminal states
- Use url from subscription context (more reliable than event._relay)

**Benefits:**
 Instant per-relay EOSE indicators (no waiting for slowest relay)
 Accurate relay state tracking (each relay independent)
 REQ transitions to LIVE/CLOSED as soon as all relays finish
 Better user feedback (see which relays are done vs still loading)

All 639 tests passing.
2025-12-22 18:10:52 +00:00
Claude
70651ae29f fix: improve relay state tracking and add relay type indicators
State Tracking Fixes:
- Sync connection state for ALL relays in query, not just initialized ones
- Defensively initialize missing relay states during sync
- Handle events from unknown relays (defensive initialization)
- Add debug console logs to track state transitions

Relay Type Indicators:
- Explicit relays: Blue link icon (relays specified directly)
- Outbox relays: Purple sparkles (NIP-65 selected)
- Fallback relays: Gray inbox icon (fallback when outbox incomplete)
- Each type has tooltip explaining source

This should fix:
- "0/4 relays but events coming in" bug
- "Stuck in LOADING" when events are arriving
- Missing visibility for relay source types

Tests: 634/634 passing
2025-12-22 16:36:56 +00:00
Claude
c60abe6df4 feat: implement production-grade REQ state machine with per-relay tracking
Core Infrastructure:
- Add ReqRelayState and ReqOverallState types for granular state tracking
- Implement deriveOverallState() state machine with 8 query states
- Create useReqTimelineEnhanced hook combining RelayStateManager + event tracking
- Add comprehensive unit tests (27 tests, all passing)

State Machine Logic:
- DISCOVERING: NIP-65 relay selection in progress
- CONNECTING: Waiting for first relay connection
- LOADING: Initial events loading
- LIVE: Streaming with active relays (only when actually connected!)
- PARTIAL: Some relays ok, some failed/disconnected
- OFFLINE: All relays disconnected after being live
- CLOSED: Query completed, all relays closed
- FAILED: All relays failed to connect

UI Updates:
- Single-word status indicators with detailed tooltips
- Condensed relay status into NIP-65 section (no duplicate lists)
- Per-relay subscription state badges (RECEIVING, EOSE, ERROR, OFFLINE)
- Event counts per relay
- Connection + Auth status integrated into single dropdown

Fixes Critical Bug:
- Solves "LIVE with 0 relays" issue (Scenario 5 from analysis)
- Distinguishes real EOSE from relay disconnections
- Accurate status for all 7 edge cases documented in analysis

Technical Approach:
- Hybrid: RelayStateManager for connections + event._relay for tracking
- Works around applesauce-relay catchError bug without forking
- No duplicate subscriptions
- Production-quality error handling

Tests: 27/27 passing including edge case scenarios
2025-12-22 16:18:15 +00:00
Claude
c40c4ae1f9 fix: TypeScript error in useStableFilters 2025-12-22 14:05:53 +00:00
Claude
912794f4e0 chore: lint fix 2025-12-22 14:04:46 +00:00
Claude
33539c291b refactor: use applesauce helpers for pointer parsing and filter comparison
Phase 2 & 3 of applesauce helpers refactoring plan.

**Phase 2: Replace manual pointer parsing**
- ReactionRenderer.tsx: replaced manual coordinate parsing with parseCoordinate helper
- Benefits: more robust, handles edge cases, consistent with applesauce patterns

**Phase 3: Improve filter comparison**
- useStable.ts: replaced JSON.stringify with isFilterEqual for filter comparison
- Benefits: handles undefined values correctly, supports NIP-ND AND operator
- Implementation uses ref pattern to maintain stable reference when filters are equal

All tests pass (607 tests).
2025-12-22 14:04:09 +00:00
Alejandro Gómez
32c895e150 chore: lint fix 2025-12-22 13:25:38 +01:00
Alejandro
d35345f720 Merge pull request #13 from purrgrammer/claude/useprofile-race-fix-EeWQZ
fix: prevent race conditions in useProfile hook
2025-12-22 13:12:25 +01:00
Claude
645e12cddc fix: prevent race conditions in useProfile hook
- Replace mounted boolean flag with AbortController pattern
- Check abort signal before initiating database writes
- Proper cleanup on unmount/pubkey change

This prevents stale data from being written to IndexedDB when:
- Component unmounts during async operations
- Pubkey changes while a fetch is in progress
2025-12-22 12:02:03 +00:00
Claude
bdfc634c54 feat: add dependency stabilization hooks
- Create src/hooks/useStable.ts with:
  - useStableValue<T>() - stabilizes any value using JSON.stringify
  - useStableArray<T>() - stabilizes string arrays (uses JSON.stringify
    for safety, handles arrays with commas in elements)
  - useStableFilters<T>() - specialized for Nostr filters

- Update timeline hooks to use stabilization:
  - useTimeline.ts - use useStableFilters for filter dependencies
  - useReqTimeline.ts - use useStableValue for filter dependencies
  - useLiveTimeline.ts - use useStableArray for relay dependencies

Prevents unnecessary re-renders and subscription restarts when
filter/relay objects are recreated with the same content.
2025-12-22 12:00:42 +00:00
Alejandro Gómez
812b719ea0 feat: debug command, simplify state 2025-12-18 23:32:00 +01:00
Alejandro Gómez
a7dd4635dc feat: kind schemas and better man pages 2025-12-18 10:18:53 +01:00
Alejandro Gómez
3b06e23686 ui: improve man page layout for options and examples
- Display option flags on separate lines with indented descriptions to prevent overflow
- Parse and separate example commands from their descriptions
- Highlight commands in accent color with muted descriptions below
- Increase spacing between items for better readability
2025-12-18 09:37:14 +01:00
Alejandro Gómez
97c89142ae wip: live video events 2025-12-17 11:44:12 +01:00
Alejandro Gómez
a7059c8e8f feat: outbox relay selection 2025-12-16 12:55:07 +01:00
Alejandro Gómez
88a6e38ee9 refactor: use applesauce for profile parsing 2025-12-15 17:38:51 +01:00
Alejandro Gómez
e5c871617e chore: cleanup, a11y and state migrations 2025-12-14 16:32:45 +01:00
Alejandro Gómez
bef7369de9 fix: normalize relay URLs 2025-12-14 12:00:55 +01:00
Alejandro Gómez
8e92a8ebfb feat: filter by arbitrary tag, relay state improvements 2025-12-13 15:49:04 +01:00
Alejandro Gómez
6568daf944 wip: relay pool view and auth 2025-12-13 15:06:05 +01:00
Alejandro Gómez
16158e7045 feat: better p and authors titles 2025-12-12 10:12:57 +01:00
Alejandro Gómez
38f5461b54 chore: setup eslint and prettier with code formatting 2025-12-11 13:00:25 +01:00
Alejandro Gómez
e926272686 feat: relay command 2025-12-11 00:09:26 +01:00
Alejandro Gómez
929365a813 refactor: copy hook 2025-12-10 23:19:34 +01:00
Alejandro Gómez
8ffd0fd2cb feat: timestamps and user locale 2025-12-10 22:32:36 +01:00
Alejandro Gómez
c6e3325720 wip: render hashtags correctly 2025-12-10 13:59:26 +01:00
Alejandro Gómez
5b00e42ddf feat: RTL support 2025-12-10 13:00:39 +01:00
Alejandro Gómez
cd41034b2f 👶 2025-12-09 16:26:31 +01:00