Commit Graph

122 Commits

Author SHA1 Message Date
Alejandro Gómez
6d01ee33ef feat: implement unified chat system with NIP-C7 and NIP-29 support
Core Architecture:
- Protocol adapter pattern for chat implementations
- Base adapter interface with protocol-specific implementations
- Auto-detection of protocol from identifier format
- Reactive message loading via EventStore observables

Protocol Implementations:
- NIP-C7 adapter: Simple chat (kind 9) with npub/nprofile support
- NIP-29 adapter: Relay-based groups with member roles and moderation
- Protocol-aware reply message loading with relay hints
- Proper NIP-29 members/admins fetching using #d tags

UI Components:
- ChatViewer: Main chat interface with virtualized message timeline
- ChatMessage: Message rendering with reply preview
- ReplyPreview: Auto-loading replied-to messages from relays
- MembersDropdown: Virtualized member list with role labels
- RelaysDropdown: Connection status for chat relays
- ChatComposer: Message input with send functionality

Command System:
- chat command with identifier parsing and auto-detection
- Support for npub, nprofile, NIP-05, and relay'group-id formats
- Integration with window system and dynamic titles

NIP-29 Specific:
- Fetch kind:39000 (metadata), kind:39001 (admins), kind:39002 (members)
- Extract roles from p tags: ["p", "<pubkey>", "<role1>", "<role2>"]
- Role normalization (admin, moderator, host, member)
- Single group relay connection management

Testing:
- Comprehensive chat parser tests
- Protocol adapter test structure
- All tests passing (704 tests)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 21:38:23 +01:00
Alejandro
5233c57a1c feat: Add Zapstore app and app curation set renderers (#49)
* feat: Add Zapstore app and app curation set renderers

Add support for rendering Zapstore app-related Nostr events:
- Kind 32267 (App Metadata): Display app details, icon, platforms, screenshots
- Kind 30267 (App Curation Set): Display curated app collections

New files:
- src/lib/zapstore-helpers.ts: Helper functions for extracting app metadata
- src/lib/zapstore-helpers.test.ts: Comprehensive test coverage (43 tests)
- src/components/nostr/kinds/ZapstoreAppRenderer.tsx: Feed view for apps
- src/components/nostr/kinds/ZapstoreAppDetailRenderer.tsx: Detail view for apps
- src/components/nostr/kinds/ZapstoreAppSetRenderer.tsx: Feed view for collections
- src/components/nostr/kinds/ZapstoreAppSetDetailRenderer.tsx: Detail view for collections

Modified:
- src/components/nostr/kinds/index.tsx: Register new renderers in kind registry

All tests pass (726 total), build succeeds, no lint errors.

* feat: Add Zapstore release renderer (kind 30063)

Add support for rendering Zapstore app release events (kind 30063):
- Kind 30063 (Release): Connects apps (32267) to file artifacts (1063)

New files:
- src/components/nostr/kinds/ZapstoreReleaseRenderer.tsx: Feed view for releases
- src/components/nostr/kinds/ZapstoreReleaseDetailRenderer.tsx: Detail view with embedded file metadata

Modified:
- src/lib/zapstore-helpers.ts: Add release helper functions
  - getReleaseIdentifier(): Extract release ID (package@version)
  - getReleaseVersion(): Parse version from identifier
  - getReleaseFileEventId(): Get file metadata event pointer
  - getReleaseAppPointer(): Get app metadata pointer
- src/lib/zapstore-helpers.test.ts: Add 18 new tests for release helpers (61 total)
- src/components/nostr/kinds/index.tsx: Register kind 30063 renderers

Complete Zapstore app ecosystem now supported:
- Kind 32267: App metadata (name, icon, description)
- Kind 30267: App curation sets (collections)
- Kind 30063: App releases (version tracking)
- Kind 1063: File metadata (downloads)

All tests pass (744 total), build succeeds.

* refactor: Simplify Zapstore app renderers with platform icons

Improve Zapstore app rendering for cleaner, more intuitive display:

Changes:
- Add detectPlatforms() helper to normalize architecture tags (e.g., "android-arm64-v8a" → "android")
- Replace verbose platform badges with clean platform icons (Android, iOS, Web, macOS, Windows, Linux)
- Remove screenshots from feed view (keep in detail view only)
- Remove repository links and license badges from feed view
- Update detail view to show "Available On" with icon+label platform items

Feed view now shows:
- App icon
- App name
- Summary (2 lines max)
- Platform icons (just icons, no text)

Detail view now shows:
- App icon, name, summary
- Publisher, Package ID, License, Repository (metadata grid)
- Available On (platforms with icons and labels)
- Screenshots gallery (unchanged)

All tests pass (744 total), build succeeds.

* fix: Polish Zapstore renderers with platform labels and clean layout

Address feedback to improve Zapstore renderer UX:

Changes:
1. App feed (ZapstoreAppRenderer):
   - Add platform text labels next to icons (e.g., "Android", "iOS", "Web")
   - Now shows icon + label for better clarity

2. Release feed (ZapstoreReleaseRenderer):
   - Remove big package icon from feed view
   - Cleaner, more compact layout with just app name, version badge, and action links

3. Registry comments:
   - Update to human-friendly names:
     * "Zapstore App" (instead of "App Metadata (Zapstore)")
     * "Zapstore App Collection" (instead of "App Curation Set (Zapstore)")
     * "Zapstore App Release" (instead of "App Release (Zapstore)")

All tests pass (744 total), build succeeds.

* refactor: Update Zapstore app set renderers with improved UX

- ZapstoreAppSetRenderer: Show ALL apps with compact spacing (gap-0.5) like relay lists, removed 5-app limit
- ZapstoreAppSetDetailRenderer: Replace raw platform tags with normalized platform icons using detectPlatforms()
- Both renderers now provide cleaner, more consistent UI following Grimoire patterns

* refactor: Add human-friendly names and simplify Zapstore renderers

- kinds.ts: Add kind 32267 (App), update 30063 to "App Release", update 30267 to "App Collection"
- Extract PlatformIcon to shared component (zapstore/PlatformIcon.tsx)
- Update all renderer comments to use human-friendly terminology
- Remove unnecessary comments throughout Zapstore renderers
- Simplify code without changing functionality

* feat: Add releases section to app detail view

- Query for all releases (kind 30063) that reference the app
- Display releases sorted by version (newest first)
- Each release shows version badge and download link
- Clicking release opens full release detail view
- Clicking download opens file metadata view

* fix: Force screenshots as images and filter releases by author

- Add type="image" to MediaEmbed for screenshots to fix "unsupported media type" errors
- Filter releases to only show those from the same author (pubkey) as the app
- Prevents releases from other apps or authors from appearing in the app detail view

* fix: Remove author filter from releases query

The a tag already uniquely identifies the app (32267:pubkey:identifier).
Releases may be published by different authors (maintainers, packagers)
than the app author, so we should show all releases that reference
the app via the a tag, regardless of who published them.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-11 21:36:47 +01:00
Alejandro
84b5ac88aa Fix emoji rendering for dashed emoji codes (#48)
* Fix custom emoji shortcode matching to include dashes

The regex pattern for matching NIP-30 custom emoji shortcodes was missing
the dash character, causing emojis like :work-out: to fail matching.
Updated both ReactionRenderer and ReactionCompactPreview to use
[a-zA-Z0-9_-] instead of [a-zA-Z0-9_].

* Refactor: extract emoji shortcode regex to constant

Move the emoji shortcode matching pattern to EMOJI_SHORTCODE_REGEX
constant in emoji-helpers.ts for reuse across reaction renderers.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 11:03:43 +01:00
Alejandro
a1fe411161 NIP-30 custom emoji kinds (#47)
* feat(emoji): implement NIP-30 custom emoji support

- Fix shortcode regex to be NIP-30 spec compliant (alphanumeric + underscore only)
- Add feed and detail renderers for kind 30030 Emoji Sets
- Create shared CustomEmoji component for consistent emoji rendering
- Add getEmojiTags helper with applesauce-style symbol caching

* fix(emoji): remove colons from emoji set title display

* style(emoji): enhance detail renderer title with icon

* style(emoji): simplify detail renderer layout

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-08 16:55:21 +01:00
Alejandro
35f55b8063 feat(calendar): add renderers for NIP-52 calendar events (kinds 31922 & 31923) (#43)
* feat(calendar): add renderers for NIP-52 calendar events (kinds 31922 & 31923)

- Add CalendarDays and CalendarClock icons for date/time event kinds
- Create calendar-event.ts helper with parsing and formatting functions
- Add feed renderers showing status badge, title, date/time, location, participant count
- Add detail renderers with full description, participant list with names, tags, and links
- Register renderers in kinds/index.tsx for both feed and detail views
- Use locale-aware date/time formatting throughout

* refactor(calendar): improve feed renderer layout

- Rename both kinds to "Calendar Event" for consistency
- Move date/time info below title
- Place time on left, status badge on right with justify-between
- Remove timezone indicator from feed view (keep in detail)

* refactor(calendar): apply feed layout to detail views

- Move title above date/time in detail views
- Use justify-between for time left, status badge right
- Use Label component for hashtags (no # prefix, consistent with feed)
- Use Label component for participant roles (subtle dotted border style)

* refactor(calendar): separate location/tags rows and tone down time font

- Separate location/participants from hashtags onto different rows in feed
- Reduce time font size: text-xs in feed, text-sm in detail (was text-sm/text-lg)
- Remove font-medium from time display

* style(calendar): tone down status badges to match date/time styling

Replace bold background-colored badges with subtle text-colored badges:
- Use text-blue-500, text-green-500, text-muted-foreground instead of backgrounds
- Lowercase labels ("upcoming", "now", "past")
- Consistent sizing with date/time text

* refactor(calendar): extract shared components and add caching

- Extract CalendarStatusBadge to shared component with variant/size props
- Add symbol-based caching to parseDateCalendarEvent and parseTimeCalendarEvent
  using applesauce's getOrComputeCachedValue for performance
- Use 'd' tag (identifier) as title fallback instead of "Untitled Event"
- Remove duplicate CalendarStatusBadge implementations from all 4 renderers
- Remove unused imports (cn, CalendarDays, CalendarClock, Clock, CheckCircle)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-07 21:20:04 +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
d14c2f3028 feat: implement NIP-89 app definitions and recommendations with rich rendering (#36)
* feat: implement NIP-89 app definitions and recommendations with rich rendering

Add comprehensive support for NIP-89 Application Handlers (kind 31990) and
Handler Recommendations (kind 31989) with rich, interactive visualizations.

Core Implementation:
- nip89-helpers.ts: Utility functions for extracting NIP-89 event metadata
  - App name, description, image from kind 31990 content JSON
  - Supported kinds from k tags
  - Platform URLs (web, ios, android) from platform tags
  - Handler references from kind 31989 a tags
  - URL template substitution for <bech32> placeholders

Feed Renderers:
- ApplicationHandlerRenderer (31990): Shows app name, supported kinds as
  clickable KindBadges (max 8 in feed), and platform badges
- HandlerRecommendationRenderer (31989): Shows recommended kind and handler
  list (max 3 in feed) with platform indicators

Detail Renderers:
- ApplicationHandlerDetailRenderer (31990): Comprehensive view with app info,
  all supported kinds in grid layout (clickable), platform URLs with copy
  buttons, and metadata JSON viewer
- HandlerRecommendationDetailRenderer (31989): Full view with platform
  filtering tabs, expanded handler cards showing app details, and raw
  reference data

Features:
- Clickable KindBadges throughout for quick navigation
- Platform-aware filtering and display
- Fetches referenced kind 31990 events reactively
- Copy buttons for URL templates
- Platform icons (web, ios, android)
- Follows existing Grimoire patterns (SpellRenderer for kinds display,
  CodeSnippetDetailRenderer for metadata sections)

Testing:
- Comprehensive test suite for nip89-helpers (50+ test cases)
- Tests cover all helper functions with edge cases
- Follows existing test patterns from codebase

Registry:
- Added both kinds (31989, 31990) to kindRenderers and detailRenderers
- Automatically expands supported kinds count in KindsViewer

* fix: remove unused imports and parameters in NIP-89 renderers

* fix: correct AddressPointer import and apply prettier formatting

- Change AddressPointer import from applesauce-core/helpers to nostr-tools/nip19
  to match codebase conventions
- Auto-fix prettier formatting for nip89 files

* fix: add defensive type checks to prevent React error 31

- Add type guards in nip89-helpers to ensure string types
- Check metadata object structure before accessing properties
- Add fallbacks for undefined address.identifier values
- Prevents accidentally rendering objects as React children

* fix: stringify contentJson for CopyableJsonViewer and support 'about' field

- Fix React error 31: CopyableJsonViewer expects string, not object
- Add JSON.stringify() with pretty printing for metadata display
- Support both 'description' and 'about' fields in content JSON (common in kind 0)
- Add tests for 'about' field handling

* refactor: simplify NIP-89 detail renderers

Remove unnecessary metadata displays:
- Remove app image from ApplicationHandlerDetailRenderer
- Remove Event ID and Created timestamp from both detail renderers
- Remove Raw Metadata section from ApplicationHandlerDetailRenderer
- Remove Raw References section from HandlerRecommendationDetailRenderer
- Clean up unused imports (getAppImage, CopyableJsonViewer, useMemo, formatAddressPointer)

Keeps the UI focused on the essential information: app name, description,
supported kinds, and platform URLs.

* feat: add website display and filter non-platform tags

NIP-89 renderer improvements:
- Add getAppWebsite() helper to extract website from content JSON
- Display website URL in both feed and detail renderers with external link
- Filter out non-platform tags (r, t, client, alt, e, p, a) to prevent garbage display
- Remove relay hint display from HandlerRecommendationDetailRenderer
- Clean up unused relayHint parameter

Fixes the 'r r' tag appearing as a platform by properly excluding
common non-platform tags when detecting platform URLs.

* refactor: create reusable ExternalLink component for consistent styling

Create ExternalLink component following patterns from HighlightRenderer and
BookmarkRenderer with:
- Two variants: 'muted' (default, text-muted-foreground with underline)
  and 'default' (text-primary with hover:underline)
- Three sizes: xs, sm, base
- Configurable icon display
- Consistent truncate behavior for long URLs
- Stop propagation on click

Apply to NIP-89 renderers:
- ApplicationHandlerRenderer: uses muted variant (feed view)
- ApplicationHandlerDetailRenderer: uses default variant (detail view)

This ensures consistent link styling across the entire application
and makes it easy to maintain a unified design language.

* refactor: consolidate JSON parsing into cached getAppMetadata helper

Performance optimization:
- Create getAppMetadata() helper that parses content JSON once and caches
  the result using Symbol.for('nip89-metadata') as cache key
- All metadata helpers (getAppName, getAppDescription, getAppWebsite) now
  use the cached metadata instead of parsing JSON multiple times
- Prevents redundant JSON.parse() calls when multiple helpers are used

Code cleanup - removed unused functions:
- getAppImage() - no longer used after removing image display
- getHandlersByPlatform() - filtering done in component state
- substituteTemplate() - not needed in current implementation
- hasPlaceholder() - utility never used
- formatAddressPointer() - not needed anymore

Updated tests:
- Replace getAppImage tests with getAppWebsite tests
- Remove tests for deleted utility functions
- All remaining tests pass

This consolidation improves performance by ensuring JSON.parse() is called
at most once per event, regardless of how many metadata fields are accessed.

* feat: use app name in window titles for NIP-89 app events

Add special handling for kind 31990 (Application Handler) events in
getEventDisplayTitle to use the app name from content JSON instead of
generic kind name. Falls back to identifier if app name not available.

This gives NIP-89 app handler events nice readable window titles.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 11:42:14 +01:00
Alejandro Gómez
c4bc3ab445 ui: improve relay tooltip, update docs 2025-12-22 19:43:00 +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
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
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
Alejandro Gómez
32c895e150 chore: lint fix 2025-12-22 13:25:38 +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
Alejandro Gómez
3346a2077d feat(req): add --view flag for list/compact display mode
- Add -v, --view <list|compact> flag to req command
- Remove UI toggle controls from ReqViewer header
- View mode is now set via command flag instead of runtime toggle
- Update man page with new flag documentation and example

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 20:29:32 +01:00
Alejandro Gómez
78a8c8e5b2 chore: apply prettier formatting fixes
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 20:12:40 +01:00
Alejandro Gómez
f4d0e86f09 feat: complete spellbook UX overhaul with enhanced state tracking
- Redesign SpellbookDropdown with clear status indicators (ownership, storage)
- Add SpellbookStatus component showing you/other and local/published/network
- Enhance activeSpellbook type with source, localId, isPublished fields
- Fix PublishSpellbook action to properly yield events (caller handles side-effects)
- Add k tags extraction from REQ windows for kind-based filtering/discovery
- Update terminology from "Layout" to "Spellbook" consistently
- Add comprehensive tests for k tags and source tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 19:19:24 +01:00
Alejandro Gómez
21335a5849 WIP 2025-12-21 18:08:10 +01:00
Alejandro Gómez
005605b385 feat: enhance preview route and add conflict resolution for spellbooks
Improve the preview route UX with better loading states, error handling, and metadata display. Add version comparison logic and conflict resolution dialog for handling local vs network spellbook conflicts.

Changes:
- Enhanced preview route in Home.tsx:
  - Add loading state with spinner while resolving actor
  - Add NIP-05 resolution timeout (10 seconds)
  - Display error banner for resolution failures
  - Show author name and creation date in preview banner
  - Add copy link button to share spellbook easily
  - Improve banner layout with metadata

- Add compareSpellbookVersions() in spellbook-manager.ts:
  - Detects conflicts between local and network versions
  - Compares timestamps, workspace counts, window counts
  - Identifies newer version and content differences
  - Returns structured comparison data

- Create ConflictResolutionDialog component:
  - Side-by-side comparison of local vs network versions
  - Shows metadata: timestamps, counts, author, publish status
  - Clear explanation of resolution choices
  - Accessible UI with proper button hierarchy

TypeScript compilation successful 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 18:08:10 +01:00
Alejandro Gómez
d59a7aee8b feat: improve spellbook UX with active tracking and update capability
- Add activeSpellbook to GrimoireState
- Display active spellbook title in header with clear button
- Add 'Update Layout' and 'Save as new' to SpellbookDropdown
- Highlight active spellbook in dropdown list
- Add 'Manage Spells' link to dropdown
- Refine dropdown styles (muted hover, no accent color)
2025-12-21 18:08:10 +01:00
Alejandro Gómez
1836deee6c feat: add wallet status indicator and replace layout on apply
- Add WalletStatus component to TabBar
- Change loadSpellbook to replace current workspaces instead of merging
- Update SpellbooksViewer toast message
2025-12-21 18:07:55 +01:00
Alejandro Gómez
5d9ff3cf56 feat: implement Nostr Wallet Connect (NWC)
- Add applesauce-wallet-connect dependency
- Create WalletService for managing NWC connection
- Create WalletViewer component for UI
- Register 'wallet' command
- Fix build errors in spell/spellbook components due to applesauce-actions upgrade
2025-12-21 18:07:55 +01:00
Claude
a34fa2becd fix: apply linter formatting and fix useMemo dependencies 2025-12-21 14:28:12 +00:00
Claude
1519e30e5c feat: add repository state (kind 30618) visualization
Implement renderers for NIP-34 repository state announcements:
- Add helper functions in nip34-helpers.ts to parse HEAD refs, branches, and tags
- Create RepositoryStateRenderer for compact feed view showing push notifications
- Create RepositoryStateDetailRenderer for detailed view with all refs
- Register both renderers in the kind registry

Feed view shows: "pushed <commit> to <branch> in <repo>"
Detail view shows: HEAD info, all branches, all tags with copyable commit hashes
2025-12-21 12:35:23 +00:00
Alejandro Gómez
2987a37e65 feat: spells 2025-12-20 14:25:40 +01:00
Alejandro Gómez
812b719ea0 feat: debug command, simplify state 2025-12-18 23:32:00 +01:00
Alejandro Gómez
3f45cebaee feat: more flexible -e flag 2025-12-18 17:08:11 +01:00
Alejandro Gómez
f6f813d382 refactor: collapse migrations 2025-12-18 16:49:24 +01:00
Alejandro Gómez
f283ef6208 docs: improve 2025-12-18 16:25:34 +01:00
Alejandro Gómez
a6650ff6e1 fix: type errors 2025-12-18 16:00:56 +01:00
Alejandro Gómez
5cc97484b5 fix: improved balanced window algo 2025-12-18 15:45:34 +01:00
Alejandro Gómez
3fba62b316 ui: simpler streams 2025-12-18 15:29:01 +01:00
Alejandro Gómez
b57ff31907 feat: balance layout and kbd workspace navigation 2025-12-18 13:47:40 +01:00
Alejandro Gómez
1581e313f3 test: add comprehensive tests for grid layouts with odd numbers
Verify that grid preset correctly handles uneven window counts:
- 2 windows → 1×2 grid
- 3 windows → 1×3 single row
- 4 windows → 2×2 perfect grid
- 5 windows → 2×3 grid (last row expands to fill)
- 7 windows → 2×4 grid (last row expands)
- 9 windows → 3×3 perfect grid
- 11 windows → 3×4 grid (last row expands)

Key behaviors verified:
- No empty space (last row windows expand to 100% width)
- All windows preserved in layout
- Window order maintained (depth-first traversal)
- Works with any N ≥ 2 windows

Also tests:
- Side-by-side with 2-4 windows (and error for 5+)
- Main + Sidebar with various window counts
- Error handling for too few/many windows

All 22 tests pass ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 13:20:38 +01:00
Alejandro Gómez
dd40a5baca feat: make layout presets adaptive to all windows
Major redesign of preset system from static templates to adaptive algorithms:

**Grid preset** (fully adaptive):
- Now arranges ALL windows in best-fit grid (2x2, 2x3, 3x3, etc.)
- Minimum: 2 windows (down from 4)
- Algorithm calculates optimal rows/cols for any N windows
- Examples:
  * 4 windows → 2×2 grid
  * 6 windows → 2×3 grid
  * 9 windows → 3×3 grid

**Side-by-side preset** (adaptive with limit):
- Arranges 2-4 windows in single horizontal row
- Maximum 4 windows (prevents unusably narrow splits)
- Equal splits: 50%, 33%, 25%
- Clear error message if >4 windows

**Main + Sidebar preset** (unchanged):
- Already worked correctly (intentionally hierarchical)
- One main window (70%) + sidebars stacked (30%)
- Adapts naturally to any N ≥ 2 windows

Architecture changes:
- Replace static MosaicNode templates with generate() functions
- Add minSlots/maxSlots instead of single slots field
- Implement buildHorizontalRow(), buildVerticalStack(), buildGridLayout()
- Remove fillLayoutTemplate() (no longer needed)
- Remove extra window stacking (all windows now equal)

Benefits:
- No arbitrary hierarchy (no "featured" vs "extra" windows)
- Matches tiling window manager patterns (Amethyst, yabai, etc.)
- More intuitive: "grid" grids ALL windows, not just first 4
- Better UX: Works with any number of windows in range

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 13:14:35 +01:00
Alejandro Gómez
f2599772f6 fix(ui): reorganize bottom bar and clarify preset requirements
Changes:
- Added TODO comment about IndexedDB unavailability in test runtime
- Moved layout preset selector to rightmost side of bottom bar
- Moved window settings button to rightmost side as well
- Removed per-workspace settings icon (now global button on right)
- Added spacer to separate workspace tabs from layout controls
- Changed preset descriptions to show "4+ windows" instead of "4 windows"
  to clarify that presets work with more windows than the minimum
- Changed dropdown alignment from "start" to "end" for right-aligned opening

The bottom bar now has clearer visual separation:
[Workspace Tabs] [+] ................ [Layout Presets] [Settings]

Generated with [Claude Code](https://claude.com/claude-code)
2025-12-18 12:27:54 +01:00
Alejandro Gómez
4db7e9690c refactor(layouts): simplify to global config + preserve windows + add UI dropdown
Simplified layout system based on user feedback:

**1. Global Layout Config (Simpler)**
- Moved layoutConfig from per-workspace to global state
- One configuration applies to all workspaces (easier to understand)
- Updated state migration v8→v9 to move config to global level
- Updated WorkspaceSettings UI to edit global config
- Renamed updateWorkspaceLayoutConfig → updateLayoutConfig

**2. Preserve Extra Windows (Fixed Bug)**
- Fixed applyPresetToLayout to keep windows beyond preset slots
- When applying 4-window grid to 6 windows, windows 5-6 are preserved
- Extra windows stacked vertically on right side (70/30 split)
- No more window loss when applying presets

**3. Layout Dropdown in TabBar (Better UX)**
- Added dropdown menu next to workspace tabs
- Shows all available presets with icons (Grid2X2, Columns2, Split)
- Displays window requirements and availability
- Disables presets that need more windows than available
- One-click preset application with toast feedback
- More accessible than /layout command

All tests passing (457 passed). State migration handles v6→v7→v8→v9 correctly.

Generated with [Claude Code](https://claude.com/claude-code)
2025-12-18 12:24:00 +01:00
Alejandro Gómez
52f39a8073 feat: add layout presets system with /layout command
Phase 3 implementation:
- Created layout-presets.ts with 3 built-in presets (side-by-side, main-sidebar, grid)
- Implemented fillLayoutTemplate() for recursive template filling with window IDs
- Added collectWindowIds() for depth-first traversal of layout trees
- Created applyPresetToLayout() to reorganize existing windows

- Created layout-parser.ts for /layout command argument parsing
- Added layout command to man.ts with documentation and examples

- Built LayoutViewer component with:
  * Visual preset gallery with diagrams
  * Window count validation
  * Apply preset functionality
  * Error handling for insufficient windows
  * Command-line preset specification support

- Wired LayoutViewer into WindowRenderer with lazy loading
- Added "layout" to AppId type definition
- Exposed applyPresetLayout in useGrimoire hook

Presets allow users to quickly reorganize multiple windows into
common layouts: 50/50 splits, 70/30 main+sidebar, or 2×2 grids.

Generated with [Claude Code](https://claude.com/claude-code)
2025-12-18 12:13:28 +01:00
Alejandro Gómez
cc6f8d646b feat(layouts): Phase 1 - workspace layout configuration system
Add per-workspace layout configuration with smart auto-balancing:

**Core Changes:**
- Add LayoutConfig interface to Workspace type with insertionMode, splitPercentage, insertionPosition
- Create layout-utils.ts with smart direction algorithm that auto-balances horizontal/vertical splits
- Update addWindow() to use workspace layoutConfig instead of hardcoded values
- Migrate state from v7→v8, adding layoutConfig to all workspaces with smart defaults

**Smart Direction Algorithm:**
- Analyzes layout tree to count horizontal vs vertical splits
- Automatically balances by favoring the less-common direction
- Defaults to horizontal (row) for first split
- Provides foundation for "Balanced (auto)" insertion mode

**Testing:**
- Add 30 comprehensive tests for layout-utils.ts (tree analysis, smart direction, window insertion)
- Add 30 tests for logic.ts addWindow() with different layout configs (row/column/smart modes)
- Update migration tests to verify v6→v7→v8 and v7→v8 paths

**Migration:**
- v7→v8 adds layoutConfig with defaults: smart mode, 50% split, second position
- All existing workspaces automatically get smart auto-balancing behavior

Implements orthogonal design: layout behavior = workspace settings (not command flags)
2025-12-18 11:59:45 +01:00
Alejandro Gómez
a066284825 chore: petrify 2025-12-18 10:19:52 +01:00
Alejandro Gómez
a7dd4635dc feat: kind schemas and better man pages 2025-12-18 10:18:53 +01:00
Alejandro Gómez
97c89142ae wip: live video events 2025-12-17 11:44:12 +01:00
Alejandro Gómez
63121f6233 feat: add title command line flag 2025-12-16 20:50:03 +01:00
Alejandro Gómez
6126f43e34 ui: aspect ratios, url styles in auth prompt 2025-12-16 17:28:50 +01:00
Alejandro Gómez
24d2640774 feat: since and until 2025-12-16 14:15:16 +01:00
Alejandro Gómez
6f05f414d3 feat: case insensitive names 2025-12-16 00:07:20 +01:00
Alejandro Gómez
f4a0d5e669 feat: -P filter tag 2025-12-15 23:42:19 +01:00
Alejandro Gómez
390290d2eb wip: skeletons 2025-12-15 22:19:28 +01:00
Alejandro Gómez
5dd885b888 feat: community NIPs 2025-12-15 15:42:11 +01:00