Commit Graph

11037 Commits

Author SHA1 Message Date
Vitor Pamplona 598a630e13 Merge branch 'main' into claude/ai-post-writing-help-CDPPw 2026-04-08 13:23:55 -04:00
Claude 9f40d14a23 fix: show all AI tone chips at once instead of progressively
Use awaitAll() to collect all tone results before updating the map,
so all chips appear simultaneously when computation finishes.

https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
2026-04-08 16:39:44 +00:00
Claude 6f6e876158 feat: precompute all AI tone results in parallel
Instead of computing on-tap, all 9 tones are precomputed in parallel
after a 1-second debounce when the user stops typing. Tone chips only
appear once results are ready. Tapping a chip instantly shows the
precomputed result. Text changes cancel and restart the computation.

https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
2026-04-08 16:33:06 +00:00
Vitor Pamplona 792b311f87 Merge pull request #2180 from vitorpamplona/claude/move-relay-to-settings-aMMmi
Move Event Sync and Import Follows to Settings screen
2026-04-08 12:30:25 -04:00
Claude 60d817b0c0 feat: move AI writing help to Application Preferences
Replace the sparkle toggle button in the compose toolbar with a
persistent setting in UI/Application Preferences. The new "Propose AI
text improvements" setting (Always/Never) controls whether the tone
chip row appears in the compose screen.

When enabled and the device supports on-device AI, the tone chips show
automatically below the text field. Removes the per-session toggle
button from BottomRowActions.

https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
2026-04-08 16:24:48 +00:00
Claude 8696a5b7c1 feat: move Relay Sync and Import Follows from drawer to Settings screen
These options are better suited as settings entries rather than top-level
drawer items. They are now in the Account Settings section, placed after
Relay Setup for logical grouping.

https://claude.ai/code/session_017bgasTSsMRR9aepmAsKEzR
2026-04-08 16:13:33 +00:00
Vitor Pamplona 782691ccea Merge pull request #2179 from vitorpamplona/claude/optimize-notecompose-layout-xcnm9
Change relay feed icon from door to storage
2026-04-08 12:00:45 -04:00
Vitor Pamplona 1c1344bd4c Change relay feed icon from door to storage 2026-04-08 11:59:57 -04:00
Vitor Pamplona 36a5576376 Merge pull request #2138 from vitorpamplona/claude/optimize-notecompose-layout-xcnm9
Refactor note layout to use custom zero-overhead NoteComposeLayout
2026-04-08 11:53:00 -04:00
Claude e674141054 feat: add mock writing assistant for UI testing
Temporary MockWritingAssistant that returns fake transformed text after
a short delay. Enabled via useMockAi flag in ShortNotePostViewModel.
Allows testing the AI writing help UI on devices without Gemini Nano.

TODO: Set useMockAi = false before shipping.

https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
2026-04-08 15:52:18 +00:00
Vitor Pamplona c091d660aa Merge branch 'main' into claude/optimize-notecompose-layout-xcnm9 2026-04-08 10:57:34 -04:00
Claude 5ed66fd699 feat: auto-detect language for AI writing help
Use ML Kit language identification to detect the post's language before
transforming. Maps detected BCP-47 tags to the 7 supported languages
(English, Japanese, Korean, German, French, Italian, Spanish). Falls
back to English for unsupported languages. Rewriter and proofreader
clients are cached per language+outputType combination.

https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
2026-04-08 14:42:03 +00:00
Vitor Pamplona 6a84edd1c2 Merge pull request #2172 from mstrofnone/fix/account-switcher-empty-list
fix: account switcher bottom sheet not showing logged-in accounts
2026-04-08 10:32:35 -04:00
Vitor Pamplona 8a90bec716 Merge pull request #2175 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-08 10:32:15 -04:00
Vitor Pamplona 2213e8a32a Merge pull request #2178 from vitorpamplona/claude/complete-marmot-ios-dM5C0
Implement Ed25519 and X25519 cryptography for native platforms
2026-04-08 10:32:06 -04:00
Claude 91eef089dc fix: handle external join in processCommit and relax KeyPackage ciphersuite check
processCommit now detects ExternalInit proposals and allows the sender
leaf index to equal tree.leafCount (the new joiner's slot), matching
RFC 9420 Section 12.4.3.2 external commit semantics.

MlsKeyPackage.decodeTls no longer rejects non-0x0001 ciphersuites so
that interop test vectors with other suites round-trip correctly.

https://claude.ai/code/session_01LpR6qsF6nep1RnXA9KbXGs
2026-04-08 14:14:24 +00:00
Claude b5ee266169 feat: implement Ed25519 and X25519 crypto for Apple/iOS and Linux native platforms
Replace TODO stubs with pure Kotlin implementations of Ed25519 (RFC 8032) and
X25519 (RFC 7748) for native targets, completing Marmot MLS support on iOS.
Shared field arithmetic over GF(2^255-19) is extracted to nativeMain. Also
fixes MarmotSubscriptionManagerTest assertions to account for the ownKeyPackageFilter
added to buildFilters().

https://claude.ai/code/session_01LpR6qsF6nep1RnXA9KbXGs
2026-04-08 13:19:38 +00:00
Vitor Pamplona 3322a953c2 Merge pull request #2177 from vitorpamplona/claude/fix-commit-ordering-test-fiEXf
Add groupId parameter to commit ordering tracker methods
2026-04-08 08:54:59 -04:00
Claude ec78be6328 fix: update CommitOrderingTest and MarmotPipelineTest for new EpochCommitTracker API
EpochCommitTracker was refactored to use (groupId, epoch) keys instead
of epoch-only keys, but the tests weren't updated to match.

https://claude.ai/code/session_01WixuPGynMsDXj1tWkJQNsh
2026-04-08 12:54:21 +00:00
Vitor Pamplona 27fcd43144 Merge pull request #2173 from vitorpamplona/claude/marmot-group-management-Jpiux
Add member removal and group info editing for Marmot groups
2026-04-08 08:54:16 -04:00
Claude 7ae536e80d feat: add AI writing help to new post screen
Add on-device AI writing assistance using ML Kit GenAI APIs (Rewriting,
Proofreading) powered by Gemini Nano via Android AICore. Users can tap
the sparkle button in the compose toolbar to access tone chips (Correct,
Rephrase, Shorter, Elaborate, Friendly, Professional, More Direct,
Punchy, + Emoji) that transform their post text. Results appear in a
card with Use/Dismiss actions.

- Play flavor: MLKitWritingAssistant using on-device Gemini Nano
- F-Droid flavor: NoOpWritingAssistant stub (returns unavailable)
- WritingAssistant interface for future DVM/NIP90 integration
- AI button hidden on unsupported devices

Also fixes pre-existing exhaustive when expression in
DecryptAndIndexProcessor for GroupEventResult.Duplicate.

https://claude.ai/code/session_01RbCYGrbbapRMike8WQy41F
2026-04-08 12:47:23 +00:00
Vitor Pamplona 086178471b Merge pull request #2168 from vitorpamplona/claude/review-call-screens-6zzLt
Add thread-safe state management to CallManager
2026-04-08 08:43:11 -04:00
Crowdin Bot 6ce3d377e3 New Crowdin translations by GitHub Action 2026-04-08 12:42:44 +00:00
Vitor Pamplona a1a4ed3586 Merge branch 'main' into claude/marmot-group-management-Jpiux 2026-04-08 08:42:40 -04:00
Vitor Pamplona 9e6fc7a9f7 Merge pull request #2174 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-08 08:41:17 -04:00
Vitor Pamplona d4959473d7 Merge pull request #2170 from vitorpamplona/claude/improve-secp256k1-performance-nFGT8
Optimize secp256k1 with unrolled arithmetic and caching
2026-04-08 08:41:06 -04:00
Crowdin Bot 1e0669eed7 New Crowdin translations by GitHub Action 2026-04-08 12:34:30 +00:00
Vitor Pamplona a010f01fbf Avoids warnings 2026-04-08 08:31:54 -04:00
M db2df9c365 fix: account switcher bottom sheet not showing logged-in accounts
DisplayAllAccounts collects from accountsFlow() which is a
MutableStateFlow initialized to null. The accounts are lazily loaded
from encrypted storage only when allSavedAccounts() is called, but
nothing in the account switcher triggers that load.

Add a LaunchedEffect that calls allSavedAccounts() when the flow
is still null, ensuring the account list is populated when the
bottom sheet opens.
2026-04-08 21:35:43 +10:00
David Kaspar 991a187dde Merge pull request #2171 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-08 12:08:48 +02:00
Crowdin Bot 17a4b54cb7 New Crowdin translations by GitHub Action 2026-04-08 09:15:42 +00:00
davotoula ade3ed5690 Added missing is GroupEventResult.Duplicate branch at DecryptAndIndexProcessor.kt:499 — it logs and ignores duplicate events 2026-04-08 11:12:02 +02:00
davotoula 5a72385a37 update translations: CZ, DE, PT, SE 2026-04-08 11:01:27 +02:00
Claude 491c31d625 perf: increase cache sizes from 256 to 1024 for ~1000 followed pubkeys
With 256 slots and ~1000 followed users, the direct-mapped caches had
~75% collision rate (multiple pubkeys mapping to the same slot, causing
constant eviction and recomputation). Increasing to 1024 slots covers
most follow lists with minimal collisions.

Memory impact:
  pubkeyCache: 1024 × ~96 bytes = ~96KB (was ~24KB)
  pTableCache: 1024 × ~1KB = ~1MB (was ~256KB)
Total ~1.1MB — acceptable for mobile.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-08 03:06:46 +00:00
Claude df9c8a9f4b feat: add same-pubkey batch Schnorr verification (5-6x throughput)
Add verifySchnorrBatch(pub, signatures, messages) that verifies n
signatures from the same public key using scalar and point summation
instead of n individual mulDoubleG calls.

The batch equation exploits linearity of Schnorr signatures:
  (Σ sᵢ)·G = (Σ Rᵢ) + (Σ eᵢ)·P
This combines n verifications into:
  - n scalar additions for S = Σsᵢ and E = Σeᵢ (trivial)
  - n liftX + (n-1) addMixed for R_sum = ΣRᵢ (cheap point additions)
  - ONE mulDoubleG(S, P, -E) (the expensive EC operation, done once)
  - Check result - R_sum = O (no toAffine needed)

JVM benchmark results (same pubkey, 1000 iterations, 500 warmup):
  batch( 4):  4.1x faster than individual  (40,121 events/s)
  batch( 8):  5.3x faster                  (49,637 events/s)
  batch(16):  5.5x faster                  (49,815 events/s)
  batch(32):  5.9x faster                  (51,272 events/s)

This fits the Nostr pattern perfectly: when loading a profile or
connecting to a relay, many events from the same followed author
arrive together and can be batch-verified.

If the batch fails (returns false), the caller falls back to
individual verification to identify which signature(s) are invalid.

Security: by linearity, individual errors cannot cancel without
solving the discrete log. For extra hardening, duplicate events
from multiple relays can be verified individually as cross-checks.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-08 02:56:45 +00:00
Claude cf7e2d4b0d perf: specialize FieldP.sub and neg for secp256k1 prime structure
Inline the P = [P0, -1, -1, -1] constant into sub and neg instead
of going through generic U256.addTo/subTo + P array reads.

For sub (add-back P on borrow): when carry from limb 0 is 1, adding
P[1..3]=-1 with carry is identity (no-op). When carry is 0, just
subtract 1 with borrow propagation through limbs 1-3. Replaces a
full 4-limb generic addition with a conditional decrement.

For neg (P - a): uses bitwise NOT for limbs 1-3 (since P[i]=-1,
P[i]-a[i] = ~a[i]) with simple borrow propagation from limb 0.
Replaces a full 4-limb generic subtraction with P array reads.

These are called ~500 times per verify. On JVM/HotSpot the gain is
marginal (inlining already handles this), but on Android ART the
reduced function call depth and eliminated array loads should help.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-08 01:35:54 +00:00
Claude f912520ee2 perf: reuse pre-allocated scratch for all entry-point allocations
Pool toAffine/toAffineX temp LongArrays (zInv, zInv2, zInv3) into
PointScratch. Add scratch-aware overloads that hot paths now use,
keeping allocating convenience overloads for one-time init paths
(buildCombTable).

Consolidate verify/sign/pubCreate/ECDH scratch into shared entry*
fields in PointScratch (entryPx, entryPy, entryPoint, entryResult,
entryTmp, entryTmp2). This replaces the old verify-specific fields
(verifyPx/Py/R/S/E/Rx/Ry/PPoint/Result) with fewer, shared buffers.

Update all Secp256k1 entry points (pubkeyCreate, signSchnorr,
signSchnorrInternal, verifySchnorr, pubKeyTweakMul, ecdhXOnly)
to use scratch instead of per-call allocations.

Add KeyCodec.liftX overload accepting a temp buffer to avoid 1
LongArray allocation per call.

Eliminate 2 longArrayOf allocations in ScalarN.reduceWideTo by
reusing the output array as temporary storage for hi limbs.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-08 01:22:05 +00:00
Claude fc690d66c7 refactor: reorganize secp256k1 for clarity without affecting performance
Extract MutablePoint, AffinePoint, and PointScratch from Point.kt into
PointTypes.kt (140 lines). Rename Point.kt to ECPoint.kt (897 lines)
to match the single top-level declaration (ktlint convention).

Remove dead code:
- addWnafJacobian: private function never called (replaced by
  addWnafMixedPP with effective-affine tables)

Remove 5 KeyCodec wrapper functions from ECPoint that just delegated
(liftX, hasEvenY, parsePublicKey, serializeUncompressed,
serializeCompressed). Callers in Secp256k1.kt and PointTest.kt now
use KeyCodec directly, making ownership clear: KeyCodec owns key
encoding/decoding, ECPoint owns point arithmetic.

Update Secp256k1.kt header documentation with current benchmark
numbers (verify 1.7x, sign 0.9x faster than native, etc.) and
a concise list of optimizations.

No functional or performance changes — all 170 tests pass, benchmark
numbers unchanged.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-08 01:10:01 +00:00
Claude 6fe2852011 feat: auto-publish key package on MarmotGroupListScreen entry
Replace the manual KeyPackage publish button with a LaunchedEffect
that auto-publishes if no key package exists yet. The subscription
already correctly downloads our own kind:30443 via
ownKeyPackageFilter() on home relays, and rotation is handled
reactively when Welcome messages are processed.

https://claude.ai/code/session_01LhfCp8DHqNVx6mSiYfpQny
2026-04-08 01:03:18 +00:00
Claude c7b164b845 perf: cache P-side wNAF tables in mulDoubleG for repeated pubkeys
In mulDoubleG (verify), the P-side wNAF-5 affine table (8 odd multiples
of P plus 8 GLV λ counterparts) is rebuilt from scratch on every call.
This costs ~437 field ops: 1 doublePoint + 7 addPoints + 8 β-multiplies
+ 1 batch inversion with full field inversion (~270 ops alone).

Add a 256-entry direct-mapped cache that stores these affine tables
keyed by the point's x-coordinate. On cache hit, the table build is
skipped entirely — the cached arrays are used directly (zero-copy).

This saves ~27% of mulDoubleG cost (~20% of total verify) for repeated
pubkeys. Combined with the liftX cache from the previous commit,
repeated-pubkey verifications now skip ~33% of the work.

JVM benchmark: verify ratio improved from 2.0x to 1.6-1.8x vs native C
(with the benchmark's 100% cache hit rate). Original baseline was 3.0x.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-08 00:55:54 +00:00
Claude bdf7ab1a15 perf: eliminate allocations in verify path and add pubkey cache
Add in-place ScalarN operations (mulTo, addTo, negTo) that write to
caller-provided output arrays instead of allocating. Add
Glv.splitScalarInto that uses pre-allocated scratch buffers from
PointScratch, eliminating ~26 LongArray allocations per call (called
2x per verify = ~52 allocs saved). Both mul and mulDoubleG now use
the allocation-free split path.

Pool all LongArray and MutablePoint allocations in verifySchnorr
into thread-local PointScratch (verifyPx/Py/R/S/E/Rx/Ry, verifyPPoint,
verifyResult). Add U256.fromBytesInto for decoding into pre-allocated
arrays. Total: ~14 object allocations eliminated per verify call.

Add pubkey decompression cache (256-entry direct-mapped) that skips
the liftX square root (~280 field ops) for repeated pubkeys. In Nostr,
the same authors are verified repeatedly, so cache hit rate is high.
This saves ~13% of verify cost per cache hit.

Increase benchmark warmup/iterations for more stable measurements
(e.g., verifySchnorr: 200/500 -> 2000/5000).

JVM benchmark result: verify ratio improved from 3.0x to 2.0x vs
native C (with 100% cache hit rate on the benchmark's single pubkey).

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-08 00:44:41 +00:00
Claude f6538d4c59 feat: add Remove Member and Edit Group Info screens for Marmot groups
Add route-based screens for group management instead of dialogs:
- RemoveMemberScreen: lists removable members with confirmation dialog
- EditGroupInfoScreen: edits group name and description via MLS metadata
- Wire Account/ViewModel methods for removeMember and updateGroupMetadata
- Add Edit and Remove Member actions to MarmotGroupInfoScreen toolbar
- Fix pre-existing exhaustive when branch for GroupEventResult.Duplicate

https://claude.ai/code/session_01LhfCp8DHqNVx6mSiYfpQny
2026-04-08 00:27:38 +00:00
Claude c96d98bdbf chore: trigger CI re-run
https://claude.ai/code/session_01TTPrYjcz1eYEzdSV5N6rHi
2026-04-08 00:15:59 +00:00
Claude 8a6d1f1d44 perf: increase benchmark warmup and iterations for stable results
The previous counts (200 warmup / 500 iterations for verify) were too
low for reliable measurements on noisy VMs. Increase to 2000+ warmup
and 3000-50000 iterations depending on operation cost, giving the JIT
time to fully optimize and reducing variance between runs.

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-07 23:46:59 +00:00
Claude 1d781d1038 refactor: reorganize call code into focused, smaller files
CallController.kt (993 lines) split into:
- CallController.kt (~380 lines) — call orchestration only
- CallMediaManager.kt (~190 lines) — WebRTC factory, EGL, sources,
  tracks, camera lifecycle
- RemoteVideoMonitor.kt (~190 lines) — remote video frame sinks,
  per-peer activity tracking, monitor jobs

CallScreen.kt (1,125 lines) split into:
- CallScreen.kt (~290 lines) — state router + simple screens
- ConnectedCallUI.kt (~320 lines) — active call UI with controls
- CallWidgets.kt (~340 lines) — shared composables (VideoRenderer,
  PeerVideoGrid, GroupCallPictures, GroupCallNames, etc.)
- PipCallUI.kt (~170 lines) — Picture-in-Picture variants

Misplaced files moved:
- ActiveCallHolder → service/call/CallSessionBridge (renamed: it's a
  process-level singleton bridging Activity↔ViewModel, not a "holder")
- CallNotificationReceiver → service/call/ (BroadcastReceiver, not UI)
- showIncomingCallNotification → NotificationUtils (profile picture
  loading doesn't belong in CallController)

https://claude.ai/code/session_01TTPrYjcz1eYEzdSV5N6rHi
2026-04-07 23:35:50 +00:00
Vitor Pamplona 5e93ff0532 Merge pull request #2169 from vitorpamplona/claude/review-marmot-mls-C5whm
Add thread safety and robustness improvements to Marmot MLS implementation
2026-04-07 19:27:23 -04:00
Vitor Pamplona 61887f97b0 Merge pull request #2167 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-07 19:26:43 -04:00
Claude 1dd8edcd41 perf: optimize secp256k1 field arithmetic and point operations for Android
Unroll hot-path loops in U256 (mulWide, sqrWide, addTo, subTo) to
eliminate loop control overhead and array bounds checks that ART JIT
on Android does not optimize as aggressively as HotSpot. These functions
are called ~1,900× per signature verification.

Optimize unsignedMultiplyHighFallback to compute the unsigned 128-bit
product directly from 32-bit sub-products, avoiding the signed
multiplyHigh + correction path. Saves ~8 instructions per call on
Android < API 31 (~30,000 calls per verify).

Unroll FieldP.reduceWide carry propagation and inline the reduceSelf
subtraction (avoiding P array access and U256.subTo call). Unroll
FieldP.half with P[1..3]=-1 inlined as mask.

Replace copyFrom pattern in mul, mulG, and mulDoubleG with ping-pong
point buffers — alternate between two MutablePoints via reference swap
instead of copying 12 Longs after every addition. Eliminates ~170
copyFrom calls per verify and avoids the internal copy buffer in
doublePoint's out===inp path (~130 per verify). Also removes the
MutablePoint() allocation in mulG (3 LongArrays per sign/pubCreate).

https://claude.ai/code/session_017UbWduFi1sLUsgVUMUH2nx
2026-04-07 23:14:11 +00:00
Claude 737e95b123 fix: remove unnecessary @Synchronized from CallController
All three annotated methods (toggleAudioMute, toggleVideo, cleanup)
are only ever called from the main thread, so the blocking lock was
never contended. Remove it to avoid misleading future readers and to
prevent accidental main-thread blocking if a call site changes.

The Mutex in CallManager is kept because it uses suspending withLock
which never blocks the thread, and it correctly serializes the
GlobalScope.launch(Dispatchers.Default) hangup/reject paths in
CallActivity against the viewModelScope state-collector path.

https://claude.ai/code/session_01TTPrYjcz1eYEzdSV5N6rHi
2026-04-07 23:13:11 +00:00
Claude 894042f056 style: spotlessApply formatting fix for MarmotManager
https://claude.ai/code/session_018gVkmmYgMFtBH7G31pCk9N
2026-04-07 23:06:16 +00:00