- Updates CLAUDE.md tech stack to current versions (Compose 1.10.3, Kotlin 2.3.20). - Reframes kotlin-multiplatform iOS as mature; adds secp256k1-kmp 0.23.0 references. - Updates desktop-expert Main.kt references (code grew from ~270 to 1341 lines and NavigationRail moved to ui/deck/SinglePaneLayout.kt); replaces obsolete "hardcoded ctrl = true" anti-pattern note with accurate isMacOS branching. - Removes compose-desktop.md (superseded by desktop-expert/). - Adds nostr-expert references: nip19-bech32, event-factory, crypto-and-encryption, large-cache. Adds kotlin-expert/common-utilities, compose-expert/rich-text-parsing, android-expert/image-loading. - New skills: account-state (Account + LocalCache), relay-client (subscriptions, filter assemblers, preloaders), feed-patterns (FeedFilter + FeedViewModel family), auth-signers (NostrSigner across internal / NIP-46 / NIP-55).
4.0 KiB
Common Utility Functions
Canonical helpers that repeatedly come up when working in Amethyst. Prefer these to hand-rolling equivalents.
Formatting (commons)
All under commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/util/:
NumberFormatters.ktcountToHumanReadable(counter: Int, noun: String): String—1500 → "1K items",2_500_000 → "2M items". SuffixesK,M,G.countToHumanReadableBytes(bytes: Int): String—1024 → "1 KB", scales through KB/MB/GB/TB.
PubKeyFormatter.kt— condense an npub tonpub1abc…xyzwith a symmetric prefix/suffix truncation. Use in chips and small UI that show an author.EmojiUtils.kt— parse custom emoji (:name:), render bridging to NIP-30emojitags.IterableUtils.kt— small shortcuts likefirstNotNullOfvariants, chunking helpers.PlatformNumberFormatter.kt— expect/actual locale-aware number formatting (delegates toNumberFormaton JVM/Android).
Time (quartz)
quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/TimeUtils.kt — the source of truth for "now in Nostr seconds" and common offsets:
TimeUtils.now() // Long seconds since epoch (Nostr `created_at`)
TimeUtils.oneMinuteAgo()
TimeUtils.fiveMinutesAgo()
TimeUtils.fifteenMinutesAgo()
TimeUtils.oneHourAgo()
TimeUtils.oneDayAgo()
TimeUtils.oneWeekAgo()
TimeUtils.withinTenMinutes(other) // |now - other| < 10m
Constants (TEN_SECONDS, ONE_MINUTE, FIVE_MINUTES, TEN_MINUTES, FIFTEEN_MINUTES, ONE_HOUR, EIGHT_HOURS, ONE_DAY, ONE_WEEK) are in seconds and are what every subscription filter and staleness check uses — keep using them instead of magic numbers.
Hex / bytes / strings (quartz)
Under quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/:
Hex.kt—ByteArray.toHex(),String.hexToByteArray(). MPP-friendly, no java.util.StringUtils.kt— generic helpers (normalization, truncation).StringExt.kt— small extensions (e.g. safe substring).UriParser.kt— NIP-19 / nostr URI friendly URL parsing without java.net.UrlEncoder.kt/Rfc3986.kt— percent-encoding / decoding for URL-safe content.UnicodeNormalizer.kt— NFC normalization for search/matching.
Threading & coroutines
commons/src/commonMain/.../threading/Threading.kt— shared dispatchers andCoroutineScopehelpers for commonMain code.amethyst/src/main/java/.../service/CoroutinesExt.kt— Android-only helpers:launchIO(block),launchMain(block)built on top ofDispatchers.IO/Dispatchers.Main. Use these in ViewModels and services to stop re-spelling the dispatcher every time.amethyst/src/main/java/.../service/MainThreadChecker.kt— debug assertion helper for catching main-thread misuse during dev.
Quartz iterables & JSON
quartz/.../utils/IterableExt.kt— mutation-free filter/map/group helpers used by the cache layer.quartz/.../utils/JsonElementExt.kt— safe navigation for Jackson nodes when parsing unknown-shape JSON.quartz/.../kotlinSerialization/OptimizedJsonMapper.kt— shared Jackson mapper with reifiedfromJson<T>(...)/toJson(...); prefer this to spinning up a localObjectMapper.
Number formatting for Android display
amethyst/src/main/java/.../service/CountFormatter.kt and ByteFormatter.kt wrap the commons formatters with Android-specific pluralization / locale. Use them from Android UI; use the commons functions directly from commonMain.
When to Add vs Reuse
Before introducing a new helper:
grep -r "fun count\|fun format\|fun toHuman" commons/ quartz/— there is almost certainly something already.- If you're about to write
System.currentTimeMillis() / 1000— useTimeUtils.now(). - If you're about to write
String.format("%.1f K", n / 1000.0)— usecountToHumanReadable. - If you need locale-specific rendering on both Android and Desktop, reach for
PlatformNumberFormatter(expect/actual) rather than hard-coding.