Commit Graph

21 Commits

Author SHA1 Message Date
Claude d9fe3b5f83 feat(audio-rooms): consume moq-lite catalog metadata in VM
The catalog subscription API shipped two commits ago but nothing
read it. This wires it up:

  * RoomSpeakerCatalog data class (commons) — kotlinx.serialization
    parser for moq-lite's `catalog.json` payload (version, audio
    track list with codec / sample_rate / channel_count / bitrate).
    Forward-compat: ignoreUnknownKeys + nullable fields tolerate
    new keys and partial publishers.
  * AudioRoomViewModel.speakerCatalogs: StateFlow<Map<String,
    RoomSpeakerCatalog>> populated lazily as per-speaker
    subscriptions land. Catalog fetch piggybacks on openSubscription
    via subscribeCatalog — best-effort, doesn't gate audio.
  * Participant context sheet renders a single-line summary
    ("OPUS · 48kHz · 2ch") below the pubkey when the catalog is
    available.
  * Enabled kotlinx.serialization plugin on :commons (was already
    a transitive dep, just not wired for @Serializable codegen).

Tests:
  * RoomSpeakerCatalogTest — 7 cases covering the canonical shape,
    describe() formatting (codec uppercased, kHz / channel
    short-formed), all-null fallback, unknown-key tolerance,
    garbage-bytes fallback, and empty-audio-list.
2026-04-27 01:35:18 +00:00
Claude f9aa762d9b feat(audio-rooms): M1 listener-only wire-up — Connect button + Connected chip + mute
Wires `connectNestsListener` into the existing NIP-53 audio-room "stage" so
a user can tap Connect, see the live connection state, and hear hosts
speaking. Per the audio-rooms completion plan
(`nestsClient/plans/2026-04-26-audio-rooms-completion.md`) phase M1.

UI (amethyst):
- AudioRoomStage gets a state-chip / Connect / Disconnect / Mute row
  above the existing hand-raise. State chip walks ResolvingRoom →
  OpeningTransport → MoQ-handshake → Connected; Failed surfaces the
  reason inline with a Retry button. The 30312 `service` tag drives
  visibility — rooms hosted on non-nests servers show "Audio not
  available" and the rest of the stage UI continues to work.

ViewModel (commons):
- New `AudioRoomViewModel` in commons/.../viewmodels/ owns one
  `NestsListener` and one `AudioRoomPlayer` per active speaker, exposes
  a single `StateFlow<AudioRoomUiState>`, and reconciles subscriptions
  whenever the screen pushes a new speaker set via `updateSpeakers`.
  Audio-pipeline construction is injected via `decoderFactory` /
  `playerFactory` so a future desktop port can reuse the orchestration
  once Compose Desktop has WebTransport. Unit-tested with a fake
  `NestsListenerConnector` driving state transitions directly.

nestsClient:
- `AudioPlayer.setMuted(Boolean)` joins the interface; `AudioTrackPlayer`
  routes it through `AudioTrack.setVolume(0f/1f)`. Mute keeps the
  decode + network pipeline running so unmute is sample-accurate.

Build:
- `:commons` commonMain now `implementation(project(":nestsClient"))` so
  the shared VM can see `NestsListener` / `AudioRoomPlayer` / interfaces.
  Concrete OkHttp / Quic / MediaCodec / AudioTrack actuals stay in
  `:nestsClient`'s platform source sets and are bound by an
  Android-side `AudioRoomViewModelFactory` co-located with the screen.

Verified: `./gradlew spotlessApply :commons:jvmTest :nestsClient:jvmTest
:quic:jvmTest :amethyst:compilePlayDebugKotlin` all green.
2026-04-26 02:54:43 +00:00
Claude c1d98f057d Merge remote-tracking branch 'origin/main' into claude/thin-material-icons-9SFWk
# Conflicts:
#	desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt
2026-04-23 22:12:15 +00:00
Claude a7c1d45d93 refactor: promote desktop upload classes to commons/jvmMain
Moves five JVM-only upload helpers from desktopApp into commons so
the CLI can reuse the same Blossom upload + AES-GCM encryption path
without depending on :desktopApp:

  desktop/service/upload/DesktopBlossomAuth.kt        → commons/service/upload/BlossomAuth.kt
  desktop/service/upload/DesktopBlossomClient.kt      → commons/service/upload/BlossomClient.kt
  desktop/service/upload/DesktopMediaCompressor.kt    → commons/service/upload/MediaCompressor.kt
  desktop/service/upload/DesktopMediaMetadata.kt      → commons/service/upload/MediaMetadata.kt
  desktop/service/upload/DesktopUploadOrchestrator.kt → commons/service/upload/UploadOrchestrator.kt

API tweaks:
- Drop the `Desktop` prefix on every class.
- Rename the `DesktopMediaMetadata` object to `MediaMetadataReader`
  to avoid colliding with the `MediaMetadata` data class in the same
  package.
- BlossomClient no longer reaches into desktop's `DesktopHttpClient`.
  Callers pass an `OkHttpClient` (or use the default one-shot client);
  desktop continues to inject its Tor-aware client.
- Tighten BlossomClient's response handling — the OkHttp version on
  commons' classpath has a nullable `Response.body`, so explicitly
  null-check it.

Mechanical updates to keep desktop building:
- ComposeNoteDialog / ChatPane / DesktopUploadTracker switch to the
  new commons imports.
- DimensionTag-from-MediaMetadata: cross-module smart cast no longer
  works on the public `width`/`height` properties — replace with
  `?.let { … }` chains.
- commons/jvmMain now declares `commons-imaging` (used only by
  MediaCompressor's EXIF stripping).

Tests come along too: file names follow the renamed classes
(spotless ktlint enforces this).

Next commit will add `amy dm send-file --file PATH --server URL`,
which calls `UploadOrchestrator.uploadEncrypted(...)` directly.
2026-04-23 19:47:41 +00:00
Claude 8ff091ca0e feat(icons): migrate Material Icons → Material Symbols (thin weight)
Replace the `material-icons-extended` library with Google's Material Symbols
variable font, rendered via a new `commons/icons/symbols/` package. Weight is
set to 100 (Thin) via a single tunable default (MaterialSymbolsDefaults.WEIGHT),
so we can re-tune line thickness globally in one line without regenerating
assets.

Key changes:
- commons/composeResources/font/material_symbols_outlined.ttf: Material Symbols
  Outlined variable font (all 3600+ glyphs, 4 design axes: wght/FILL/GRAD/opsz).
- commons/icons/symbols/: MaterialSymbol data class, 219 codepoint constants,
  variable-font FontFamily helper, Canvas-backed Icon composable (overload that
  accepts MaterialSymbol and supports auto-mirror for RTL), and a Painter
  wrapper for use with Image / AsyncImage.
- Bulk-rewrote 250+ call sites across amethyst/, commons/, desktopApp/:
  Icons.{Default,Outlined,Filled,Rounded,AutoMirrored.*}.X → MaterialSymbols.X
  or MaterialSymbols.AutoMirrored.X. `imageVector =` renamed to `symbol =`
  inside Icon() calls; Image(imageVector = MS.X) converted to
  Image(painter = rememberMaterialSymbolPainter(MS.X)).
- Dropped material-icons-extended dependency from commons, desktopApp,
  amethyst build files.

Hand-drawn custom icons (Bookmark, Like, Reply, Zap, chess pieces, robohash)
remain as ImageVector and are untouched.

https://claude.ai/code/session_016gV9gEaSud6kDMdcaqy8ti
2026-04-23 19:44:17 +00:00
Vitor Pamplona 5727d50fce Fixes test cases 2026-04-06 09:22:21 -04:00
Claude 9854209843 test: add comprehensive NIP-AC WebRTC call state machine tests
Add 81 tests across two test suites covering the full NIP-AC spec:

- quartz/NipACStateMachineTest (31 tests): Protocol compliance test vectors
  for event structure, tags, P2P/group flows, ICE serialization, staleness,
  renegotiation glare rules, and multi-device support

- commons/CallManagerTest (50 tests): State machine integration tests using
  real NostrSignerInternal with actual crypto, covering:
  * Full call lifecycle (Idle → Offering/IncomingCall → Connecting → Connected → Ended → Idle)
  * Call rejection, busy auto-reject, hangup from any state
  * Self-event filtering (ICE, hangup, answer-elsewhere)
  * Mid-call renegotiation (voice ↔ video)
  * Group calls (mesh discovery, partial disconnect, invite peer)
  * Interface-level tests with real signing + gift wrapping pipeline
  * Full end-to-end P2P flow with two CallManager instances

Also adds test vector tables to NIP-AC.md spec for other implementers.

https://claude.ai/code/session_01AfRYTRCvtKqqDxeKQujUrx
2026-04-04 15:26:20 +00:00
nrobi144 5ef36363cd feat(reads): Phase 1 — article reader with markdown, ToC, reading time
- Add richtext-commonmark deps to commons for shared markdown rendering
- Create RenderMarkdown composable with URL scheme allowlist (security)
- Create ArticleMediaRenderer interface for platform-specific media
- Create ArticleHeader with banner, title, author, reading time metadata
- Create TableOfContents with heading extraction and scroll-spy
- Create ReadingTimeCalculator (238 WPM prose, 80 WPM code, image decay)
- Create ArticleReaderScreen with Medium-style typography (680dp, 1.58x)
- Add DesktopScreen.Article navigation with address tag routing
- Update ReadsScreen to navigate via address tags instead of event IDs
- Responsive ToC sidebar (shown when window > 1100dp)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:59:52 +02:00
Vitor Pamplona 8b4a45cb5b Removing deprecated library addresses and deprecated apis 2026-03-15 20:52:01 -04:00
Vitor Pamplona f4d401bc56 Switches to our own version of the Url Detector 2026-03-06 10:58:59 -05:00
Vitor Pamplona edb73b3556 Updates AGP 2026-03-03 11:52:03 -05:00
Vitor Pamplona c755908db5 Removes string-concat compiler argument since it is not supported anymore 2026-02-17 19:02:53 -05:00
Vitor Pamplona 6125394354 Migrates to AGP 9 2026-02-08 16:33:39 -05:00
nrobi144 71e68dc35b refactor(commons): migrate from Moko to Compose Multiplatform Resources
Remove Moko Resources dependency that blocks AGP 9.0 migration.
Migrate to built-in Compose Multiplatform Resources which is compatible
with the new KMP Android plugin.

- Remove moko-resources plugin and libraries from commons module
- Move strings.xml from moko-resources/ to composeResources/values/
- Update imports from SharedRes.strings.* to Res.string.*
- Add compose.components.resources dependency to desktopApp
- Configure resource package as com.vitorpamplona.amethyst.commons.resources

Closes #1675

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 13:29:55 +02:00
nrobi144 a6d90c319e feat: Add lifecycle-viewmodel KMP dependencies to commons
Adds androidx.lifecycle.viewmodel.compose and lifecycle.runtime.compose
to commons/commonMain as preparation for extracting ViewModels to shared code.

These dependencies have KMP support since 2.8.0 (currently on 2.10.0).

Preparation for Phase A2: Extract Thread ViewModels to commons.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 07:01:36 +02:00
Vitor Pamplona ce01e8f70c moves keystore to commons to avoid unnecessary dependencies on Quartz 2026-01-07 11:09:45 -05:00
nrobi144 a9141763e1 previous PR fixes 2026-01-07 07:12:00 +02:00
nrobi144 7d3ac9ec8a add avatars 2026-01-05 13:43:00 +02:00
Vitor Pamplona c5133e98f8 - Fixes Preview Tag
- Fixes Email Pattern
2025-12-29 18:57:56 -05:00
nrobi144 201acc8140 feat: Extract shared UI components to commons for Android/Desktop reuse
Extract reusable components from desktopApp to commons module:

Utilities (commonMain):
- NumberFormatters: countToHumanReadable, countToHumanReadableBytes
- PubKeyFormatter: toShortDisplay, toDisplayHexKey for key formatting

Utilities (jvmAndroid):
- TimeAgoFormatter: Human-readable time formatting for Nostr timestamps
- ZapFormatter: BigDecimal amount formatting with G/M/k suffixes

UI Components (commonMain):
- AppScreen enum for shared navigation
- LoadingState, EmptyState, ErrorState with refresh/retry support
- FeedHeader with relay status indicator
- NoteCard for displaying Nostr notes
- ActionButtons (AddButton, RemoveButton)
- RobohashImage for avatar display
- PlaceholderScreens (Search, Messages, Notifications)
- RelayStatusColors and shared color definitions

UI Components (jvmAndroid):
- LoginCard with key input field
- NewKeyWarningCard for new key backup warnings
- KeyInputField and SelectableKeyText
- ProfileInfoCard for account display
- RelayStatusCard for relay management

Core (jvmAndroid):
- AccountManager with login/logout/key generation
- RelayConnectionManager base class
- RelayStatus and DefaultRelays

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 15:12:04 +02:00
nrobi144 131252f19d feat: Convert commons module to Kotlin Multiplatform
- Rewrite build.gradle.kts for KMP with Android + JVM targets
- Restructure source sets: commonMain, jvmAndroid, androidMain, jvmMain
- Replace android.util.LruCache with androidx.collection.LruCache (KMP-ready)
- Replace android.util.Patterns with local regex constants
- Move shared code to commonMain (icons, hashtags, robohash, compose, etc.)
- Move JVM-shared code to jvmAndroid (richtext, base64Image detection)
- Keep Android-specific code in androidMain (blurhash, bitmap handling)
- Remove @Preview annotations from shared code (Android-only feature)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 07:52:45 +02:00