Commit Graph

119 Commits

Author SHA1 Message Date
nrobi144 fbde0052a8 fix(media): video respects max height, stays within NoteCard bounds
Remove fillMaxWidth before aspectRatio so the caller's heightIn(max)
constraint is respected. Video now sizes to fit within the NoteCard
without overlapping the author header or overflowing the card.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:56:42 +02:00
nrobi144 9a19148da8 fix(media): stable hover controls, fix text cutoff
- Single hover zone on entire video — no more flickering from competing
  zones. Play button always visible when paused, bottom controls appear
  on hover (works in fullscreen too)
- Media height capped to 50% of window (min 200dp) so post text is
  never pushed off-screen by large media

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:51:44 +02:00
nrobi144 248f38cc47 fix(media): zone-based video controls — play always visible, controls on hover
- Center play button always visible when paused
- Pause button appears on center hover when playing
- Bottom controls bar only appears when hovering the bottom area
- Controls also show when paused for discoverability

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:32:08 +02:00
nrobi144 e9249c924c fix(media): only show video controls on hover
Controls (play button, seek bar, volume) are hidden by default and
appear when the mouse enters the video area. They auto-hide 2s after
the mouse leaves.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:27:57 +02:00
nrobi144 bce6c12ab3 feat(media): video thumbnails in feed via VLC first-frame extraction
Add VideoThumbnailCache that grabs the first rendered frame from VLC
and caches it in memory. Videos in the feed now show a thumbnail
preview instead of a blank background before the user clicks play.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:12:16 +02:00
nrobi144 89f39fa77e feat(search): show images and media in search results
Replace plain-text NotePreviewCard with full NoteCard in search results
so images, videos, and author avatars are visible inline without having
to open the note.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:40:48 +02:00
nrobi144 d091084064 fix(media): resize media to fit visible window, never truncate text
- Remove maxLines/TextOverflow from RichTextContent so post text is
  never truncated
- Media height = window height minus 200dp chrome, so each image/video
  fits in the visible area without scrolling
- Use ContentScale.Fit for images to scale down while keeping aspect
  ratio instead of filling width and overflowing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:33:54 +02:00
nrobi144 bdd1feae3e fix(ui): show full post text, scrollable profile header with floating bar
- Remove maxLines=10 on post text so content is never truncated
- Profile header/card/tabs now scroll with content instead of staying
  sticky — scrolls away naturally as you browse posts
- Floating compact header (back + avatar + name) slides in when
  scrolling up and the full header is out of view

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 15:26:20 +02:00
nrobi144 2aa84c98ad fix(media): cap feed media height to 70% of window height
Replace hardcoded 400.dp max with window-relative height so media
(images and videos) never overflow the visible area and controls
are always accessible without scrolling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 13:53:58 +02:00
nrobi144 dffb996335 fix(media): immersive fullscreen via GraphicsDevice, separate view mode buttons
Replace WindowPlacement.Fullscreen (macOS maximize-only) with
GraphicsDevice.setFullScreenWindow() for true exclusive fullscreen that
hides menu bar and dock. Split single cycling view mode button into two
distinct toggles (theater + fullscreen). Hide all navigation chrome
(sidebar, nav rail, dividers, arrows, overlay menus) during fullscreen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 13:49:39 +02:00
nrobi144 7d3d633e03 feat(media): bundle VLC, lazy video player, fullscreen with seek continuity
- Bundle VLC via ir.mahozad.vlc-setup plugin (all plugins for network playback)
- BundledVlcDiscoverer (Win/Linux) and MacOsVlcDiscoverer for bundled VLC discovery
- Lazy player activation — no VLC calls during composition, only on play click
- Pre-init VLC on background thread at startup
- Video controls: seek slider, volume, mute, fullscreen (two-row layout, no clipping)
- Buffering indicator (CircularProgressIndicator) while loading
- ActiveMediaManager enforces single-player — pauses others when new video plays
- Fullscreen passes seek position so lightbox resumes from current playback point
- LightboxOverlay renders videos (DesktopVideoPlayer) or images (ZoomableImage)
- Gitignore downloaded VLC binaries (appResources/{linux,macos,windows}/)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 07:23:42 +02:00
nrobi144 cd7ee69f50 feat(media): add imeta tags on upload and drag-and-drop
- Build IMetaTag (NIP-92) from upload metadata (mime, sha256, dim, blurhash, size)
- Use TextNoteEvent.build directly with tag initializer for imeta injection
- Add drag-and-drop file support via Compose DragAndDropTarget
- Filter dropped files by media extension whitelist
- Visual border highlight on drag-over state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 adecc35721 feat(media): wire profile gallery tab and lightbox
- Add Notes/Gallery PrimaryTabRow to UserProfileScreen
- Subscribe to PictureEvent (kind 20) for gallery grid
- Wire GalleryTab composable with image click callbacks
- Wire LightboxOverlay for full-screen image viewing
- Pass onImageClick to FeedNoteCard in notes tab

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 7773844d2f feat(media): wire encrypted media, server settings, and server preferences
- ChatPane: display ChatFileAttachment for encrypted file header events
- DesktopPreferences: add blossom server list persistence
- Settings: add MediaServerSettings section above relay settings
- ComposeNoteDialog: use preferred blossom server from preferences

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 e654d48cdf fix(media): code review — dead code removal, perf fixes, bug fixes
- Remove unused: MediaAspectRatioCache, VideoPlayerState, resetZoom,
  onDoubleClick, delete/headUpload/createDeleteAuth, encryptAndUpload,
  EncryptedUploadResult, unused options params, formatAudioTime dupe
- Fix VlcjPlayerPool.release() accumulating stale event listeners
- Fix SaveMediaAction FileDialog running on IO instead of EDT
- Pre-allocate video frame ByteArray to avoid ~240MB/s GC pressure
- Pool audio players in VlcjPlayerPool instead of factory-per-instance
- Remove listener in onDispose before returning player to pool
- Fix acquire() race condition by moving poll inside synchronized
- Reduce memory cache to 15%/256MB with weak references
- Parallelize server health checks
- Remove redundant Content-Type/Content-Length headers
- Deduplicate BlurHashFetcher bitmap conversion via bufferedImageToSkiaBitmap
- Hoist audioExtensions to top-level constant, rename allMediaUrls
- Remove nonfunctional Save button and dead decryptedBytes state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 d5facd90df test(media): add 49 unit tests for media upload, encryption, and server services
Coverage for DesktopMediaMetadata, DesktopUploadTracker, DesktopMediaCompressor,
DesktopBlossomClient (MockK), DesktopUploadOrchestrator (MockK + mockkObject),
ServerHealthCheck, and AESGCM encrypt/decrypt round-trip.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 dfaca39528 feat(media): inline audio playback with VLCJ no-video mode
Phase 9: AudioPlayer composable using VLCJ --no-video factory,
play/pause/seek controls, NoteCard splits audio from video URLs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 4450aacdf9 feat(media): Blossom server management settings with health checks
Phase 8: MediaServerSettings UI for add/remove/reorder Blossom servers,
ServerHealthCheck with 5s timeout HEAD requests, status indicators.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 ca4e51afa9 feat(media): NIP-68 picture display and profile gallery grid
Phase 7: PictureDisplay composable for kind 20 events with image-first
layout, GalleryTab with adaptive grid of thumbnails, lightbox support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 f0acbb1934 feat(media): encrypted media service for NIP-17 DM file sharing
Phase 6: AESGCM encrypt/decrypt for DM file attachments, Blossom
upload of encrypted files, ChatFileAttachment composable with
auto-decrypt for images and file type display.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 1a2dc9efac feat(media): lightbox overlay with zoom, gallery navigation, and save
Phase 5: Full-screen lightbox overlay with mouse wheel zoom + pan,
left/right gallery carousel, keyboard shortcuts (Esc/arrows/Ctrl+S),
save to disk via FileDialog. Images in NoteCard are clickable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 405601463e feat(media): VLCJ video playback with DirectRendering and player pool
Phase 4: VLCJ player pool (max 3 instances), DirectRendering via
CallbackVideoSurface → Skia Bitmap → Compose Image. Video controls
with auto-hide, seek, play/pause. Graceful fallback when VLC missing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 2181407647 feat(media): desktop upload UX with file picker, clipboard paste, and attachment row
Phase 3: Native file dialog, AWT clipboard paste handler, media
attachment UI with thumbnails, and ComposeNoteDialog media integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 443f7c6a09 feat(media): Phase 2 — Desktop Blossom upload client
- DesktopBlossomClient: PUT /upload, DELETE, HEAD /upload (BUD-06)
- DesktopBlossomAuth: kind 24242 auth with base64 Nostr header
- DesktopMediaMetadata: SHA-256, dimensions, blurhash via ImageIO
- DesktopMediaCompressor: lossless EXIF stripping (Commons Imaging)
- DesktopUploadOrchestrator: strip EXIF → metadata → auth → upload
- DesktopUploadTracker: StateFlow-based upload state tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
nrobi144 212dda40ca feat(media): Phase 0+1 — Coil3 image loading + inline images in notes
- DesktopImageLoaderSetup: Coil3 with explicit memory cache (25% heap,
  max 512MB), persistent disk cache (OS-appropriate paths), SVG decoder
- DesktopBlurHashFetcher: blurhash placeholder images via Skia Bitmap
- DesktopBase64Fetcher: base64 data: URI image decoding
- SkiaGifDecoder: GIF first-frame rendering via Skia Codec
- MediaAspectRatioCache: thread-safe LruCache for aspect ratios
- NoteCard: inline AsyncImage for image URLs, stripped from text
- Added coil-compose, coil-okhttp, coil-svg, vlcj, commons-imaging,
  androidx-collection deps to desktopApp

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 05:44:33 +02:00
Vitor Pamplona 874d750bf0 removing unused import 2026-03-16 17:01:59 -04:00
Vitor Pamplona 95c7adc90b Less warnings 2026-03-16 16:53:17 -04:00
Vitor Pamplona 2acf02945b Merge pull request #1840 from nrobi144/feat/desktop-advanced-search
feat(desktop): advanced search with NIP-50, collapsible sections, and nav state preservation
2026-03-16 08:14:18 -04:00
Vitor Pamplona d426dbd4ff adds icons for desktop releases 2026-03-15 21:10:59 -04:00
Vitor Pamplona 8b4a45cb5b Removing deprecated library addresses and deprecated apis 2026-03-15 20:52:01 -04:00
nrobi144 327bb1efc8 fix(nav): preserve screen state across overlay navigation
RootContent now stays composed when overlays are shown, preventing
search results and other state from being destroyed on navigation.
Overlay wrapped in opaque Surface to cover root content visually.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:32:05 +02:00
nrobi144 5a437ed5ac feat(search): collapsible section headers + sorting + search improvements
- Collapsible sticky section headers with animated chevron
- Full header row clickable to toggle collapse
- SearchResultSorter, SearchSortOrder, pseudo-kind filtering
- TextFieldValue cursor preservation, relay timeout improvements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:23:27 +02:00
nrobi144 7180edf162 Merge upstream/main into feat/desktop-advanced-search
Resolve conflicts:
- RelayStatus.kt: keep both relay URLs
- SearchScreen.kt: keep advanced search implementation
- SinglePaneLayout.kt: add missing Spacer import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:22:15 +02:00
nrobi144 f0ffe3cbe3 fix(ui): remove duplicate back button from SinglePaneLayout
Overlay screens (UserProfileScreen, ThreadScreen) already render their
own back+title header. Remove the redundant SinglePaneHeader bar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:36:46 +02:00
nrobi144 3c6c368582 refactor(search): extract thread-safe EventDeduplicator, unify dedup
Replace raw MutableSet with synchronized EventDeduplicator class.
Remove dual dedup in addNoteResults — trackRelayEvent now gates all
event processing. SearchScreen callbacks skip dupes early via return
value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:36:39 +02:00
nrobi144 36efbd1544 fix(desktop): remove noisy debug logs and silence SLF4J warnings
Remove feedSub/contactList debug prints from FeedScreen, suppress
GiftWrapEvent decryption failure log (expected for others' wraps),
and add slf4j-nop to silence "No SLF4J providers" console warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:20:19 +02:00
nrobi144 4d1d92db39 feat(desktop): add bunker heartbeat indicator to sidebar navigation
Pulsating green heart icon shows NIP-46 signer connection status in both
DeckSidebar and SinglePaneLayout NavigationRail. Tooltip displays last
ping time. SignerConnectionState moved to commons for KMP sharing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:20:18 +02:00
nrobi144 5475d6c2bc fix(desktop): verify user pubkey via get_public_key after nostrconnect login
The nostrconnect flow was trusting params[0] from the signer's connect
message as the user's pubkey. Some signers (e.g. nsec.app) don't put
the actual user pubkey there, causing all relay subscriptions to query
the wrong identity — contact lists, profiles, and DMs all returned empty.

Now calls remoteSigner.getPublicKey() after the handshake to get the
verified pubkey from the signer. Also stabilizes FeedScreen relay
subscriptions with distinctUntilChanged() and adds relay.primal.net
to defaults.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:20:18 +02:00
nrobi144 3a1e38a2f2 fix(desktop): address code review findings for NIP-46 bunker login
- Add bounds check in fromBunkerUri for missing query params (P2-1)
- Preserve bunker keys on transient force-logout (P2-3)
- Replace debug printlns with DebugConfig.log() (P2-4)
- Replace unused Unstable with Disconnected state (P2-5)
- Move validateBunkerUri to account package (P3-9)
- Fix Compose state update from IO thread (P3-11)
- Add disconnectNip46Client() to onDispose cleanup (P3-12)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:10 +02:00
nrobi144 12c91aae3d feat(desktop): add nostrconnect:// login flow for QR-based signer pairing
Adds client-initiated NIP-46 (nostrconnect://) flow so users can pair
with a remote signer (e.g. Amber) by scanning a QR code instead of
manually copying a bunker:// URI.

Changes:
- Fix RemoteSignerManager to decrypt NIP-44 content before parsing
- Add CoroutineScope to NostrSignerRemote for async event callbacks
- Add loginWithNostrConnect() to AccountManager with 120s timeout
- Add QrCodeCanvas composable using ZXing for QR generation
- Add "Connect" tab to LoginCard showing QR + copyable URI + waiting state
- Wire nostrconnect flow through LoginScreen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:10 +02:00
nrobi144 bff6723ba6 revert: remove QR code scanning from login
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:10 +02:00
nrobi144 d030843522 feat(desktop): webcam QR scanning via ffmpeg for bunker login
Replaces webcam-capture (broken on Apple Silicon) with ffmpeg
avfoundation capture. Opens a live preview dialog that scans
for QR codes from the FaceTime camera. Works natively on arm64.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:10 +02:00
nrobi144 6e04482981 feat(desktop): add webcam QR code scanning for bunker login
Uses webcam-capture + ZXing to scan QR codes from the camera.
Click the QR icon on the login screen to open a webcam scanner
dialog that auto-detects bunker:// URIs from Amber's QR display.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:10 +02:00
nrobi144 fd1b435020 feat(desktop): add QR code scanning for bunker login
Adds a QR scan button to the login card that decodes QR codes from
clipboard images or image files using ZXing. Useful for scanning
bunker:// URIs from Amber's QR display.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:09 +02:00
nrobi144 5661c6df27 fix(desktop): clear stored credentials on logout
All user-initiated logouts now pass deleteKey=true to remove
nsec from secure storage, clear last_npub, and delete bunker state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:09 +02:00
nrobi144 26ea0be4c7 fix(desktop): simplify settings logout to just a button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:09 +02:00
nrobi144 2346d92f16 feat(desktop): add logout option to menu bar and settings
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:09 +02:00
nrobi144 1821b9ff71 test: add NIP-46 test suite for desktop, quartz, and fix pre-existing chess test errors
Add comprehensive test coverage for NIP-46 bunker login across quartz and desktopApp:

Quartz (4 files, 48 tests):
- ResponseParserTest: all 7 response parsers (success/error/unexpected)
- FromBunkerUriTest: URI parsing, validation, edge cases
- ConvertExceptionsTest: SignerResult→Exception mapping
- NostrConnectEventTest: canDecrypt, talkingWith, verifiedRecipientPubKey

Desktop (6 files, 45 tests):
- BunkerUriUtilsTest: validateBunkerUri + stripBunkerSecret
- AccountManagerKeyLoginTest: nsec/npub/invalid login, save, generate
- AccountManagerLogoutTest: logout, forceLogout, state transitions
- AccountManagerLoadAccountTest: internal/bunker/missing-key scenarios
- AccountManagerBunkerLoginTest: hasBunkerAccount, setConnectingRelays
- AccountManagerHeartbeatTest: start/stop, no-crash with internal signer

Production changes:
- AccountManager: constructor private→internal, add homeDir param for test injection, extract stripBunkerSecret to internal top-level, constants internal
- desktopApp/build.gradle.kts: add mockk test dependency

Fix pre-existing chess test compilation errors:
- ChessStateReconstructorTest: add missing jester subpackage imports
- ChessGameEventTest: altText()→alt() + add nip31Alts import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:16:09 +02:00
nrobi144 6c24c52104 feat(desktop): add NIP-46 remote signer (bunker://) login
Support bunker:// URI login for desktop, enabling private key delegation
to remote signers (nsec.app, Amber). Includes heartbeat monitoring,
force-logout on revocation, and ConnectingRelays startup state.

- AccountManager: bunker login/save/load, heartbeat ping, force logout
- LoginCard: auto-detect bunker:// URI, validation, connecting state
- LoginScreen: wire bunker callback, ConnectingRelays screen
- Main.kt: relay timeout, error recovery, scope cleanup, data objects
- ForceLogoutDialog: alert on signer revocation/disconnect

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:15:24 +02:00
nrobi144 979d07679c fix(search): loading indicator, result delivery, and history race
- Replace boolean isSearching with counter-based activeSubscriptionCount
  so loading state stays true until both subs (people + notes) complete
- Add indeterminate LinearProgressIndicator overlaid on search bar bottom
  border with matching 12dp rounding, replacing "Searching relays..." text
- Send search REQs to all configured relays (relayStatuses) instead of
  only connected ones — fixes NIP-50 relays never receiving search queries
- Move clearResults/startSearching out of remember() into LaunchedEffect
  to avoid side effects during composition
- Fix history saving partial queries by replacing LaunchedEffect with
  snapshotFlow to properly observe isSearching transitions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 09:29:27 +02:00