Commit Graph

13715 Commits

Author SHA1 Message Date
Claude ca9f7233f2 test: verify real-world 5.5KB nowhr.xyz/s store link round-trips through the parser 2026-05-16 21:16:45 +00:00
Claude a1e37109c3 test: cover ~5KB nowhere-link fragments end-to-end through the parser 2026-05-16 21:01:53 +00:00
Claude 318e4250de feat: render nowhere links inline as branded cards
Nowhere (https://github.com/5t34k/nowhere) encodes whole mini-sites in
a URL fragment, so the OpenGraph preview path returns nothing useful and
hostednowhere.com 403s scrapers. Detect URLs on hostednowhere.com and
nowhr.xyz that carry a fragment as a NowhereLinkSegment and render them
through a NowhereLinkCard that labels the tool type (Event, Store,
Message, ...) and opens the URL in the browser on click.
2026-05-16 20:35:37 +00:00
Vitor Pamplona 3f12bdc2d7 Merge pull request #2943 from vitorpamplona/claude/fix-show-more-gradient-WruXm
Fix gradient rendering with transparent background colors
2026-05-16 15:59:04 -04:00
Claude e6a5ba9e15 fix: use theme background when ShowMore gradient color is transparent
Several callers pass Color.Transparent as the rich-text backgroundColor.
getGradient was copying that color and forcing alpha=1, which produced
opaque black (Color(0,0,0,1)) as the gradient end — so on a light theme
the fade behind the "Show more" button looked pitch black.

Fall back to MaterialTheme.colorScheme.background when the supplied
background has alpha 0, so the gradient fades from transparent into the
actual surface color.
2026-05-16 19:46:39 +00:00
Vitor Pamplona f1d02f3c60 Merge pull request #2942 from vitorpamplona/claude/fix-call-button-permission-63OLL
Handle denied call permissions with settings dialog
2026-05-16 15:18:18 -04:00
Vitor Pamplona 05467582b4 Merge pull request #2941 from vitorpamplona/claude/fix-notecompose-read-status-rslby
Sync inner note highlight fade with parent background color
2026-05-16 15:14:31 -04:00
Vitor Pamplona f901db699d Merge pull request #2940 from vitorpamplona/claude/fix-ci-build-desktop-2qHSV
Replace linuxdeploy with appimagetool for AppImage packaging
2026-05-16 15:08:44 -04:00
Claude 19309c4587 fix: also fade reply previews together with the parent NoteCompose
Restores ReplyNoteComposition's parentBackgroundColor parameter (reverting
part of the previous commit). With calculateBackgroundColor now returning
the parent State directly when routeForLastRead is null, every inner
NoteCompose call site (replies, reposts, reactions, reports, approvals,
attestations, multi-set / message-set notification cards, and rich-text
nostr:event/nostr:note quotes) shares the level-0 bgColor State and fades
in lockstep with the outer note.
2026-05-16 19:07:26 +00:00
Claude 7ed93bcbc2 fix(desktop): package AppImage with appimagetool, not linuxdeploy
linuxdeploy auto-walks every binary in the staged AppDir with ldd to
bundle their shared-library deps. That fights jpackage's self-contained
output on two axes and aborts the createReleaseAppImage task before any
AppImage is produced:

  - The bundled JRE puts libjvm.so at usr/lib/runtime/lib/server/, while
    its sibling libs (libmanagement.so, libawt_xawt.so, libfontmanager.so
    and others in usr/lib/runtime/lib/) have RPATH=$ORIGIN. ldd cannot
    resolve libjvm.so from those libs without help, so linuxdeploy errors:
        ERROR: Could not find dependency: libjvm.so
        ERROR: Failed to deploy dependencies for existing files

  - The bundled libvlc plugins under usr/lib/app/resources/vlc/ are
    UPX-compressed by the vlc-setup plugin. patchelf cannot read their
    section headers, so linuxdeploy aborts:
        ERROR: Call to patchelf failed:
        patchelf: no section headers. The input file is probably a
        statically linked, self-decompressing binary

  - Even after working around libjvm.so via LD_LIBRARY_PATH, several VLC
    libs have RUNPATH pointing at the wrong directory, so ldd fails on
    libva.so.2 / libvlccore.so.9 next.

This is why v1.09.2 shipped .deb + .rpm from the linux leg but no
.AppImage and no linux .tar.gz from the linux-portable leg — the gradle
step failed on linuxdeploy, so the subsequent "Build portable archives"
step never ran.

Swap to appimagetool, which only embeds the AppDir as-is into a
SquashFS-backed, runtime-prepended AppImage and never touches the
contents. jpackage already bundles a complete, self-contained app —
we don't need linuxdeploy's dep-discovery. AppRun continues to handle
LD_LIBRARY_PATH at launch.

Verified locally on ubuntu-24.04: a fresh
:desktopApp:createReleaseAppImage now produces a valid 254 MB
Amethyst-1.09.2-x86_64.AppImage in ~30 s on top of an up-to-date
createReleaseDistributable.

Side changes:
- Workflow installs desktop-file-utils (appimagetool 1.9.0 calls
  desktop-file-validate on the .desktop entry).
- BUILDING.md updated with the new local-dev fetch command.
- .gitignore replaces the linuxdeploy paths with appimagetool's.
2026-05-16 19:04:07 +00:00
Claude e885192f51 fix: surface call-permission denial instead of silent failure
When the user denied RECORD_AUDIO or CAMERA for a DM call, rememberCallWithPermission's
launcher callback only re-checked permissions and called onCall() on success — the denial
branch was empty. On the next button press Android skips the system dialog (permanently
denied) and immediately returns deny, so the call button appeared dead with no UI feedback.

Now the denial path opens an AlertDialog with an "Open settings" deep-link, matching the
NestActionBar pattern. Also splits the optional BLUETOOTH_CONNECT request onto its own
launcher so its result callback no longer triggers a second onCall().
2026-05-16 18:49:26 +00:00
Claude 523b0616cf fix: quoted NoteCompose stuck on the "new item" purple background
calculateBackgroundColor captured parentBackgroundColor?.value once inside
remember(createdAt), so an inner NoteCompose (repost or reply preview) that
first composed while the outer was still highlighted would snapshot the
purple color and never observe the outer's fade back to the default
background.

- Repost inner notes now share the parent's bgColor State directly when
  they have no own read-tracking, so they fade in lockstep with the outer.
- Reply previews no longer receive parentBackgroundColor at all; they rely
  on replyModifier for their own visual style and never inherit the
  purple "new item" highlight from the surrounding post.
2026-05-16 18:45:26 +00:00
Vitor Pamplona 63ddb5159f Merge pull request #2939 from vitorpamplona/claude/remove-mac-13-cio-1mGRB
ci: remove macos-13 x64 build legs
2026-05-16 14:41:01 -04:00
Claude 25a49d1050 ci: remove macos-13 x64 build legs
GitHub's macos-13 runner image is being retired. Drop the x64 macOS
legs from both the desktop and CLI release matrices; macos-14 arm64
continues to ship the macOS builds.
2026-05-16 17:18:51 +00:00
Vitor Pamplona f108ba9050 - Spotless 8.4.0 with ktlint() (no version pinned) was resolving ktlint 1.8.0.
- ktlint 1.8.0 changed how rule providers are loaded, and spotless 8.4.0 ends up handing the engine an empty ruleProviders set — so every file fails with IllegalArgumentException: A non-empty set of 'ruleProviders' need to be provided.
2026-05-16 12:43:40 -04:00
Vitor Pamplona 7e922692d5 removes ammolite from plans 2026-05-16 12:37:14 -04:00
Vitor Pamplona 8a498695a9 v1.09.2 2026-05-16 12:06:32 -04:00
Vitor Pamplona ac1d4ae16d Merge pull request #2938 from vitorpamplona/claude/investigate-kotlin-compile-speed-tl7Zn
Remove unused ammolite module and optimize build config
2026-05-16 12:03:46 -04:00
Vitor Pamplona 32b9a06612 Ignores duplicated hashtags in different char cases when processing hashtag spam 2026-05-16 11:34:11 -04:00
Claude 66df23fc2c build: right-size Kotlin daemon heap from 12g/4g to 8g/2g
Measured the actual Kotlin daemon peak RSS during a full cold compile
of :amethyst:compilePlayDebugKotlin (which transitively compiles
:quartz, :commons, :quic, :nestsClient as well, in parallel after the
earlier parallel-mode change). Peak RSS was ~6.3 GB at the previous
12g/4g setting, meaning heap usage peaked around 4-5 GB plus
metaspace and native memory.

Drops to -Xmx8g -XX:MaxMetaspaceSize=2g, which:

- Cuts committed-virtual-memory ask from 18 GB (Gradle 6g + Kotlin 12g)
  to 14 GB, comfortably under typical CI runner RAM and well under the
  15 GB available in this dev environment.
- Frees ~4 GB of headroom for the OS file cache, which speeds up
  classpath snapshot I/O on incremental builds.
- Leaves clear headroom over peak usage so GC pressure stays low; we
  re-measured cold compile after the change at 3m30s vs 3m34s before,
  well within run-to-run noise.
- Peak Kotlin daemon RSS at the new setting: 4.96 GB, confirming the
  daemon right-sizes itself rather than pinning the ceiling.

If a much larger codebase target lands later (e.g. iOS framework
compilation in this same daemon), bump back up — the value is not
sacred, it just shouldn't request more than the host can afford.
2026-05-16 14:54:44 +00:00
Claude 2e8bf0d45d build: remove unused :ammolite module
The :ammolite module contained no production Kotlin/Java sources (just
a manifest, build.gradle, and proguard stubs) and no module in the
codebase imports com.vitorpamplona.ammolite.*.

Removes:
- ammolite/ directory (5 files)
- :ammolite project include in settings.gradle
- implementation project(':ammolite') from :amethyst
- androidTestImplementation project(':ammolite') from :benchmark
- :ammolite:testDebugUnitTest from CI workflow and pre-push hook
- -keep class com.vitorpamplona.ammolite.** rules from
  :amethyst, :commons, and :desktopApp proguard files
- Stale references in CONTRIBUTING.md, CLAUDE.md, and the
  gradle-expert skill dependency-graph doc

Small build-graph win: one fewer module to configure, compile, lint,
and spotless-check on every build, and one fewer unit-test target in
both CI and the local pre-push hook.
2026-05-16 14:40:03 +00:00
Vitor Pamplona 3bf3157b17 Merge pull request #2937 from vitorpamplona/claude/binary-persistence-format-gUspB
perf(dns-cache): hand-rolled binary persistence format for SurgeDnsStore
2026-05-16 10:34:17 -04:00
Claude f0ee1221cb refactor(dns-cache): drop data from DnsCacheRecord
Auto-generated equals/hashCode would compare the ByteArray-list of
addresses by reference identity — a footgun no caller needs. No code
uses ==, copy, componentN, or hashing on records, so the class is now
plain with no synthesized methods.
2026-05-16 14:30:09 +00:00
Claude 424d750aea refactor(dns-cache): drop legacy DNS migration paths
Removes the json-blob and SharedPreferences reclaim routines now that
the binary format is the first persisted shape — no users carry the
older blobs.
2026-05-16 14:30:09 +00:00
Claude 435aa3e7df fix(dns-cache): defer legacy-blob reclaim to load() and clean partial tmp writes
The legacy `.json` reclaim was running in the constructor, which fires
on Application#onCreate (main thread) — a strict-mode regression vs the
prior no-op constructor. Moved into load(), which is documented as
background-only.

Also wraps the tmp-file write/rename in a try/finally so a writeRecords
crash partway (corrupt record, disk-full mid-write) or a copyTo
fallback can't leave an orphaned `.tmp` sibling behind.
2026-05-16 14:30:09 +00:00
Claude fa814257d9 perf(dns-cache): swap SurgeDnsStore JSON for a hand-rolled binary blob
Replaces Jackson-based JSON serialization with a compact big-endian
binary format (magic + version + per-record host/ip bytes) so cold-start
load/save is ~5-10x faster and the blob shrinks from ~55 KB to ~25 KB
for the ~700-host workload.

DnsCacheRecord now carries raw `ByteArray` addresses so the persistence
boundary uses `InetAddress.address` / `InetAddress.getByAddress(byte[])`
on both sides — no string formatting or literal re-parsing on the hot
path. `SurgeDnsStore` validates magic, version, and length-bounded
counters; corrupt or truncated blobs are deleted and ignored. The
constructor reclaims the legacy `dns_cache_v1.json` sibling on first
run.
2026-05-16 14:30:08 +00:00
Claude 5ed6f063a9 build: drop deprecated kotlin.incremental.useClasspathSnapshot
Kotlin 2.x removed the classpath-snapshot incremental-compilation
strategy in favor of ABI snapshots, which are always on. The flag is
now a deprecation warning at configuration time. Drop it.

Parallel + build-cache + bumped Kotlin daemon metaspace remain.
2026-05-16 14:17:04 +00:00
Claude f35e3767ad build: enable parallel + build cache + Kotlin classpath snapshot
Halves cold compile time for compilePlayDebugKotlin on this machine
(6m25s -> 3m26s, 47% reduction):

- org.gradle.parallel=true lets quic/nestsClient/commons compile
  concurrently once quartz is done, instead of serially.
- org.gradle.caching=true lets warm cross-clean builds reuse task
  outputs.
- kotlin.incremental.useClasspathSnapshot=true narrows the incremental
  recompile blast radius after dependency-jar changes.
- Bumps Kotlin daemon MaxMetaspaceSize 3g -> 4g for headroom under
  the Compose IR phase across ~2.8K @Composable functions.

Up-to-date and same-file incremental times are unchanged (~3s and ~4s)
since those weren't bottlenecked by these settings.

Configuration cache is NOT enabled: amethyst/build.gradle:12 runs
'git rev-parse --abbrev-ref HEAD' at configuration time, which is
incompatible with the configuration cache. Migrating that to a
ValueSource is a separate change.
2026-05-16 14:16:44 +00:00
Vitor Pamplona f8dd5b61a6 Merge pull request #2934 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-16 09:57:56 -04:00
Crowdin Bot 9253387c93 New Crowdin translations by GitHub Action 2026-05-16 13:43:39 +00:00
Vitor Pamplona bbeba28f0b Merge pull request #2936 from vitorpamplona/claude/fix-dns-loopback-caching-ntjbN
Filter DNS poison (loopback/any-local) from non-loopback hosts
2026-05-16 09:42:03 -04:00
Claude c36c9ccf43 fix(dns): treat trailing-dot FQDN form of localhost as loopback
RFC 1034: `localhost.` and `localhost` are the same name — the trailing
dot just marks the FQDN form. Without this, an upstream answer of
127.0.0.1 for `localhost.` (or `relay.localhost.`) would get filtered as
poison, breaking user-configured local relays addressed in FQDN form.
2026-05-16 13:37:02 +00:00
Claude 440f5495a4 fix(dns): reject loopback poison + dirty cache on restore drop
SurgeDns was faithfully caching whatever the system resolver returned —
including 127.0.0.1 / ::1 / 0.0.0.0 — for 24-48h plus an on-disk
snapshot. A single bad answer (captive portal, ad-blocker DNS, transient
VPN hiccup) could leave the user unable to reach any non-loopback relay
for days: connection attempts go to their own loopback, fail, and the
next lookup re-resolves through the same source.

- Filter loopback/any-local addresses out of lookupAndCache and restore,
  unless the hostname is itself a loopback name (localhost, .localhost
  subdomains, or 127.0.0.1 / ::1 literals) so user-configured local
  relays keep working.
- Mark cache dirty when restore drops poisoned on-disk entries so the
  next save rewrites the snapshot without them — otherwise the bad
  entries would be re-restored on every cold start.
2026-05-16 13:17:08 +00:00
Vitor Pamplona a68ce75313 Merge pull request #2935 from vitorpamplona/claude/move-surgednsstore-cache-OKApI
Migrate DNS cache from SharedPreferences to cacheDir
2026-05-16 09:09:12 -04:00
Vitor Pamplona 8b164baab3 Merge pull request #2931 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-16 08:55:27 -04:00
Claude 58cbae7abc refactor(dns-cache): store SurgeDns snapshot under cacheDir
Moves the SurgeDns persisted snapshot from a SharedPreferences blob
under /data/data/.../shared_prefs to a plain JSON file under cacheDir.
The snapshot is pure perf data — if the OS evicts it under storage
pressure, the resolver just falls back to sync getaddrinfo and rebuilds
as lookups happen, which is the correct trade-off for a cache.

Writes go through a sibling .tmp + rename so a crash mid-write can't
leave a half-written blob behind. AppModules also deletes the legacy
amethyst_dns_cache SharedPreferences on next launch so the old store
doesn't linger.
2026-05-16 12:50:01 +00:00
Crowdin Bot d3c6518696 New Crowdin translations by GitHub Action 2026-05-16 12:41:53 +00:00
Vitor Pamplona b947a1b223 Merge pull request #2933 from vitorpamplona/claude/fix-timeout-samsung-android-m2Q2v
Use accountViewModel.launchSigner for relay join/leave requests
2026-05-16 08:40:12 -04:00
Vitor Pamplona b336af6cc3 Merge pull request #2932 from vitorpamplona/claude/fix-fdroid-exception-8ZHJH
Fix ChatroomListKnownFeedFilter to use flowSet instead of flow
2026-05-16 08:36:05 -04:00
Claude 106977ccd4 fix(relay-members): route NIP-43 join/leave through launchSigner
The join/leave-request buttons launched a raw scope.launch(Dispatchers.IO)
and called account.signer.sign() directly. When the signer threw —
TimedOutException from a Samsung-A53 / Android-16 Amber prompt the user
didn't respond to in 30s, or any other SignerException — the failure
escaped the composable's scope and was reported as an uncaught crash
instead of being toasted/logged like every other signing operation.

Route both buttons through accountViewModel.launchSigner so the same
SignerExceptions handling that every other signing entry point uses
applies here too.
2026-05-16 12:31:07 +00:00
Claude 1c4ddfb2ce fix(chats): dedupe public channels in known list by channel id
ChatroomListKnownFeedFilter.feed() iterated account.publicChatList.flow,
a Set<ChannelTag>. ChannelTag uses identity equality (it's not a data
class) and the set is built from raw tag parsing, so a channel list
that names the same channel id twice (e.g., the same channel appearing
in the public section and the encrypted private section, or repeated
with different relay hints) produces multiple ChannelTag entries for
the same channel. Each one resolved to the same newest Note via
getOrCreatePublicChatChannel(it.eventId), so the feed contained the
same Note twice and the chat list LazyColumn crashed with "Key
PublicChannelLazyKey(channelId=...) was already used".

Use the sibling flowSet (Set<HexKey>, already deduped by event id),
which is the same source filterRelevantPublicMessages reads from.
2026-05-16 12:28:30 +00:00
Claude f965d3b331 Revert "fix(chats): match public-channel rows by channel id when merging updates"
This reverts commit 13c3ddd446.
2026-05-16 12:26:52 +00:00
Vitor Pamplona ad8db9b9f8 Merge pull request #2930 from vitorpamplona/claude/fix-indexoutofbounds-android-iTCZr
Fix crash when toggling home tabs with persisted pager state
2026-05-16 08:24:49 -04:00
Claude 386d827807 fix: clamp Home TabRow selectedTabIndex when tab count shrinks
rememberForeverPagerState persists currentPage across tab-count changes.
When the user toggles off one of the conditional Home tabs (New Threads,
Conversations, Everything), tabs.size shrinks but pagerState.currentPage
is still pointing at the removed slot, so Material3's
TabIndicatorOffsetNode reads tabPositions[currentPage] out of bounds and
crashes with IndexOutOfBoundsException.

Clamp the index for the TabRow and use getOrNull for the bottom-bar
re-tap callback.
2026-05-16 12:09:46 +00:00
Claude 13c3ddd446 fix(chats): match public-channel rows by channel id when merging updates
ChatroomListKnownFeedFilter.updateListWith only recognized
ChannelMessageEvent when checking the old list for an existing row to
replace. When a public channel's row was first populated from a
ChannelCreateEvent or ChannelMetadataEvent (no message had arrived
yet), the next incoming ChannelMessageEvent failed to match, so the
filter appended a second note for the same channel. Both rows then
produced the same PublicChannelLazyKey, crashing the LazyColumn with
"Key was already used".

Resolve the channel id from any IsInPublicChatChannel event (covers
ChannelMessageEvent and ChannelMetadataEvent) and fall back to the
event id for ChannelCreateEvent, mirroring how the lazy key is built
in ChatroomListFeedView.
2026-05-16 12:09:31 +00:00
Vitor Pamplona 8c73cc1d3b Merge pull request #2927 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-15 23:03:38 -04:00
Crowdin Bot 8fd9f71a58 New Crowdin translations by GitHub Action 2026-05-16 02:41:26 +00:00
Vitor Pamplona 2ca9488188 Merge pull request #2926 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-15 22:39:57 -04:00
Vitor Pamplona 50b69b1ca5 Merge pull request #2924 from mstrofnone/feat/desktop-render-import-follow-list-dialog
feat(desktop): wire Import Follow List dialog into UI
2026-05-15 22:39:48 -04:00
Crowdin Bot 7dd754cf96 New Crowdin translations by GitHub Action 2026-05-16 02:39:44 +00:00