Kgothatso Ngako 03d1e8e3b1 feat: give the app the five breakpoints, and let the screen margin follow them
Phase 6, steps 1 and 2. The app had no notion of window width at all -- two
`BoxWithConstraints` in 30,000 lines of UI, both inside a view model -- so every
layout decision in it was made once, for a phone, and then rendered unchanged
into a 1800dp desktop window.

**The dependency question the plan asked to settle first.** `material3-adaptive`
publishes multiplatform under `org.jetbrains.compose.material3.adaptive`, with
android, desktop and ios variants; the ios ones carry `ios_arm64` and
`ios_simulator_arm64` attributes despite the `uikit*` artifact names, so the
targets this project declares on a mac resolve. Version **1.2.0**, not the newer
1.3.0-beta02, because that is the version the pinned material3 itself resolves:
`material3-adaptive-navigation-suite:1.10.0-alpha05` names `adaptive:1.2.0` in
its pom, and 1.3.0 would pull window-core 1.5.0 in beside the 1.4.0 the pinned
material3 compiled against. Nothing is lost by staying: 1.2.0 already computes
the large and extra-large breakpoints through `supportLargeAndXLargeWidth`, and
carries `ListDetailPaneScaffold` for the pane work. So steps 3-4 can use the
library scaffolds rather than a hand-rolled equivalent.

**`Breakpoint`** is the five-value enum -- compact / medium / expanded / large /
extra-large at 0 / 600 / 840 / 1200 / 1600dp -- with `ofWidth` as a pure function
so the thresholds are assertable without a Compose runtime. `TorchTheme`
classifies once and provides `LocalBreakpoint`, so no two screens can disagree
about the window they are both in.

It reads `currentWindowDpSize()` rather than `currentWindowAdaptiveInfo()`.
The latter also computes a `Posture` from the platform's fold state, which on
android reaches for `WindowInfoTracker` and an activity; this call sits in
`TorchTheme`, which wraps all 51 `@Preview` bodies in the tree, and a preview
context is not an activity. The pane scaffolds ask for posture themselves, at
the one place a fold changes the answer.

**Spacing now adapts, and exactly one value moves.** M3 publishes a margin per
breakpoint -- 16dp compact, 24dp everywhere wider -- and publishes nothing else
that varies with window width. The scale itself is absolute: `space200` is 16dp
on a phone and 16dp on a desktop, and what adapts is which token a job reaches
for, not the token. So `screenMargin` goes 16 -> 24 at medium and holds there,
and `containerPadding`, `itemGap` and the rest do not move -- a card does not
become a different component because the window grew. Widening all of them is
the "everything breathes on a big screen" instinct, and it reads as a zoomed
phone rather than as a layout. A test asserts the non-movement, because that is
the edit a later reviewer would wave through.

Mechanically this made the eight semantic names constructor parameters instead
of `get()`s over the scale, so a breakpoint can reassign one without moving the
stop underneath it. Kotlin resolves a default expression against the parameters
before it, so each still reads its stop by name and still follows it when the
scale is overridden -- phase 2's `Spacing(space200 = 24.dp)` assertion holds
unchanged. The two instances are singletons because `LocalSpacing` is a
`staticCompositionLocalOf` and invalidates on identity, not equality.

**A test found a real defect while being written.** `ofWidth` was
`entries.last { width >= it.minWidth }`, which throws `NoSuchElementException`
below 0dp. A desktop window reports a zero size for the frame before its first
layout pass, and this is called from the theme on every composition, so the
crash would have arrived on a resize rather than on anything a user did. Now
total.

`:composeApp:compileDebugKotlinAndroid` and `:composeApp:compileKotlinJvm` both
green; 28 theme tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 07:15:43 +02:00
2026-07-05 23:21:02 +02:00
2026-07-28 09:10:20 +02:00
2026-03-24 04:47:19 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00

This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).

  • /composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:

    • commonMain is for code thats common for all targets.
    • Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apples CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
  • /iosApp contains iOS applications. Even if youre sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.

Build and Run Android Application

To build and run the development version of the Android app, use the run configuration from the run widget in your IDEs toolbar or build it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:assembleDebug
    
  • on Windows
    .\gradlew.bat :composeApp:assembleDebug
    

Build and Run Desktop (JVM) Application

To build and run the development version of the desktop app, use the run configuration from the run widget in your IDEs toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:run
    
  • on Windows
    .\gradlew.bat :composeApp:run
    

Build and Run iOS Application

To build and run the development version of the iOS app, use the run configuration from the run widget in your IDEs toolbar or open the /iosApp directory in Xcode and run it from there.


Learn more about Kotlin Multiplatform

Description
Mantra as a kotlin multiplatform project
https://mantra.press
Readme 8.2 MiB
Languages
Kotlin 100%