Created bookmarkStream.ts with shared helpers:
- getEventKey: deduplication logic
- hasEncryptedContent: encryption detection
- loadBookmarksStream: streaming with non-blocking decryption
Refactored bookmarkService.ts to use shared helpers:
- Uses loadBookmarksStream for consistent behavior with Debug page
- Maintains progressive loading via callbacks
- Added accountManager parameter to fetchBookmarks
Updated App.tsx to pass accountManager to fetchBookmarks:
- Progressive loading indicators via onProgressUpdate callback
All bookmark loading now uses the same battle-tested streaming logic as Debug page.
Removed duplicate bookmark loading logic from Debug page:
- Debug 'Load Bookmarks' button now calls centralized onRefreshBookmarks
- Removed redundant state (bookmarkEvents, bookmarkStats, decryptedEvents)
- Removed unused helper functions (getKindName, getEventSize, etc.)
- Cleaned up imports (Helpers, queryEvents, collectBookmarksFromEvents)
- Simplified UI to show timing only, bookmarks visible in sidebar
Now there's truly ONE place for bookmark loading (bookmarkService.ts),
called from App.tsx and used throughout the app. Debug page's button
is now the same as clicking refresh in the bookmark sidebar.
Fixed type error in Debug.tsx:
- Changed highlightVisibility from string to proper HighlightVisibility object
- Used 'support' prop instead of invalid 'children' prop for ThreePaneLayout
- Set showSupport={true} to properly render debug content
All linting and type checks now pass.
Added ThreePaneLayout to Debug page so bookmarks are visible:
- Debug page now has same layout as other pages
- Shows bookmarks sidebar on the left
- Debug content in the main pane
- Can compare centralized app bookmarks with Debug bookmarks side-by-side
This makes it easy to verify that centralized bookmark loading
works the same as the Debug page implementation.
Simplified bookmark loading by chaining loading and decryption:
- Events with encrypted content are automatically decrypted as they arrive
- Removed separate "Decrypt" button - now automatic
- Removed individual decrypt buttons - happens automatically
- Removed handleDecryptSingleEvent and related state
- Cleaner UI with just "Load Bookmarks" and "Clear" buttons
Benefits:
- Simpler, more intuitive UX
- DRY - single flow instead of 2-step process
- Shows decryption results inline as events stream in
- Uses same collectBookmarksFromEvents for consistency
Each event with encrypted content (NIP-04 or NIP-44) is decrypted
immediately in the onEvent callback, with results displayed inline.
Changed all debug console logs to use [bunker] prefix with emojis:
- 🔵 Individual decrypt clicked
- 🔓 Decrypting event (with details)
- ✅ Event decrypted (with results)
- ⚠️ Warnings (no account, 0 private items)
- ❌ Errors
Now users can filter console by 'bunker' to see all relevant logs.
Added extensive debug logging to help diagnose decryption issues:
- Event details (kind, content length, encryption type)
- Signer information (type, availability)
- Warning when 0 private items found despite encrypted content
This will help identify why decryption might be failing silently.
Added explicit detection for NIP-04 encrypted content format:
- NIP-04: base64 content with ?iv= suffix
- NIP-44: detected by Helpers.hasHiddenContent()
- Encrypted tags: detected by Helpers.hasHiddenTags()
Created hasEncryptedContent() helper that checks all three cases.
Now properly shows padlock emoji and decrypt button for events with
NIP-04 encrypted content (like the example with ?iv=5KzDXv09...).
Fixed mismatch between padlock display and decrypt button visibility:
- Both now use Helpers.hasHiddenContent() and Helpers.hasHiddenTags()
- Previously padlock showed for ANY content, button only for encrypted
- Now both correctly detect actual encrypted content
This ensures decrypt buttons appear whenever padlocks are shown.
Added per-event decryption on debug page:
- Small 'decrypt' button appears on events with encrypted content
- Shows spinner while decrypting individual event
- Displays decryption results (public/private counts) inline
- Button disappears after successful decryption
Uses Helpers.hasHiddenContent() and Helpers.hasHiddenTags() to detect
which events need decryption.
Allows testing individual event decryption without batch operation.
Updated debug page to display the actual account type:
- Browser Extension (type: 'extension')
- Bunker Connection (type: 'nostr-connect')
- Account Connection (fallback)
Changes:
- Section title now reflects active account type
- Connection status message updated accordingly
- No longer always shows 'Bunker Connection' regardless of type
Makes it clear to users which authentication method they're using.
Set accounts.disableQueue = true on AccountManager during initialization:
- Applies to all accounts automatically
- No need for temporary queue toggling in individual operations
- Makes all bunker requests instant (no internal queueing)
Removed temporary queue disabling from bookmarkProcessing.ts since
it's now globally disabled.
Updated Amber.md to document the global approach.
This eliminates the root cause of decrypt hangs - requests no longer
wait in an internal queue for previous requests to complete.
Reduced timeouts to trust EOSE from fast relays:
- Local: 800ms (down from 1200ms)
- Remote: 2000ms (down from 6000ms)
The query completes when relays send EOSE, not when timeout expires.
Fast relays send EOSE in <1 second, so total time should be much less
than the previous 6-second wait.
Result: Debug page bookmark loading now completes in ~1-2 seconds instead of always 6 seconds.
Implemented live streaming of bookmark events as they arrive from relays:
- Events appear immediately as relays respond
- Live deduplication of replaceable events (30003, 30001, 10003)
- Keep newest version when duplicates found
- Web bookmarks (39701) not deduplicated (each unique)
Benefits:
- Much more responsive UI - see events immediately
- Better user experience with progress visibility
- Deduplication happens in real-time
Uses queryEvents onEvent callback to process events as they stream in.
- Track load and decrypt operation durations
- Display live timing with spinner during operations
- Show completed timing in milliseconds
- Uses same Stat component as encryption timing
- Show kind names (Simple List, Replaceable List, etc)
- Display data size in human-readable format (B, KB, MB)
- Show count of public bookmarks per event
- Indicate presence of encrypted content
- Show d-tag and title for better identification