Commit Graph
17152 Commits
Author SHA1 Message Date
Claude 00ea0f0a01 feat(theme): keep the by-role color-pairs @Preview alongside old-vs-new
Restore ThemeColorPairsPreview (every fg/bg pair listed by ColorScheme role
name, current theme, dark | light) next to the by-component old-vs-new table.
The role view answers "what is primaryContainer right now"; the comparison
answers "how did this surface change". Both read the live scheme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVwY2Qzth2EREWg8qLhyzF
2026-07-17 04:18:30 +00:00
Claude d76c803e1d feat(theme): old-vs-new styleguide @Preview for the color normalization
Replace the single-theme pair preview with a side-by-side comparison: each app
surface (Save button, checked/unchecked switch, settings tile, card, links,
error) rendered Old·Dark | New·Dark | Old·Light | New·Light with live contrast
and hexes. "Old" reconstructs the pre-normalization scheme (purple primary +
teal secondary, Material's violet-tinted surfaces/containers, deep default
onPrimary); "new" uses the real production schemes, so editing Theme.kt
re-renders both sides. Makes darkColors/lightColors internal so the preview
builds the actual current scheme rather than a copy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVwY2Qzth2EREWg8qLhyzF
2026-07-17 04:03:03 +00:00
Claude f937925fbf fix(theme): route new UI-Preferences toggle through AmethystSwitch
The modernized UI Preferences screen (merged from main) added a raw Switch,
which would show the old pale checked colors. Point it at AmethystSwitch like
every other toggle so it picks up the deep filledAccent fill.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVwY2Qzth2EREWg8qLhyzF
2026-07-17 04:03:01 +00:00
Claude 1e94e6ab29 Merge remote-tracking branch 'origin/main' into claude/save-button-switcher-colors-7vnmm5 2026-07-17 03:54:54 +00:00
Vitor PamplonaandGitHub f1162037f9 Merge pull request #3606 from vitorpamplona/claude/modernize-ui-preferences-56auhn
Redesign settings screen with segmented controls and sections
2026-07-16 23:54:05 -04:00
Claude 069376f2ac fix(amethyst): separate settings cards by tone, not a hard border
The 1dp outlineVariant outline around every SettingsSection card read boxy.
Replace it with a surface-tone step: fill the card with surfaceContainer
instead of surfaceContainerLow. The neutral surface ramp puts the page at
background #FDFDFD, where …Low (#F7F7F7) was nearly invisible; …Container
(#F2F2F2) reads as a soft card in light and #252525 stands clear of black in
dark — the Material way to separate a card, without an outline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cy9tooutEdFQV5CwMhaP3j
2026-07-17 03:46:40 +00:00
Claude ef6c9bd5c4 fix(amethyst): compile errors in UI Preferences tiles
Caught by the first successful build after the Gradle distribution became
seedable in the web sandbox:

- BooleanSwitchTile's toggle inferred as (Boolean) -> Boolean because
  MutableStateFlow.tryEmit returns Boolean; annotate it (Boolean) -> Unit so
  it fits Switch.onCheckedChange and SettingsControlRow.onClick.
- Font tile referenced MaterialSymbols.Article, which lives under the nested
  AutoMirrored object; use the top-level MaterialSymbols.Description glyph.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cy9tooutEdFQV5CwMhaP3j
2026-07-17 02:42:55 +00:00
Claude 914d24c672 Merge remote-tracking branch 'origin/main' into claude/save-button-switcher-colors-7vnmm5 2026-07-17 02:22:47 +00:00
Claude 1430aeec97 feat(theme): @Preview of every fg/bg color pair, dark + light
A living reference composable that renders each foreground/background pair
the mobile theme uses (filledAccent, primary, containers, surfaces, the
unchecked-switch pair, …) with the real AmethystTheme colors — no
approximation — so editing Theme.kt re-renders it. Left column dark, right
column light, via the existing ThemeComparisonRow. Each row shows the live
WCAG contrast, the two hexes, and the concrete in-app surfaces that use it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVwY2Qzth2EREWg8qLhyzF
2026-07-17 02:22:41 +00:00
Vitor PamplonaandGitHub 95ed961c08 Merge pull request #3604 from vitorpamplona/claude/bitchat-ephemeral-interop-8epkek
Add geohash location chat support (Bitchat interop Phase 2)
2026-07-16 22:22:30 -04:00
Claude 010684c6ae chore(amethyst): full-screen @Preview for UI Preferences
Upgrade the preview to render the real screen — top bar plus the redesigned
content — side by side in dark and light (ThemeComparisonRow), so both
themes and the card-hairline contrast can be checked at a glance instead of
the content-only, chrome-less preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cy9tooutEdFQV5CwMhaP3j
2026-07-17 02:21:29 +00:00
Claude 5bb0140543 fix(amethyst): polish UI Preferences from the design audit
Applies the audit findings on the reworked screen:

- Cards get a 1dp outlineVariant hairline so they separate from the page in
  light mode, where surfaceContainerLow (#F7F7F7) barely differs from the
  background (#FDFDFD). Applies to every SettingsSection, keeping settings
  screens consistent.
- Segmented buttons drop the default selected check icon (the fill already
  signals selection) and use compact labels in the tight 3-4-up rows
  (Wi-Fi, Full/Simple/Fast, System/Sans/Serif/Mono) so labels stop
  ellipsizing to "Unmet…", "Simpl…", "System D…".
- Language becomes a disclosure row (icon + title + current language +
  chevron, whole row opens the existing picker dialog) instead of a lone
  text-field dropdown, matching the app's other "opens a picker" rows.
- Font-size preview uses a gentler 13sp base so the row no longer lurches
  taller for "Huge"; still previews small-to-huge.
- Accent swatches use FlowRow so all six wrap into view instead of scrolling
  off-edge with no affordance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cy9tooutEdFQV5CwMhaP3j
2026-07-17 02:21:29 +00:00
Claude 946303c2e5 feat(amethyst): make font tiles preview each option live
The font family and font size segmented rows now render each option's label
in the very typeface / at the very scale it selects — Sans Serif in sans,
Monospace in mono, Small small and Huge huge — so the control demonstrates
the choices instead of only naming them.

Adds an optional per-option text-style hook to SegmentedChoiceTile, reusing
the existing FontFamilyType.toFontFamily() mapping and FontSizeType.scale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cy9tooutEdFQV5CwMhaP3j
2026-07-17 02:21:29 +00:00
Claude ece16e8373 feat(amethyst): modernize UI Preferences with grouped in-screen options
Replace the flat list of dropdown (TextSpinner) rows on the UI Preferences
screen with the card-based settings design already used by the Compose and
security settings: contextual SettingsSection cards holding in-screen
SingleChoiceSegmentedButtonRow controls and Switch tiles, so every option
is visible and one tap away instead of hidden behind a spinner.

Group the settings by context:
- Appearance: theme, accent color, font, font size
- Media & Data: image preview, video playback, autoplay, URL preview,
  profile pictures
- General: language, UI mode, immersive scrolling

The two boolean (Always/Never) settings — autoplay videos and immersive
scrolling — become proper switches. Language keeps a dropdown (too many
locales for segmented buttons) but restyled into the new card. The
externally-consumed SettingsRow overloads and language helpers are kept.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cy9tooutEdFQV5CwMhaP3j
2026-07-17 02:21:29 +00:00
Vitor PamplonaandGitHub b8e2f7d314 Merge pull request #3605 from vitorpamplona/claude/agp-download-revert-utzlot
Pre-seed Gradle distribution in web sandbox to unblock ./gradlew
2026-07-16 22:20:24 -04:00
Claude 422a51cf33 style: fix import ordering in FollowingGeohashChatSubAssembler
spotless orders java.* after kotlinx.* in this project's ktlint layout;
move the ConcurrentHashMap import below the kotlinx imports.
2026-07-17 02:18:30 +00:00
Claude acbd0f4ac8 refactor(hooks): drop the pre-push spotless infra-skip
With the Gradle distribution now pre-seeded in the web sandbox
(session-start.sh) and dependencies resolving through the proxy, spotlessApply
no longer fails for infra reasons. Remove the network-error skip branch so any
spotlessApply failure blocks the push: a failure now means a real
formatting/compile error, and the narrow regex can no longer mask one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A7bocRTSPyXwz6zVMzDbKR
2026-07-17 02:16:35 +00:00
Claude e7f883f08b fix(hooks): seed Gradle distribution from a verified mirror in web sandbox
Claude Code Web routes github.com through a git-only proxy, so the Gradle
wrapper's distributionUrl (services.gradle.org, which 307-redirects to a
github.com release asset) can't bootstrap — it 403s even at Full network
access, and `./gradlew` fails before running any task.

Seed the pinned distribution in the existing web-only SessionStart hook,
reusing the same idempotent curl-download pattern already used there for the
Android SDK and Kotlin/Native deps: skip if already installed, else fetch
Gradle's OFFICIAL sha256 (served from services.gradle.org, reachable here),
download the zip from a mirror, and verify before extracting so a tampered or
wrong mirror file is rejected and never executed. The wrapper cache dir is
derived as base36(md5(distributionUrl)) so it survives version bumps.

Also pin distributionSha256Sum in gradle-wrapper.properties as defense in
depth: Gradle then verifies any distribution it installs (mirror-seeded, CI,
or local) against the known-good hash.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A7bocRTSPyXwz6zVMzDbKR
2026-07-17 02:11:56 +00:00
Claude e2743ed0b6 fix: address pre-merge audit findings for geohash chat
Correctness:
- GeoRelayDirectory.relays is now @Volatile; on the process-wide `shared`
  directory the CSV refresh was written by one thread and read by others with
  no memory barrier, so readers could keep using the FALLBACK list forever and
  never route to the correct rendezvous relays.
- sendPostSync bails before cancel() when a geohash cell has no resolvable
  relays, so the composer text + draft are preserved instead of the message
  being silently dropped with its draft deleted.
- Teleport detection compares on the common geohash prefix; a cell finer than
  the fixed 8-char device fix could never be a startsWith prefix, so the user
  was wrongly marked teleported even when physically present.

Performance:
- GeoRelayDirectory.closest precomputes each relay's great-circle distance
  once instead of recomputing the trig inside the sort comparator (was
  O(n log n) haversine calls over the ~370-relay directory).
- GeohashChatChannel.relays() memoizes the derived set, invalidated by a new
  directory version token, instead of re-sorting the whole directory (and
  allocating a fresh Set) on every call.
- filterFollowingGeohashChats groups cells by relay into one filter each
  (g = [cells]) rather than one REQ per (cell, relay).

Leak/thread-safety:
- FollowingGeohashChatSubAssembler.userJobMap is a ConcurrentHashMap and
  endSub now removes the entry (it previously cancelled the jobs but left the
  stale entry behind).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV
2026-07-17 02:10:18 +00:00
Claude bb38525fc6 feat(theme): deepen accent fill for Save button and switches on dark
The color standardization set the dark theme's `primary` to Purple200
(#BB86FC), a light pastel tuned for accent text/icons on black. Filled
controls (the top-bar Save/Post/Send/Create button and checked switches)
inherit `primary` + `onPrimary`, so they rendered as a washed-out pastel
fill under white content — low contrast on the black background.

`primary` can't simply be deepened: it's read by ~470 accent text/link/
icon sites that need a light tint on black, and `onPrimary` drives ~40 FAB
glyphs and chip labels. So instead of bending a shared role, add a
dedicated fill role:

- `ColorScheme.filledAccent` / `onFilledAccent`: on dark, deepen `primary`
  halfway toward the accent's deep variant (carried in the scheme as
  `inversePrimary`) for a rich, saturated fill; content picks black/white
  by WCAG contrast. Light themes already use the deep variant, unchanged.
- Consumed only by the top-bar action button, the standalone SaveButton,
  and a new `AmethystSwitch` wrapper (`amethystSwitchColors()`) that every
  Switch call site now routes through.

Derives per-accent, so Blue/Green/Orange/Red/Pink get the same treatment.
`primary`/`onPrimary` are untouched, so links, NIP-05, chat bubbles,
unread dots and FAB glyphs are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVwY2Qzth2EREWg8qLhyzF
2026-07-17 01:57:59 +00:00
Claude fcf772d505 feat: persist the geohash nickname so it survives app restarts
The location-chat nickname was in-memory only (ChannelNewMessageViewModel
.geohashNickname), so it reset to empty on process death. It can't be
recovered from relays either: the anonymous per-cell key publishes no kind-0
profile, and kind-20000 messages are ephemeral, so the nickname (a per-message
`n` tag) has no durable home on the network.

Persist it on-device instead, as a single global handle:
- GeohashChatIdentityState gains nickname()/setNickname(), stored in the
  account's encrypted storage next to the per-cell device seed (survives
  restarts, switches with the account).
- The chat screen restores it into the composer on room open, and the
  nickname dialog writes it back on Save.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV
2026-07-17 01:45:01 +00:00
Claude d07bac7489 feat: geohash composer polish + hide the room's own cell in bubbles
- Center the composer identity avatar vertically instead of bottom-aligning
  it (the "post as me" picture was sinking to the bottom of the edit row).
- Halve the gap between the avatar and the text field (drop the avatar's end
  padding and nudge the field left into its fixed inset).
- Drop the divider line between the feed and the composer — the other chat
  screens don't use one, and the rounded field is separation enough.
- In a location room, suppress the room's own geohash in each message's
  footer/time row (every message repeats the room cell, so showing it is
  noise). Done via a new LocalChatSuppressGeohash composition-local, factored
  into both the footer-has-metadata gate and the footer render; any other
  geohash still shows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV
2026-07-17 01:21:21 +00:00
Claude bd6635b311 Revert "fix(hooks): treat Gradle distribution download failure as infra skip"
This reverts commit a2ee1880a6.
2026-07-17 00:40:27 +00:00
Claude 4c9f599a79 Merge remote-tracking branch 'origin/main' into claude/bitchat-ephemeral-interop-8epkek 2026-07-17 00:24:33 +00:00
Vitor PamplonaandGitHub bc34a4cf59 Merge pull request #3601 from vitorpamplona/claude/amethyst-mobile-colors-kgdfhn
Make theme colors follow selected accent, not fixed purple
2026-07-16 20:22:47 -04:00
Vitor PamplonaandGitHub c2b2be322d Merge branch 'main' into claude/amethyst-mobile-colors-kgdfhn 2026-07-16 20:15:27 -04:00
Claude 22a3bb31d5 Merge remote-tracking branch 'origin/main' into claude/bitchat-ephemeral-interop-8epkek 2026-07-17 00:10:58 +00:00
Vitor Pamplona a094767aff Updates AGP 2026-07-16 20:03:18 -04:00
Claude 011b2ee862 fix: use the accent's deep tone for the Following shield
The dark-theme primary is a pale pastel, so the follow shield read washed out
versus the original dark #7F2EFF. Use the accent's deep tone (its light-theme
primary) in both themes — carried by inversePrimary in the dark scheme — so the
shield stays a dark, saturated accent with a white figure, matching the
original badge's depth while still following the selected accent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1F9jXNwRGTP8qmsV3Wd69
2026-07-16 23:58:41 +00:00
Claude 420fdfea53 Merge remote-tracking branch 'origin/main' into claude/bitchat-ephemeral-interop-8epkek
# Conflicts:
#	amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
2026-07-16 23:52:28 +00:00
Vitor PamplonaandGitHub 17e45d4e68 Merge pull request #3600 from vitorpamplona/claude/amethyst-note-ui-refactor-n36nvd
refactor: converge rich-text rendering across Android + Desktop into commons
2026-07-16 19:49:51 -04:00
Claude 2af730e5f7 feat: give the Following badge a vivid accent shield with a white figure
Tying the badge to the tonal-button palette (secondaryContainer) made it read
too dark against profile photos. Match the original badge look instead — a
vivid shield with a white figure — recoloured to the accent: shield = primary,
figure = onPrimary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1F9jXNwRGTP8qmsV3Wd69
2026-07-16 23:46:29 +00:00
Claude 374fc3fc8b feat: nickname affordance under the anonymous geohash avatar
Adds a compact caption beneath the anonymous composer avatar so the nickname
control is discoverable without knowing the long-press gesture: it shows the
chosen nickname, or a primary-tinted "+ name" prompt when blank. Tapping it
opens the same nickname editor as long-pressing the avatar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV
2026-07-16 23:44:26 +00:00
Vitor PamplonaandGitHub 0d219e219c Merge pull request #3602 from vitorpamplona/claude/invite-not-working-x1mhrf
Improve Concord invite error messaging and Gradle infra detection
2026-07-16 19:44:05 -04:00
Claude 0fa1a26c26 fix(amethyst): restore no-preview suppression of invoice/withdraw UI
Audit of the rich-text convergence found a behavior regression: the Android
RichTextSegmentRenderer.Payment ignored canPreview and always rendered the
interactive MayBeInvoicePreview / MayBeWithdrawal UI. The original
RenderWordWithoutPreview switchboard deliberately showed plain text for
invoice and withdraw segments when previews are suppressed ("Don't offer to
pay invoices" / "Don't offer to withdraw"). Branch on canPreview for those two;
Cashu and Clink stay unconditional (they decode locally, network only on tap),
matching the original.

Verified: :amethyst play debug compiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUiGxXMbjVmgspa1X15o1V
2026-07-16 23:42:26 +00:00
Claude f11dcd74bc feat: collapse geohash composer identity into a tappable avatar
Replaces the always-on nickname field + "post as me" chip in the location-chat
composer with a single leading identity avatar, next to the text field:

- Shows the account's profile picture when posting as yourself, or an
  incognito avatar (person-off) when posting under the anonymous per-cell key.
- Tap toggles anonymous ↔ me (routing through the existing "post as your real
  account?" warning when switching to your real identity).
- Long-press opens a small dialog to set/edit the per-cell nickname, so the
  nickname field no longer has to sit in the chat all the time.

The read-only "✈ Teleported" indicator moves to its own line above the
composer, shown only when the app has determined you're not in the cell.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV
2026-07-16 23:38:31 +00:00
Claude a2ee1880a6 fix(hooks): treat Gradle distribution download failure as infra skip
The pre-push spotless gate already skips itself (warn + exit 0) when
`./gradlew spotlessApply` fails for infra reasons, so a restricted sandbox
can't strand a push over formatting. But its detection regex only covered
dependency-resolution/network errors, not the case where the Gradle wrapper
can't download the pinned distribution itself — e.g. an egress-proxy 40x on
`gradle-<ver>-bin.zip`. That surfaced as a hard BLOCK on an XML-only change
even though CI's spotlessCheck would pass.

Add the distribution-bootstrap markers (the `gradle-<ver>-bin.zip` URL, the
Java `Server returned HTTP response code` download exception, and "could not
install gradle") to the infra-skip regex. These strings only appear when
gradlew failed before running any task, so they can't mask a real
formatting/compile failure (verified: a spotlessKotlinCheck violation still
blocks).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157AWJNEzybTuegstbWiQ6B
2026-07-16 23:38:14 +00:00
Claude 9eb3b75615 fix(concord): clarify invite failure copy for token/decrypt mismatch
concord_invite_failed_incompatible is shown for the Unreadable status,
which fires both for a genuinely newer bundle format AND for the far more
common case where the fragment's unlock token does not decrypt the
published kind-33301 bundle (a stale link, or a coordinate re-posted under
a new token). The old copy asserted "created with a newer version of the
app", which is usually wrong, and suggested "try again after updating"
even though this state is non-retryable.

Reword to lead with the likely cause (outdated/replaced link), keep the
newer-version case as a secondary possibility, and point the user at
getting a fresh invite link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157AWJNEzybTuegstbWiQ6B
2026-07-16 23:33:30 +00:00
Claude 8891e3a1a7 feat: match the Following badge to the FilledTonalButton palette
Per review, the follow badge now uses the same two colours as the tonal
buttons (Show more, profile actions): the shield is secondaryContainer and the
inner figure is onSecondaryContainer. The vector is a following(shield, figure)
builder cached per colour at the call site, so the badge follows the accent and
stays visually consistent with the tonal buttons in both themes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1F9jXNwRGTP8qmsV3Wd69
2026-07-16 23:33:01 +00:00
Claude 69177af390 feat: geohash chat polish — auto teleport, city names, relay list, own-name
Addresses several UX papercuts in the location-chat screens:

- Teleport is no longer a manual composer toggle (which was confusing and
  gameable). The app now determines it: when the device's location is known
  it compares the current cell to the channel cell (objective); otherwise it
  falls back to how the user arrived (map picker = teleported, near-me /
  manual = not). The composer shows a read-only "✈ Teleported" indicator only
  when you're not in the cell. Reading the permission state never prompts.

- The chat top bar now leads with the city name instead of the raw geohash
  code (which users don't care about); the "#geohash · N relays" line is
  secondary and tappable, opening a dialog that lists exactly which relays
  the cell broadcasts to.

- The home "live near you" bubble resolves the cell to a city name too,
  falling back to the #geohash while the reverse-geocode is in flight.

- Own ("purple") messages now show my name/nickname via a new
  LocalChatShowSelfAuthorName composition-local — because a geohash chat can
  post under two identities (anonymous per-cell key or the real account), the
  bubble should say which one each message went out under. Off everywhere
  else, so normal chats keep hiding the redundant self name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV
2026-07-16 23:19:30 +00:00
Claude dd03307ac3 fix: solid accent container tones + keep Following its brand purple
Two more on-device fixes:

- Container roles were a faint 0.12/0.16 tint over the surface, so filled
  shapes like the Settings icon boxes nearly vanished (accent icon on almost no
  background) and tonal buttons looked washed. Retune the container tones to a
  moderately saturated fill (mirroring Material's baseline containers, recoloured
  to the accent) with near-white content in dark mode, so the settings icon
  boxes and profile-header tonal buttons read as white-on-accent again.

- The Following badge keeps its original deep purple (#7F2EFF) instead of
  following the accent — it reads as a brand identity mark, and the accent-tinted
  version looked flat. Reverted to the fixed two-tone vector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1F9jXNwRGTP8qmsV3Wd69
2026-07-16 22:59:40 +00:00
Claude 853d5e1b25 fix: address accent regressions — teal tonal buttons, FAB glyph, follow badge
Three fixes from on-device review:

- Secondary/tertiary CONTAINER roles now derive from the accent (primary)
  instead of the purple theme's teal secondary. FilledTonalButton (profile
  follow/edit/message, "Show more"/"Show anyway", tonal chips) was neutral
  before and had turned teal-on-teal; it now reads as an accent tonal button.
  The solid secondary/tertiary roles stay teal (unchanged, as before).

- onPrimary is white again. Deriving it by max contrast made it black on the
  light accents used in dark mode, flipping FAB glyphs from white to black;
  white reads better on the accent.

- The Following badge is two-tone again: the shield follows the accent while
  the inner figure stays white, instead of a flat single-colour shield. The
  vector is now a following(accent) builder cached per accent at the call site.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1F9jXNwRGTP8qmsV3Wd69
2026-07-16 22:45:20 +00:00
Claude c90353d944 refactor(amethyst): adopt the shared ClickableUrl for plain links
Amethyst's ClickableUrl now delegates plain http(s) links to the shared
commons ClickableUrl (one LocalUriHandler-based implementation for both front
ends); only the Android-specific blossom: case (opening a Blossom media URI via
an Intent) stays local. The public signature is unchanged, so all 12 call sites
are untouched.

Verified: :amethyst play debug compiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUiGxXMbjVmgspa1X15o1V
2026-07-16 22:43:58 +00:00
Claude d43b9c121f feat: dedicated Location Channels list screen in the drawer
Brings geohash location channels to full parity with Public Chats (NIP-28),
Relay Groups (NIP-29) and Concord: a top-level list screen reached from the
drawer's "Feeds" section (and pinnable as a bottom-bar tab), rather than the
joined-cells list living inside the "New location channel" builder.

- Route.GeohashChats (plural) + AppNavigation wiring
- GeohashChatsScreen: DisappearingScaffold + AppBottomBar + FAB, a gradient
  hero banner, rich per-cell cards (gradient location pin, "#cell", precision
  level + resolved city, overflow menu with Open/Leave), and an inviting
  empty state
- GEOHASH_CHATS catalog entry now resolves to the list screen; the "+" FAB
  opens the builder (near me / manual / teleport)
- GEOHASH_CHATS added to the drawer's Feeds section alongside the other
  chat-group types

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV
2026-07-16 22:32:45 +00:00
Claude 19f5065af4 refactor(commons): shared ClickableUrl/ClickableEmail; Desktop drops its duplicate
Desktop's ClickableLink and Amethyst's ClickableUrl were near-identical: the only
real differences are mouse-first styling (Desktop underlines + shows a hand
cursor) and the open mechanism. But LocalUriHandler.openUri opens the browser on
Android AND Desktop (and the mail client for mailto:), so the "open a link"
logic never needed to be platform-specific.

Add ClickableUrl/ClickableEmail to commons/ui/components on LocalUriHandler, with
an `underline` flag so each front end keeps its exact look. Desktop's rich-text
renderer now reuses them (underline = true) for url/email/link-preview/withdraw
and its bespoke ClickableLink is deleted. Amethyst keeps its own blossom-intent-
aware ClickableUrl (blossom never applies to plain links, so this path is
equivalent); Phone stays platform-specific (Android dials, Desktop has no dialer).

Verified: :commons JVM and :desktopApp compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUiGxXMbjVmgspa1X15o1V
2026-07-16 22:24:27 +00:00
Claude dddfa1d380 Merge remote-tracking branch 'origin/main' into claude/amethyst-mobile-colors-kgdfhn 2026-07-16 22:21:55 +00:00
Claude 9eb827fed6 feat(commons): route url/email/phone through the platform strategy
Closes the last rich-text fidelity residual. url/email/phone were rendered
generically by the shared core via RichTextInteractions callbacks, losing each
front end's per-type styling and open behavior. They now go through the
RichTextSegmentRenderer strategy:
- Add Url(url, displayText)/Email(address)/Phone(number) to the contract (with
  plain-text defaults).
- Core routes LinkSegment(no-preview)/SchemelessUrl -> Url, Email -> Email,
  Phone -> Phone; drop the in-core ClickableSpan.
- Amethyst renders them with ClickableUrl/ClickableEmail/ClickablePhone (blossom
  intent + dial preserved); Desktop with ClickableLink / underlined mailto / plain
  phone text.
- RichTextInteractions now carries only onClickHashtag (the one segment the core
  draws itself, with shared icons); the onOpen* callbacks are gone.

Verified: :commons JVM, :desktopApp, :amethyst play debug compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUiGxXMbjVmgspa1X15o1V
2026-07-16 22:12:28 +00:00
Claude 3bd490b58c feat: pin followed geohash channels to the bottom nav
Adds a "Location Channels" category to the bottom-bar settings picker so a
user can pin their joined geohash cells as avatars alongside PublicChat,
RelayGroup and Concord groups. Each pinned cell renders a location-pin
robohash keyed by the geohash, labelled "#<cell>", opening the location
chat when tapped.

- BottomBarEntry.Geohash sealed variant (+ stable key)
- NavBarItem.GEOHASH_CHATS catalog entry in the chats category
- rememberGeohashEntryDisplay resolves the anonymous avatar/route (no
  metadata REQ — the cell is anonymous by design)
- picker/settings and live-bar group branches route geohash through the
  shared GroupEntryDisplay path
- no feed preloader (the cell's message subscription opens from the chat
  screen itself)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172JoMccseEKenyWan6txWV
2026-07-16 22:11:44 +00:00
Claude 901650f4a9 feat: make NIP-05 and image hash-verify symbols follow the accent
The NIP-05 identifier colour and its verified badge were a fixed purple, and
the image hash-verification "passed" seal was a fixed purple drawable — none
followed the selected accent. Redefine the nip05 token to the theme primary
(covering the identifier text, badge and the other nip05 call sites) and tint
the nip_05 and original drawables with the accent at render.

Also switch the hash-verification "failed" icon from a raw Color.Red to
colorScheme.error, matching the themed error red used everywhere else. Removes
the now-unused Nip05EmailColor light/dark constants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1F9jXNwRGTP8qmsV3Wd69
2026-07-16 21:56:29 +00:00
Claude 51f005d22f refactor(commons): unify the rich-text parser cache across Android + Desktop
Replaces the two forked cached parsers (amethyst CachedRichTextParser on
android.util.LruCache + desktop DesktopCachedRichTextParser on ConcurrentLruCache
with a naive isMarkdown) with one shared object in commons/jvmAndroid/richtext,
built on quartz ConcurrentLruCache and keeping amethyst's CommonMark-aware
computeIsMarkdown and content-addressed key (content+tags+callbackUri+authorPubKey).

- Add ConcurrentLruCache.trimToSize(maxItems) (+ tests) for the onTrimMemory path.
- Repoint all amethyst callers (incl. the markdown unit test) and both desktop
  callers; delete both forks.

Verified: :commons JVM, :desktopApp, :amethyst play debug + unit tests compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUiGxXMbjVmgspa1X15o1V
2026-07-16 21:56:12 +00:00