Commit Graph
15345 Commits
Author SHA1 Message Date
Claude 39dc64ff3c fix: stop seeding a fresh timestamp into translators.json every run
The `sinceLastTag.updated` field was set to `date -u` on every
`scripts/translators.sh --seed` run, but nothing ever reads it. The
release-time credit generator only consumes `.mappings` and
`.sinceLastTag.translators`.

Because the field changed on every run, the seed-translators CI job
produced a diff (and therefore a new Crowdin/seed PR) on every push to
main even when the translator set was unchanged. Drop the field from the
seed write and from the committed JSON so the file only changes when a
contributor actually appears.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RSoN4DDC5F1ehwGeC33652
2026-06-19 20:02:21 +00:00
Vitor Pamplona f247f5a431 updating known mappings for translators and github 2026-06-19 15:53:53 -04:00
Vitor PamplonaandGitHub 1bfdf02884 Merge pull request #3283 from vitorpamplona/claude/friendly-fermat-fj6xxd
Move Cashu token parsing to quartz, unify v3/v4 parsers
2026-06-19 13:55:33 -04:00
Claude b8db7ae6b7 perf(cashu): dispatch token prefixes via DualCase startsWith
Replace startsWith(..., ignoreCase = true) — which case-folds on every
call — with the precomputed DualCase prefixes and the new
String.startsWith(DualCase) helper, so the cashuA/cashuB dispatch only
compares against already-cased strings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013UMNKix4qEfiAPP9s2a4gB
2026-06-19 17:26:48 +00:00
Vitor PamplonaandGitHub 558370db25 Merge pull request #3281 from vitorpamplona/chore/seed-translators
Seed translator npub placeholders
2026-06-19 13:26:41 -04:00
vitorpamplonaandgithub-actions[bot] 874503a2eb chore: seed translator npub placeholders from Crowdin 2026-06-19 17:21:55 +00:00
Vitor PamplonaandGitHub c37562dc51 Merge pull request #3280 from vitorpamplona/claude/epic-hamilton-m4xors
Add translator credit automation via Crowdin sync
2026-06-19 13:21:34 -04:00
Vitor PamplonaandClaude Opus 4.8 3d26a3d818 v.1.12.5
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v1.12.5
2026-06-19 12:55:37 -04:00
Vitor PamplonaandClaude Opus 4.8 61defaa2e9 fix(ci): resolve the exact Developer ID common name for Compose signing
v1.12.4 got the keychain right ("...in keychain [/Users/.../amethyst-signing
.keychain-db]") but createReleaseDistributable still failed with "Could not
find certificate". Different layer of the same problem:

Compose's MacSigner maps its identity to a cert by running `security
find-certificate -c <identity>`, prepending "Developer ID Application: " when
the identity doesn't already start with it. `codesign --sign` (used by the
signMacJarNatives task, which succeeds in the same job) instead matches a
SHA-1 hash OR any common-name substring. So a MAC_SIGN_IDENTITY secret that is
a fingerprint or a team-ID/partial name signs fine with codesign but, once
prefixed by Compose, is not a substring of the cert's common name -> zero
matches -> failure.

Reproduced locally against the real Developer ID cert:
  find-certificate -c "Developer ID Application: <TEAMID>"      -> 0 matches
  find-certificate -c "Developer ID Application: <full CN>"     -> 1 match

Fix: import-macos-cert now resolves the certificate's full "Developer ID
Application: NAME (TEAMID)" common name from the keychain (via find-identity)
and exposes it as an `identity` output. The desktop build feeds that to
Compose's signing.identity, falling back to the raw secret if resolution
fails. Independent of whatever form the secret takes. The amy CLI leg keeps
using bare codesign with the secret directly and is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 12:54:56 -04:00
Vitor PamplonaandClaude Opus 4.8 c006831f9c v.1.12.4
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v1.12.4
2026-06-19 12:32:52 -04:00
Vitor PamplonaandClaude Opus 4.8 e82237840a fix(ci): point Compose macOS signing at the CI keychain explicitly
The desktop DMG release leg (build-desktop macos, packageReleaseDmg) has never
produced a signed artifact: createReleaseDistributable fails with "Could not
find certificate for '***' in keychain []". This is independent of the v1.12.3
notarization fix, which addressed the separate amy CLI leg.

Root cause: Compose's MacSignerImpl maps the signing identity to a certificate
by running `security find-certificate -a -c <identity>` with no keychain
argument. On the GitHub macOS runners that lookup does not resolve the cert that
import-macos-cert imported into a throwaway keychain and added only to the user
search list — even though bare `codesign --sign` (e.g. the signMacJarNatives
task, which succeeds in the same job) finds it fine. The "keychain []" in the
error is just the null settings.keychain being echoed.

Fix: export the throwaway keychain path from the import-macos-cert action and
feed it to Compose's `signing.keychain` via AMETHYST_MAC_SIGN_KEYCHAIN, so the
certificate lookup searches that keychain directly. Also set it as the default
keychain for good measure. No-op on local/PR builds (env unset -> Compose keeps
its previous default-search-list behavior).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 12:32:46 -04:00
Claude 765c1dfd7e feat: generate translator credits offline from the committed file
Makes docs/changelog/translators.json self-sufficient so a release no longer
needs to re-query Crowdin:

- sinceLastTag entries now carry each translator's languages ({user, languages}),
  recorded by the --seed run.
- Default mode (no flags) is offline: it generates the "## Translations" block
  straight from the committed file — reading sinceLastTag, grouping by the stored
  languages, and resolving npubs via the mappings registry. No token, no network.
- --seed/--raw remain the online paths (CI seeding / debugging). curl + git +
  credentials are only required there; the offline path needs just jq.

RELEASE_OPS now points at the tokenless `scripts/translators.sh` for the
changelog credits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQWVdWLAwBUBX2gJ55y6b
2026-06-19 16:02:47 +00:00
Claude ebdf18139b feat(cashu): tolerate both base64 alphabets + redeem multi-mint tokens
- parseCashuA now decodes standard *and* url-safe base64. NUT-00 v3
  specifies base64-urlsafe, but legacy encoders (and older Amethyst
  builds) emitted standard base64; try standard first, fall back to
  url-safe so both round-trip.
- CashuWalletViewModel.redeemToken now redeems every mint/keyset group
  in a pasted token instead of only the first, validating all mints are
  in the wallet up front and summing the redeemed amounts.

Adds parser coverage for both base64 alphabets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013UMNKix4qEfiAPP9s2a4gB
2026-06-19 16:01:22 +00:00
Claude 6b30486667 feat: track a rolling "since last tag" translator list
Reworks docs/changelog/translators.json into two lists maintained by
scripts/translators.sh:

- mappings: a forever-growing Crowdin-username/id -> npub registry. --seed
  appends new contributors with a blank npub and never deletes or overwrites
  existing entries.
- sinceLastTag: a rolling snapshot of who has translated since the last v* tag,
  fully refreshed on every --seed run.

The contribution window now defaults to the most recent v* tag instead of a
fixed two months (falling back to two months ago when no tag is reachable). The
CI seed job fetches tags (fetch-depth: 0) so it can resolve that window.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQWVdWLAwBUBX2gJ55y6b
2026-06-19 15:52:27 +00:00
Claude 3515afa34c fix(cashu): consistent prefix casing + reuse one CBOR codec
Audit follow-ups on the cashu token codec move:

- CashuTokenB64Parser.parse() dispatches on the prefix case-insensitively
  (matching commons RichTextParser's case-insensitive cashuA/cashuB
  detection), but parseCashuA/parseCashuB stripped it with a case-sensitive
  removePrefix — so a mixed-case prefix passed dispatch and then fed its
  own prefix bytes into the Base64 decoder, failing to parse. Strip the
  fixed 6-char prefix with drop() so dispatch and stripping agree.
- hoist a single shared CashuV4Cbor instance instead of allocating a new
  Cbor on every encode (V4Encoder) and every cashuB parse.

Adds an acceptsMixedCasePrefix regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013UMNKix4qEfiAPP9s2a4gB
2026-06-19 15:47:09 +00:00
Claude 6c9ca11602 ci: seed translators.json from Crowdin on every sync
Adds a seed-translators job to the Crowdin workflow that runs
scripts/translators.sh --seed (past two months) and opens/updates a single
PR via peter-evans/create-pull-request whenever a new contributor appears, so
docs/changelog/translators.json stays current without manual upkeep. The
action is MIT and CI-only (not linked into any shipped artifact), and no-ops
when there is no diff.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQWVdWLAwBUBX2gJ55y6b
2026-06-19 15:46:33 +00:00
Claude fc816f2afd feat: add --seed mode to pre-load translators.json from Crowdin
scripts/translators.sh --seed fetches every contributor in the window
(default: past two months) and merges their Crowdin usernames into
docs/changelog/translators.json with blank npubs, preserving existing
entries and deduping case-insensitively. Fill in the npubs afterwards.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQWVdWLAwBUBX2gJ55y6b
2026-06-19 15:42:39 +00:00
Claude a5bf4451ed refactor: move translator-credits script to scripts/, document in RELEASE_OPS
Moves the Crowdin translator-credits generator from tools/translators/ to
scripts/translators.sh to sit with the other flat shell scripts. Drops the
standalone README (the script is self-documenting via --help) and folds the
release-time usage into RELEASE_OPS.md next to the changelog step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQWVdWLAwBUBX2gJ55y6b
2026-06-19 15:41:02 +00:00
Claude 41c686ebb3 feat: generate changelog translator credits from Crowdin
Adds tools/translators/translators.sh, which pulls a Crowdin "Top Members"
report for a release window (between two tags/dates) and prints the changelog
"## Translations" block grouped by language.

Crowdin contributors are joined against docs/changelog/translators.json, a
Crowdin-username/id -> npub mapping kept alongside the changelogs. Contributors
with no mapping are listed under UNMAPPED so they can be credited by hand and
backfilled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQWVdWLAwBUBX2gJ55y6b
2026-06-19 15:34:05 +00:00
Claude c5b1d0051a refactor(cashu): move out-of-band token parser + model to quartz
Follow-up to the V4Encoder move. Quartz could encode a cashuB string but
could not parse cashuA/cashuB back, and the parsing lived in amethyst even
though it is pure NUT-00 wire-format protocol. Worse, commons.RichTextParser
already detects cashuA/cashuB words while the parser sat up in the app, so
Desktop (its own rich-text viewer) could not parse a received token at all.

Consolidate the legacy out-of-band redeem stack onto quartz:

- new quartz CashuTokenB64Parser parses cashuA (standard-Base64 JSON, rewritten
  off Jackson onto kotlinx.serialization to satisfy quartz's no-Jackson rule)
  and cashuB (Base64URL CBOR, reusing the V4Token models), returning quartz
  types. It is the inverse of V4Encoder.
- move the CashuToken container model from commons to quartz, switching its
  proofs from the duplicate commons Proof (field C, amount Int) onto the
  canonical quartz CashuProof (field c, amount Long). The duplicate Proof
  type is deleted.
- delete amethyst V3Parser/V3Token/V4Parser; CashuParser/CachedCashuParser
  stay as thin amethyst adapters (off-main-thread guard + GenericLoadable +
  LruCache) over the quartz parser.
- this removes the manual Proof -> CashuProof conversion shims that
  MeltProcessor and CashuWalletViewModel previously carried.

Tests: full cashuA + cashuB vector coverage moves to quartz commonTest
(CashuTokenB64ParserTest, runs on JVM) plus an encode/parse round-trip; the
superseded amethyst CashuV4ParserTest is removed and CashuBTest stays as the
adapter integration test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013UMNKix4qEfiAPP9s2a4gB
2026-06-19 15:32:48 +00:00
Vitor Pamplona 7096819f3b v.1.12.3 v1.12.3 2026-06-19 11:13:00 -04:00
Claude 6a65680e61 refactor(cashu): move v4 (cashuB) token codec to quartz
The NUT-00 v4 `cashuB` wire-format encoder + CBOR models lived in
`amethyst/service/cashu/v4`, even though they are pure protocol code with
no app/UI dependencies. quartz had the proof/mint primitives but no
`cashuB` string codec at all, so this was a layering gap rather than a
duplicate.

Move `V4Encoder` and the `V4Token`/`V4T`/`V4Proof`/`V4DleqProof` wire
models into `quartz/nip60Cashu/token`, rewriting the JVM-only
`java.util.Base64` to the multiplatform `kotlin.io.encoding.Base64.UrlSafe`
so the codec lives in commonMain. This unblocks sharing CashuWalletOps /
CashuWalletState into commons (V4Encoder was one of the Android-only ties).

- add kotlinx-serialization-cbor (Apache-2.0) to quartz commonMain
- repoint CashuWalletOps (encoder) and V4Parser (V4Token) imports to quartz
- add V4EncoderTest round-trip coverage in quartz commonTest

amethyst's V4Parser stays put: it returns UI types (GenericLoadable +
commons CashuToken) and now just reuses the relocated quartz V4Token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013UMNKix4qEfiAPP9s2a4gB
2026-06-19 14:48:40 +00:00
Vitor PamplonaandClaude Opus 4.8 ee58355e6e fix(ci): sign macOS Mach-O natives embedded in bundled jars before notarization
The v1.12.2 release was the first to actually codesign + notarize the macOS
artifacts (signing was wired after v1.12.1, which shipped unsigned). Both macOS
legs failed with "Notarization status: Invalid": Apple's notary service recurses
into the bundled jars and rejects the unsigned Mach-O natives inside them
(secp256k1, sqlite-bundled, jna, skiko, jkeychain, kdroidFilter mediaplayer) —
codesign on the .app and the CLI's loose-file loop never descend into jars.

Notary log confirmed the offending entries, e.g.
  sqlite-bundled-jvm.jar/natives/osx_arm64/libsqliteJni.dylib
    -> "not signed with a valid Developer ID certificate" / "no secure timestamp"

Add scripts/sign-macos-jar-natives.sh: a shared helper that signs every macOS
Mach-O inside the bundled jars with hardened runtime + a secure timestamp,
skipping Linux ELF via a `file` Mach-O gate and no-opping when no identity is
set (local/PR builds unchanged). Wire it into:
  - the CLI notarize step (runs before the loose-file signing loop)
  - a desktop signMacJarNatives Gradle task that signs the proguarded jars
    between proguardReleaseJars and createReleaseDistributable, so Compose
    seals already-signed code.

Validated locally on arm64: clean signed createReleaseDistributable produces an
.app that passes `codesign --verify --deep --strict`, with every nested native
carrying Developer ID + hardened runtime + secure timestamp.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:42:06 -04:00
Vitor PamplonaandGitHub 8ef4e42c16 Merge pull request #3278 from nrobi144/feat/desktop-relay-latency-health
feat(desktopApp): relay latency health — tracking, classifier & dashboard UI
2026-06-19 10:22:07 -04:00
Vitor Pamplona b8312f023a v.1.12.2 v1.12.2 2026-06-19 09:22:33 -04:00
David KasparandGitHub 47ac693d96 Merge pull request #3279 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-06-19 10:59:54 +01:00
Crowdin Bot 648f3f2bd4 New Crowdin translations by GitHub Action 2026-06-19 09:26:34 +00:00
David KasparandGitHub 29d6bc308e Merge pull request #3260 from nrobi144/worktree-fix-desktop-macos-keychain-proguard
fix(desktop): macOS forced re-login on cold boot — ProGuard strips java-keyring backend
2026-06-19 10:24:49 +01:00
nrobi144andClaude Opus 4.7 49929a4afa build(desktop): add osxkeychain.so regression guard task — fail release if stripped
The original "ProGuard strips the keychain backend" hypothesis turned out
to be wrong twice (PR 3260 comments document the binary PoW that refuted
both H1 strip-of-classes and H1b strip-of-native-resource). The full
117 KB osxkeychain.so resource ships intact in the proguarded
jkeychain-1.1.0-*.jar today, and Keyring.create() round-trips fine
against the proguarded classpath on macOS.

But the user-reported bug pattern (every cold boot, keychain key missing
→ forced re-login) maps so cleanly onto a hypothetical future
strip-of-native-resource that the guard is worth keeping. Cheap to run
(one unzip scan after proguardReleaseJars), wired onto every release
packaging task (DMG, MSI, DEB, RPM, current-OS distributable, runRelease)
so a regression can't slip past. Fails the build with a self-contained
explanation pointing at the next person who has to debug it.

The actual root cause of the reported bug remains unidentified after
three refuted hypotheses (see plan doc PoW table); needs the affected
user's Console.app logs + ~/.amethyst state to make further progress.
The LoginScreen "keychain-unavailable" diagnostic banner from the
earlier commit is unchanged and still earns its keep regardless of
which failure mode eventually turns out to be the cause.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-19 10:35:13 +03:00
Róbert NagyandGitHub 5e3fea94a1 Merge branch 'main' into feat/desktop-relay-latency-health 2026-06-19 10:28:07 +03:00
Vitor PamplonaandGitHub 552cd5d7e1 Merge pull request #3277 from vitorpamplona/claude/modest-planck-o0fy3v
Roadstr: visual overhaul with map hero and category pills
2026-06-18 19:45:19 -04:00
Claude 0a3ce4535e refactor: limit road event feed inclusion to geohash only
Back out the hashtag-screen wiring (its `t` tag is the event category,
not a topic, so road events only matched category-named hashtags). Keep
the geohash data source + feed filter, where road events belong by their
`g` tags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 23:42:28 +00:00
Claude e93dffa9b5 feat: include road events (1315/1316) in hashtag and geohash feeds
Add the Roadstr report (1315) and confirmation (1316) kinds to both the
relay subscription filters and the local feed gates for the hashtag and
geohash screens (the geohash kind list is shared with the home "around
me" feed). Reports carry geohash `g` tags and a category `t` tag, so they
now surface in nearby-geohash feeds and in the matching category hashtag
feed; confirmations carry `g` tags and surface in geohash feeds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 23:36:09 +00:00
Claude f0ea68f440 fix: night map preserves hue instead of crushing to black
The previous night filter used a desaturating invert whose coefficients
summed to ~1.4 per channel, which pushed every (light) OSM tone to
near-black and erased forest/water/land differentiation. Replace it with
a lightness invert composed with a 180° hue rotation: the map darkens but
hue is preserved, so forests stay dark green, water dark blue, and labels
invert to readable white.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 23:24:30 +00:00
Claude 2d563aba07 feat: make the road event map follow the app's dark theme
osmdroid's MAPNIK tiles are always light, so in dark mode the bright map
clashed with the UI and light overlays. Apply a night-mode colour-matrix
filter to the tiles overlay when MaterialTheme.colorScheme.isLight is
false (lightness-inverted + desaturated for a clean dark-grey map, not a
plain invert that turns forests magenta). Light theme keeps normal tiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 23:15:38 +00:00
Claude bdc0c19257 fix: give road event comment an explicit on-surface color
The comment Text inherited LocalContentColor, which resolved to a
near-white in this card and made the report content hard to read on the
light map card. Color it onSurface so it stays readable in both themes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 23:08:48 +00:00
Claude ca11b0a37b Revert "fix: readable pill text via WCAG contrast, not a luminance threshold"
This reverts commit 595d12f81b.
2026-06-18 23:06:47 +00:00
Claude 595d12f81b fix: readable pill text via WCAG contrast, not a luminance threshold
The category pill picked black/white text from a luminance threshold
(0.55) that left white text on mid-tone colors (fog gray, ice turquoise,
"other" gray), barely legible over a light map. Choose the text color by
the higher WCAG contrast ratio against the pill color instead, so light
pills now get black text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 23:00:55 +00:00
Claude 2b02a1341f feat: modernize road event cards with a map-hero + floating pill
Redesign the kind 1315/1316 cards from a text-title-over-small-map
layout into a map-hero card: the OSM preview becomes a full-bleed hero
with a floating, color-coded category pill overlaid on it (auto-contrast
text), and the comment/status sits in a clean padded block below. Shared
RoadEventCard + CategoryPill chrome backs both the report and the
confirmation, with a graceful no-location fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 22:52:35 +00:00
Claude aee2f77654 refactor: drop redundant expiry/age row from road event cards
NoteCompose's header already shows the post time and the NIP-40
expiration. The inner card's own "🕒 age · expires in …" line duplicated
that and, worse, conflicted with it: the header reads the relay-side
`expiration` tag (always created_at + 14d) while the card used the
per-type effective TTL (e.g. 30d for a speed camera), so the two showed
different countdowns for the same note. Remove the inner row; keep the
freshness-based pin dimming, which is visual-only and doesn't conflict.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 22:47:44 +00:00
Claude 2e32660d9b feat: roadstr-accurate road event rendering (kind 1315/1316)
Align Amethyst's road event cards with the roadstr reference clients
(https://github.com/jooray/roadstr) for both interoperability and a
richer presentation.

Interop:
- Match roadstr's exact emoji set: road_closure 🚫 (was ) and
  other ℹ️ (was 📍). The t codes and per-type TTLs already matched.
- Make the kind 1316 NIP-31 alt status-dependent ("Roadstr: event
  confirmed" / "Roadstr: event denied") per the spec, instead of a
  single "Roadstr: event confirmation".

Rendering:
- Colored teardrop map pin per category, using roadstr's exact color
  palette, with the category emoji on the head (new MapPinIcon).
- Freshness: fade the report pin to 0.6 under 25% of effective TTL and
  0.4 once effectively expired, matching roadstr's opacity rule.
- Subtitle meta line: "🕒 23m · expires in 1h" / "· Expired" on reports
  and "🕒 23m" on confirmations.
- Confirmations get a green  / red  status pin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 22:38:07 +00:00
Vitor PamplonaandGitHub bdb16bfc2c Merge pull request #3276 from vitorpamplona/claude/wizardly-mendel-o3ar8d
Add resumable FTS reindex for NIP-50 search index rebuilds
2026-06-18 18:35:38 -04:00
Claude af1e34a34d fix(quartz): clamp non-positive FTS reindex batch size
A batchSize <= 0 made the SQLite resumable reindex select no rows yet
never report done, so a caller's loop would spin forever. Clamp the page
size to at least one in both stores and add a regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZqPFds2TPPUKkMmBngwys
2026-06-18 22:32:51 +00:00
Claude b419db4fe0 feat(quartz): make FTS reindex pausable/resumable for large stores
A full FTS rebuild can run for a long time on a big store, so add a
resumable, batched overload alongside the one-shot:

    reindexFullTextSearch(resumeFrom: String?, batchSize): FtsReindexProgress

Each call processes ~batchSize events in its own write transaction and
returns an opaque cursor + done flag. The caller loops until done and may
stop at any point — the cursor is durable across crash/app-restart, and
the writer lock is released between batches, so "pause" is just "don't
make the next call". The path is additive/refresh and keeps search usable
throughout (no up-front wipe); the one-shot variant remains for a
guaranteed-clean rebuild.

- SQLite: FullTextSearchModule.reindexBatch walks event_headers ordered
  by the monotonic row_id (a free, stable cursor), restricted to
  searchable kinds, delete-then-insert per event so batches are
  idempotent and never duplicate rows.
- Filesystem: FsEventStore walks one idx/kind/<k>/ dir per step (linear,
  no re-sort); cursor is the next searchable kind. Idempotent linkFts, so
  nothing is wiped. Pauses between kinds.
- Wrappers delegate; new FtsReindexProgress value type carries cursor +
  progress + done.
- cli: `amy store reindex-fts` now loops the batched path to completion
  and reports processed/batch counts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZqPFds2TPPUKkMmBngwys
2026-06-18 22:16:48 +00:00
Vitor PamplonaandGitHub c84333c753 Merge pull request #3275 from vitorpamplona/fix/tor-circuit-dead-warm-reset
fix(tor): warm-reset on dead-circuit self-heal instead of wiping state
2026-06-18 18:14:17 -04:00
Vitor PamplonaandClaude Opus 4.8 048aa821b6 fix(tor): warm-reset on dead-circuit self-heal instead of wiping state
When Tor is Active but every Tor-routed relay fails (the ExitTimeout /
RESOLVEFAILED barrage), onTorCircuitsDead() now does a warm reset() —
drop the in-process client to rebuild the circuit pool with a fresh exit
draw, while keeping guards and the consensus cache — instead of
resetWithCleanState().

The failure is exit-side, not entry-side: circuits build fine, but the
exits can't reach the relays. Wiping arti/state/ + cache can't improve
exit selection (exits aren't persisted) and only forces a ~60s cold
bootstrap — exactly the blackout that strands users on the
connection-failure dialog. A warm restart reconnects in ~5s.

The poisoned-guards safety net is preserved: the next start() re-runs
noUsableGuards(), so genuinely unusable guards are still wiped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:08:17 -04:00
Claude 31a375034a fix: make road event location map square (1:1 aspect ratio)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 21:52:29 +00:00
Claude 0c67f63bad feat(quartz): add FTS reindex to SQLite and filesystem event stores
The set of event kinds that implement SearchableEvent — and the text
each contributes via indexableContent() — is baked into the quartz
build, so it changes across app versions. Events stored under older code
keep their old (or missing) NIP-50 full-text-search rows, so search
silently misses them after an upgrade.

Add IEventStore.reindexFullTextSearch() so the app can wipe and rebuild
the FTS index from already-stored events when it has spare cycles.

Speed: only kinds that currently map to a SearchableEvent are scanned.
Kind alone selects the event class in EventFactory, so a single probe per
distinct kind is authoritative, letting us push a `kind IN (...)` filter
(SQLite) / skip whole idx/kind dirs (filesystem) so the non-searchable
bulk — reactions, zaps, follow lists — is never deserialised.

- SQLite: FullTextSearchModule.reindexAll drops+recreates the virtual
  table (O(1) wipe) then streams only searchable-kind rows in one write
  transaction, reusing a single INSERT statement.
- Filesystem: rebuilds only idx/fts/, driving the walk from
  idx/kind/<searchable kind>/ via the new FsIndexer.linkFts.
- Wrappers (EventStore, ObservableEventStore, InterningEventStore)
  delegate; the observable layer emits nothing since no event changes.
- cli: `amy store reindex-fts`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZqPFds2TPPUKkMmBngwys
2026-06-18 21:49:06 +00:00
Claude a9ce0df8f2 feat: render road event (kind 1315/1316) location on an OSM map
Replace the reverse-geocoded "📍 City" line on the Roadstr road event
report and confirmation cards with an osmdroid OpenStreetMap preview
pinned at the event's coordinates.

- Add osmdroid-android 6.1.20 (Apache-2.0) to the version catalog and
  the amethyst module.
- New LocationPreviewMap composable: an AndroidView-wrapped MapView with
  MAPNIK tiles, a single marker, lifecycle-aware onResume/onPause/onDetach,
  and nested-scroll-friendly touch handling so panning the map doesn't
  fight the feed scroll. Sets the OSM User-Agent to the package name
  (required or OSM returns 403).
- RoadEvent cards resolve a point from the explicit lat/lon tags, falling
  back to the center of the finest published geohash.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017tYbcy4UGWxqQbcycyL7Yd
2026-06-18 21:36:56 +00:00
Vitor PamplonaandGitHub 84976b0a3e Merge pull request #3274 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-06-18 17:10:05 -04:00