Replace hand-rolled ~350-entry emoji list with unicode-emoji-json (~1,900 emojis) and emojilib
keywords for richer search matching. Move EmojiSearchService to a singleton with Dexie-backed
caching for instant availability on startup. Add recency+frequency emoji usage tracking shared
across the emoji picker and editor autocomplete.
- Use AGGREGATOR_RELAYS as fallback for follows without kind:10002,
not the user's personal relays. Personal inbox/write relays were
being assigned as outbox for hundreds of unknown follows, inflating
counts and sending unnecessary queries to niche relays.
- Per-relay REQ badges now show assigned count (from reasoning) as
the primary number, with unassigned users shown dimmed as +N.
Tooltips show the full breakdown.
- Switch to useStableRelayFilterMap for structural comparison.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stabilizes relay filter map references using isFilterEqual per relay
instead of JSON.stringify. Avoids serialization overhead for large
filter maps with many relays and pubkeys.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Skip duplicate events in setEventsMap (return prev if event.id exists)
- Only create new relayStates Map on actual state transitions (waiting→receiving),
not on every event — counter increments applied in-place
- Don't add unknown relays to the state map (skip defensive init)
- Cap streaming eventsMap at 2000 with 25% batch eviction of oldest events
- Decouple relay filter map from subscription lifecycle: store in ref,
only tear down subscriptions when the relay SET changes (not filter content)
- Use useStableRelayFilterMap for structural comparison instead of JSON.stringify
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend relay filter chunking to route #p tags to inbox/read relays,
matching the existing outbox/write routing for authors. Remove debug
console.log statements across the codebase while preserving error-level
logging. Delete unused logger utility. Expand test coverage for all
chunking scenarios.
Uncomment kind 10051 in kinds registry with NIP-EE attribution and add
it to the relay list editor so users can manage MLS KeyPackage relays.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Spellbook URLs only queried hardcoded aggregator relays, missing events
published to other relays. Now fetches the author's kind:10002 relay list
and includes their outbox relays when loading kind:30777 spellbook events.
Extract useUserRelays hook from inline pattern and refactor
useRepositoryRelays to use it.
* feat: add Educational Resource (kind 30142) with AMB metadata support
- Add feed and detail renderers for AMB Educational Resource events
- Add amb-helpers library with cached helper functions and tests
- Handle broken thumbnail images with BookOpen placeholder fallback
- Surface primary resource URL prominently in both renderers (bookmark-style)
- Register kind 30142 with GraduationCap icon in kind registry
- Link NIP-AMB badge to community NIP event (kind 30817)
* fix: address PR #260 review comments for Educational Resource renderers
- Rename Kind30142*Renderer to EducationalResource*Renderer (human-friendly names)
- Localize language names with Intl.DisplayNames via shared locale-utils
- Use ExternalLink component for license and reference URLs
- Localize ISO dates with formatISODate, fixing UTC timezone shift bug
- Remove # prefix from keyword labels in both feed and detail renderers
- Remove image/thumbnail from feed renderer
- Extract getBrowserLanguage to shared locale-utils, reuse in amb-helpers
* fix: mock getBrowserLanguage in tests for Node < 21 compat
Tests were directly mutating navigator.language which doesn't exist
as a global in Node < 21, causing ReferenceError in CI.
* feat: centralize publish flow with RxJS-based PublishService
Create a unified PublishService that:
- Provides consistent relay selection (outbox + state + hints + fallbacks)
- Emits RxJS observables for per-relay status updates
- Handles EventStore integration automatically
- Supports both fire-and-forget and observable-based publishing
Refactor all publish locations to use the centralized service:
- hub.ts: Use PublishService for ActionRunner publish
- delete-event.ts: Use PublishService (fixes missing eventStore.add)
- publish-spell.ts: Use PublishService with relay hint support
- PostViewer.tsx: Use publishWithUpdates() for per-relay UI tracking
This lays the groundwork for the event log feature by providing
observable hooks into all publish operations.
* feat: add LOG command for relay event introspection
Add an ephemeral event log system that tracks relay operations:
- EventLogService (src/services/event-log.ts):
- Subscribes to PublishService for PUBLISH events with per-relay status
- Monitors relay pool for CONNECT/DISCONNECT events
- Tracks AUTH challenges and results
- Captures NOTICE messages from relays
- Uses RxJS BehaviorSubject for reactive updates
- Circular buffer with configurable max entries (default 500)
- useEventLog hook (src/hooks/useEventLog.ts):
- React hook for filtering and accessing log entries
- Filter by type, relay, or limit
- Retry failed relays directly from the hook
- EventLogViewer component (src/components/EventLogViewer.tsx):
- Tab-based filtering (All/Publish/Connect/Auth/Notice)
- Expandable PUBLISH entries showing per-relay status
- Click to retry failed relays
- Auto-scroll to new entries (pause on scroll)
- Clear log button
- LOG command accessible via Cmd+K palette
* fix: prevent duplicate log entries and check relay OK response
- EventLogService: Check for existing entry before creating new one
when handling publish events (prevents duplicates from start/complete)
- PublishService: Check response.ok from pool.publish() to detect
relay rejections instead of assuming success on resolve
- Update test mock to return proper publish response format
* feat: keep relay selection in call site, compact logs
* chore: cleanup
* fix: make Timestamp component locale-aware via formatTimestamp
Timestamp was hardcoded to "es" locale. Now uses formatTimestamp()
from useLocale.ts for consistent locale-aware time formatting.
Added Timestamp to CLAUDE.md shared components documentation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: improve event-log reliability, add ERROR type and per-relay timing
Service improvements:
- Fix notice$ duplicate logging with per-relay dedup tracking
- Remove dead Array.isArray code path (notice$ emits strings)
- Increase relay poll interval from 1s to 5s
- Clean publishIdToEntryId map on terminal state, not just overflow
- Immutable entry updates (spread instead of in-place mutation)
- Extract NewEntry<T>/AddEntryInput helper types for clean addEntry signature
- Clear lastNoticePerRelay on log clear
New capabilities:
- ERROR log type: subscribes to relay.error$ for connection failure reasons
- RelayStatusEntry with updatedAt timestamp for per-relay response timing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: improve EventLogViewer with virtualization, timing, and error display
- Virtualize log list with react-virtuoso for 500-entry buffer performance
- Add ErrorEntry renderer for new ERROR log type (AlertTriangle icon)
- Show per-relay response time (e.g. "142ms", "2.3s") in publish details
- Make all entry types expandable (connect/disconnect now have details)
- Show absolute timestamp in all expanded detail views
- Group ERROR events under Connect tab filter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent duplicate PUBLISH log entries from completion event
PublishService emits publish$ twice: once at start, once on completion.
The eager publishIdToEntryId cleanup in handleStatusUpdate fired before
the completion emission, causing handlePublishEvent to create a second
entry. Removed eager cleanup — overflow eviction is sufficient.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Removes ~45 lines of identical relay resolution boilerplate duplicated
across 6 renderers (Issue, Patch, PR - feed and detail views).
The hook encapsulates the 3-tier fallback: repo relays → owner outbox →
aggregators, and also returns the repository event needed for
getValidStatusAuthors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>