- UserAvatar is now the one shared avatar. It keeps the remember(size, modifier) keys and hands drawing to an AvatarImage expect: jvmAndroid uses RobohashFallbackAsyncImage (GIF/AVIF playback included), iOS a plain AsyncImage. UserPictureImage is gone; InnerUserPicture calls UserAvatar. - The ProfilePictureUrl thumbnail-cache wrapping and the local-Blossom request marker are now gated on LocalProfilePictureCache, which the Android AmethystTheme provides. Desktop has neither the fetcher nor the interceptor, so it loads plain URLs and never leaks the marker header. The unused per-call useThumbnailCache flag is removed. - AnimatedImageAutoPlay remembers the drawable, so a bitmap is no longer rewrapped and the effect no longer restarts on every recomposition. The desktop no-op and the expect document that autoPlay has no effect there. - One animated-URL check: isAnimatedGifUrl / isAvifUrl / isAnimatedMediaUrl in commons richtext, used by ZoomableContentView and the desktop GIF paths. - Drop the app's forwarding 3-arg UserFinderFilterAssemblerSubscription. - commonsUI/ARCHITECTURE.md lists the avatar engine and its actuals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168wY9t7i9NC5u3svyMxEz6
3.8 KiB
commonsUI — the Compose half of the shared layer
commonsUI holds every piece of shared code that needs Compose UI and is
therefore useless to the headless cli:
- composables (
ui/<area>,<feature>/ui— e.g.nip64Chess/ui,profile/ui— and the historical flataudiorenderers), ImageVectoricons (icons,hashtags,robohash) and the icon fonts,- theme, layouts, markdown rendering (
ui/markdown, jvmAndroid), - Coil (
service/image:CoilImageBridge+ BlurHash/ThumbHash/Base64/Blossom fetchers), - the
composeResourcestree (translated strings, fonts, the napplet shell + shim files) and the generatedcom.vitorpamplona.amethyst.commons.resources.Res, - the
@Composablerelay-client entry points (observeUser*,*FilterAssemblerSubscription,KeyDataSourceSubscription), - state holders that carry a
foundation/uitype (LevelFeedViewModelwith itsLazyListState,ChatNewMessageStatewithTextFieldValue,EmojiSuggestionStatewithTextFieldState).
It depends on :commons (and :quartz) as api, so a consumer that adds
:commonsUI sees the headless layer transitively. amethyst, desktopApp,
nappletHost and benchmark depend on it; cli, geode, marmotBench
must never.
Same packages as commons
Every file keeps its com.vitorpamplona.amethyst.commons.* package. The split
is a build-graph boundary, not a package rename: nothing in the apps had to
change an import, and a file can move between the two modules with a git mv.
The rule for which module a file lives in is mechanical:
imports
androidx.compose.ui/foundation/material3/animation, Coil,org.jetbrains.compose.resources,Res,org.jetbrains.skia, or declares a@Composable→commonsUI. Otherwise →commons.
The only cross-module gotcha is internal: a commonsUI file cannot see an
internal declaration in commons. Make it public (or move the caller).
The full package taxonomy — including which packages are "mixed" (logic in
commons, composables here, same package name) — is the table in
commons/ARCHITECTURE.md §2. Read that first; this file only documents what
is specific to the UI module.
Source sets
| Source set | For |
|---|---|
commonMain |
Composables, icons, theme, Coil fetchers, composeResources. Gated by verifyKmpPurity like commons. |
jvmAndroid |
Markdown renderer (ui/markdown), Coil-OkHttp + Blossom read-auth fetcher, the viewModel() helper, and the avatar/image engine (ui/components/RobohashAsyncImage.kt: RobohashFallbackAsyncImage, GifProfilePicture) behind the UserAvatar AvatarImage actual. For a user avatar call the commonMain UserAvatar; don't add another avatar composable. |
jvmMain |
Desktop Coil bridge (CoilImageBridge.jvm.kt), a no-op AnimatedImageAutoPlay (Coil decodes only the first frame), compose.desktop.currentOs. dependsOn(jvmAndroid) + skikoMain. |
androidMain |
Android Coil bridge, the AnimatedImageAutoPlay actual that starts/stops GIF/AVIF drawables. dependsOn(jvmAndroid). |
skikoMain |
org.jetbrains.skia pixel helpers shared by desktop JVM + iOS (SkiaBitmapConverter). |
iosMain |
iOS Coil bridge, a plain AsyncImage AvatarImage actual. Compile-only spike today. |
Tooling that points here
- Icon fonts:
tools/material-symbols-subset/subset.shandtools/icon-font/build_icon_font.pyreadicons/and writecomposeResources/font/in this module (see root.claude/CLAUDE.md, "Icons"). - Translations:
crowdin.ymlandtools/strings-migrate/targetcommonsUI/src/commonMain/composeResources/. - CI:
.github/workflows/build.ymlruns:commonsUI:jvmTest,:commonsUI:verifyKmpPurityand the iOS compile/test tasks next to the:commonsones.