Commit Graph

250 Commits

Author SHA1 Message Date
Claude
cdeca71ef0 feat(relay-selection): add per-relay filter optimization
Add RelayFilterMap type and buildPerRelayFilters function to group
authors by their target relays. This enables consumers to send only
relevant authors to each relay instead of the full filter.

Result now includes:
- perRelayFilters: Array of { relay, authors[] } mappings

This reduces bandwidth and improves relay processing efficiency.
2025-12-24 14:48:56 +00:00
Claude
29dbdadc8b feat(relay-selection): integrate performance scoring into relay selection
Replace applesauce's selectOptimalRelays with custom implementation that:
- Uses relay performance scores from RelayScoreboard
- Incorporates health status from RelayLiveness
- Weights coverage (60%) + performance (40%) × health multiplier

Algorithm: Greedy set-cover that prefers fast, reliable relays while
maintaining optimal coverage across all users.
2025-12-24 14:46:36 +00:00
Claude
75e92e3b3b test(relay-scoreboard): add unit tests for scoring algorithms
Add comprehensive tests for RelayScoreboard:
- Score calculation (response time, connect time, stability, success rate)
- Adaptive timeout calculation with boundary conditions
- Unreliable relay penalty handling
- Edge cases and min/max bounds

22 tests covering all score calculation and timeout logic.
2025-12-24 14:41:39 +00:00
Claude
e6ac7e5572 feat(relay-metrics): integrate scoreboard with pool events
Add RelayMetricsCollector service that hooks into the relay pool:
- Tracks connection time when WebSocket connects
- Tracks session duration when connection drops
- Integrates with useReqTimelineEnhanced to record response times

Integration points:
- AppShell.tsx: Initialize collector on app start
- useReqTimelineEnhanced: Record response time on EOSE, failures on error
2025-12-24 14:40:15 +00:00
Claude
773da9afb5 feat(relay-scoreboard): implement performance scoring service
Add RelayScoreboard service for tracking relay performance:
- Response time tracking with exponential moving average
- Connection time tracking
- Session stability/duration tracking
- Success/failure rate tracking
- Score calculation (0-10) with weighted metrics
- Adaptive timeout calculation based on historical data
- Auto-save to Dexie every 30 seconds
- Singleton pattern for consistent metrics

Exported functions:
- calculateRelayScore(): Calculate score from metrics
- calculateAdaptiveTimeout(): Get smart timeout for relay
2025-12-24 14:36:20 +00:00
Claude
c89f8efeec feat(db): add RelayPerformanceEntry interface and schema v15
Add database table for tracking relay performance metrics:
- Response time (exponential moving average)
- Connection time
- Session stability/duration
- Success/failure rate
- Timestamps for last success/failure

This is the foundation for relay performance scoring in outbox selection.
2025-12-24 14:34:15 +00:00
Claude
501f2746fb docs: add outbox relay selection improvement plans
Research and analysis of outbox implementations from nosotros, noStrudel,
and jumble to create a comprehensive improvement plan for Grimoire.

Priority improvements (docs/outbox-improvements-plan.md):
1. Relay Performance Scoring - track response time, connection time, stability
2. Adaptive Timeouts - use historical data for per-relay timeouts
3. Per-Relay Filter Optimization - send only relevant authors to each relay
4. Custom Scoring Function - combine coverage + performance in selection

Future work saved in docs/outbox-future-work.md:
5. Progressive Relay Selection
6. NIP-66 Relay Discovery
2025-12-24 13:23:21 +00:00
Alejandro
7241b3fb5a feat: add copy button for NIP markdown (#26)
* feat: add copy button for NIP markdown

- Add copy button to WindowToolbar for regular NIPs (appId: "nip")
  - Button appears in window toolbar next to edit button
  - Uses Copy/CopyCheck icons from lucide-react
  - Fetches NIP content via useNip hook
  - Shows toast notification on successful copy

- Add copy button to CommunityNIPDetailRenderer for community NIPs (kind 30817)
  - Button appears in header next to title
  - Copies event.content (markdown) to clipboard
  - Uses same Copy/CopyCheck icon pattern
  - Shows toast notification on successful copy

Both implementations use the existing useCopy hook for state management
and maintain consistent styling with other toolbar buttons.

* refactor: use Button component and remove misleading shortcuts

- Replace native button elements with Button component from shadcn/ui
  - Use variant="ghost" and size="icon" for consistent styling
  - Apply h-8 w-8 classes for uniform button sizing
  - Remove manual className styling in favor of component variants

- Remove misleading keyboard shortcut hints from button titles
  - Changed "Edit command (Cmd+E)" to "Edit command"
  - Changed "Close window (Cmd+W)" to "Close window"
  - These shortcuts don't actually work, so they were misleading

- Clean up imports and formatting
  - Format lucide-react imports across multiple lines
  - Add Button component import
  - Run prettier for consistent code style

* refactor: use link variant and remove size class overrides

- Change all Button components from variant="ghost" to variant="link"
- Remove className="h-8 w-8" overrides to use default Button sizing
- Maintains size="icon" for proper icon button behavior
- Applies to WindowToolbar and CommunityNIPDetailRenderer

* style: add muted color to window toolbar icon buttons

- Add className="text-muted-foreground" to all Button components in WindowToolbar
- Improves visual contrast for toolbar buttons
- Applies to Edit, Copy NIP, More actions, and Close window buttons

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-23 00:00:52 +01:00
Alejandro
e5a715aefe fix: memoize contact list pointer to fix $contacts alias (#25)
The $contacts alias in REQ command was broken due to an object reference issue.
The contact list pointer object was being created inline on every render, causing
useNostrEvent to treat it as a new pointer each time and disrupting the subscription.

Now properly memoize the pointer object so it only changes when accountPubkey or
needsAccount actually changes, ensuring stable subscriptions and proper contact
list retrieval.

Fixes the $contacts alias resolution in REQ commands.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 23:07:04 +01:00
Alejandro
75500fa298 feat: add clickable example commands to welcome screen (#23)
* feat: add clickable example commands to welcome screen

Add 4 example commands to the welcome splash screen to make the client
more friendly on cold start and to new users:
- nip 29: View relay-based groups spec
- profile verbiricha@habla.news: Explore a Nostr profile
- req -k 1 -l 20: Query recent notes
- nips: Browse all NIPs

Commands are clickable and execute directly, opening the appropriate
window without requiring users to type or use the command palette.

Changes:
- Update GrimoireWelcome component with EXAMPLE_COMMANDS constant
- Add onExecuteCommand prop to handle direct command execution
- Update WorkspaceView to pass handleExecuteCommand callback
- Use parseAndExecuteCommand from command-parser for execution

* fix: lint

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-22 22:41:10 +01:00
Alejandro Gómez
5a97caffb1 nit: casing 2025-12-22 22:38:52 +01:00
Alejandro Gómez
abd0cc9750 Merge branch 'pr/feat--add-NIP-34-user-grasp-list-rendering(68a54c52)' 2025-12-22 22:32:56 +01:00
DanConwayDev
6853a2f7c9 feat: add NIP-34 user grasp list rendering
because you wanted to know what it feels like:
nostr:nevent1qvzqqqqqqypzqla9dawkjc4trc7dgf88trpsq2uxvhmmpkxua607nc5g6a634sv5qyd8wumn8ghj7urewfsk66ty9enxjct5dfskvtnrdakj7qgmwaehxw309a6xsetxdaex2um59ehx7um5wgcjucm0d5hsqgxwfrfg9kvh99tj6adq5v2spwm9vhnp76zljuseqcpz4nd7w9d6pg000a03
2025-12-22 20:58:58 +00:00
Alejandro
7994b8aaca Merge pull request #24 from purrgrammer/claude/add-mit-license-wIFjZ
Add MIT license to the project
2025-12-22 21:41:31 +01:00
Claude
389be2df8f Add MIT license to the project
- Create LICENSE file with MIT license text
- Update package.json with license field
- Copyright holder: Alejandro Gómez
2025-12-22 20:40:16 +00:00
Alejandro
363840b756 Merge pull request #20 from purrgrammer/claude/improve-reqviewer-state-machine-cBkEO
docs: add comprehensive ReqViewer state machine analysis and improvement plan
2025-12-22 19:51:21 +01:00
Alejandro Gómez
9f0c383ff8 fix: add icon back 2025-12-22 19:46:12 +01:00
Alejandro Gómez
c4bc3ab445 ui: improve relay tooltip, update docs 2025-12-22 19:43:00 +01:00
Alejandro Gómez
3f1c66ec01 ui: adjustments 2025-12-22 19:33:53 +01:00
Claude
b9756b119b refactor: simplify relay list UI with compact status indicators and rich tooltips
**Compact Relay Item Display:**
- Removed left-side inbox/outbox count indicators (were causing misalignment)
- Replaced "EOSE" text with checkmark icon (✓)
- Event count shown as [N] badge (only if > 0)
- Auth icon now always visible (even for unauthenticated relays)
- Clean right-side layout: [count] [✓] [auth] [wifi]

**Always-Visible Auth Status:**
- Modified getAuthIcon() to always return an icon (never null)
- Unauthenticated relays show subtle shield icon (muted-foreground/40)
- Provides at-a-glance view of auth status for all relays
- Label: "No Authentication Required" for clarity

**Rich Hover Tooltips:**
- Comprehensive tooltip shows all relay details on hover
- Displays: connection status, auth status, subscription state, event count
- Shows inbox/outbox counts when available (moved from inline display)
- Formatted as structured table for easy scanning
- Positioned on left side to avoid blocking content

**Benefits:**
 Perfect alignment (no variable-width counts on left)
 Cleaner, more scannable visual design
 All information still accessible via hover
 Consistent icon count (always 2-4 icons per relay)
 Easy to spot EOSE status at a glance (green checkmark)

All 639 tests passing.
2025-12-22 18:23:12 +00: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
b95ce25955 refactor: normalize hardcoded relay URLs and reorganize relay dropdown UI
**Normalize All Relay URLs:**
- Added trailing slashes to AGGREGATOR_RELAYS constants
- Ensures consistency with RelayStateManager's normalization
- Fixes fallback relay connection state tracking issue
- All hardcoded relay URLs now match normalized keys in relayStates

**Reorganize Relay Item UI:**
- Removed type indicator icons (LinkIcon/Sparkles/Inbox) from individual relay items
- Strategy type is already shown in header, no need to repeat per-item
- Moved inbox/outbox indicators from right side to left side of relay URL
- Left side now shows: inbox count (Mail icon) and/or outbox count (Send icon)
- Right side shows: event count, EOSE indicator, auth status, connection status
- Cleaner, more semantic layout with better visual hierarchy

**Why This Matters:**
The relay URL normalization fix ensures that fallback relays (AGGREGATOR_RELAYS)
now show accurate connection state in the UI. Previously, the non-normalized
URLs couldn't match keys in relayStates, making them appear disconnected even
when connected. This was the root cause of the "fallback relays not tracking"
issue.

All 639 tests passing.
2025-12-22 18:02:41 +00:00
Claude
b5e1cffc91 fix: add EOSE indicator, mute all icons, and fix relay URL normalization bug
Critical fixes for ReqViewer relay state accuracy:

1. **URL Normalization Fix** (fixes mismatch with CONN):
   - Added normalizeRelayURL to normalize all relay URLs in finalRelays
   - RelayStateManager normalizes URLs (adds trailing slash, lowercase) but
     finalRelays did not, causing lookup failures in relayStates
   - Now normalizedRelays is used for all state lookups and passed to
     useReqTimelineEnhanced to ensure consistency
   - This fixes the bug where ReqViewer showed different connected relay
     counts than CONN viewer

2. **EOSE Indicator**:
   - Added back EOSE indicator to relay dropdown (was removed in UI redesign)
   - Shows subtle "EOSE" text when relay has sent End of Stored Events
   - Includes tooltip explaining "End of stored events received"

3. **Muted Icons** (per user request for subtlety):
   - Type indicators: blue-500/purple-500 → muted-foreground/60
   - Strategy header icons: all → muted-foreground/60
   - Section headers: green-500 → muted-foreground
   - Connection icons: green-500/yellow-500/red-500 → /70 opacity variants
   - Auth icons: same color reduction for consistency
   - Maintains semantic meaning while reducing visual noise

All 639 tests passing.
2025-12-22 17:53:24 +00:00
Claude
ce3a4a7322 fix: handle all relays disconnecting before EOSE (stuck in LOADING bug)
Critical Edge Case Fix:
Previously, when all relays disconnected before sending EOSE, the state
remained stuck in LOADING because overallEoseReceived stayed false.

Solution: Check if all relays are in terminal states
- Terminal states: eose, error, or disconnected
- If all terminal AND no overall EOSE yet, derive state from events:
  * No events → FAILED
  * Has events, all disconnected, streaming → OFFLINE
  * Has events, all disconnected, non-streaming → CLOSED
  * Some active, some terminal → PARTIAL

New Test Coverage (5 tests):
1. All relays disconnect before EOSE, no events → FAILED
2. All relays disconnect before EOSE, with events (streaming) → OFFLINE
3. All relays disconnect before EOSE, with events (non-streaming) → CLOSED
4. Some EOSE, others disconnect before EOSE → PARTIAL
5. Mix of EOSE and errors, all terminal → PARTIAL

This fixes the user-reported issue where disconnected relays show LOADING
instead of transitioning to appropriate terminal state.

Tests: 639/639 passing (added 5 new edge case tests)
2025-12-22 17:38:31 +00:00
Alejandro
21e972600c Merge pull request #22 from purrgrammer/claude/fix-command-overflow-BOH8k
fix: prevent command text overflow in spell dialogs
2025-12-22 18:10:36 +01:00
Claude
e904fcbaf1 fix: prevent command text overflow in spell dialogs
Replace `break-all` with `break-words overflow-x-auto` to improve
text wrapping behavior in command preview areas. This prevents
words from breaking awkwardly in the middle while still allowing
long commands to be displayed properly.

Changes:
- CreateSpellDialog.tsx: Fixed command preview overflow
- SpellDialog.tsx: Fixed command display overflow
- SpellRenderer.tsx: Fixed detail view command overflow

The new approach breaks at word boundaries when possible and
provides horizontal scrolling for exceptionally long commands.
2025-12-22 17:00:24 +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
c9bf2fe599 fix: show ALL queried relays in dropdown (outbox + fallback + explicit)
Previously only showed relays from NIP-65 reasoning array, missing fallback
relays. Now always iterates over finalRelays (actual queried relays) and
looks up NIP-65 info if available.

Fixes:
- Fallback relays now visible in dropdown
- Relays show connection/subscription status regardless of source
- NIP-65 info (inbox/outbox counts) shown when available
- Works for outbox, fallback, and explicit relay configurations

Tests: 634/634 passing
2025-12-22 16:30:35 +00:00
Claude
1bb2727930 fix: remove unused variables and apply prettier formatting
- Remove unused connectedCount and relayStatesForReq variables
- Fix prettier formatting in ReqViewer.tsx
- All tests passing (634/634)
- Build successful
2025-12-22 16:25:42 +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
bebb4ed834 docs: add comprehensive ReqViewer state machine analysis and improvement plan
Analysis document:
- Identified critical bug in applesauce-relay catchError handling
- Documented 7 edge cases causing "LIVE with 0 relays" issue
- Root cause: relay disconnections treated as EOSE messages
- Detailed Nostr protocol semantics and applesauce behavior

Implementation plan:
- Hybrid approach: RelayStateManager + event metadata tracking
- New state types: ReqRelayState, ReqOverallState
- Enhanced hook: useReqTimelineEnhanced with per-relay tracking
- 3-phase rollout: infrastructure → UI → testing
- Comprehensive state machine with 8 query states, 8 relay states

This provides the foundation for production-quality REQ status tracking
that accurately handles disconnections, timeouts, and partial failures.
2025-12-22 15:59:00 +00:00
Alejandro
0d8f8b7807 Merge pull request #17 from purrgrammer/claude/applesauce-helpers-investigation-EWmaW
docs: add applesauce helpers investigation and refactoring plan
2025-12-22 15:19:41 +01: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
Claude
4b7148510a refactor: remove unnecessary useMemo from applesauce helper calls
Phase 1 of applesauce helpers refactoring plan.

Removed useMemo from direct applesauce helper calls since these helpers
cache their results internally using symbol-based caching. This improves
code clarity without sacrificing performance.

Changes:
- ArticleRenderer.tsx: removed useMemo from getArticleTitle, getArticleSummary
- HighlightRenderer.tsx: removed useMemo from 6 highlight helpers
- HighlightDetailRenderer.tsx: removed useMemo from 6 highlight helpers
- CodeSnippetDetailRenderer.tsx: removed useMemo from 8 NIP-C0 helpers
- ChatView.tsx: removed useMemo from getNip10References, getTagValue

Kept useMemo in LiveActivityRenderer.tsx for parseLiveActivity and related
functions since these don't implement their own caching (noted for future
optimization).

All tests pass (607 tests).
2025-12-22 13:18:44 +00:00
Claude
2189d5a969 docs: add applesauce helpers investigation and refactoring plan
- Created APPLESAUCE_REFACTORING_PLAN.md with detailed analysis
- Updated CLAUDE.md with Applesauce Helpers & Caching section
- Enhanced applesauce-core skill with helper documentation

Key findings:
- Applesauce helpers cache internally using symbols
- No need for useMemo when calling applesauce helpers
- Identified 40+ useMemo instances that can be removed
- Documented available helpers and custom grimoire helpers
- Provided migration strategy and refactoring opportunities
2025-12-22 13:12:56 +00:00
Alejandro Gómez
32c895e150 chore: lint fix 2025-12-22 13:25:38 +01:00
Alejandro
ea3d0e1119 Merge pull request #16 from purrgrammer/claude/pluggable-storage-engine-F53Fc
Make storage engine pluggable for testing
2025-12-22 13:24:24 +01:00
Claude
729c83011e fix: add IndexedDB polyfill for test environment
Use fake-indexeddb to provide IndexedDB API in Node.js test environment.
This fixes 10 failing tests in spellbook-storage.test.ts that were
previously blocked by missing IndexedDB.

Changes:
- Add fake-indexeddb as dev dependency
- Create vitest setup file that imports the polyfill
- Update vitest.config.ts to use setup file
- Fix relay-selection.test.ts to clear cache between tests for isolation
2025-12-22 12:18:38 +00:00
Alejandro
2fda75ef71 Merge pull request #15 from purrgrammer/claude/documentation-EeWQZ
docs: add codebase analysis and accessibility plan
2025-12-22 13:14:00 +01:00
Alejandro
addea243d1 Merge pull request #12 from purrgrammer/claude/kind-utilities-EeWQZ
feat: add centralized nostr kind utilities
2025-12-22 13:12:52 +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
a86b783f58 docs: add plan for pluggable storage engine
Analyze test failures due to missing IndexedDB in Node environment.
Present two options: fake-indexeddb polyfill vs full storage abstraction.
Recommend fake-indexeddb as pragmatic solution.
2025-12-22 12:11:33 +00:00
Alejandro
8862695ee6 Merge pull request #14 from purrgrammer/claude/stabilization-hooks-EeWQZ
feat: add dependency stabilization hooks
2025-12-22 13:10:51 +01:00
Claude
cdad01dc03 docs: add codebase analysis and accessibility plan
CODEBASE_ANALYSIS.md:
- Comprehensive architecture review
- State management analysis (Jotai + EventStore + Dexie)
- Component patterns assessment
- Security audit findings (zero vulnerabilities)
- Performance analysis
- 12-week S-tier improvement roadmap

ACCESSIBILITY_PLAN.md:
- Current state assessment (16% ARIA coverage)
- WCAG 2.1 AA compliance roadmap
- Phased implementation plan:
  - Phase 1: Foundation (keyboard nav, focus management)
  - Phase 2: Screen reader support
  - Phase 3: Visual accessibility
  - Phase 4: Advanced features
- Component-specific accessibility requirements
2025-12-22 12:03:33 +00: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
62676feeba fix: respect zap message 2025-12-22 12:59:56 +01:00
Claude
96216450f4 feat: add centralized nostr kind utilities
- Create src/lib/nostr-kinds.ts with:
  - Re-exports from nostr-tools/kinds (isRegularKind, isReplaceableKind, etc.)
  - New isParameterizedReplaceableKind() function
  - New isAddressableKind() for determining naddr vs nevent encoding
  - NIP-01 boundary constants with clarifying comments
  - getKindCategory() for display purposes

- Update KindRenderer.tsx to use shared utilities:
  - Replace inline range checks with helper functions
  - Fix "Regular Lists" -> "Replaceable Events" naming
  - Simplify redundant condition (isReplaceableKind includes kinds 0, 3)

- Update BaseEventRenderer.tsx to use isAddressableKind()

- Add comprehensive tests for all utilities
2025-12-22 11:58:46 +00:00