Commit Graph

11248 Commits

Author SHA1 Message Date
Vitor Pamplona 080be06a2f Fixes messages FAB's position when open 2026-04-14 10:31:13 -04:00
Vitor Pamplona d23f6f8ec1 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-04-14 10:30:51 -04:00
Vitor Pamplona 887e0bf302 Improves stability and permission checks for voice calls when the app is in the background. 2026-04-14 10:19:47 -04:00
Vitor Pamplona d2b5508288 Merge pull request #2384 from davotoula/feat/adaptive-video-playback
Add YouTube-style video quality picker for adaptive videos
2026-04-14 07:54:52 -04:00
davotoula 606d976b57 code review fixes:
Close the listener race window by re-snapshotting tracks inside the
DisposableEffect, hoist the openDialog remember above the early returns
so the hook count stays stable when the player loses its video group,
return ImmutableList from buildQualityChoices for Compose stability,
and remember the TextButton ButtonColors copy.

Consolidate VideoQualitySheet into VideoQualityButton using the existing
PlaybackSpeedPopUpButton Popup pattern. Derives selection state from
tracks instead of duplicating it, drops the dead QualityOption.Auto
case, gates listener-driven UI behind early returns, and reuses the
existing call_settings_video_quality string. Net -168 lines.
2026-04-14 10:45:56 +02:00
davotoula 4fc739ad7e feat: add YouTube-style video quality picker
Adds a manual override for HLS/DASH adaptive streams via a gear icon
that opens a ModalBottomSheet with Auto + per-rendition options. The
button is hidden for single-rendition videos. Overrides reset when
players return to the pool so each video starts in Auto.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-14 10:04:37 +02:00
Vitor Pamplona 500bc8e4e7 Puts the user's own video up top 2026-04-13 19:55:11 -04:00
Vitor Pamplona 0b4ba6a6b8 Fixes crash on old samsungs when calling 2026-04-13 19:42:39 -04:00
Vitor Pamplona 05318d5f7d Using better icons for the ear sound 2026-04-13 19:37:16 -04:00
Vitor Pamplona 075ca6ef41 Merge pull request #2383 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-13 18:28:21 -04:00
Crowdin Bot a8e5ea2986 New Crowdin translations by GitHub Action 2026-04-13 21:39:53 +00:00
Vitor Pamplona fc0e24faba Merge pull request #2382 from davotoula/feat/lightcompressor-v2
Update LightCompressor-enhanced to v2.0.0
2026-04-13 17:38:19 -04:00
Vitor Pamplona c00222fa5b Merge branch 'main' of https://github.com/vitorpamplona/amethyst
# Conflicts:
#	gradle/libs.versions.toml
2026-04-13 17:28:28 -04:00
Vitor Pamplona 117ba4318b Updates AGP and other dependencies 2026-04-13 17:26:36 -04:00
davotoula 77cd51ec86 update LightCompressor-enhanced to v2.0.0
Migrates imports from com.abedelazizshe.lightcompressorlibrary to
com.davotoula.lightcompressor due to package rename in the fork.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-13 23:23:02 +02:00
Vitor Pamplona cd8957ad7d Merge pull request #2381 from nrobi144/feat/tor-support
feat: Desktop Tor support — embedded kmp-tor, full privacy routing, settings UI
2026-04-13 11:52:42 -04:00
Vitor Pamplona b3e997b6c8 Merge pull request #2377 from mstrofnone/fix/desktop-vlc-runtime
fix: VLC video playback shows Install VLC fallback when running via Gradle (#2062)
2026-04-13 08:27:09 -04:00
nrobi144 c9c0cda2c1 feat(tor): full app rebuild on Tor toggle via key() + bootstrap gate + retry
Complete Tor toggle UX:
- key(appRestartKey) wraps App — full Compose tree rebuild on toggle
- TorManager at Window level — survives rebuild, no stop/start collision
- Tor bootstrap gate at top of App — blocks ALL creation (clients, relays,
  Coil, subscriptions) until Tor proxy is ready. Zero clearnet during bootstrap.
- Coil ImageLoader reinitialized after setInstance on each rebuild
- Connection pool evicted on rebuild
- DesktopTorManager retries start up to 3x (handles previous daemon stopping)
- Settings reload from prefs on rebuild (fixes stale toggle display)
- Confirmation dialog on mode change in both TorSettingsSection and Dialog

Known: 2-4 stale Coil CDN connections may persist briefly after toggle
(image loads, not relay traffic — close after OkHttp 5min keep-alive).
Relay WebSocket connections are fully through Tor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 2df621056e feat(tor): restart confirmation dialog + key() app rebuild on Tor toggle
When user changes Tor settings, shows "Restart Required" confirmation
dialog. On confirm: saves prefs, triggers appRestartKey++ which forces
Compose to unmount/remount entire App tree.

Key behaviors:
- Window stays open, user stays logged in (AccountManager outside App)
- Column layout preserved (DeckState outside App)
- DisposableEffect stops old Tor daemon before new one starts
- TorSettingsSection mode change → confirmation dialog
- TorSettingsDialog Save → confirmation dialog
- Relays, subscriptions, caches all recreated fresh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 01fc5f3ac6 fix(tor): remove broken runtime reconnect, add evictConnections
Remove the LaunchedEffect/scope.launch reconnect code that didn't
work (relay subscriptions lost after disconnect+connect cycle).
Tor toggle will use app rebuild via key() instead (next commit).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 70356d541e fix(tor): reconnect relays on Tor OFF too, not just ON
Relay reconnect was only triggered when Tor transitioned to Active.
When toggling OFF, relays stayed on the dead SOCKS proxy and never
reconnected over clearnet — feed went blank.

Now triggers reconnect on both Active and Off transitions, so relays
switch between proxy and direct connections when toggling Tor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 566a4bf452 fix(tor): wire Coil image loading through Tor via custom Fetcher.Factory
Port Android's OkHttpFactory pattern to desktop — creates a custom
Fetcher.Factory<Uri> that calls DesktopHttpClient.currentClient() per
image request, routing all image loads through the SOCKS proxy when
Tor is active.

Avoids the OkHttpNetworkFetcher.factory() import issue by directly
constructing NetworkFetcher with the Tor-aware Call.Factory, matching
the proven Android implementation.

This closes the last network leak — ALL egress paths now route through
Tor when enabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 11d0a657ac fix(tor): close 8 network traffic leaks — fail-closed when Tor expected
SECURITY: With Tor ON, all HTTP traffic now routes through SOCKS proxy.
Previously only relay WebSocket connections used Tor; 8 other egress
paths created bare OkHttpClient() instances bypassing Tor entirely.

Fail-closed behavior:
- When Tor expected but bootstrapping → dead SOCKS proxy on port 1
  (requests fail instead of leaking IP)
- lateinit var crashes on misconfiguration (loud failure vs silent leak)

Leak sites fixed (all use DesktopHttpClient.currentClient()):
- AnimatedGifImage: GIF fetch
- SaveMediaAction: media downloads
- EncryptedMediaService: NIP-17 DM media
- ServerHealthCheck: Blossom server probes
- NoteActions: zap/lightning LNURL resolution
- DesktopBlossomClient: media uploads
- AccountManager: NIP-46 bunker relay connections
- Coil image loader: TODO (OkHttpNetworkFetcher import issue)

Also:
- Relay reconnect on Tor Active (prevents stale clearnet connections)
- isTorExpected() for fail-closed logic
- Tests updated for new torTypeProvider parameter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 8a23a1cd65 fix(tor): move shield to bottom of SinglePane sidebar to prevent cutoff
Shield was getting pushed off screen when RelayHealthIndicator ("x s ago")
appeared above it. Moved to very last position (after BunkerHeartbeat)
so it's always visible at the bottom edge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 df401c823b fix(tor): add TorStatusIndicator to SinglePaneLayout sidebar
Shield icon was only in DeckSidebar (deck mode) but missing from
SinglePaneLayout's NavigationRail. Added between RelayHealthIndicator
and BunkerHeartbeatIndicator. Clicking navigates to Settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 4aa4d39a91 fix(tor): default to Internal + Full Privacy, clickable shield, sticky dialog buttons
UX improvements based on manual testing feedback:

- Default TorType changed from OFF to INTERNAL (Tor on by default)
- Default preset changed to Full Privacy (all routing via Tor)
- Shield icon always visible in sidebar (not hidden when Off)
- Shield icon clickable — opens Settings
- TorSettingsDialog Cancel/Save buttons sticky at bottom (don't scroll away)
- HorizontalDivider above buttons for visual separation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 95ec190b0d feat(tor): wire per-relay routing, .onion badge, shutdown hook, CompositionLocal
Phase 6: Polish and wiring.

Per-relay routing:
- TorRelayEvaluation wired into DesktopHttpClient with actual settings
- shouldUseTorForRelay uses real evaluation instead of { false }
- Settings changes propagate to torTypeFlow/externalPortFlow reactively

CompositionLocal for Tor state:
- LocalTorState provides TorState (status, settings, onSettingsChanged)
- Avoids threading Tor params through every composable layer
- DeckColumnContainer reads from LocalTorState for RelaySettingsScreen

.onion relay badge:
- RelayStatusCard shows "Requires Tor" (red) when Tor is off
- Shows "via Tor" (green) when Tor is active
- Only on .onion relay URLs

Shutdown hook:
- activeTorManager.stopSync() in existing shutdown hook
- Ensures no orphaned Tor processes on app exit
- Set via volatile var from App composable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 e6068c0916 feat(tor): desktop Tor settings UI — indicator, toggle, dialog
Phase 5: Desktop settings UI for Tor.

TorStatusIndicator:
- Shield icon in sidebar footer (gray/yellow/green/red)
- Tooltip shows status (no port number for security)
- Only visible when Tor is not Off

TorSettingsSection:
- Inline in settings screen between Media Server and Dev Settings
- Mode selector (Off/Internal/External) with segmented buttons
- External SOCKS port input with validation (1-65535)
- "Advanced..." button opens full dialog

TorSettingsDialog:
- Full DialogWindow with preset selector (radio buttons)
- 4 relay routing toggles + 7 content routing toggles
- Preset auto-detection (whichPreset)
- Save/Cancel with DesktopTorPreferences persistence

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 8a149af34b feat(tor): proxy-aware DesktopHttpClient + relay connection wiring
Phase 3: Network plumbing for Tor-routed relay connections.

DesktopHttpClient refactored from static singleton to Tor-aware class:
- Dual client: direct (30s timeouts) + SOCKS proxy (60s, 2x multiplier)
- Per-relay routing: localhost=direct, .onion=proxy, others via evaluator
- Shared ConnectionPool across client rebuilds
- Companion getSimpleHttpClient() for backward compat (NIP-46 bunker)

DesktopRelayConnectionManager now takes DesktopHttpClient instance.
Main.kt wires TorManager → DesktopHttpClient → RelayManager.

Tests: 10 new DesktopHttpClientTest cases covering Tor on/off,
.onion routing, localhost bypass, timeout multipliers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 7cad30b899 feat(tor): add kmp-tor desktop daemon + preferences
Phase 2: Desktop Tor runtime implementation.

DesktopTorManager:
- Implements ITorManager using kmp-tor (Apache 2.0)
- Reactive: auto-starts/stops Tor based on TorType flow
- Supports INTERNAL (embedded kmp-tor) and EXTERNAL (user SOCKS port)
- NEWNYM/Dormant/Active signal support via TorCmd
- OS-specific data dirs (~/Library/Application Support, ~/.local/share, %APPDATA%)
- Directory permissions set to 700
- stopSync() for shutdown hook integration

DesktopTorPreferences:
- Implements ITorSettingsPersistence using java.util.prefs.Preferences
- Underscore-separated keys matching project convention
- Default TorType.OFF (user must opt-in)
- No explicit flush() (matches existing DesktopPreferences pattern)

Gradle:
- kmp-tor runtime 2.6.0 + resource-exec-tor 409.5.0 in version catalog
- java.management module added to nativeDistributions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:43 +03:00
nrobi144 c564d4532b test: add commons/commonTest for shared Tor logic
Mirror TorSettingsTest and TorRelayEvaluationTest in commons/commonTest
using kotlin.test for KMP compatibility. Tests verify the shared types
directly without going through Android typealiases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:24:31 +03:00
nrobi144 77f9905b69 feat(tor): add TorServiceStatus, ITorManager, ITorSettingsPersistence to commons
New shared types in commons/commonMain/tor/:
- TorServiceStatus: clean sealed class with Off, Connecting, Active(port),
  Error(message). Uses data object for stateless variants. No TorControlConnection
  dependency (Android keeps its own version for now).
- ITorManager: reactive interface with status flow + dormant/active/newIdentity
- ITorSettingsPersistence: load/save interface for platform persistence

Android TorServiceStatus unchanged — will migrate to commons version when
RelayProxyClientConnector is refactored to use ITorManager signals.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:24:31 +03:00
nrobi144 84c3189cea refactor(tor): extract TorRelaySettings, TorRelayEvaluation to commons/commonMain
Move relay routing logic to shared commons module:
- TorRelaySettings data class
- TorRelayEvaluation with useTor() routing logic

Android files replaced with typealiases for backward compatibility.
All tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:24:31 +03:00
nrobi144 1d9c1eb9b6 refactor(tor): extract TorSettings, TorType, TorPresetType to commons/commonMain
Move core Tor data models and preset logic to shared commons module:
- TorSettings data class, TorType enum, TorPresetType enum
- parseTorType, parseTorPresetType, isPreset, whichPreset functions
- All preset constants (torOnlyWhenNeededPreset, etc.)

R.string resource IDs removed from enums in commons. Android retains
resourceId/explainerId as extension properties in ui.tor package.

15 Android files updated to import from commons.tor. All 69 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:24:31 +03:00
nrobi144 ff9093f5cc test: add pre-extraction test coverage for Tor logic
69 tests covering TorSettings/presets, TorRelayEvaluation routing,
and OkHttpClientFactory proxy/timeout behavior. Documents current
behavior before extracting to commons/ for desktop Tor support.

Coverage:
- TorSettings: parsing, preset matching, whichPreset, isPreset
- TorRelayEvaluation: .onion/localhost/DM/trusted/new routing,
  priority order, empty relay lists, Tor OFF/INTERNAL/EXTERNAL
- OkHttpClientFactory: SOCKS proxy creation, timeout multipliers,
  null port fallback (9050 security issue documented)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:24:31 +03:00
M 4341137e4a fix: VLC discovery for desktop Gradle run task 2026-04-13 06:17:07 +10:00
David Kaspar 6a75fd9240 Merge pull request #2375 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-12 20:52:06 +02:00
Crowdin Bot c1939fd68c New Crowdin translations by GitHub Action 2026-04-12 18:51:24 +00:00
davotoula 9f8745864f update cz,de,pt,se 2026-04-12 20:47:51 +02:00
Vitor Pamplona 9de0c363ca Merge pull request #2270 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-04-12 14:38:52 -04:00
Crowdin Bot ecabb9cae7 New Crowdin translations by GitHub Action 2026-04-12 18:17:55 +00:00
Vitor Pamplona 7d5a2b2016 Merge pull request #2269 from vitorpamplona/claude/fix-relay-connectivity-pJ29t
Improve Tor bootstrap resilience and WebSocket connection handling
2026-04-12 14:16:34 -04:00
Vitor Pamplona 7c83714da2 Merge pull request #2374 from vitorpamplona/claude/secp256k1-c-implementation-KwkWV
Add high-performance C secp256k1 implementation with JNI bindings
2026-04-12 14:15:26 -04:00
Claude 3ae1fb36d2 perf: inline fe_mul into point operations — 10% faster verify
Move fe_mul/fe_sqr to static inline in field.h when ASM is available
(FE_MUL_ASM=1). This allows the compiler to inline the entire field
multiply directly into gej_double and gej_add_ge, eliminating function
call boundaries.

Before: gej_double had 9 function calls (to fe_mul/fe_sqr)
After:  gej_double has 2 function calls (fe_half only)

The compiler can now:
- Keep intermediate results in registers across multiply boundaries
- Schedule MULX instructions across adjacent field operations
- Eliminate push/pop register saves at call boundaries

gej_double: 738 → 1311 instructions (larger but no call overhead)

Impact:
  verifyFast: 35.1µs → 31.6µs (10% faster, 1.19x vs ACINQ)
  verify:     39.7µs → 38.4µs (0.98x vs ACINQ — essentially tied!)
  sign:       15.2µs → 14.2µs (1.48x vs ACINQ)
  batch(200): 6.2µs → 4.5µs per event (7.9x vs ACINQ)

https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-12 03:02:52 +00:00
Claude 854bf9379a perf: fe_sqr calls fe_mul — eliminates 5ns/sqr gap, 33% faster fe_inv
fe_sqr was 20.6ns (using mul_wide + reduce_wide as separate functions)
while fe_mul was 15.6ns (inlined). Simply making fe_sqr call fe_mul
eliminates the gap.

This has a massive impact on fe_inv/fe_sqrt which do 255 squarings:
  fe_inv:  6107ns → 4085ns (33% faster!)
  fe_sqr:  20.6ns → 15.8ns (23% faster)
  fe_mul:  15.6ns → 14.9ns (stable)

Impact on operations:
  sign (cached):     15.2µs → 13.6µs (1.30x faster than ACINQ)
  pubkeyCreate:      15.3µs → 14.1µs (1.24x faster)
  verifyFast:        35.1µs → 32.2µs (1.01x vs ACINQ — tied!)
  verify (BIP-340):  39.7µs → 36.5µs (0.89x vs ACINQ)
  batch(200)/event:   6.2µs →  4.5µs (8.3x faster than ACINQ!)

https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-12 02:52:51 +00:00
Vitor Pamplona bc8c5e2108 Merge pull request #2239 from davotoula/update-video-compressor-library-and-migrage-gif2mp4
Use GifToMp4Converter from LightCompressor-enhanced 1.9.0
2026-04-11 22:48:53 -04:00
Claude 070affb4e4 fix: restore accidentally deleted test files
https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-12 01:40:40 +00:00
Claude 4dea4b5db5 perf: lazy fe_mul — remove normalize from mul/sqr output (both C and Kotlin)
Remove fe_normalize/reduceSelf from the end of field multiply and
square. After reduceWide, the output is in [0, 2^256) which may
include values in [P, P+C) where C = 2^32+977. This is the same
"unreduced" range that lazy fe_add produces, and is safe because:

- mul/sqr: mulWide handles any 256-bit input via reduceWide ✓
- add: carry fold handles overflow past 2^256 ✓
- sub: P-add-back on underflow produces correct field element ✓
- neg/half: already normalize input via reduceSelf ✓
- isZero/cmp/toBytes: caller normalizes before use ✓

Native C-to-C results (x86_64, vs ACINQ):
  verifyFast: 0.95x → 0.99x (essentially tied with ACINQ!)
  sign (cached): 1.18x → 1.24x faster
  ECDH: 1.05x → 1.06x faster
  batch(200)/event: 7.2µs → 6.4µs

Kotlin JVM results (vs previous lazy-add-only):
  Kotlin numbers stable — reduceSelf in reduceWide was already
  cheap on JVM since the branch is almost never taken.

https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-12 01:40:15 +00:00
Claude 8291987145 docs: document why lazy fe_sub is NOT safe with 4x64 limbs
Analyzed lazy fe_sub: on underflow, the unsigned-wrapped value
(a - b + 2^256) differs from (a - b + P) by C = 2^32 + 977.
When multiplied: (a-b+2^256)*x ≠ (a-b)*x mod p (off by x*C mod p).
The P-add-back on underflow is mandatory for correctness.

This is a fundamental difference from 5x52 lazy reduction where
magnitude tracking keeps values representable. With 4x64 fully-packed
limbs, sub MUST add P back, but add CAN skip reduceSelf since values
in [P, 2^256) differ from [0, C) which is handled by mulWide+reduceWide.

Also evaluated:
- WINDOW_G 12→14: only 1.2µs savings for 4x memory (128→512KB).
  Not worth it on phones where L1 cache is 128-256KB.
- fe_sqrt optimization: only 831ns overhead over theoretical minimum
  of 5.85µs. The addition chain is already near-optimal.

https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-12 01:32:21 +00:00
Claude ae77935625 perf: lazy field addition in Kotlin — 10-16% faster across all operations
Remove reduceSelf from FieldP.add, making it lazy (same approach as
the C implementation). Values may be in [0, 2^256) between additions.

Safety analysis:
- mul/sqr: reduceWide handles any 256-bit input ✓
- neg: added reduceSelf before P - a (prevents underflow) ✓
- half: added reduceSelf before conditional add P ✓
- sub: already adds P back on borrow (self-normalizing) ✓
- isZero/cmp: called on sub outputs (normalized) or after mul ✓
- verifySchnorrCore: added reduceSelf before Jacobian x-check ✓
- toBytes: only called on toAffine outputs (from mul, normalized) ✓

JVM benchmark results (ops/sec, HotSpot C2):
  pubkeyCreate:  32,096 → 37,211 (+15.9%)
  signXOnly:     31,149 → 34,507 (+10.8%)
  sign:          16,137 → 17,822 (+10.4%)
  verify:        12,733 → 14,027 (+10.2%)
  verifyFast:    14,734 → 15,449 (+4.9%)
  ECDH:          10,975 → 12,102 (+10.3%)
  batch(200):    91,611 → 102,354 (+11.7%)

The improvement is larger than expected (10-16% vs estimated 6%)
because HotSpot's branch prediction overhead for reduceSelf
(virtual dispatch + 4 Long comparisons) is higher than the
~2.5ns estimated for native code.

https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
2026-04-11 23:59:44 +00:00