Commit Graph

32 Commits

Author SHA1 Message Date
Alejandro
9f6e524ea9 feat: add tap-to-blur privacy feature for wallet balances (#143)
* feat: add tap-to-blur privacy feature for wallet balances

Implement privacy toggle for wallet balances and transaction amounts.
Tapping any balance display toggles a global blur effect across all
wallet UIs. Persisted to localStorage for consistent privacy.

- Add walletBalancesBlurred state to GrimoireState
- Add toggleWalletBalancesBlur pure function in core/logic
- Make big balance in WalletViewer clickable with eye icon indicator
- Apply blur to all transaction amounts in list and detail views
- Add blur to send/receive dialog amounts
- Make balance in user menu wallet info clickable with eye icon
- Apply blur to balance in dropdown menu item

UX matches common financial app pattern: tap balance → blur on/off

* refactor: replace blur with fixed-width placeholders for privacy

Prevent balance size information leakage by using fixed-width
placeholder characters instead of blur effect. A blurred "1000000"
would still reveal it's a large balance vs "100" even when blurred.

Changes:
- Replace blur-sm class with conditional placeholder text
- Use "••••••" for main balances
- Use "••••" for transaction amounts in lists
- Use "•••••• sats" for detailed amounts with unit
- Use "•••• sats" for smaller amounts like fees

Security improvement: No information about balance size is leaked
when privacy mode is enabled. All hidden amounts appear identical.

* refactor: improve privacy UX with stars and clearer send flow

Three UX improvements to the wallet privacy feature:

1. Don't hide amounts in send confirmation dialog
   - Users need to verify invoice amounts before sending
   - Privacy mode now only affects viewing, not sending

2. Replace bullet placeholders (••••) with stars (✦✦✦✦)
   - More visually distinct and recognizable as privacy indicator
   - Unicode BLACK FOUR POINTED STAR (U+2726)
   - Better matches common "redacted" aesthetic

3. Reduce eye icon sizes for subtler presence
   - Main balance: size-6 → size-5
   - Wallet info dialog: size-3.5 → size-3
   - Smaller icons feel less intrusive

Result: Clearer privacy state, safer payment flow, better aesthetics.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-18 23:22:00 +01:00
Alejandro
7fae344dd9 feat: add Nostr Wallet Connect (NWC) integration (#131)
* feat: add Nostr Wallet Connect (NWC) integration

Add NWC (NIP-47) support to connect Lightning wallets:
- Add NWCConnection type and state management
- Implement custom NWC client service for wallet communication
- Create ConnectWalletDialog for entering connection strings
- Add wallet button to user menu showing balance
- Display wallet info (balance, alias) in user menu dropdown
- Support get_info, get_balance, pay_invoice, make_invoice commands
- Persist wallet connection to localStorage
- Install applesauce-wallet (for future NIP-60 support)

Note: Current implementation uses custom NWC client. Can optionally
switch to @getalby/sdk for official Alby SDK support.

* refactor: use applesauce-wallet-connect and move wallet to header

Replace custom NWC client implementation with applesauce-wallet-connect:
- Install applesauce-wallet-connect for official NIP-47 support
- Create nwc.ts service wrapper for WalletConnect singleton
- Update NWCConnection type to match WalletConnectURI interface
- Use service/relays/secret properties instead of custom names

Move wallet display from user menu to header:
- Create standalone WalletButton component
- Add WalletButton to header next to UserMenu
- Remove wallet UI from user menu dropdown
- Show balance in header with yellow zap icon
- Clicking wallet button opens connect dialog

This provides better UX with wallet status visible in header
and uses the official applesauce implementation for reliability.

* feat: improve wallet button UX and add detailed info dialog

UI improvements:
- Remove border and padding from wallet button (use ghost variant)
- Remove "sats" suffix from balance display (show just the number)
- Change click behavior to show detailed wallet info dialog

Wallet info dialog:
- Show balance prominently without suffix
- Display wallet alias if available
- Show lightning address (lud16) if present
- List all supported NWC methods as badges
- Display connected relay URLs
- Add disconnect button with confirmation toast

This provides a cleaner header appearance and better wallet
management with all details accessible in one place.

* refactor: move wallet functionality to user menu

Move wallet connection and info from separate header button into
the user menu dropdown for better organization.

Changes:
- Remove standalone WalletButton component
- Add wallet section to user menu dropdown
- Show "Connect Wallet" option when no wallet is connected
- Display wallet balance and alias when connected
- Clicking wallet info opens detailed dialog with:
  - Balance (without suffix)
  - Wallet name/alias
  - Lightning address (lud16)
  - Supported NWC methods
  - Connected relay URLs
  - Disconnect button

This consolidates all user-related settings (account, relays,
blossom servers, wallet) in one consistent location.

* feat: improve wallet UX with profile-based naming and better layout

UX improvements:
- Use service pubkey username/profile as wallet name instead of alias
- Remove format hint text from connection string input
- Move wallet to single row directly below user name (remove label)
- Use RelayLink component for relay URLs in wallet info dialog
- Show wallet name from service profile with fallback to alias/pubkey

This provides better integration with Nostr profiles and a cleaner,
more compact menu layout. The wallet service provider's identity is
now shown using their actual Nostr profile name.

* feat: add persistent wallet service with auto-updating balance

Implement comprehensive NWC wallet management architecture:

**Service Layer** (`src/services/nwc.ts`):
- Maintain singleton WalletConnect instance across app lifetime
- Poll balance every 30 seconds for automatic updates
- Subscribe to NIP-47 notifications (kind 23197) for real-time updates
- Expose RxJS observable (balance$) for reactive balance changes
- Restore wallet from saved connection on app startup
- Proper cleanup on disconnect

**Hook Layer** (`src/hooks/useWallet.ts`):
- useWallet() hook exposes wallet throughout the app
- Provides methods: payInvoice, makeInvoice, getBalance, etc.
- Auto-syncs balance updates with Jotai state
- Handles wallet restoration on mount
- Type-safe wrapper around WalletConnect API

**UI Updates**:
- Add refresh button to wallet info dialog
- Auto-update balance display when transactions occur
- Proper cleanup on wallet disconnect

**Architecture**:
```
User Action → useWallet() → NWC Service → WalletConnect
                ↓                ↓
         Update State ← balance$ observable ← Polling/Notifications
```

This enables:
- Real-time balance updates when paying/receiving
- Easy wallet access: `const { payInvoice, balance } = useWallet()`
- Persistent connection across page reloads
- Automatic polling as fallback for notifications

* refactor: make NWC fully reactive with notifications and graceful balance handling

Changes:
- Remove polling mechanism in favor of pure reactive notifications$ observable
- Subscribe to wallet.notifications$ for real-time balance updates
- Make balance display conditional (only show if available)
- Fix TypeScript errors (notification.type access, unused variable)
- Remove Jotai callback mechanism for balance updates
- Use use$() directly for reactive balance subscription
- Update comments to reflect reactive architecture (no polling)

The wallet now updates balance automatically when payments are sent/received
via NIP-47 notifications, with no polling overhead.

* feat: improve wallet UX with profile-based naming and better layout

Improvements to NWC wallet UI:
- Add separator between user info and wallet section in menu
- Show wallet icon instead of zap icon for better clarity
- Display connection status indicator (green/red dot) in both menu and dialog
- Make wallet service username clickable in wallet info dialog to open profile
- Use wallet relays as hints when fetching service profile for better resolution
- Enhanced useProfile hook to accept optional relay hints parameter

The wallet now properly resolves service profiles using the NWC relay
and shows visual connection status at a glance.

* fix: remove toast descriptions for better contrast

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-18 11:14:47 +01:00
Alejandro
16764e1aca Display user's blossom servers in menu (#90)
* feat: Display blossom servers in user menu with caching

Implements caching and display of user's blossom server lists (kind 10063) in the user menu dropdown.

**Key Changes:**

1. **Database Schema (db.ts)**:
   - Added `CachedBlossomServerList` interface
   - Added `blossomServers` table to Dexie v15
   - Indexed by pubkey and updatedAt for efficient querying

2. **Caching Service (blossom-server-cache.ts)**:
   - Dual-layer caching: LRU memory cache (100 entries) + Dexie persistent cache
   - 24-hour TTL to reduce network requests
   - Auto-caches kind:10063 events from EventStore
   - Mirrors relay-list-cache pattern for consistency

3. **Sync Hooks**:
   - `useBlossomServerCacheSync`: Subscribes to EventStore for auto-caching kind:10063 events
   - `useAccountSync`: Extended to fetch and watch active account's blossom servers

4. **State Management**:
   - Added `blossomServers` field to `activeAccount` in GrimoireState
   - Created `setActiveAccountBlossomServers` pure function and callback
   - Reactive updates when user's server list changes

5. **User Menu UI (user-menu.tsx)**:
   - Added blossom servers section with HardDrive icon
   - Shows server count badge (e.g., "3 servers")
   - Clickable links open servers in new tabs
   - Displays only when user has configured servers

**Architecture:**
- Follows existing relay list caching pattern for consistency
- Reactive: UI auto-updates when kind:10063 events arrive
- Incremental sync: Fetches on login, caches for 24h
- Performance: Memory cache for <1ms lookups, Dexie for persistence

**Testing:**
- Build: ✓ No TypeScript errors
- Tests: ✓ All 838 tests passing

* feat: Open blossom server file lists directly from menus

**User Menu & Profile Viewer Improvements:**

1. **Enhanced Click Behavior**:
   - Clicking a blossom server now opens the file list for that server
   - Shows blobs uploaded by the user (user menu) or profile owner (profile viewer)
   - Pre-selects the clicked server in the dropdown

2. **UX Improvements**:
   - Removed server count from user menu label (cleaner UI)
   - Added `cursor-crosshair` to blossom server items (consistent with other clickable items)
   - Removed external link icon (not opening external URL anymore)

3. **Technical Changes**:
   - Updated `ListBlobsView` to accept optional `serverUrl` prop for pre-selection
   - User menu: Opens `blossom list` with `serverUrl` for active user
   - Profile viewer: Opens `blossom list` with both `pubkey` and `serverUrl`

**Flow:**
- User menu → Click server → Opens files for active user on that server
- Profile viewer → Click server → Opens files for viewed user on that server

* fix: Properly fetch blossom servers for any profile view

**Problem:**
Blossom servers were only visible for the logged-in user's profile,
not for other users' profiles being viewed.

**Solution:**
Enhanced ProfileViewer blossom server fetching with multi-layer approach:

1. **Cache-first loading**: Check blossomServerCache for instant display
2. **EventStore check**: Use existing cached event if available
3. **Reactive subscription**: Subscribe to EventStore for real-time updates
4. **Network fetch**: Use addressLoader to fetch latest from relays
5. **Auto-caching**: Update cache when new events arrive

**Benefits:**
- Blossom servers now display for ANY user's profile
- Instant display from cache (< 1ms)
- Reactive updates when data changes
- Proper cache hydration for future visits
- Consistent with relay list fetching pattern

**Technical:**
- Imported and integrated blossomServerCache service
- Added cache check before network fetch
- Separated EventStore subscription from network fetch
- Added cache updates on event arrival

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-14 14:52:16 +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
9b7c17b733 fix: resolve infinite update loop by stabilizing useGrimoire hook
- Introduce activeGrimoireStateAtom to handle switching between persistent and temporary state
- Ensure setState and all logic callbacks are stable across re-renders
- Correctly type Jotai updaters to avoid implicit any errors
2025-12-21 18:08:10 +01:00
Alejandro Gómez
cef6da87f4 feat: implement dual-state system for temporary layout sessions
- Add temporaryStateAtom to track in-memory sessions
- Update useGrimoire to handle conditional state management
- Previews and Direct links now use temporary state, preserving dashboard
- Add Apply and Discard logic to persist or revert temporary sessions
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
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
Alejandro Gómez
2987a37e65 feat: spells 2025-12-20 14:25:40 +01:00
Alejandro Gómez
a39dc658cd feat: make workspace tabs reorderable via drag and drop 2025-12-19 22:10:42 +01:00
Alejandro Gómez
8f80742ef1 feat: tab names 2025-12-19 12:49:29 +01:00
Alejandro Gómez
812b719ea0 feat: debug command, simplify state 2025-12-18 23:32:00 +01:00
Alejandro Gómez
f6f813d382 refactor: collapse migrations 2025-12-18 16:49:24 +01:00
Alejandro Gómez
967f1eb89b ux: improvements to keyboard navigation 2025-12-18 16:14:56 +01:00
Alejandro Gómez
a6650ff6e1 fix: type errors 2025-12-18 16:00:56 +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
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
a78b5226ce feat(layouts): Phase 2 - workspace settings UI
Add per-workspace layout configuration UI with visual controls:

**Core Changes:**
- Add updateWorkspaceLayoutConfig() function to logic.ts for updating workspace layout settings
- Expose updateWorkspaceLayoutConfig in useGrimoire hook

**UI Components:**
- Create WorkspaceSettings dialog with three sections:
  * Insertion Mode selector (Balanced/Horizontal/Vertical) with icons
  * Split Percentage slider (10-90%) with real-time preview
  * Insertion Position toggle (Left-Top/Right-Bottom)
- Add settings icon (SlidersHorizontal) to workspace tabs that appears on hover
- Settings button opens configuration dialog for that workspace

**UX Details:**
- Settings icon only visible on hover to reduce visual clutter
- Clear visual feedback for selected options with primary color highlights
- Preview section shows current configuration in plain language
- Reset to Defaults button restores smart mode defaults
- Prevents workspace switch when clicking settings icon

**Icons Used:**
- Sparkles: Balanced (smart auto-balancing)
- SplitSquareHorizontal: Horizontal splits
- SplitSquareVertical: Vertical splits
- SlidersHorizontal: Settings access

Each workspace can now have independent layout behavior configured through an intuitive UI.
2025-12-18 12:05:47 +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
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
9496af6273 feat: better generic event detail relay list and JSON viewer 2025-12-14 16:41:43 +01:00
Alejandro Gómez
e5c871617e chore: cleanup, a11y and state migrations 2025-12-14 16:32:45 +01:00
Alejandro Gómez
d877e51317 feat: editable commands 2025-12-13 22:53:27 +01:00
Alejandro Gómez
38f5461b54 chore: setup eslint and prettier with code formatting 2025-12-11 13:00:25 +01:00
Alejandro Gómez
9c9d04c947 fix: more robust storage 2025-12-11 00:18:28 +01:00
Alejandro Gómez
8ffd0fd2cb feat: timestamps and user locale 2025-12-10 22:32:36 +01:00
Alejandro Gómez
06166523f9 feat: empty canvas on load 2025-12-10 17:37:36 +01:00
Alejandro Gómez
5b00e42ddf feat: RTL support 2025-12-10 13:00:39 +01:00
Alejandro Gómez
cd41034b2f 👶 2025-12-09 16:26:31 +01:00