mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 11:18:24 +00:00
docs: one UI for Android and Desktop, and the measured Wave 4 plan
Android now ships on laptops, so the whole UI (screens and the navigation shell) moves to commonsUI, amethyst becomes an Android shim, and a new JVM desktopApp replaces the current one with the same UI. - commons/plans/2026-09-27-one-ui-android-desktop.md: the decision, what it supersedes in the sweep tracker, the prerequisites (androidx navigation-compose publishes only jvmStubs for JVM; the Amethyst.instance app root is read by 178 files; Android-only libraries inside screens), and Wave 4 measured: Account's move-group is 77 files / ~23.9k lines inside model/, with 5 hard-blocked files and 14 exit edges, each with a proposed cut; AccountViewModel's Android imports and 29 outside dependencies. - CLAUDE.md, commons/ARCHITECTURE.md, commonsUI/ARCHITECTURE.md and the kotlin-multiplatform, compose-expert and desktop-expert skills no longer say screens and navigation stay platform-native. - The sweep tracker marks its STAY list, Wave 2 part B and the Desktop-phase merges as superseded, and re-scopes Wave 4 from decomposition to moving Account and AccountViewModel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7FuNBSKiyVecARSoE4B9P
This commit is contained in:
+27
-10
@@ -11,9 +11,11 @@ contain implementations of Nostr specifications and utilities to help implement
|
|||||||
`concord/` (`cordXX`), `buzz/`, plus the binding-agnostic RFC 9420 engine in `mls/`. A new protocol
|
`concord/` (`cordXX`), `buzz/`, plus the binding-agnostic RFC 9420 engine in `mls/`. A new protocol
|
||||||
over Nostr belongs here as a package, not as a Gradle module; `quic`/`nestsClient`/`marmotQuic` are
|
over Nostr belongs here as a package, not as a Gradle module; `quic`/`nestsClient`/`marmotQuic` are
|
||||||
modules because they are transports with no Nostr in them. Commons stores
|
modules because they are transports with no Nostr in them. Commons stores
|
||||||
shared code between Amethyst Android (`amethyst`) and Amethyst Desktop (`desktopApp`). The Desktop
|
shared code between Amethyst Android (`amethyst`) and Amethyst Desktop (`desktopApp`). Android now
|
||||||
App is designed to be mouse first and so uses a completely different screen and navigation
|
also ships on laptops, so the direction is **one UI**: every screen and the navigation shell at every
|
||||||
architecture while sharing the back end components with the android counterpart. `cli` ships `amy`,
|
window size move to `commonsUI`, `amethyst` shrinks to an Android shim, and a new `desktopApp`
|
||||||
|
becomes a JVM shim that renders the same UI (see `commons/plans/2026-09-27-one-ui-android-desktop.md`).
|
||||||
|
Until it lands, today's `desktopApp` still has its own mouse-first screens and navigation. `cli` ships `amy`,
|
||||||
a non-interactive JVM command-line client that drives the same `quartz` + `commons` code — used by
|
a non-interactive JVM command-line client that drives the same `quartz` + `commons` code — used by
|
||||||
humans, agents, and interop tests. `quic` is a from-scratch pure-Kotlin QUIC v1 + HTTP/3 +
|
humans, agents, and interop tests. `quic` is a from-scratch pure-Kotlin QUIC v1 + HTTP/3 +
|
||||||
WebTransport client (no JNI, no BouncyCastle), built because no Android-compatible Java QUIC library
|
WebTransport client (no JNI, no BouncyCastle), built because no Android-compatible Java QUIC library
|
||||||
@@ -105,7 +107,9 @@ amethyst/
|
|||||||
codecs. Not WebTransport — the binding has its own ALPNs and writes frames
|
codecs. Not WebTransport — the binding has its own ALPNs and writes frames
|
||||||
straight onto QUIC streams, so it deliberately does not reuse
|
straight onto QUIC streams, so it deliberately does not reuse
|
||||||
`nestsClient`'s `WebTransportSession`.
|
`nestsClient`'s `WebTransportSession`.
|
||||||
- `amethyst/` & `desktopApp/` = Platform-native layouts and navigation
|
- `amethyst/` & `desktopApp/` = Platform shims: process/window entry points, services, system
|
||||||
|
integrations and the actuals of shared ports. Screens and navigation are shared UI and are
|
||||||
|
moving to `commonsUI` (today's `desktopApp` screens are legacy, to be replaced).
|
||||||
- `cli/` = Thin assembly layer over `quartz/` + `commons/` (no new logic
|
- `cli/` = Thin assembly layer over `quartz/` + `commons/` (no new logic
|
||||||
allowed). May also depend on `:geode` (for `amy serve`, which embeds the
|
allowed). May also depend on `:geode` (for `amy serve`, which embeds the
|
||||||
standalone relay); never on `:commonsUI`, `:amethyst` or `:desktopApp`.
|
standalone relay); never on `:commonsUI`, `:amethyst` or `:desktopApp`.
|
||||||
@@ -207,10 +211,20 @@ etc. instead of re-implementing them.
|
|||||||
(StateFlow/SharedFlow), so they belong in `commons`; anything that imports
|
(StateFlow/SharedFlow), so they belong in `commons`; anything that imports
|
||||||
`androidx.compose.ui`/`foundation`/`material3`, Coil, or `Res` belongs in
|
`androidx.compose.ui`/`foundation`/`material3`, Coil, or `Res` belongs in
|
||||||
`commonsUI`.
|
`commonsUI`.
|
||||||
- **Keep native** → screen composables/scaffolding (Desktop `Window` vs Android
|
**Screens and navigation are shared too**: screen composables, the nav host,
|
||||||
`Activity`), navigation (sidebar vs bottom nav), platform interactions
|
and the navigation chrome for every window size (bottom bar, rail, permanent
|
||||||
(gestures, keyboard shortcuts), system integrations (notifications, file
|
drawer) belong in `commonsUI`, because Android runs on laptops and the new
|
||||||
pickers).
|
Desktop app renders the same UI. Adapt to the window with `ScreenLayoutSpec`
|
||||||
|
/ `LocalScreenLayout`, not with a per-platform screen.
|
||||||
|
- **Keep native** → only what is the platform itself: the process/window entry
|
||||||
|
point (Android `Activity`/`Service`, Desktop `Window`/tray/menu bar),
|
||||||
|
system integrations (notifications, file pickers, share sheets, camera,
|
||||||
|
media3, WebView, keyring/Keystore), and the platform `actual`s or port
|
||||||
|
implementations the shared UI calls. A new screen goes in `commonsUI` when
|
||||||
|
its dependencies allow. While `AccountViewModel` is still app-side, one that
|
||||||
|
needs it may live in `amethyst/`, but keep Android APIs out of it (behind a
|
||||||
|
port or slot) so it can move later. Don't add screens to today's `desktopApp`
|
||||||
|
that the shared UI will have to re-create.
|
||||||
|
|
||||||
When extracting a composable: move it to `commonsUI/commonMain/` (see
|
When extracting a composable: move it to `commonsUI/commonMain/` (see
|
||||||
`/compose-expert`), add expect/actual for any platform behavior (see
|
`/compose-expert`), add expect/actual for any platform behavior (see
|
||||||
@@ -429,8 +443,11 @@ Do this before considering the task complete.
|
|||||||
at byte level (`perl -CSD -pe 's/\x{202E}/\\u202E/g'`).
|
at byte level (`perl -CSD -pe 's/\x{202E}/\\u202E/g'`).
|
||||||
|
|
||||||
### Navigation Shell
|
### Navigation Shell
|
||||||
- **Desktop**: Sidebar + main content area
|
One shell, picked by window size rather than by platform: `ScreenLayoutSpec`
|
||||||
- **Android**: Bottom navigation
|
chooses the bottom bar (compact), the rail, or the permanent drawer (wide,
|
||||||
|
landscape, tall enough), and docks the notification panel on very wide windows.
|
||||||
|
It is moving to `commonsUI` with `AppNavigation`. Today's `desktopApp` still has
|
||||||
|
its own sidebar shell, which will be replaced.
|
||||||
|
|
||||||
## Git Workflow
|
## Git Workflow
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: compose-expert
|
name: compose-expert
|
||||||
description: Advanced Compose Multiplatform UI patterns for shared composables. Use when working with visual UI components, state management patterns (remember, derivedStateOf, produceState), recomposition optimization (@Stable/@Immutable visual usage), Material3 theming, custom ImageVector icons, or determining whether to share UI in commonMain vs keep platform-specific. Delegates navigation to android-expert/desktop-expert. Complements kotlin-expert (handles Kotlin language aspects of state/annotations).
|
description: Advanced Compose Multiplatform UI patterns for shared composables. Use when working with visual UI components, state management patterns (remember, derivedStateOf, produceState), recomposition optimization (@Stable/@Immutable visual usage), Material3 theming, custom ImageVector icons, or determining whether to share UI in commonMain vs keep platform-specific. Delegates platform entry points (Activity, Window, tray) to android-expert/desktop-expert; the navigation shell itself is shared UI. Complements kotlin-expert (handles Kotlin language aspects of state/annotations).
|
||||||
---
|
---
|
||||||
|
|
||||||
# Compose Multiplatform Expert
|
# Compose Multiplatform Expert
|
||||||
@@ -34,12 +34,17 @@ Visual UI patterns for sharing composables across Android and Desktop.
|
|||||||
- **Theme utilities**: Color calculations, style helpers
|
- **Theme utilities**: Color calculations, style helpers
|
||||||
- **Material3 components**: Any UI using Material primitives
|
- **Material3 components**: Any UI using Material primitives
|
||||||
|
|
||||||
|
**Screens and navigation are shared too.** Android also ships on laptops and the new
|
||||||
|
Desktop app renders the same UI, so screens, the nav host and the bottom bar / rail /
|
||||||
|
permanent drawer all belong in `commonsUI`. They adapt to the window with
|
||||||
|
`ScreenLayoutSpec` / `LocalScreenLayout`, not per platform. See
|
||||||
|
`commons/plans/2026-09-27-one-ui-android-desktop.md`.
|
||||||
|
|
||||||
### Keep Platform-Specific
|
### Keep Platform-Specific
|
||||||
|
|
||||||
- **Navigation structure**: Bottom nav (Android) vs Sidebar (Desktop)
|
- **Entry points**: Android `Activity`/`Service`, Desktop `Window`, tray, menu bar
|
||||||
- **Screen layouts**: Platform-specific scaffolding
|
- **System integrations**: File pickers, notifications, share sheets, camera, media3, WebView
|
||||||
- **System integrations**: File pickers, notifications, share sheets
|
- **Platform leaves inside shared UI**: behind expect/actual or a slot the shim fills
|
||||||
- **Platform UX**: Gestures, keyboard shortcuts, window management
|
|
||||||
|
|
||||||
### Decision Framework
|
### Decision Framework
|
||||||
|
|
||||||
@@ -563,12 +568,14 @@ fun FeedList(items: List<Item>) {
|
|||||||
4. Add caching if generated dynamically
|
4. Add caching if generated dynamically
|
||||||
5. Wrap in @Composable for easy use
|
5. Wrap in @Composable for easy use
|
||||||
|
|
||||||
### Navigation (Delegate)
|
### Navigation
|
||||||
|
|
||||||
For navigation patterns:
|
The navigation shell is shared (`ScreenLayoutSpec` picks bottom bar, rail or permanent
|
||||||
- Android bottom nav → `android-expert`
|
drawer by window size) and is moving to `commonsUI` with `AppNavigation`. Delegate only
|
||||||
- Desktop sidebar → `desktop-expert`
|
the platform leaves:
|
||||||
- Multi-window → `desktop-expert`
|
- Activity / intent plumbing → `android-expert`
|
||||||
|
- Window, tray, menu bar, multi-window → `desktop-expert`
|
||||||
|
- Today's legacy `desktopApp` sidebar shell → `desktop-expert` (to be replaced)
|
||||||
|
|
||||||
## Related Skills
|
## Related Skills
|
||||||
|
|
||||||
|
|||||||
@@ -248,9 +248,16 @@ application {
|
|||||||
|
|
||||||
## 5. Desktop Navigation Patterns
|
## 5. Desktop Navigation Patterns
|
||||||
|
|
||||||
### NavigationRail (Current Pattern)
|
### NavigationRail (Current Pattern — legacy)
|
||||||
|
|
||||||
Desktop uses **NavigationRail** (vertical sidebar) instead of Android's bottom navigation.
|
> **Being replaced.** Android now also ships on laptops, and the plan is one UI: the
|
||||||
|
> shared navigation shell (bottom bar / rail / permanent drawer picked by window size
|
||||||
|
> via `ScreenLayoutSpec`) moves to `commonsUI`, and a new `desktopApp` becomes a JVM
|
||||||
|
> shim that renders it. Don't grow this sidebar shell or add Desktop-only screens; see
|
||||||
|
> `commons/plans/2026-09-27-one-ui-android-desktop.md`. What stays Desktop-specific is
|
||||||
|
> the window, tray, menu bar, keyboard shortcuts and file system integration.
|
||||||
|
|
||||||
|
Today's desktop app uses **NavigationRail** (vertical sidebar) instead of Android's bottom navigation.
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
Row(Modifier.fillMaxSize()) {
|
Row(Modifier.fillMaxSize()) {
|
||||||
|
|||||||
@@ -52,8 +52,9 @@ Q: Does it vary by platform or by JVM vs non-JVM?
|
|||||||
│ Example: Jackson JSON parsing (JVM library)
|
│ Example: Jackson JSON parsing (JVM library)
|
||||||
│
|
│
|
||||||
└─ Complex/UI-related
|
└─ Complex/UI-related
|
||||||
→ Keep platform-specific
|
→ Share it in commonsUI; expect/actual or a slot only for the platform leaf
|
||||||
Example: Navigation (Activity vs Window too different)
|
Example: Navigation. One shell in commonsUI, picked by window size
|
||||||
|
(ScreenLayoutSpec); only the Activity / Window entry point is native
|
||||||
|
|
||||||
Final check:
|
Final check:
|
||||||
Q: Maintenance cost of abstraction < duplication cost?
|
Q: Maintenance cost of abstraction < duplication cost?
|
||||||
@@ -85,10 +86,13 @@ val jvmAndroid = create("jvmAndroid") {
|
|||||||
```
|
```
|
||||||
**Why:** Jackson is JVM-only, works on Android + Desktop, not iOS/web.
|
**Why:** Jackson is JVM-only, works on Android + Desktop, not iOS/web.
|
||||||
|
|
||||||
**Navigation → platform-specific:**
|
**Navigation → shared, entry points → platform-specific:**
|
||||||
- Android: `MainActivity` (Activity + Compose Navigation)
|
- Shared (`commonsUI`, moving there): the nav host, bottom bar / rail /
|
||||||
- Desktop: `Window` + sidebar + MenuBar
|
permanent drawer, chosen by window size via `ScreenLayoutSpec`
|
||||||
**Why:** UI paradigms fundamentally different.
|
- Android: `MainActivity` hosts it; Desktop: a `Window` (+ MenuBar, tray) hosts it
|
||||||
|
**Why:** Android runs on laptops too, and the new Desktop app renders the same
|
||||||
|
UI. See `commons/plans/2026-09-27-one-ui-android-desktop.md`. Today's
|
||||||
|
`desktopApp` sidebar shell is legacy.
|
||||||
|
|
||||||
## Mental Model: Source Sets as Dependency Graph
|
## Mental Model: Source Sets as Dependency Graph
|
||||||
|
|
||||||
@@ -183,15 +187,15 @@ Quick decision guidelines based on codebase patterns:
|
|||||||
### Sometimes Abstract
|
### Sometimes Abstract
|
||||||
- **Business logic:** YES - state machines, data processing
|
- **Business logic:** YES - state machines, data processing
|
||||||
- **ViewModels:** YES - state + business logic shareable (StateFlow/SharedFlow)
|
- **ViewModels:** YES - state + business logic shareable (StateFlow/SharedFlow)
|
||||||
- **Screen layouts:** NO - platform-native (Window vs Activity)
|
- **Screen layouts:** YES - shared in `commonsUI`; they adapt to window size, not platform
|
||||||
- **Why:** ViewModels contain platform-agnostic state; Screens render differently per platform
|
- **Why:** One UI for Android (phones to laptops) and Desktop; only entry points differ
|
||||||
|
|
||||||
### Rarely Abstract
|
### Rarely Abstract
|
||||||
- **Complex UI components** (composables with heavy platform dependencies)
|
- **Complex UI components** (composables with heavy platform dependencies)
|
||||||
- **Why:** Platform paradigms can differ significantly
|
- **Why:** Platform paradigms can differ significantly
|
||||||
|
|
||||||
### Never Abstract
|
### Never Abstract
|
||||||
- **Navigation** (Activity vs Window fundamentally different)
|
- **Process / window entry points** (Activity, Service, Window, tray)
|
||||||
- **Permissions** (Android vs iOS APIs incompatible)
|
- **Permissions** (Android vs iOS APIs incompatible)
|
||||||
- **Platform UX patterns**
|
- **Platform UX patterns**
|
||||||
- **Why:** Too platform-specific, abstraction creates leaky APIs
|
- **Why:** Too platform-specific, abstraction creates leaky APIs
|
||||||
@@ -203,7 +207,7 @@ Quick decision guidelines based on codebase patterns:
|
|||||||
| PubKeyFormatter, ZapFormatter | ✅ YES | Pure Kotlin, no platform APIs |
|
| PubKeyFormatter, ZapFormatter | ✅ YES | Pure Kotlin, no platform APIs |
|
||||||
| TimeAgoFormatter | ⚠️ ABSTRACTED | Needs StringProvider for localized strings |
|
| TimeAgoFormatter | ⚠️ ABSTRACTED | Needs StringProvider for localized strings |
|
||||||
| ViewModels (state + logic) | ✅ YES | StateFlow/SharedFlow platform-agnostic, Compose Multiplatform lifecycle compatible |
|
| ViewModels (state + logic) | ✅ YES | StateFlow/SharedFlow platform-agnostic, Compose Multiplatform lifecycle compatible |
|
||||||
| Screen layouts (Scaffold, nav) | ❌ NO | Window vs Activity, sidebar vs bottom nav fundamentally different |
|
| Screen layouts (Scaffold, nav) | ✅ YES (moving) | One UI; `ScreenLayoutSpec` picks bottom bar / rail / drawer by window size |
|
||||||
| Image loading (Coil) | ⚠️ ABSTRACTED | Coil 3.x supports KMP, needs expect/actual wrapper |
|
| Image loading (Coil) | ⚠️ ABSTRACTED | Coil 3.x supports KMP, needs expect/actual wrapper |
|
||||||
|
|
||||||
## expect/actual Mechanics
|
## expect/actual Mechanics
|
||||||
@@ -333,8 +337,9 @@ fun validateSignature(...) { ... } // Duplicated!
|
|||||||
// ❌ BAD
|
// ❌ BAD
|
||||||
expect fun NavigationComponent(...)
|
expect fun NavigationComponent(...)
|
||||||
```
|
```
|
||||||
**Why:** Navigation paradigms too different (Activity vs Window)
|
**Why:** It forks the whole navigation UI per platform.
|
||||||
**Fix:** Keep platform-specific, accept duplication
|
**Fix:** Write the navigation once in `commonsUI`; put only the platform leaf
|
||||||
|
(an Activity result, a file picker) behind expect/actual or a slot
|
||||||
|
|
||||||
### 2. Under-Sharing
|
### 2. Under-Sharing
|
||||||
**Problem:** Duplicating business logic across platforms
|
**Problem:** Duplicating business logic across platforms
|
||||||
@@ -384,8 +389,8 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
| **ViewModels** | **commons/commonMain/viewmodels/** | **StateFlow/SharedFlow + logic shareable, Compose MP lifecycle compatible** |
|
| **ViewModels** | **commons/commonMain/viewmodels/** | **StateFlow/SharedFlow + logic shareable, Compose MP lifecycle compatible** |
|
||||||
| UI formatters (pure) | commons/commonMain | Reusable, no dependencies |
|
| UI formatters (pure) | commons/commonMain | Reusable, no dependencies |
|
||||||
| UI components (simple) | commonsUI/commonMain | Cards, buttons, dialogs (Compose UI never goes in `commons`) |
|
| UI components (simple) | commonsUI/commonMain | Cards, buttons, dialogs (Compose UI never goes in `commons`) |
|
||||||
| **Screen layouts** | **Platform-specific** | **Window vs Activity, sidebar vs bottom nav** |
|
| **Screen layouts** | **commonsUI** | **One UI; adapt to window size with `ScreenLayoutSpec`** |
|
||||||
| Navigation | Platform-specific only | Activity vs Window too different |
|
| Navigation | commonsUI (entry point native) | Nav host + chrome shared; Activity / Window hosts it |
|
||||||
| Permissions | Platform-specific only | APIs incompatible |
|
| Permissions | Platform-specific only | APIs incompatible |
|
||||||
| Platform UX (menus, etc.) | Platform-specific only | Native feel required |
|
| Platform UX (menus, etc.) | Platform-specific only | Native feel required |
|
||||||
|
|
||||||
|
|||||||
+15
-10
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
| Consumer | Kind | Uses from `commons` |
|
| Consumer | Kind | Uses from `commons` |
|
||||||
|----------------|------------------------------|----------------------------------------------|
|
|----------------|------------------------------|----------------------------------------------|
|
||||||
| `amethyst` | Android app (touch-first) | everything (models, state, ViewModels) + `commonsUI` |
|
| `amethyst` | Android app (phones, tablets, laptops) | everything (models, state, ViewModels) + `commonsUI` |
|
||||||
| `desktopApp` | Desktop JVM app (mouse-first)| everything (models, state, ViewModels) + `commonsUI` |
|
| `desktopApp` | Desktop JVM app | everything (models, state, ViewModels) + `commonsUI`; today's mouse-first screens are to be replaced by the shared UI |
|
||||||
| `cli` (`amy`) | Headless JVM CLI (no UI) | everything — `commons` is headless by construction; it never sees `commonsUI` |
|
| `cli` (`amy`) | Headless JVM CLI (no UI) | everything — `commons` is headless by construction; it never sees `commonsUI` |
|
||||||
| `nappletHost` | Android WebView sandbox | napplet contract + `commonsUI` (for the shell/shim Compose resources) |
|
| `nappletHost` | Android WebView sandbox | napplet contract + `commonsUI` (for the shell/shim Compose resources) |
|
||||||
| iOS (future) | iOS app | everything + `commonsUI`; expected to share most UI with Android |
|
| iOS (future) | iOS app | everything + `commonsUI`; expected to share most UI with Android |
|
||||||
@@ -15,17 +15,21 @@
|
|||||||
|
|
||||||
- **`quartz/`** — Nostr protocol: events, NIPs, crypto, relay framing. No app
|
- **`quartz/`** — Nostr protocol: events, NIPs, crypto, relay framing. No app
|
||||||
state, no UI, no caches of "what this user follows."
|
state, no UI, no caches of "what this user follows."
|
||||||
- **`commons/`** — everything an Amethyst *client* needs that isn't a
|
- **`commons/`** — everything an Amethyst *client* needs that isn't
|
||||||
platform-native screen, navigation shell, **or Compose UI**: domain models
|
platform-specific **or Compose UI**: domain models
|
||||||
(`Note`, `User`), in-memory state holders, ViewModels, the relay-subscription
|
(`Note`, `User`), in-memory state holders, ViewModels, the relay-subscription
|
||||||
client, shared business services.
|
client, shared business services.
|
||||||
- **`commonsUI/`** — the Compose UI components that more than one front end
|
- **`commonsUI/`** — the Compose UI components that more than one front end
|
||||||
renders, plus everything only they need (icons, theme, Coil fetchers,
|
renders, plus everything only they need (icons, theme, Coil fetchers,
|
||||||
markdown, the `composeResources` strings/fonts and the generated `Res`).
|
markdown, the `composeResources` strings/fonts and the generated `Res`).
|
||||||
Depends on `commons` as `api`. See `commonsUI/ARCHITECTURE.md`.
|
Depends on `commons` as `api`. See `commonsUI/ARCHITECTURE.md`.
|
||||||
- **`amethyst/` & `desktopApp/`** — platform-native screens, navigation
|
- **`amethyst/` & `desktopApp/`** — platform shims: the Activity / Window
|
||||||
(bottom-nav vs sidebar), gestures, system integration. They assemble
|
entry points, services, system integration, and the platform implementations
|
||||||
`commons` pieces; they should not re-implement them.
|
of shared ports. They assemble `commons` pieces; they should not
|
||||||
|
re-implement them. **Screens and navigation are being moved into
|
||||||
|
`commonsUI`** so Android (which now also runs on laptops) and a new JVM
|
||||||
|
Desktop app render one UI; see
|
||||||
|
[`plans/2026-09-27-one-ui-android-desktop.md`](plans/2026-09-27-one-ui-android-desktop.md).
|
||||||
|
|
||||||
> The goal is **"write it once in `commons`"**. Before adding a manager, cache,
|
> The goal is **"write it once in `commons`"**. Before adding a manager, cache,
|
||||||
> filter, ViewModel, or composable to an app module, check whether it already
|
> filter, ViewModel, or composable to an app module, check whether it already
|
||||||
@@ -223,9 +227,10 @@ compiles: `commonMain` → `jvmAndroid` → platform-specific. See
|
|||||||
|
|
||||||
### Where does my code go? (quick guide)
|
### Where does my code go? (quick guide)
|
||||||
1. **Pure Nostr protocol** (events/NIPs/crypto)? → not here, it's `quartz`.
|
1. **Pure Nostr protocol** (events/NIPs/crypto)? → not here, it's `quartz`.
|
||||||
2. **A composable** rendered by ≥2 front ends, or that you want iOS to share? →
|
2. **A composable** (screens and navigation chrome included)? → `commonsUI`, in
|
||||||
`commonsUI`, in `ui/<area>` or `<feature>/ui` (same package tree as here).
|
`ui/<area>` or `<feature>/ui` (same package tree as here). Also anything that
|
||||||
Also anything that imports Coil, `Res`, or a `foundation`/`ui` state type.
|
imports Coil, `Res`, or a `foundation`/`ui` state type. Only the platform
|
||||||
|
entry points and system integrations stay in `amethyst/`/`desktopApp/`.
|
||||||
3. **A ViewModel / `StateFlow` state holder**? → `viewmodels` or `state` (or
|
3. **A ViewModel / `StateFlow` state holder**? → `viewmodels` or `state` (or
|
||||||
`<feature>` if feature-scoped). Keep it CLI-safe where practical.
|
`<feature>` if feature-scoped). Keep it CLI-safe where practical.
|
||||||
4. **Relay subscription / filter assembly**? → `relayClient`.
|
4. **Relay subscription / filter assembly**? → `relayClient`.
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
# Full sweep: `amethyst/` → `:commons` migration candidates
|
# Full sweep: `amethyst/` → `:commons` migration candidates
|
||||||
|
|
||||||
|
> **Direction change (2026-09-27): one UI.** Android now ships on laptops, so every
|
||||||
|
> screen and the navigation shell move to `commonsUI`, `amethyst` becomes an Android
|
||||||
|
> shim, and a new JVM `desktopApp` replaces the current one with the same UI. See
|
||||||
|
> [2026-09-27-one-ui-android-desktop.md](2026-09-27-one-ui-android-desktop.md). That
|
||||||
|
> supersedes, below: the STAY list's screens/navigation entries, "decompose `Account`,
|
||||||
|
> shrink `AccountViewModel`" (both now **move**), Wave 2 part B (dropped), and the
|
||||||
|
> "Desktop phase" merges of Desktop's own forks (dropped). This file stays the log of
|
||||||
|
> what moved; Wave 4 is planned and measured in the new plan.
|
||||||
|
|
||||||
> **Execution status (updated 2026-08-30, same branch):** Waves 0-1 are DONE
|
> **Execution status (updated 2026-08-30, same branch):** Waves 0-1 are DONE
|
||||||
> on this branch — the 12 shim deletions, the 38-file relayClient batch
|
> on this branch — the 12 shim deletions, the 38-file relayClient batch
|
||||||
> (with `AccountScopedQuery` generalized to `IAccount`), the okhttp stack →
|
> (with `AccountScopedQuery` generalized to `IAccount`), the okhttp stack →
|
||||||
@@ -297,10 +306,15 @@ formatters).
|
|||||||
|
|
||||||
## STAY (correctly platform-native)
|
## STAY (correctly platform-native)
|
||||||
|
|
||||||
- **Navigation shell & screens**: `*Screen.kt`, `*TopBar.kt`, `New*Button.kt`,
|
> **Revised 2026-09-27.** The navigation shell and the screens no longer stay: they move
|
||||||
|
> to `commonsUI` (see the one-UI plan). What is listed below is what remains
|
||||||
|
> platform-native. The media, capture and service entries stay as *implementations*;
|
||||||
|
> the screens that render them reach them through ports or slots.
|
||||||
|
|
||||||
|
- ~~**Navigation shell & screens**: `*Screen.kt`, `*TopBar.kt`, `New*Button.kt`,
|
||||||
`INav`/`Route`/`RouteMaker`/`AppNavigation`, drawer/bottom-bar,
|
`INav`/`Route`/`RouteMaker`/`AppNavigation`, drawer/bottom-bar,
|
||||||
`AccountScreen`/`AccountSessionManager`/`LoggedInPage`, `loggedOff/`,
|
`AccountScreen`/`AccountSessionManager`/`LoggedInPage`, `loggedOff/`,
|
||||||
`settings/` screens (~480 files import INav/Route — by design).
|
`settings/` screens.~~ Moving to `commonsUI` (`RouteMaker` to `commons`).
|
||||||
- **Process/DI roots**: `Amethyst.kt`, `AppModules.kt`, `EncryptedStorage`,
|
- **Process/DI roots**: `Amethyst.kt`, `AppModules.kt`, `EncryptedStorage`,
|
||||||
`LocalPreferences`, `DebugUtils`, `model/accountsCache`,
|
`LocalPreferences`, `DebugUtils`, `model/accountsCache`,
|
||||||
`model/preferences/` (the one genuinely-Android model package: DataStore/
|
`model/preferences/` (the one genuinely-Android model package: DataStore/
|
||||||
@@ -807,7 +821,11 @@ Options, for the maintainer to pick:
|
|||||||
`ui/theme/Shape.kt` into `commons/ui/theme/Sizes.kt`, plus
|
`ui/theme/Shape.kt` into `commons/ui/theme/Sizes.kt`, plus
|
||||||
`painterRes`/`TimeAgo`/`NewItemsBubble` decisions — the audit's
|
`painterRes`/`TimeAgo`/`NewItemsBubble` decisions — the audit's
|
||||||
"strings-only" tally under-counted transitive deps.
|
"strings-only" tally under-counted transitive deps.
|
||||||
- **Wave 4: Account/AccountViewModel decomposition** — long-tail.
|
- **Wave 4: Account/AccountViewModel** — **re-scoped 2026-09-27** from
|
||||||
|
"decomposition" to **moving both to `commons`**. Measured and sequenced in
|
||||||
|
[2026-09-27-one-ui-android-desktop.md](2026-09-27-one-ui-android-desktop.md): the
|
||||||
|
`Account` group is 77 files / ~23.9k lines inside `model/`, with 5 hard-blocked files
|
||||||
|
and 14 exit edges to cut.
|
||||||
|
|
||||||
### Environment notes for the next session (hard-won)
|
### Environment notes for the next session (hard-won)
|
||||||
|
|
||||||
@@ -924,6 +942,10 @@ and `RailCapability` use it, `OnchainZapResolver` does not.
|
|||||||
|
|
||||||
### Wave 2 part B — compatibility analysis and the road to deleting `DesktopLocalCache` (2026-09-19)
|
### Wave 2 part B — compatibility analysis and the road to deleting `DesktopLocalCache` (2026-09-19)
|
||||||
|
|
||||||
|
> **Dropped 2026-09-27.** The new desktop app uses `LocalCache` and `Account` directly;
|
||||||
|
> the current `desktopApp` keeps its fork until it is retired. The analysis below is
|
||||||
|
> kept for the record.
|
||||||
|
|
||||||
Part A moved the cache. Part B is retiring the Desktop fork. The naive framing
|
Part A moved the cache. Part B is retiring the Desktop fork. The naive framing
|
||||||
("repoint ~70 consumers and delete 1,177 lines") is wrong; what follows is the
|
("repoint ~70 consumers and delete 1,177 lines") is wrong; what follows is the
|
||||||
measured picture.
|
measured picture.
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
---
|
||||||
|
title: "One UI: the whole app moves to commonsUI, Android and Desktop become shims"
|
||||||
|
type: refactor
|
||||||
|
status: in-progress
|
||||||
|
date: 2026-09-27
|
||||||
|
owner: commons
|
||||||
|
consumers: amethyst, desktopApp, commonsUI
|
||||||
|
---
|
||||||
|
|
||||||
|
# One UI for Android and Desktop
|
||||||
|
|
||||||
|
## The decision (maintainer, 2026-09-27)
|
||||||
|
|
||||||
|
Android now ships on laptops, so Amethyst Android needs a desktop-class UI. Rather than
|
||||||
|
maintain two desktop UIs, there will be one:
|
||||||
|
|
||||||
|
- **`commonsUI` holds the whole app UI.** Every screen, the navigation host, and the
|
||||||
|
navigation chrome at every size (bottom bar, rail, permanent drawer, docked panels).
|
||||||
|
- **`amethyst/` becomes an Android shim**: Activities, services, notifications, media3,
|
||||||
|
camera, WebView, Health Connect, Keystore/DataStore actuals, flavours. Nothing that is not
|
||||||
|
Android itself.
|
||||||
|
- **A new `desktopApp` becomes a JVM shim** that runs the same `commonsUI` app, so Desktop
|
||||||
|
looks exactly like Android on a laptop: Window, tray, menu bar, keyring, file pickers.
|
||||||
|
It **replaces** the current `desktopApp`, which keeps shipping until the new one reaches
|
||||||
|
parity, then goes.
|
||||||
|
|
||||||
|
This supersedes the "screens and navigation stay platform-native" rule that
|
||||||
|
`.claude/CLAUDE.md`, `commons/ARCHITECTURE.md`, the `kotlin-multiplatform` /
|
||||||
|
`compose-expert` / `desktop-expert` skills and the sweep tracker's STAY list used to state.
|
||||||
|
Those were updated alongside this plan.
|
||||||
|
|
||||||
|
## What this changes in the running migration
|
||||||
|
|
||||||
|
The tracker ([2026-08-30-commons-migration-sweep.md](2026-08-30-commons-migration-sweep.md))
|
||||||
|
stays the log of what moved. These of its conclusions no longer hold:
|
||||||
|
|
||||||
|
| Old conclusion | Now |
|
||||||
|
|---|---|
|
||||||
|
| `Account` is a god object: "decompose, don't move". `AccountViewModel`: "shrink, don't move". | **Move both** to `commons`. They were only to be decomposed because Desktop was going to keep its own; the shared screens now need the same `Account` on both platforms. Narrow interfaces can still be carved out later, for tests and the CLI, but they are not a prerequisite. |
|
||||||
|
| STAY: `*Screen.kt`, `*TopBar.kt`, `AppNavigation`, `INav`/`Route`/`RouteMaker`, drawer/bottom bar, `LoggedInPage`, `loggedOff/`, `settings/` screens | **All move** to `commonsUI` (`RouteMaker` to `commons`, it has no Compose). |
|
||||||
|
| Wave 2 part B: turn `DesktopLocalCache` into a facade over `EventCache` | **Dropped.** The new desktop app uses `LocalCache` and `Account` directly. The old `desktopApp` keeps its fork until it is retired. |
|
||||||
|
| "Desktop phase": merge Desktop's `ToggleableTimeAgoText`, `TimeAgoFormatter`, `ChatBubbleLayout` forks onto the shared ones | **Dropped** for the same reason. |
|
||||||
|
| `jdk.localedata` in Desktop's packaged runtime (+~28 MB) is "a packaging call" | **Required** by the new desktop app: the shared date formatters need the CLDR data. |
|
||||||
|
|
||||||
|
## Where we start from
|
||||||
|
|
||||||
|
The Android app already contains the laptop UI. `ScreenLayoutSpec` (now in
|
||||||
|
`commonsUI/…/ui/layouts/ScreenLayout.kt`) picks one of three navigation tiers from the window
|
||||||
|
size alone (bottom bar below 600dp, rail, permanent drawer when wide, landscape and at least
|
||||||
|
600dp tall), docks the notification panel from 1200dp, and caps every destination to a 600dp
|
||||||
|
reading column (`CappedScreenContent`). A JVM window can feed it `widthDp`/`heightDp` and
|
||||||
|
get the same answer. Still app-side and part of the move: `AppNavigation.kt` (1,370 lines),
|
||||||
|
`AppNavigationRail.kt`, the permanent drawer in `AccountSwitcherAndLeftDrawerLayout.kt`,
|
||||||
|
`MessagesTwoPane.kt`, and `MainActivity.kt` (493 lines, the part that is not Activity
|
||||||
|
plumbing).
|
||||||
|
|
||||||
|
## Prerequisites the move surfaced
|
||||||
|
|
||||||
|
- **Navigation library.** The app uses `androidx.navigation:navigation-compose` 2.10.1. Its
|
||||||
|
Gradle module metadata publishes an `androidJvm` variant and only **`jvmStubs`** for `jvm`
|
||||||
|
(checked 2026-09-27), so it cannot run the nav host on Desktop. The multiplatform path is
|
||||||
|
JetBrains' `org.jetbrains.androidx.navigation:navigation-compose`, which resolves to the
|
||||||
|
androidx artifact on Android. That swap (and its licence check, per CLAUDE.md) comes before
|
||||||
|
`AppNavigation` can move.
|
||||||
|
- **The app root.** `Amethyst.instance` (the `AppModules` graph) is read by 178 files, 115 of
|
||||||
|
them under `ui/`. Shared screens cannot reach an Android `Application`. The root needs a
|
||||||
|
commons-side interface for what screens read from it, provided once at the composition root
|
||||||
|
(as `LocalUserFinderAccount` / `LocalEventFinder` already are), with an Android and a JVM
|
||||||
|
implementation.
|
||||||
|
- **Android-only libraries rendered inside screens.** Each needs an expect/actual or a slot the
|
||||||
|
shim fills: media3 (46 files), Vico charts (8), WebView (5), CameraX (4), Health Connect (4),
|
||||||
|
ML Kit (3, `play` flavour only).
|
||||||
|
- **Current-Desktop-only features.** The current `desktopApp` (260 files, ~70k lines) has
|
||||||
|
features Android lacks: deck columns, the article editor, highlights, scheduled-post
|
||||||
|
screens, keyboard shortcuts, menu bar, tray. Each needs a call before the old app is
|
||||||
|
retired: bring it into `commonsUI` for both platforms, or keep it in the new JVM shim. That
|
||||||
|
inventory is not done yet.
|
||||||
|
|
||||||
|
## Wave 4, measured: what moves with `Account`
|
||||||
|
|
||||||
|
Measured 2026-09-27 on `main` @ `c13e496c` with a closure script (not committed; the method
|
||||||
|
is below) over `amethyst/src/{main,play,fdroid}`.
|
||||||
|
|
||||||
|
**An unconstrained closure is useless.** Following every edge from `Account.kt` reaches
|
||||||
|
1,724 of the app's 1,762 files, because a few edges leave `model/` for the app root
|
||||||
|
(`Amethyst.kt`), `ui/navigation` and `ui/screen`, and those reach everything. The useful
|
||||||
|
measure is the group reachable **inside `model/`**, plus the list of edges that leave it.
|
||||||
|
Each leaving edge is a seam to cut before the group can move.
|
||||||
|
|
||||||
|
### The group
|
||||||
|
|
||||||
|
- **75 files, 22,753 lines**: `Account.kt`, `AccountSettings.kt`, the `Account*Actions` files,
|
||||||
|
`EventBroadcaster`, and 60-odd per-feature state holders (`nip51Lists/*`, `nip65RelayList`,
|
||||||
|
`serverList/*`, `topNavFeeds/*`, `nip46Signer/*`, `cordn/*`, …). That is 75 of the 89 files
|
||||||
|
in `model/`.
|
||||||
|
- **Adding `EventProcessor`** (`ui/screen/loggedIn/DecryptAndIndexProcessor.kt`, which
|
||||||
|
`Account` constructs) brings it to 77 files, 23,850 lines, and adds no new exit edge except
|
||||||
|
two `Amethyst.instance.notificationDispatcher` calls.
|
||||||
|
- **11 files use `java.*`** (`BigDecimal`, `ConcurrentHashMap`, `UUID`, `Base64`, `File`,
|
||||||
|
`Locale`, and `OkHttpClient` in `CashuWalletState`). So the group lands in
|
||||||
|
**`commons/jvmAndroid`** first and promotes to `commonMain` later, the same route
|
||||||
|
`LocalCache` took.
|
||||||
|
- **No Compose UI** in any of them.
|
||||||
|
|
||||||
|
### Hard blockers inside the group (5 files)
|
||||||
|
|
||||||
|
| File | Blocker | Proposed cut |
|
||||||
|
|---|---|---|
|
||||||
|
| `Account.kt` | `BuildConfig.VERSION_NAME` (donation prompt, 3 sites) | constructor parameter `appVersion: String` |
|
||||||
|
| `AccountSyncedSettingsInternal.kt` | `Resources.getSystem()` + `ConfigurationCompat` for the system language list; `DefaultBottomBarEntries` from `ui/navigation/bottombars/NavBarItem.kt` | languages: an injected `() -> List<String>` or a small expect/actual; defaults: move the default entry list to `commons/model/navigation`, beside `BottomBarEntry` |
|
||||||
|
| `GeohashChatIdentityState.kt` | `androidx.core.content.edit`, `LegacySharedPreferences`, `LocalPreferences.LEGACY_WRITES_RETIRED`, `Amethyst.instance.encryptedStorage` | the legacy-prefs read/write is a migration path; put it behind a `GeohashIdentityLegacyStore` port, implemented in the app |
|
||||||
|
| `AccountZapActions.kt` | `onError: (StringResource, String?)` with `Res.string.bolt12_*` (compose resources, which `commons` cannot see) | a typed error (sealed class) that the UI maps to a string |
|
||||||
|
| `nip46Signer/Nip46ConsentBridge.kt` | `Res` + `loadStringRes`; `Amethyst.instance.appContext`; the app's `SignerConnectCoordinator` / `SignerConsentCoordinator` / napplet op labels | it is the Android consent-dialog bridge: leave it in the app and inject it into `Account` through an interface |
|
||||||
|
|
||||||
|
### Edges that leave the group (14 targets)
|
||||||
|
|
||||||
|
| Target | Used for | Proposed cut |
|
||||||
|
|---|---|---|
|
||||||
|
| `Amethyst.kt` | `keyCache` (Account), `encryptedStorage` (Geohash), `appContext` (Nip46 bridge), `notificationDispatcher` (EventProcessor) | constructor parameters / ports; the notification dispatcher gets an interface |
|
||||||
|
| `LocalPreferences.kt` | `saveToEncryptedStorage(accountSettings)` on settings change | a `AccountSettingsPersister` port, implemented in the app |
|
||||||
|
| `DebugUtils.kt` | `logTime` (2 sites) | move `logTime` to `commons/util` (it is timing + `Log`) |
|
||||||
|
| `service/MainThreadChecker.kt` | `checkNotInMainThread` (HiddenUsersState) | the same settable hook `LocalCache` already needed |
|
||||||
|
| `service/location/LocationState.kt` | the `LocationResult` type in `geolocationFlow` and the around-me feed | move the result type to `commons`; the `LocationManager` half stays |
|
||||||
|
| `service/uploads/FileHeader.kt` | the `FileHeader` data type in three send methods | split: the data class to `commons`, the `MediaMetadataRetriever` reader stays |
|
||||||
|
| `service/relayClient/…/BuzzMembershipEoseManager.kt` | the `MembershipNotificationKinds` constant | move the constant to `commons/model/buzz` |
|
||||||
|
| `ui/navigation/bottombars/NavBarItem.kt` | `DefaultBottomBarEntries` | see the table above |
|
||||||
|
| `ui/screen/loggedIn/DecryptAndIndexProcessor.kt` | `EventProcessor`, built by `Account` | moves with the group (see above) |
|
||||||
|
| `AccountSecretsStore.kt`, `LegacySharedPreferences.kt` | Geohash identity storage | behind the Geohash port above |
|
||||||
|
| `connectedApps/consent/*Coordinator.kt`, `napplet/NostrSignerOpLabels.kt` | Nip46 consent bridge | stay in the app with the bridge |
|
||||||
|
|
||||||
|
About twenty small cuts, most of them "pass it in" or "move one declaration". None is a
|
||||||
|
redesign.
|
||||||
|
|
||||||
|
## Wave 4, measured: `AccountViewModel`
|
||||||
|
|
||||||
|
`AccountViewModel.kt` is 3,303 lines.
|
||||||
|
|
||||||
|
- **Android imports.** Its Android imports are `Context` (3 methods take one), `Toast`,
|
||||||
|
`Uri`, `Handler`/`Looper`, `android.util.LruCache` (5 sites), `NotificationManager` and
|
||||||
|
`ContextCompat`. It has no `R` references left.
|
||||||
|
- **`Amethyst.instance`.** It reads the app root in six places: `websocketBuilder`,
|
||||||
|
`relayStats`, `powPublishQueue`, `localBlossomCacheProbe`, `blossomResolver` and
|
||||||
|
`appContext`.
|
||||||
|
- **Other app files.** Outside the `Account` group it depends on 29 app files. They fall into
|
||||||
|
three kinds:
|
||||||
|
- **Headless, should move with it:** `RelaySubscriptionsCoordinator`, `ClinkDebitPayer`,
|
||||||
|
`CallSessionBridge`, `NestBridge`, `MarkChatRoomsAsRead`, `RowUnread`,
|
||||||
|
`ReloadMintViewModel`, `EventSync`, `CardFeedContentState`, `RoleBasedHttpClientBuilder`.
|
||||||
|
- **Payment and intent handlers:** `ZapPaymentHandler`, `V4VPaymentHandler`,
|
||||||
|
`LightningAddressResolver`, `MeltProcessor`, `ZapCustomDialog.payViaIntent`. They take an
|
||||||
|
Android `Context` to fire payment intents; that needs a `PaymentLauncher` port.
|
||||||
|
- **Composable files it borrows a type or constant from:** `ZapAmountCommentNotification`
|
||||||
|
(`MultiSetCompose`), `ZapraiserStatus` (`ReactionsRow`), `NOTIFICATION_LAST_READ_KEY`
|
||||||
|
(`NotificationScreen`). The declarations move to `commons`; the composables don't have to.
|
||||||
|
- **Genuinely Android:** `MediaSaverToDisk`, `MarmotGroupIconUploader`,
|
||||||
|
`dismissNotificationForEvent` (`NotificationUtils`). These go behind ports.
|
||||||
|
|
||||||
|
`AccountViewModel` moves after the `Account` group, into `commons/viewmodels` (jvmAndroid
|
||||||
|
first).
|
||||||
|
|
||||||
|
## Sequence
|
||||||
|
|
||||||
|
1. **Docs** (this plan, and the rule changes in CLAUDE.md, both ARCHITECTURE files, three
|
||||||
|
skills and the tracker). Done 2026-09-27.
|
||||||
|
2. **Cut the `Account` group's seams**, one small PR each, in the app, with no move yet. Every
|
||||||
|
cut is a behaviour-preserving refactor that compiles and tests on its own:
|
||||||
|
- the `BuildConfig` parameter;
|
||||||
|
- `DefaultBottomBarEntries`;
|
||||||
|
- `MembershipNotificationKinds`;
|
||||||
|
- `logTime`;
|
||||||
|
- `checkNotInMainThread`;
|
||||||
|
- the `LocationResult` and `FileHeader` types;
|
||||||
|
- the settings-persister, Geohash legacy-store, Nip46-bridge and notification-dispatcher
|
||||||
|
ports;
|
||||||
|
- the typed zap error.
|
||||||
|
3. **Move the group** (77 files) to `commons/jvmAndroid` in one PR. Desktop keeps its
|
||||||
|
`DesktopIAccount` until the old app is retired; `IAccount` stays as the port it already is.
|
||||||
|
4. **`AccountViewModel`**: the same recipe, using the dependency list above.
|
||||||
|
5. **The shared composables and their helpers** (sized in the tracker's 2026-09-27 section):
|
||||||
|
- `RouteMaker`;
|
||||||
|
- drop the `accountViewModel` overloads of the `observe*` helpers;
|
||||||
|
- `DisappearingScaffold`'s immersive-scrolling read goes onto `DisplaySettings`;
|
||||||
|
- then `UserProfilePicture`, `UsernameDisplay`, `Loaders`, `RichTextViewer`,
|
||||||
|
`NoteCompose`.
|
||||||
|
|
||||||
|
Once `AccountViewModel` is in `commons`, these move without retyping.
|
||||||
|
6. **Screens**, feature by feature, into `commonsUI`.
|
||||||
|
7. **Navigation**: the library swap, then `AppNavigation` + rail + drawer + bottom bar.
|
||||||
|
8. **The app root port** and the new JVM shim. Then the Desktop feature inventory, and
|
||||||
|
retiring the old `desktopApp`.
|
||||||
|
|
||||||
|
Steps 2–5 can interleave. Step 5's helpers can start before 3–4 if they take `Account` /
|
||||||
|
`AccountViewModel` unchanged and only move later.
|
||||||
|
|
||||||
|
## Method (so the numbers can be re-run)
|
||||||
|
|
||||||
|
The closure script indexes every public top-level declaration in
|
||||||
|
`amethyst/src/{main,play,fdroid}`. For each file it follows edges of four kinds:
|
||||||
|
|
||||||
|
- explicit imports;
|
||||||
|
- wildcard imports;
|
||||||
|
- inline fully-qualified names;
|
||||||
|
- **same-package references**, which need no import. This is the undercount the tracker's
|
||||||
|
header warns about.
|
||||||
|
|
||||||
|
A same-package lowercase name reached through a `.` counts only when it is an extension. The
|
||||||
|
lexer blanks strings but keeps `${…}` templates. A file is marked blocked by any of:
|
||||||
|
|
||||||
|
- `android.*`, `com.google.*`, or a non-KMP `androidx.*`;
|
||||||
|
- `R`/`BuildConfig`;
|
||||||
|
- a library `commons` lacks.
|
||||||
|
|
||||||
|
Symbols from `commons.*` that actually live in `commonsUI` (`Res`, `loadStringRes`) are
|
||||||
|
checked separately. The first pass missed them. Known over-count: same-package token matching
|
||||||
|
can link a file to a same-named declaration it does not use; the edges above were checked by
|
||||||
|
hand.
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
# commons plans
|
# commons plans
|
||||||
|
|
||||||
_Audited 2026-06-30 (+ 2026-09-12 split entry, 2026-09-27 migration entries)._
|
_Audited 2026-06-30 (+ 2026-09-12 split entry, 2026-09-27 migration and one-UI entries)._
|
||||||
|
|
||||||
## In progress
|
## In progress
|
||||||
| Plan | Summary |
|
| Plan | Summary |
|
||||||
| ---- | ------- |
|
| ---- | ------- |
|
||||||
| [2026-05-04-custom-feeds-plan.md](2026-05-04-custom-feeds-plan.md) | Custom feed creation/discovery/management for Desktop; core model + builder + kind 31890 + desktop UI shipped, but relay-filter layer, DVM marketplace, kind 10090 sync, and list resolution still pending. |
|
| [2026-05-04-custom-feeds-plan.md](2026-05-04-custom-feeds-plan.md) | Custom feed creation/discovery/management for Desktop; core model + builder + kind 31890 + desktop UI shipped, but relay-filter layer, DVM marketplace, kind 10090 sync, and list resolution still pending. |
|
||||||
| [2026-05-06-nest-subscription-manager-extraction.md](2026-05-06-nest-subscription-manager-extraction.md) | Split the per-speaker subscription state machine out of `NestViewModel`; only the `ActiveSubscription` stepping-stone is extracted so far. |
|
| [2026-05-06-nest-subscription-manager-extraction.md](2026-05-06-nest-subscription-manager-extraction.md) | Split the per-speaker subscription state machine out of `NestViewModel`; only the `ActiveSubscription` stepping-stone is extracted so far. |
|
||||||
| [2026-08-30-commons-migration-sweep.md](2026-08-30-commons-migration-sweep.md) | The running tracker for moving `amethyst/` code into `commons`/`commonsUI`: waves, what moved each round, what stays and why. `LocalCache` has moved; retiring `DesktopLocalCache` (Wave 2 part B) and the `AccountViewModel` hub composables are next. |
|
| [2026-09-27-one-ui-android-desktop.md](2026-09-27-one-ui-android-desktop.md) | **The target.** Android ships on laptops, so the whole UI (screens + navigation shell) moves to `commonsUI`, `amethyst` becomes an Android shim, and a new JVM `desktopApp` renders the same UI. Measures Wave 4: `Account`'s 77-file move-group, its 5 blocked files and 14 seams, and `AccountViewModel`'s dependencies; sequences the rest. |
|
||||||
|
| [2026-08-30-commons-migration-sweep.md](2026-08-30-commons-migration-sweep.md) | The running log of moving `amethyst/` code into `commons`/`commonsUI`: waves, what moved each round. `LocalCache` has moved. Its STAY list and Wave 2 part B are superseded by the one-UI plan above. |
|
||||||
| [2026-05-30-amethyst-to-commons-migration.md](2026-05-30-amethyst-to-commons-migration.md) | The original roadmap for the same move; superseded in practice by the sweep tracker above. |
|
| [2026-05-30-amethyst-to-commons-migration.md](2026-05-30-amethyst-to-commons-migration.md) | The original roadmap for the same move; superseded in practice by the sweep tracker above. |
|
||||||
|
|
||||||
## Queued
|
## Queued
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ therefore useless to the headless `cli`:
|
|||||||
its `LazyListState`, `ChatNewMessageState` with `TextFieldValue`,
|
its `LazyListState`, `ChatNewMessageState` with `TextFieldValue`,
|
||||||
`EmojiSuggestionState` with `TextFieldState`).
|
`EmojiSuggestionState` with `TextFieldState`).
|
||||||
|
|
||||||
|
Its end state is **the whole app UI**: every screen, the navigation host and the
|
||||||
|
navigation chrome for every window size (bottom bar, rail, permanent drawer), with
|
||||||
|
`amethyst` and a new JVM `desktopApp` as thin shims around it. Screens still in
|
||||||
|
`amethyst/` are waiting on `AccountViewModel` and the app root, not staying there
|
||||||
|
by design. See `commons/plans/2026-09-27-one-ui-android-desktop.md`.
|
||||||
|
|
||||||
It depends on `:commons` (and `:quartz`) as **`api`**, so a consumer that adds
|
It depends on `:commons` (and `:quartz`) as **`api`**, so a consumer that adds
|
||||||
`:commonsUI` sees the headless layer transitively. `amethyst`, `desktopApp`,
|
`:commonsUI` sees the headless layer transitively. `amethyst`, `desktopApp`,
|
||||||
`nappletHost` and `benchmark` depend on it; `cli`, `geode`, `marmotBench`
|
`nappletHost` and `benchmark` depend on it; `cli`, `geode`, `marmotBench`
|
||||||
|
|||||||
Reference in New Issue
Block a user