Previously, the auth dialog would appear whenever a relay sent an AUTH
challenge, even when there was no active user session. This was confusing
for users who weren't logged in.
Now the shouldPromptAuth() method checks if there's an active account
before returning true, ensuring auth dialogs only appear for logged-in users.
Co-authored-by: Claude <noreply@anthropic.com>
**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.
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
Problem:
- Function signature was accidentally changed to curried pattern
- Tests were failing because they expected async generator pattern
Solution:
- Restore async function* signature that takes hub and options
- Matches test expectations and existing usage patterns
- Linter also fixed property destructuring in spellbook-storage
Tests:
- ✅ All publish-spellbook tests passing (14/14)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problem:
- Saving active spellbooks was creating duplicates each time
- No deduplication logic for spellbooks with same slug + pubkey
Solution:
- Add findExistingSpellbook() to match by slug and pubkey
- Update saveSpellbook() to detect and update existing spellbooks:
* For local-only: match by slug (no pubkey)
* For published: match by slug AND pubkey
* For updates: use provided ID
- SaveSpellbookDialog now passes existing ID in update mode
Testing:
- Added comprehensive spellbook-storage.test.ts
- Tests verify deduplication logic (requires jsdom environment)
- Existing tests still pass (14/14 for publish-spellbook)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sharing Enhancements:
- Install qrcode library for QR code generation
- Create ShareSpellbookDialog with tabbed interface
- Support multiple share formats: Web URL, naddr, nevent
- QR code generation and download for each format
- Quick copy buttons with visual feedback
- Integrated into SpellbookDetailRenderer
Network Discovery:
- Add "Discover" filter to browse spellbooks from other users
- Query AGGREGATOR_RELAYS for network spellbook discovery
- Show author names using UserName component
- Conditional UI: hide owner actions for discovered spellbooks
- Support viewing and applying layouts from the community
Preview Route Polish:
- Loading states with spinner during NIP-05 resolution
- 10-second timeout for NIP-05 resolution
- Error banners for resolution failures
- Author name and creation date in preview banner
- Copy link button in preview mode
Conflict Resolution:
- compareSpellbookVersions() function in spellbook-manager
- ConflictResolutionDialog component for version conflicts
- Side-by-side comparison of local vs network versions
- Show workspace/window counts and timestamps
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactor PublishSpellbookAction from class-based to async generator pattern
following official applesauce-actions patterns. This provides better integration
with ActionHub and enables proper reactive event publishing.
Changes:
- Add publishEvent function to ActionHub with relay selection fallback strategy
(outbox relays → seen relays → error)
- Convert PublishSpellbookAction to PublishSpellbook async generator
- Update SaveSpellbookDialog and SpellbooksViewer to use hub.run()
- Add comprehensive test suite (14 tests covering validation, event creation,
slug generation, factory integration, and workspace selection)
- Improve error handling with specific error messages
- Add JSDoc documentation with usage examples
All tests passing ✅ (14/14)
TypeScript compilation successful ✅🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update spellbook icon to BookHeart across the app
- Implement Preview mode with routing /:actor/:identifier
- Add Preview banner in Home component with Apply/Discard actions
- Add Preview and Share buttons to Spellbook renderers
- Clean up unused imports