`:commons` is on the CLI classpath, yet it declared Compose UI, Coil, Compose resources, markdown and desktop Compose as dependencies, dragging ~40 MB of UI/Skiko jars into every `amy` distribution. This moves every Compose-dependent file into a new KMP module, `:commonsUI`, that `api`-depends on `:commons`; `:commons` keeps only the Compose runtime (stability annotations + snapshot state) and lifecycle-viewmodel. Files keep their `com.vitorpamplona.amethyst.commons.*` packages, so the split is a build-graph boundary and no consumer import changed. 236 files were `git mv`'d (composables, icons, robohash, theme, Coil fetchers, the `@Composable` relay-client entry points, `composeResources`, and the tests that exercise them). Two headless files needed surgery instead of a move: `GalleryParser` lost a vestigial foundation `@OptIn`, and the `LocalPrivacyLockState`/`lockStateFor` CompositionLocal accessor moved out of `PrivacyLockState` into its own commonsUI file. The feed DAL under `ui/feeds` and `ui/note/ParentNote`+`ReplyContext` stay in `commons` because ViewModels depend on them. `amethyst`, `desktopApp`, `nappletHost` (NappletWebContract serves the shell from composeResources) and `benchmark` now depend on `:commonsUI`; `cli`, `geode` and `marmotBench` do not. commons' androidMain gains an explicit androidx.core KTX dep it previously got transitively through Compose UI. CI, crowdin, the icon-font tools and the escaping hook point at the new composeResources location; CLAUDE.md, commons/ARCHITECTURE.md, a new commonsUI/ARCHITECTURE.md, CONTRIBUTING, BUILDING and the affected skills document the boundary. A plan doc under commons/plans records the classification method and follow-ups. Verified: JVM compiles for commons, commonsUI, cli, desktopApp; Android debug compiles for nappletHost and amethyst; commons/commonsUI/cli JVM test suites; both verifyKmpPurity gates; the cli runtime classpath no longer resolves Compose UI, material3, Skiko or Coil. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N56KzPSYiN5edMRamvKEgD
Material Symbols Subset
The font shipped at
commonsUI/src/commonMain/composeResources/font/material_symbols_outlined.ttf
is a subset of Google's Material Symbols
Outlined variable font,
trimmed to only the codepoints referenced from
commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/icons/symbols/MaterialSymbols.kt.
| Full upstream | Subset | |
|---|---|---|
| Size | ~11 MB | ~410 KB |
| Glyphs | ~3500 | ~210 |
The subset cuts >10 MB out of every commons build artifact, every Android APK, and every Desktop app bundle.
When to regenerate
Run subset.sh whenever you:
- add or remove a
MaterialSymbol("\uXXXX")entry inMaterialSymbols.kt - pin a newer upstream font version
Prerequisites
pip install fonttools brotli
Regenerating
From the repo root:
./tools/material-symbols-subset/subset.sh
That downloads the upstream variable font, intersects its codepoints with the
ones referenced from MaterialSymbols.kt, and overwrites the checked-in
material_symbols_outlined.ttf.
If you already have an upstream .ttf on disk:
./tools/material-symbols-subset/subset.sh /path/to/MaterialSymbolsOutlined-Regular.ttf
Commit the regenerated .ttf alongside the MaterialSymbols.kt change.
How it works
pyftsubset (from fonttools) reads the codepoints we care about, drops every
glyph and OpenType feature not reachable from those codepoints, and rewrites
name, cmap, and GSUB tables accordingly. The Compose resource pipeline
treats the result as a normal TTF — no code changes needed.
Custom glyphs
custom/ holds traced SVG outlines for icons the upstream font will never
carry (third-party logos — currently the OpenTimestamps stamp).
add_custom_glyphs.py bakes them into the subset TTF at end-of-PUA
codepoints (U+F8F0 and up); subset.sh runs it automatically after
pyftsubset, so regenerating the subset keeps them. To add one: trace the
logo to a single-color SVG (potrace output works as-is), drop it in
custom/, register it in the CUSTOM map in add_custom_glyphs.py, add the
matching MaterialSymbol("\uF8Fx") to MaterialSymbols.kt, and rerun
subset.sh (or add_custom_glyphs.py <ttf> to patch the existing font).