app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 2414653336d1d03707c90ad7246ef4138e7e3fe8
parent 7dd1057aa12927acfb355f035442f9fb165e8b51
Author: triesap <tyson@radroots.org>
Date:   Fri, 14 Aug 2026 16:42:31 +0000

ui: center tabs and retain native controls

- center tab labels and optional icons within their stable footprint
- assert label alignment remains exact across selection changes
- keep macOS native window controls visible in integrated chrome
- refresh governed macOS goldens and their verified digests

Diffstat:
Mapp/design_system/src/commonMain/kotlin/org/harvestcircle/designsystem/shell/HarvestCircleShellControls.kt | 2+-
Mapp/desktop/src/main/kotlin/org/harvestcircle/desktop/Main.kt | 12++++++++----
Mapp/desktop/src/test/kotlin/org/harvestcircle/desktop/MainTest.kt | 12++++++++++++
Mapp/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/MainPanelHeaderTest.kt | 31+++++++++++++++++++++----------
Mapp/shared/src/desktopTest/resources/goldens/macos-aarch64/design-surface-dark.png | 0
Mapp/shared/src/desktopTest/resources/goldens/macos-aarch64/design-surface-light.png | 0
Mconfig/design/source_baseline_v1.toml | 4++--
Mtools/xtask/src/lib.rs | 4++--
8 files changed, 46 insertions(+), 19 deletions(-)

diff --git a/app/design_system/src/commonMain/kotlin/org/harvestcircle/designsystem/shell/HarvestCircleShellControls.kt b/app/design_system/src/commonMain/kotlin/org/harvestcircle/designsystem/shell/HarvestCircleShellControls.kt @@ -260,7 +260,7 @@ public fun HarvestCircleShellTab( .selectable(selected, sources.activation, null, true, Role.Tab, onClick) .padding(horizontal = 12.dp), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally), ) { icon?.let { HarvestCircleIcon(it, null, Modifier.size(16.dp), tint = colors.contentSecondary) } HarvestCircleShellText( diff --git a/app/desktop/src/main/kotlin/org/harvestcircle/desktop/Main.kt b/app/desktop/src/main/kotlin/org/harvestcircle/desktop/Main.kt @@ -33,6 +33,7 @@ import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException import javax.imageio.ImageIO +import javax.swing.JRootPane import kotlin.system.exitProcess private const val APPLICATION_NAME = "HarvestCircle" @@ -77,10 +78,7 @@ fun main(args: Array<String>) { window.minimumSize = Dimension(MINIMUM_WINDOW_WIDTH, MINIMUM_WINDOW_HEIGHT) if (isMacOs) { - val rootPane = window.rootPane - rootPane.putClientProperty("apple.awt.fullWindowContent", true) - rootPane.putClientProperty("apple.awt.transparentTitleBar", true) - rootPane.putClientProperty("apple.awt.windowTitleVisible", false) + configureMacOsWindowChrome(window.rootPane) } onDispose { } @@ -98,6 +96,12 @@ fun main(args: Array<String>) { } } +internal fun configureMacOsWindowChrome(rootPane: JRootPane) { + rootPane.putClientProperty("apple.awt.fullWindowContent", true) + rootPane.putClientProperty("apple.awt.transparentTitleBar", true) + rootPane.putClientProperty("apple.awt.windowTitleVisible", true) +} + internal fun isHealthCheck(args: Array<String>): Boolean = args.size == 1 && args.single() == HEALTH_CHECK_ARGUMENT private fun runHealthCheck(): Int { diff --git a/app/desktop/src/test/kotlin/org/harvestcircle/desktop/MainTest.kt b/app/desktop/src/test/kotlin/org/harvestcircle/desktop/MainTest.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.runBlocking import org.harvestcircle.application.ApplicationLifecycle import java.io.ByteArrayInputStream import java.nio.file.Files +import javax.swing.JRootPane import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -22,6 +23,17 @@ class MainTest { } @Test + fun macOsWindowChromeKeepsNativeControlsVisible() { + val rootPane = JRootPane() + + configureMacOsWindowChrome(rootPane) + + assertEquals(true, rootPane.getClientProperty("apple.awt.fullWindowContent")) + assertEquals(true, rootPane.getClientProperty("apple.awt.transparentTitleBar")) + assertEquals(true, rootPane.getClientProperty("apple.awt.windowTitleVisible")) + } + + @Test fun missingOrInvalidRuntimeIconFailsSafely() { assertNull(loadRuntimeIcon { null }) assertNull(loadRuntimeIcon { ByteArrayInputStream("not an image".encodeToByteArray()) }) diff --git a/app/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/MainPanelHeaderTest.kt b/app/shared/src/desktopTest/kotlin/org/harvestcircle/ui/shell/MainPanelHeaderTest.kt @@ -45,28 +45,39 @@ class MainPanelHeaderTest { } @Test - fun tabFootprintsRemainStableWhenSelectionChanges() = + fun tabFootprintsAndLabelAlignmentRemainStableWhenSelectionChanges() = runComposeUiTest { - val relays = TemplateTab(TemplateSelectionKey("public-relays"), "Public relays") - val diagnostics = TemplateTab(TemplateSelectionKey("runtime-diagnostics"), "Runtime diagnostics") - var selected by mutableStateOf(relays.key) + val overview = TemplateTab(TemplateSelectionKey("overview"), "Overview") + val identity = TemplateTab(TemplateSelectionKey("identity"), "Identity") + var selected by mutableStateOf(overview.key) setHarvestCircleContent { MainPanelHeader( MainPanelHeaderModel( title = "Network", - tabs = listOf(relays, diagnostics), + tabs = listOf(overview, identity), selectedTab = selected, ), onTabSelected = { selected = it }, ) } - val relaysBefore = onNodeWithTag("main-tab-public-relays").fetchSemanticsNode().boundsInRoot - val diagnosticsBefore = onNodeWithTag("main-tab-runtime-diagnostics").fetchSemanticsNode().boundsInRoot - onNodeWithTag("main-tab-runtime-diagnostics").performClick() + val overviewBefore = onNodeWithTag("main-tab-overview").fetchSemanticsNode().boundsInRoot + val identityBefore = onNodeWithTag("main-tab-identity").fetchSemanticsNode().boundsInRoot + val overviewLabelBefore = onNodeWithText("Overview", useUnmergedTree = true).fetchSemanticsNode().boundsInRoot + val identityLabelBefore = onNodeWithText("Identity", useUnmergedTree = true).fetchSemanticsNode().boundsInRoot + assertEquals(overviewBefore.center.x, overviewLabelBefore.center.x, absoluteTolerance = 0.5f) + assertEquals(identityBefore.center.x, identityLabelBefore.center.x, absoluteTolerance = 0.5f) + + onNodeWithTag("main-tab-identity").performClick() waitForIdle() - assertEquals(relaysBefore, onNodeWithTag("main-tab-public-relays").fetchSemanticsNode().boundsInRoot) - assertEquals(diagnosticsBefore, onNodeWithTag("main-tab-runtime-diagnostics").fetchSemanticsNode().boundsInRoot) + val overviewAfter = onNodeWithTag("main-tab-overview").fetchSemanticsNode().boundsInRoot + val identityAfter = onNodeWithTag("main-tab-identity").fetchSemanticsNode().boundsInRoot + val overviewLabelAfter = onNodeWithText("Overview", useUnmergedTree = true).fetchSemanticsNode().boundsInRoot + val identityLabelAfter = onNodeWithText("Identity", useUnmergedTree = true).fetchSemanticsNode().boundsInRoot + assertEquals(overviewBefore, overviewAfter) + assertEquals(identityBefore, identityAfter) + assertEquals(overviewAfter.center.x, overviewLabelAfter.center.x, absoluteTolerance = 0.5f) + assertEquals(identityAfter.center.x, identityLabelAfter.center.x, absoluteTolerance = 0.5f) } } diff --git a/app/shared/src/desktopTest/resources/goldens/macos-aarch64/design-surface-dark.png b/app/shared/src/desktopTest/resources/goldens/macos-aarch64/design-surface-dark.png Binary files differ. diff --git a/app/shared/src/desktopTest/resources/goldens/macos-aarch64/design-surface-light.png b/app/shared/src/desktopTest/resources/goldens/macos-aarch64/design-surface-light.png Binary files differ. diff --git a/config/design/source_baseline_v1.toml b/config/design/source_baseline_v1.toml @@ -9,8 +9,8 @@ source_license = "GPL-3.0-only" golden_host = "macos-aarch64" golden_status = "verified" golden_source_snapshot_sha256 = "c2fe49f3c3ea43105cb2fff4a67c7cd9c21561c71825440a91654a0c1b12e3b8" -golden_light_sha256 = "f6c513064320e144ddc92dbd377f91dcbc275f17e6c0645c2ac3c94c0808ebb9" -golden_dark_sha256 = "49b9af1d391feb599ce4a3ce862d7dfc403cae973a8f0f6abf5c0a7ee66cfca4" +golden_light_sha256 = "01fab45de6622497568b7655aac59d4e65a64e7c41a314bb2453fae56893f515" +golden_dark_sha256 = "187ba53d1c71ebe2bb7c072a68697c0217d537e58c4f72a6115df4120972913c" [[mapping]] source = "core/designsystem/src/commonMain" diff --git a/tools/xtask/src/lib.rs b/tools/xtask/src/lib.rs @@ -942,12 +942,12 @@ fn design_source_audit(root: &Path, inventory: &Inventory, findings: &mut Vec<St ( "app/shared/src/desktopTest/resources/goldens/macos-aarch64/design-surface-light.png", "golden_light_sha256", - "f6c513064320e144ddc92dbd377f91dcbc275f17e6c0645c2ac3c94c0808ebb9", + "01fab45de6622497568b7655aac59d4e65a64e7c41a314bb2453fae56893f515", ), ( "app/shared/src/desktopTest/resources/goldens/macos-aarch64/design-surface-dark.png", "golden_dark_sha256", - "49b9af1d391feb599ce4a3ce862d7dfc403cae973a8f0f6abf5c0a7ee66cfca4", + "187ba53d1c71ebe2bb7c072a68697c0217d537e58c4f72a6115df4120972913c", ), ] { let authority = format!("{key} = \"{sha256}\"");