diff --git a/.github/workflows/docs-governance.yml b/.github/workflows/docs-governance.yml index 8c3548031..4f014b3de 100644 --- a/.github/workflows/docs-governance.yml +++ b/.github/workflows/docs-governance.yml @@ -243,11 +243,11 @@ jobs: # Preview files changed preview_changed=$(echo "$changed" | grep -E 'Preview.*\.kt$' || true) - # Screenshot test files changed - screenshot_tests_changed=$(echo "$changed" | grep -E '^screenshot-tests/src/screenshotTest/.*\.kt$' || true) + # Screenshot test files changed (regression gate + generate-only docs module) + screenshot_tests_changed=$(echo "$changed" | grep -E '^(screenshot-tests|docs-screenshots)/src/screenshotTest/.*\.kt$' || true) - # Reference images changed - refs_changed=$(echo "$changed" | grep -E '^screenshot-tests/src/screenshotTestDebug/reference/.*\.png$' || true) + # Reference images changed (either module) + refs_changed=$(echo "$changed" | grep -E '^(screenshot-tests|docs-screenshots)/src/screenshotTestDebug/reference/.*\.png$' || true) echo "ui_changed<> "$GITHUB_OUTPUT" echo "$ui_changed" >> "$GITHUB_OUTPUT" @@ -305,9 +305,9 @@ jobs: '', '**Adding previews checklist:**', '1. Create or update a `*Previews.kt` file in the same module with `@PreviewLightDark`', - '2. Add `@Suppress("PreviewPublic")` if the preview is consumed by screenshot-tests', - '3. Add corresponding `@PreviewTest` function in `screenshot-tests/src/screenshotTest/`', - '4. Run `./gradlew :screenshot-tests:updateDebugScreenshotTest` to generate reference images', + '2. Baseline the public preview in the module `detekt-baseline.xml` (or `@Suppress("PreviewPublic")`) if it is consumed cross-module by a screenshot wrapper', + '3. Add a `@PreviewTest` wrapper: in `screenshot-tests/src/screenshotTest/` for a regression-gated component, or `docs-screenshots/src/screenshotTest/` for a doc-framed composition', + '4. Run `./gradlew :screenshot-tests:updateDebugScreenshotTest` (and `:docs-screenshots:updateDebugScreenshotTest` for doc compositions) to generate reference images', '', 'If this PR does **not** require preview updates (e.g., logic-only change, non-visual refactor), add the **`skip-preview-check`** label to dismiss.', ].join('\n'); @@ -357,9 +357,11 @@ jobs: '', '**How to update:**', '```bash', - './gradlew :screenshot-tests:updateDebugScreenshotTest', + './gradlew :screenshot-tests:updateDebugScreenshotTest # regression goldens', + './gradlew :docs-screenshots:updateDebugScreenshotTest # doc-framed compositions', + './gradlew :screenshot-tests:copyDocsScreenshots # refresh docs/assets from both', '```', - 'Then commit the updated reference PNGs.', + 'Then commit the updated reference PNGs (and any refreshed `docs/assets/screenshots/*.png`).', '', 'If this change is intentionally preview-only (e.g., adding a preview that doesn\'t need a test yet), add the **`skip-preview-check`** label.', ].join('\n'); diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 7f9291acf..e3d8a8ac7 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -37,6 +37,7 @@ jobs: - 'core/**' - 'feature/**' - 'screenshot-tests/**' + - 'docs-screenshots/**' # Shared build infrastructure - 'build-logic/**' - 'config/**' diff --git a/.skills/testing-ci/SKILL.md b/.skills/testing-ci/SKILL.md index 0bdb2fb8c..465ea6422 100644 --- a/.skills/testing-ci/SKILL.md +++ b/.skills/testing-ci/SKILL.md @@ -62,9 +62,24 @@ Run these when relevant to map, provider, or flavor-specific behavior: ./gradlew testFdroidDebug testGoogleDebug ``` +## 3b) Screenshot testing (two modules) + +Compose Preview Screenshot Testing (AGP/layoutlib) is split into two modules — keep the distinction: + +- **`:screenshot-tests`** — visual-regression **gate**. CI runs `:screenshot-tests:validateDebugScreenshotTest`. Holds atomic, dual-purpose components. Touching one of these previews is expected to move a gated baseline. +- **`:docs-screenshots`** — **generate-only**, NOT validated in CI. Holds doc-framed compositions (crops/full screens tuned for the docs site). Reframe these freely; it never churns the regression gate. + +```bash +./gradlew :screenshot-tests:updateDebugScreenshotTest # regression goldens +./gradlew :docs-screenshots:updateDebugScreenshotTest # doc-framed composition images +./gradlew :screenshot-tests:copyDocsScreenshots # copy doc images from BOTH modules → docs/assets +``` + +Rendering is **host-deterministic** (layoutlib): a local `update` produces references byte-identical to CI, so locally-recorded goldens pass `validate`. `copyDocsScreenshots` overwrites a stale committed `nodes_detail_local.png` each run — `git checkout` it. Public previews consumed cross-module by a wrapper need a `detekt-baseline.xml` entry (PreviewPublic). New screenshot? Pick the module by purpose; see `docs/assets/screenshots/README.md`. + ## 4) CI Pipeline Architecture -CI is defined in `.github/workflows/reusable-check.yml` and structured as four parallel job groups: +CI is defined in `.github/workflows/reusable-check.yml` and structured as parallel job groups: 1. **`lint-check`** — Runs spotless, detekt, Android lint, and KMP smoke compile in a single Gradle invocation (avoids 3x cold-start overhead). Uses `fetch-depth: 0` (full clone) for spotless ratcheting and version code calculation. Produces `cache_read_only` output and computed `version_code` for downstream jobs. 2. **`test-shards`** — A 3-shard matrix that runs unit tests in parallel (depends on `lint-check`): @@ -75,6 +90,7 @@ CI is defined in `.github/workflows/reusable-check.yml` and structured as four p Downstream jobs use `fetch-depth: 1` and receive `VERSION_CODE` from lint-check via env var, enabling shallow clones. 3. **`android-check`** — Builds APKs for all flavors (depends on `lint-check`). 4. **`build-desktop`** — Multi-OS matrix (`macos-latest`, `windows-latest`, `ubuntu-24.04`, `ubuntu-24.04-arm`) that builds desktop distributions via `createDistributable` (depends on `lint-check`). +5. **`screenshot-check`** — Runs `:screenshot-tests:validateDebugScreenshotTest` (the visual-regression gate) and uploads a diff report. Note: `:docs-screenshots` is intentionally NOT validated here (generate-only). ### Runner Strategy (Three Tiers) - **`ubuntu-24.04-arm`** — Lightweight/utility jobs (status checks, labelers, triage, changelog, release metadata, stale, moderation). Benefits from ARM runners' shorter queue times. diff --git a/.specify/memory/agent-governance.md b/.specify/memory/agent-governance.md index c1a8e45ba..9695b2f05 100644 --- a/.specify/memory/agent-governance.md +++ b/.specify/memory/agent-governance.md @@ -42,8 +42,8 @@ changing linked areas; child directories change with their parent. **Top-level areas requiring review**: `.agent_memory/`, `.agent_plans/`, `.agent_refs/`, `.github/`, `.specify/`, `androidApp/`, `app/`, `build-logic/`, `config/`, `core/`, -`desktop/`, `desktopApp/`, `docs/`, `docs-site/`, `fastlane/`, `feature/`, `gradle/`, -`ios/`, `iosApp/`, `offline-repository/`, `screenshot-tests/`, `scripts/`, `specs/` +`desktop/`, `desktopApp/`, `docs/`, `docs-screenshots/`, `docs-site/`, `fastlane/`, `feature/`, +`gradle/`, `ios/`, `iosApp/`, `offline-repository/`, `screenshot-tests/`, `scripts/`, `specs/` ## Development Commands diff --git a/docs-screenshots/build.gradle.kts b/docs-screenshots/build.gradle.kts new file mode 100644 index 000000000..da3092413 --- /dev/null +++ b/docs-screenshots/build.gradle.kts @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026 Meshtastic LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import com.android.build.api.dsl.LibraryExtension +import org.gradle.testretry.TestRetryTaskExtension + +// Documentation screenshots — GENERATE-ONLY, intentionally NOT gated in CI. +// +// Unlike :screenshot-tests (a visual-regression gate run via :screenshot-tests:validateDebugScreenshotTest), this +// module holds doc-framed composition previews whose framing is tuned for the documentation site. Its reference +// images are regenerated on demand (./gradlew :docs-screenshots:updateDebugScreenshotTest) and consumed by +// :screenshot-tests:copyDocsScreenshots; CI does NOT run validateDebugScreenshotTest here, so reframing a doc image +// never churns the regression gate. Keep regression checks in :screenshot-tests, doc-only compositions here. +plugins { + alias(libs.plugins.meshtastic.android.library) + alias(libs.plugins.meshtastic.android.library.compose) + alias(libs.plugins.compose.screenshot) +} + +configure { + namespace = "org.meshtastic.screenshot.docs" + + experimentalProperties["android.experimental.enableScreenshotTest"] = true + + testOptions { screenshotTests { imageDifferenceThreshold = 0.0005f } } +} + +// CST screenshot tests use a custom runner incompatible with test-retry +tasks.withType().configureEach { + if (name.contains("ScreenshotTest", ignoreCase = true)) { + extensions.configure { maxRetries.set(0) } + } +} + +dependencies { + implementation(project(":core:ui")) + implementation(project(":core:resources")) + implementation(project(":core:model")) + implementation(project(":core:common")) + implementation(project(":feature:connections")) + implementation(project(":feature:firmware")) + + implementation(libs.compose.multiplatform.foundation) + implementation(libs.compose.multiplatform.material3) + implementation(libs.compose.multiplatform.runtime) + implementation(libs.compose.multiplatform.ui) + + screenshotTestImplementation(libs.screenshot.validation.api) +} diff --git a/docs-screenshots/src/main/AndroidManifest.xml b/docs-screenshots/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8072ee00d --- /dev/null +++ b/docs-screenshots/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/docs-screenshots/src/screenshotTest/kotlin/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTests.kt b/docs-screenshots/src/screenshotTest/kotlin/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTests.kt new file mode 100644 index 000000000..724a4190b --- /dev/null +++ b/docs-screenshots/src/screenshotTest/kotlin/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTests.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026 Meshtastic LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.meshtastic.screenshots.docs.feature + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.android.tools.screenshot.PreviewTest +import org.meshtastic.feature.connections.component.BluetoothScanPreview +import org.meshtastic.feature.connections.component.EmptyStateContentPreview + +// Doc-framed connections compositions (bounded-height crops tuned for the docs site). The atomic connections +// components (device list item, transport selector, disconnect button, etc.) remain regression-gated in +// :screenshot-tests. + +@PreviewTest +@PreviewLightDark +@Composable +fun ScreenshotConnectionsBluetoothScan() { + BluetoothScanPreview() +} + +@PreviewTest +@PreviewLightDark +@Composable +fun ScreenshotEmptyStateContent() { + EmptyStateContentPreview() +} diff --git a/screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/FirmwareScreenshotTests.kt b/docs-screenshots/src/screenshotTest/kotlin/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTests.kt similarity index 97% rename from screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/FirmwareScreenshotTests.kt rename to docs-screenshots/src/screenshotTest/kotlin/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTests.kt index 831b3afd5..749cd6f61 100644 --- a/screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/FirmwareScreenshotTests.kt +++ b/docs-screenshots/src/screenshotTest/kotlin/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTests.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.meshtastic.screenshots.feature +package org.meshtastic.screenshots.docs.feature import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.PreviewLightDark diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotConnectionsBluetoothScan_Dark_d19fbf1f_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotConnectionsBluetoothScan_Dark_d19fbf1f_0.png new file mode 100644 index 000000000..da6708a4f Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotConnectionsBluetoothScan_Dark_d19fbf1f_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotConnectionsBluetoothScan_Light_b29dc7a7_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotConnectionsBluetoothScan_Light_b29dc7a7_0.png new file mode 100644 index 000000000..5394ee802 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotConnectionsBluetoothScan_Light_b29dc7a7_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotEmptyStateContent_Dark_d19fbf1f_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotEmptyStateContent_Dark_d19fbf1f_0.png new file mode 100644 index 000000000..36e232abe Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotEmptyStateContent_Dark_d19fbf1f_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotEmptyStateContent_Light_b29dc7a7_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotEmptyStateContent_Light_b29dc7a7_0.png new file mode 100644 index 000000000..a6584d606 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/ConnectionsDocScreenshotTestsKt/ScreenshotEmptyStateContent_Light_b29dc7a7_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Dark_d19fbf1f_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Dark_d19fbf1f_0.png new file mode 100644 index 000000000..a147afdf9 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Dark_d19fbf1f_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Light_b29dc7a7_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Light_b29dc7a7_0.png new file mode 100644 index 000000000..1227e8993 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Light_b29dc7a7_0.png differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareDisclaimer_Dark_d19fbf1f_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareDisclaimer_Dark_d19fbf1f_0.png similarity index 100% rename from screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareDisclaimer_Dark_d19fbf1f_0.png rename to docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareDisclaimer_Dark_d19fbf1f_0.png diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareDisclaimer_Light_b29dc7a7_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareDisclaimer_Light_b29dc7a7_0.png similarity index 100% rename from screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareDisclaimer_Light_b29dc7a7_0.png rename to docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareDisclaimer_Light_b29dc7a7_0.png diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Dark_d19fbf1f_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Dark_d19fbf1f_0.png new file mode 100644 index 000000000..75e976102 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Dark_d19fbf1f_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Light_b29dc7a7_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Light_b29dc7a7_0.png new file mode 100644 index 000000000..81f09ae6b Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Light_b29dc7a7_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Dark_d19fbf1f_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Dark_d19fbf1f_0.png new file mode 100644 index 000000000..419a4c044 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Dark_d19fbf1f_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Light_b29dc7a7_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Light_b29dc7a7_0.png new file mode 100644 index 000000000..e17ea8803 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Light_b29dc7a7_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Dark_d19fbf1f_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Dark_d19fbf1f_0.png new file mode 100644 index 000000000..cfa81a4d3 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Dark_d19fbf1f_0.png differ diff --git a/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Light_b29dc7a7_0.png b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Light_b29dc7a7_0.png new file mode 100644 index 000000000..f83143b52 Binary files /dev/null and b/docs-screenshots/src/screenshotTestDebug/reference/org/meshtastic/screenshots/docs/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Light_b29dc7a7_0.png differ diff --git a/docs/assets/screenshots/README.md b/docs/assets/screenshots/README.md index ab3b37eb3..6b0c91ac8 100644 --- a/docs/assets/screenshots/README.md +++ b/docs/assets/screenshots/README.md @@ -10,14 +10,27 @@ documentation pages. It is consumed by both: `DocImageWiringTest` (in `:feature:docs`) fails the build if a doc page references an image that is not present here. +## Two source modules + +Doc screenshots come from Compose Preview Screenshot Testing references in **two** modules: + +- **`:screenshot-tests`** — visual-regression gate (CI runs `:screenshot-tests:validateDebugScreenshotTest`). + Holds atomic, dual-purpose components (signal/battery/hops info, list items, preference widgets, + alerts) that are both regression-checked **and** used as doc images. Don't reframe these for docs. +- **`:docs-screenshots`** — generate-only, **not** gated in CI. Holds doc-framed compositions whose + framing is tuned for the docs site (e.g. the firmware status crops, the connections BLE-scan / + empty-state crops). Reframing one here never churns the regression gate. + +`copyDocsScreenshots` (in `:screenshot-tests`) aggregates the reference images from **both** modules. + ## Updating Screenshots -Most screenshots are generated from the Compose Preview Screenshot Testing reference images -in `screenshot-tests/src/screenshotTestDebug/reference/`. After changing a UI component: +After changing a UI component, regenerate references for whichever module owns the wrapper, then copy: ```bash -./gradlew :screenshot-tests:updateDebugScreenshotTest # regenerate reference images -./gradlew :screenshot-tests:copyDocsScreenshots # refresh this directory +./gradlew :screenshot-tests:updateDebugScreenshotTest # regression references +./gradlew :docs-screenshots:updateDebugScreenshotTest # doc-framed composition references +./gradlew :screenshot-tests:copyDocsScreenshots # refresh this directory from both ``` `copyDocsScreenshots` copies **only** the light-mode reference images that have a semantic @@ -27,13 +40,15 @@ Commit the refreshed PNGs together with the reference-image changes. ## Adding a Screenshot for a New Doc Page 1. Add (or reuse) a `Preview*`/`*Preview` composable with representative mock data in the - feature module, and a `Screenshot*` wrapper in `screenshot-tests` (see - `DiscoveryScreenshotTests.kt` for the pattern). If the component renders timestamps, give - it a `timeTextOverride`-style parameter so renders stay deterministic across machines. -2. Make sure the test class is covered by `screenshot-tests/docs-screenshots-manifest.txt`. + feature module. Add a `Screenshot*` wrapper: in **`:docs-screenshots`** if it's a doc-framed + composition (full screen / doc-specific crop), or in **`:screenshot-tests`** if it's an atomic + component you also want regression-gated. If the component renders timestamps, give it a + `timeTextOverride`-style parameter so renders stay deterministic across machines. +2. Make sure the test class is covered by a pattern in `screenshot-tests/docs-screenshots-manifest.txt` + (the patterns are `**/{Class}Kt/...`, so they match in either module). 3. Map the semantic name in `screenshot-tests/docs-screenshot-aliases.properties`: `{page-id}_{description}.png=Screenshot{Name}_Light_{hash}_0.png` -4. Run the two Gradle tasks above and reference the image from the doc page. +4. Run the relevant update task(s) + `copyDocsScreenshots`, then reference the image from the doc page. ## Naming Convention @@ -48,5 +63,7 @@ Examples: `onboarding_welcome.png`, `connections_bluetooth_scan.png`, `discovery - PNG format, light-mode only (dark variants live in the reference directory) - Name screenshots to match the docs page they appear in - Keep filenames lowercase with underscores -- A few screenshots (`connections_wifi_*.png`) are manual captures with no CST source yet; - they are hand-maintained until matching previews exist +- Prefer CST-generated screenshots — they render real app composables, so they cannot drift from + reality. Avoid hand-pasted captures: a stray screenshot from another app slipped in this way + before (the old `connections_wifi_*.png` were from an unrelated WiFi-provisioning app, not + Meshtastic). If a manual capture is unavoidable, it must be a genuine Meshtastic-Android screen. diff --git a/docs/assets/screenshots/connections_bluetooth_scan.png b/docs/assets/screenshots/connections_bluetooth_scan.png index 67df31f38..5394ee802 100644 Binary files a/docs/assets/screenshots/connections_bluetooth_scan.png and b/docs/assets/screenshots/connections_bluetooth_scan.png differ diff --git a/docs/assets/screenshots/connections_empty_state.png b/docs/assets/screenshots/connections_empty_state.png index 096d5690b..a6584d606 100644 Binary files a/docs/assets/screenshots/connections_empty_state.png and b/docs/assets/screenshots/connections_empty_state.png differ diff --git a/docs/assets/screenshots/connections_wifi_device_found.png b/docs/assets/screenshots/connections_wifi_device_found.png deleted file mode 100644 index 6f499bd45..000000000 Binary files a/docs/assets/screenshots/connections_wifi_device_found.png and /dev/null differ diff --git a/docs/assets/screenshots/connections_wifi_scanning.png b/docs/assets/screenshots/connections_wifi_scanning.png deleted file mode 100644 index 9e45f1cdb..000000000 Binary files a/docs/assets/screenshots/connections_wifi_scanning.png and /dev/null differ diff --git a/docs/assets/screenshots/connections_wifi_success.png b/docs/assets/screenshots/connections_wifi_success.png deleted file mode 100644 index fe4b68550..000000000 Binary files a/docs/assets/screenshots/connections_wifi_success.png and /dev/null differ diff --git a/docs/assets/screenshots/firmware_checking.png b/docs/assets/screenshots/firmware_checking.png index a22d68b3b..1227e8993 100644 Binary files a/docs/assets/screenshots/firmware_checking.png and b/docs/assets/screenshots/firmware_checking.png differ diff --git a/docs/assets/screenshots/firmware_error.png b/docs/assets/screenshots/firmware_error.png index 30c34d13e..81f09ae6b 100644 Binary files a/docs/assets/screenshots/firmware_error.png and b/docs/assets/screenshots/firmware_error.png differ diff --git a/docs/assets/screenshots/firmware_success.png b/docs/assets/screenshots/firmware_success.png index d6ac649ff..e17ea8803 100644 Binary files a/docs/assets/screenshots/firmware_success.png and b/docs/assets/screenshots/firmware_success.png differ diff --git a/docs/assets/screenshots/firmware_verifying.png b/docs/assets/screenshots/firmware_verifying.png new file mode 100644 index 000000000..f83143b52 Binary files /dev/null and b/docs/assets/screenshots/firmware_verifying.png differ diff --git a/docs/assets/screenshots/messages_edit_quick_chat.png b/docs/assets/screenshots/messages_edit_quick_chat.png new file mode 100644 index 000000000..0506b10e3 Binary files /dev/null and b/docs/assets/screenshots/messages_edit_quick_chat.png differ diff --git a/docs/assets/screenshots/node-metrics_iaq_scale.png b/docs/assets/screenshots/node-metrics_iaq_scale.png new file mode 100644 index 000000000..589eeebbe Binary files /dev/null and b/docs/assets/screenshots/node-metrics_iaq_scale.png differ diff --git a/docs/assets/screenshots/tak_server_enabled.png b/docs/assets/screenshots/tak_server_enabled.png new file mode 100644 index 000000000..c6271875d Binary files /dev/null and b/docs/assets/screenshots/tak_server_enabled.png differ diff --git a/docs/en/developer.md b/docs/en/developer.md index 7d100b6ae..3b3939ea5 100644 --- a/docs/en/developer.md +++ b/docs/en/developer.md @@ -38,6 +38,8 @@ Keep the last 5–8 entries and trim older ones from the bottom. **June 2026** — AIDL/`IMeshService` removed (#5586). The mesh service is now in-process only, driven entirely through `RadioController` — no cross-process binder, no `aidl` stubs. +**June 2026** — [Testing](developer/testing) — Split the screenshot pipeline: the new generate-only `:docs-screenshots` module holds doc-framed compositions, while `:screenshot-tests` stays the CI visual-regression gate — so reframing a doc image no longer churns a test baseline. + **June 2026** — New feature modules: `feature:discovery` (mesh network discovery, #5275) and `feature:car` (Android Auto / Car App Library, google flavor only, #5633). **June 2026** — [Testing](developer/testing) — Added the `:baselineprofile` module (#5735): a Macrobenchmark cold-start journey generates a Baseline Profile for `:androidApp` to AOT-compile hot startup paths. diff --git a/docs/en/developer/codebase.md b/docs/en/developer/codebase.md index c0851d6d4..c5daedc7e 100644 --- a/docs/en/developer/codebase.md +++ b/docs/en/developer/codebase.md @@ -56,7 +56,8 @@ Meshtastic-Android/ │ ├── testing/ │ └── ui/ ├── baselineprofile/ # Baseline Profile generation for :androidApp -├── screenshot-tests/ # Compose Preview screenshot tests +├── screenshot-tests/ # Compose Preview screenshot tests (visual-regression gate) +├── docs-screenshots/ # Doc-framed composition screenshots (generate-only, not CI-gated) ├── build-logic/ # Convention plugins and build helpers │ ├── convention/ │ └── flatpak/ diff --git a/docs/en/developer/testing.md b/docs/en/developer/testing.md index 6cdc4ed57..cf421cf56 100644 --- a/docs/en/developer/testing.md +++ b/docs/en/developer/testing.md @@ -56,14 +56,20 @@ Located in `commonTest` or `jvmTest` source sets. ### Screenshot Tests -Uses Android Gradle Plugin's native screenshot testing framework: +Uses Android Gradle Plugin's native (layoutlib) screenshot testing framework, split across two modules: + +- **`:screenshot-tests`** — the **visual-regression gate**. CI runs `validateDebugScreenshotTest` on it; reframing one of these baselines is a real diff to review. Holds atomic, dual-purpose components. +- **`:docs-screenshots`** — **generate-only**, *not* validated in CI. Holds doc-framed compositions whose framing is tuned for the docs site, so reframing a doc image never churns the regression gate. ```bash -./gradlew :screenshot-tests:updateDebugScreenshotTest # Record golden images -./gradlew :screenshot-tests:validateDebugScreenshotTest # Compare against goldens -./gradlew :screenshot-tests:copyDocsScreenshots # Copy reference images to docs pipeline +./gradlew :screenshot-tests:updateDebugScreenshotTest # record regression goldens +./gradlew :screenshot-tests:validateDebugScreenshotTest # compare against goldens (CI gate) +./gradlew :docs-screenshots:updateDebugScreenshotTest # record doc-framed composition images +./gradlew :screenshot-tests:copyDocsScreenshots # copy doc images from BOTH modules into docs/assets ``` +Rendering is host-deterministic here (layoutlib): a local `update` produces references byte-identical to CI, so locally-recorded goldens pass `validate`. See `docs/assets/screenshots/README.md` for which module a new screenshot belongs in. + ### Baseline Profile / Startup Performance The `:baselineprofile` module (#5735) generates a [Baseline Profile](https://developer.android.com/topic/performance/baselineprofiles/overview) for `:androidApp`, AOT-compiling the hot startup paths so ART doesn't pay the JIT cost on first launch. It targets the **google** flavor (the variant most users run). diff --git a/docs/en/user.md b/docs/en/user.md index f55d77468..a702fcadd 100644 --- a/docs/en/user.md +++ b/docs/en/user.md @@ -19,6 +19,10 @@ Documentation for using the Meshtastic Android and Desktop app. Keep the last 5–8 entries and archive older ones by removing them. --> +**June 2026** — [Help & In-App Docs](user/help-and-docs) — New page covering the in-app documentation browser, search, and the on-device Chirpy AI assistant. + +**June 2026** — [Home Screen Widget](user/widget) — New page covering the Android home screen widget that shows your connected radio's local stats at a glance. + **June 2026** — [Discovery](user/discovery) — Added the Local Mesh Discovery scanner: a dedicated mode that cycles your radio through LoRa presets, dwells on each to collect packets, and ranks which preset works best at your location. **June 2026** — [Node Metrics](user/node-metrics) — Added Air Quality metrics (PM1.0, PM2.5, PM10, and CO₂ with severity color bands), a separate view from the BME680 IAQ reading. diff --git a/docs/en/user/connections.md b/docs/en/user/connections.md index dfaa90163..caabbc1a3 100644 --- a/docs/en/user/connections.md +++ b/docs/en/user/connections.md @@ -2,7 +2,7 @@ title: Connections parent: User Guide nav_order: 2 -last_updated: 2026-05-20 +last_updated: 2026-06-25 description: Connect your phone or desktop to a Meshtastic radio via Bluetooth, USB, or TCP/IP. aliases: - bluetooth @@ -27,7 +27,7 @@ Bluetooth Low Energy is the default and most common connection method on Android 4. Select your device from the list. 5. Accept the Bluetooth pairing prompt if shown. -![Device list item](../../assets/screenshots/connections_bluetooth_scan.png) +![Scanning for Bluetooth devices, with a discovered radio in the list](../../assets/screenshots/connections_bluetooth_scan.png) You can filter devices by transport type using the filter chips at the top: @@ -70,26 +70,20 @@ USB connections provide a wired alternative, useful for desktop or when Bluetoot > ⚠️ **Note:** USB connections require OTG support on Android devices. -## TCP/IP (WiFi) +## TCP/IP (Network) -Some Meshtastic radios support WiFi connectivity, allowing TCP-based connections. +Some Meshtastic radios support WiFi/Ethernet connectivity, allowing TCP-based connections over your local network. Get the radio onto your network first — using the radio's own WiFi settings (via the firmware web interface or another connection) — then connect to it from the app. -### Configuration +### Connecting over the Network -1. Connect your radio to a WiFi network via the radio's web interface or settings. -2. In the app, go to **Connect → TCP**. -3. Enter the radio's IP address and port (default: 4403). -4. Tap **Connect**. +1. Make sure the radio is on the same local network as your phone/desktop. +2. On the Connect screen, select the **Network** transport filter. +3. Choose the radio one of two ways: + - **Scan Network Devices** — toggle this on to auto-discover radios that advertise themselves on the local network (mDNS / `_meshtastic._tcp`). Discovered devices appear in the list; tap one to connect. + - **Add Network Device Manually** — enter the radio's IP address (or hostname) and port (default: `4403`). +4. Previously-used network addresses are remembered under **Recent Network Devices** for quick reconnection (long-press to remove one). -![WiFi scanning for devices](../../assets/screenshots/connections_wifi_scanning.png) - -When a device is found, it appears in the connection list: - -![WiFi device found](../../assets/screenshots/connections_wifi_device_found.png) - -A successful connection is confirmed with a status indicator: - -![WiFi connection success](../../assets/screenshots/connections_wifi_success.png) +> 💡 **Tip:** Network discovery uses mDNS, which only works when both devices are on the same subnet. On Android 17+ the app needs the local-network permission for scanning; if discovery finds nothing, add the device manually by IP. ### When to Use TCP diff --git a/docs/en/user/firmware.md b/docs/en/user/firmware.md index b132e6442..37fe9736b 100644 --- a/docs/en/user/firmware.md +++ b/docs/en/user/firmware.md @@ -62,7 +62,11 @@ Before updating: ## Post-Update -After a successful update: +After the firmware is written, the app verifies the update and waits for the device to come back online: + +![Verifying update and waiting for the device to reconnect](../../assets/screenshots/firmware_verifying.png) + +Once the update succeeds: - The radio will reboot automatically - Bluetooth connection will re-establish - Verify your settings are intact diff --git a/docs/en/user/help-and-docs.md b/docs/en/user/help-and-docs.md new file mode 100644 index 000000000..df527022a --- /dev/null +++ b/docs/en/user/help-and-docs.md @@ -0,0 +1,49 @@ +--- +title: Help & In-App Docs +parent: User Guide +nav_order: 21 +last_updated: 2026-06-25 +description: Browse this documentation inside the app, search it, and ask Chirpy — the on-device AI assistant — questions about Meshtastic. +aliases: + - help + - docs-browser + - chirpy + - assistant +--- + +# Help & In-App Docs + +This same user documentation ships **inside the app**, so you can read it offline without leaving Meshtastic. Open it from **Settings → Help & Documentation**. + +## Browsing + +The docs browser lists every user-guide page. Tap a page to read it; images and cross-links work just like they do here. + +![In-app documentation browser table of contents](../../assets/screenshots/docs-browser_toc.png) + +### Search + +Tap the search icon and type to filter pages by title and keywords — results update as you type. + +![Searching the in-app documentation](../../assets/screenshots/docs-browser_search.png) + +A page open in the browser: + +![A documentation page rendered in the app](../../assets/screenshots/docs-browser_page.png) + +## Chirpy — the AI Assistant + +**Chirpy** answers plain-language questions about Meshtastic using this bundled documentation as its source. Tap the Chirpy button in the docs browser, type a question, and it replies with an answer and links to the relevant pages. + +![Chirpy AI assistant answering a question with page links](../../assets/screenshots/docs-browser_chirpy.png) + +> 🔒 **Privacy:** On supported Google-flavor devices, Chirpy runs **on-device** using Gemini Nano — your questions never leave your phone. A small model downloads on first use. + +> ⚠️ **Note:** On F-Droid, Desktop, and iOS builds, Chirpy falls back to a **keyword search** over the documentation rather than a generative model. If your device doesn't support on-device AI, the assistant is hidden and you can still browse and search the docs normally. + +## Related Topics + +- [Translate the App](translate) — how these pages get localized into other languages +- [App Functions](app-functions) — the separate system-AI integration (distinct from Chirpy) + +--- diff --git a/docs/en/user/map-and-waypoints.md b/docs/en/user/map-and-waypoints.md index f969488d3..a852afdda 100644 --- a/docs/en/user/map-and-waypoints.md +++ b/docs/en/user/map-and-waypoints.md @@ -2,7 +2,7 @@ title: Map & Waypoints parent: User Guide nav_order: 6 -last_updated: 2026-05-13 +last_updated: 2026-06-25 description: View node positions on the map, create and share waypoints, and manage position sharing and privacy. aliases: - map @@ -24,13 +24,7 @@ The map displays: ### Node Markers -Node markers on the map indicate: -| Color | Meaning | -|-------|---------| -| Green | Online (heard recently) | -| Yellow | Away (heard within 2 hours) | -| Gray | Offline (stale position) | -| Blue | Your own node | +Each node that reports a position is shown as a **node chip** marker displaying the node's short name. The chip is colored by the node's own identity color (a stable color derived from its node number) — the same chip used in the node list, so a node looks the same everywhere. Marker color does **not** encode online/offline status. When a node's position updates live, its marker briefly pulses. Nearby markers are clustered as you zoom out. ### Map Controls diff --git a/docs/en/user/messages-and-channels.md b/docs/en/user/messages-and-channels.md index cdfeaee2c..719a74dc9 100644 --- a/docs/en/user/messages-and-channels.md +++ b/docs/en/user/messages-and-channels.md @@ -2,7 +2,7 @@ title: Messages & Channels parent: User Guide nav_order: 3 -last_updated: 2026-06-11 +last_updated: 2026-06-25 description: Send and receive messages, manage channels, configure encryption, search conversations, and use quick chat, reactions, and message actions. aliases: - channels @@ -79,7 +79,7 @@ When a message fails to deliver, the error indicator shows what went wrong: | No Interface | No radio interface available to send | Check that your radio is connected and the channel is configured. | | Max Retransmit | All retry attempts exhausted | The mesh path is unreliable. Try a different channel or wait for conditions to improve. | | No Channel | The destination channel doesn't exist | Verify both nodes share the same channel configuration. | -| Too Large | Message exceeds maximum payload size | Shorten your message (max ~230 characters). | +| Too Large | Message exceeds maximum payload size | Shorten your message (max ~200 characters). | | No Response | Node received message but didn't respond | The recipient's radio may be busy or in low-power sleep mode. | | Duty Cycle Limit | Regional airtime limit reached | Your radio has used its allowed transmit time. Wait for the duty cycle window to reset (typically 1 hour in EU regions). | | Bad Request | Malformed or invalid message | This usually indicates a software bug. Try restarting the app. | @@ -98,6 +98,10 @@ Pre-configured messages for rapid communication: ![Quick chat option](../../assets/screenshots/messages_quick_chat.png) +Each quick chat entry has a short **Name** (the button label), the **Message** it inserts, and an **Instantly send** toggle — when enabled, tapping the button sends the message immediately instead of placing it in the input field for editing: + +![New quick chat dialog with name, message, and instantly-send toggle](../../assets/screenshots/messages_edit_quick_chat.png) + The channel list shows each channel with its latest message preview. ### Searching Messages @@ -147,7 +151,7 @@ Messages are queued and transmitted based on priority: ### Message Limits -- **Maximum length:** 237 bytes (approximately 230 characters for ASCII text) +- **Maximum length:** 200 bytes (approximately 200 characters for ASCII text) - **Rate limiting:** The mesh enforces airtime fairness; heavy message volume may be throttled - **Delivery:** Messages are retried automatically if no acknowledgment is received diff --git a/docs/en/user/node-metrics.md b/docs/en/user/node-metrics.md index 8c29ac610..3e6aceddc 100644 --- a/docs/en/user/node-metrics.md +++ b/docs/en/user/node-metrics.md @@ -2,7 +2,7 @@ title: Node Metrics parent: User Guide nav_order: 5 -last_updated: 2026-06-16 +last_updated: 2026-06-25 description: Telemetry dashboards for each mesh node — device health, environment sensors, air quality, signal quality, power, traceroute, and position history. aliases: - metrics @@ -45,6 +45,10 @@ Environmental sensor data (requires compatible hardware): Environment metrics are charted over time for easy trend analysis — temperature, humidity, and pressure each get their own line chart with the measurement unit displayed on the Y axis. +The BME680 **IAQ (Indoor Air Quality)** index is a single 0–500+ value derived from gas resistance, shown against a color-coded scale from *Excellent* to *Dangerously Polluted*: + +![IAQ index scale from Excellent to Dangerously Polluted](../../assets/screenshots/node-metrics_iaq_scale.png) + > 💡 **Tip:** Environment metrics require a sensor connected to the remote node. Not all nodes report environmental data. See [Telemetry & Sensors](telemetry-and-sensors) for a full list of supported sensors. ## Air Quality Metrics @@ -92,12 +96,16 @@ Radio signal quality information: ### Signal Quality Reference -| SNR Range | Quality | -|-----------|---------| -| > 10 dB | Excellent | -| 0 to 10 dB | Good | -| -10 to 0 dB | Fair | -| < -10 dB | Poor | +Signal quality is rated from **SNR relative to the active LoRa modem preset's demodulation floor**, not from fixed thresholds — a given SNR means different things on different presets (e.g. −15 dB is fine on LongSlow but unusable on ShortFast). RSSI is shown but is not part of the rating. Letting `limit` be the preset's SNR limit: + +| Quality | Criteria | +|---------|----------| +| Good | SNR above the preset's limit | +| Fair | within 5.5 dB below the limit | +| Bad | within 7.5 dB below the limit | +| None | more than 7.5 dB below the limit | + +See [Understanding the Signal Meter](signal-meter) for the full explanation. Local Stats from your connected radio are also shown in Signal Quality when available. These logs include noise floor, traffic counters, relay counters, online node counts, and radio uptime. The noise floor chart uses a dashed reference line at -85 dBm to help identify a busy RF environment. Use **Request** to ask the connected radio for a fresh Local Stats telemetry report, **Clear** to remove Local Stats logs for that node, and **Save** to export the visible Local Stats history as CSV. diff --git a/docs/en/user/nodes.md b/docs/en/user/nodes.md index 07ba15460..9d8bf490c 100644 --- a/docs/en/user/nodes.md +++ b/docs/en/user/nodes.md @@ -2,7 +2,7 @@ title: Nodes parent: User Guide nav_order: 4 -last_updated: 2026-06-02 +last_updated: 2026-06-25 description: Browse, filter, and sort mesh nodes — view details, signal quality, roles, and quick actions. aliases: - node-list @@ -28,11 +28,12 @@ The node list shows every node your radio has heard, including: | Badge | Meaning | |-------|---------| -| 🟢 Online | Node heard within the last 15 minutes | -| 🟡 Away | Node heard within the last 2 hours | -| 🔴 Offline | Node not heard for over 2 hours | +| 🟢 Online | Node heard within the last 2 hours | +| ⚪ Offline | Node not heard for over 2 hours | | ⭐ Favorite | Node marked as favorite by the user | +A node is considered **online** if it was heard within the last 2 hours, and **offline** otherwise — there is no separate "away" tier. + ### Node Roles Nodes can be configured with different roles that affect their mesh behavior: @@ -101,7 +102,7 @@ Type in the search field to filter nodes by name or short name. The filter updat | Filter | Description | |--------|-------------| -| **Only online** | Show only nodes heard within the last 15 minutes | +| **Only online** | Show only nodes heard within the last 2 hours | | **Only direct** | Show only nodes with direct (non-relayed) connections | | **Include unknown** | Show nodes that haven't sent user info yet | | **Exclude infrastructure** | Hide infrastructure-role nodes (Router, Repeater, Router Late, Client Base) | diff --git a/docs/en/user/settings-radio-user.md b/docs/en/user/settings-radio-user.md index 7876ba406..7ce45ba26 100644 --- a/docs/en/user/settings-radio-user.md +++ b/docs/en/user/settings-radio-user.md @@ -55,6 +55,8 @@ After modifying settings, tap **Save** to write the configuration to your radio. ### Modem Presets +> 💡 **Tip:** The **SNR Limit** values are negative on purpose. LoRa can decode signals *below* the noise floor, so a more-negative limit means the preset tolerates a weaker, noisier signal (more range). See [How the Signal Meter Works](signal-meter) for the full explanation. + | Preset | Range | Speed | SNR Limit | Best For | |--------|-------|-------|-----------|----------| | Short Turbo | ~1 km | 21.9 kbps | −5 dB | Dense urban with line-of-sight; data-heavy applications | diff --git a/docs/en/user/signal-meter.md b/docs/en/user/signal-meter.md index 6e97cc69e..b3f7f4111 100644 --- a/docs/en/user/signal-meter.md +++ b/docs/en/user/signal-meter.md @@ -2,8 +2,8 @@ title: How the Meshtastic Signal Meter Works parent: User Guide nav_order: 15 -last_updated: 2026-05-13 -description: How the signal meter calculates quality from RSSI and SNR — LoRa spread spectrum, presets, and what the bars really mean. +last_updated: 2026-06-25 +description: How the signal meter rates quality from SNR relative to the LoRa modem preset — spread spectrum, presets, and what the bars really mean. aliases: - signal - signal-meter @@ -13,7 +13,7 @@ aliases: # How the Meshtastic Signal Meter Works -The Meshtastic signal meter — the familiar bars or status color in the app — is calculated very differently than the "bars" on a traditional cell phone or Wi-Fi router. +The Meshtastic signal meter — the familiar bars or status color in the app — is calculated very differently than the "bars" on a traditional cell phone or WiFi router. Most consumer devices simply measure how "loud" a signal is. However, because Meshtastic uses **LoRa (Long Range)** technology, its signal meter measures how **clear** the signal is, relative to the specific settings your mesh is using. @@ -26,7 +26,7 @@ Every time the LoRa radio chip receives a message, it reports two measurements: * **RSSI (Received Signal Strength Indicator):** The **loudness** of the raw power hitting your antenna. * **SNR (Signal-to-Noise Ratio):** The **clarity** of the signal compared to the background static. -> **Tip — The Analogy:** Imagine you are trying to hear a friend talking to you. +> 💡 **Tip:** Here's an analogy — imagine you are trying to hear a friend talking to you. > * **RSSI** is how loud their voice is. > * **The Noise Floor** is the background noise in the room (air conditioning, other people talking, traffic). > * **SNR** is how easily you can distinguish your friend's voice from the background noise. @@ -37,7 +37,7 @@ If your friend shouts at you at a deafening rock concert, the signal is incredib ## 2. The Magic of LoRa: Hearing "Below the Noise Floor" -For standard radios (like FM or Wi-Fi), if the background noise is louder than the signal (a negative SNR), the receiver just hears static. +For standard radios (like FM or WiFi), if the background noise is louder than the signal (a negative SNR), the receiver just hears static. LoRa is special. It uses **"Spread Spectrum"** modulation, which allows the radio to mathematically pull a signal out of the air even when it is buried deep *underneath* the background noise. This is why you will frequently see **negative SNR numbers** in Meshtastic (e.g., -10 dB, which means the signal is 10 decibels weaker than the background static). @@ -47,16 +47,18 @@ Depending on which Meshtastic preset you are using (e.g., `LongFast` vs. `ShortF ## 3. How the Signal Meter Calculates Quality -The Meshtastic apps take both RSSI and SNR and run them through a specific formula to assign your signal a quality rating (None, Bad, Fair, or Good). It specifically scales these values based on the physical limits of the radio preset you are using. +The app rates your signal quality (None, Bad, Fair, or Good) from **SNR alone, measured relative to the preset's SNR Limit** — the demodulation floor described above. It deliberately does **not** factor RSSI into the rating: without the local noise floor, RSSI cannot tell you whether a signal is actually decodable, so SNR-versus-the-preset-limit is the meaningful measure. (RSSI is still displayed to you elsewhere.) -Here is exactly how the app decides how many bars (or what color) to show you: +Because the rating is relative to the preset limit, the *same* SNR can rate differently on different presets — `-15 dB` is healthy on `LongSlow` but unusable on `ShortFast`. Letting `limit` be the active preset's SNR Limit, here is how the app picks the bars (or color): | Level | Bars | Criteria | Meaning | |-------|------|----------|---------| -| Good | 3 | RSSI better than `-115 dBm` **AND** SNR better than `-7 dB` | Signal is both loud and clear — healthy connection. | -| Fair | 2 | RSSI better than `-126 dBm` with good SNR, **OR** SNR better than `-15 dB` with good RSSI | Signal getting quieter or noisier, but still decodable. | -| Bad | 1 | Falls between Fair and None thresholds | At the edge of range or experiencing interference. | -| None | 0 | RSSI worse than `-126 dBm` **AND** SNR worse than `-15 dB` | Transmission completely buried in noise. | +| Good | 3 | SNR **above** the preset's `limit` | Signal is comfortably above the demodulation floor — healthy connection. | +| Fair | 2 | within `5.5 dB` below the `limit` | Decodable, but getting close to the floor. | +| Bad | 1 | within `7.5 dB` below the `limit` | At the very edge of what the preset can recover. | +| None | 0 | more than `7.5 dB` below the `limit` | Below the floor — transmission lost to noise. | + +> **Note:** The fixed SNR thresholds you may have seen elsewhere (`-7 dB` / `-15 dB`) are now only used for coloring individual hops in traceroute results — not for the per-node signal meter described here. --- @@ -64,9 +66,9 @@ Here is exactly how the app decides how many bars (or what color) to show you: Because Meshtastic's meter acts as a **"Clarity Meter"**, it behaves differently than what most people expect: -> **Tip — Don't panic over low RSSI:** You might see a seemingly terrible RSSI value like `-118 dBm`. On a cell phone, you would have zero bars. But if you have an SNR of `+2 dB`, Meshtastic will still show a strong signal! *The library is quiet, so the whisper is heard perfectly.* +> 💡 **Tip:** Don't panic over low RSSI. You might see a seemingly terrible RSSI value like `-118 dBm`. On a cell phone, you would have zero bars. But if you have an SNR of `+2 dB`, Meshtastic will still show a strong signal! *The library is quiet, so the whisper is heard perfectly.* -> **Warning — Watch out for local noise:** If you hook up a massive antenna and see a great RSSI (e.g., `-90 dBm`) but your signal meter is only showing **1 Bar (Bad)**, you have a problem. It means you have local interference — perhaps a cheap power supply, a noisy computer, or a nearby radio tower — creating so much static that it is drowning out your mesh. +> ⚠️ **Warning:** Watch out for local noise. If you hook up a massive antenna and see a great RSSI (e.g., `-90 dBm`) but your signal meter is only showing **1 Bar (Bad)**, you have a problem. It means you have local interference — perhaps a cheap power supply, a noisy computer, or a nearby radio tower — creating so much static that it is drowning out your mesh. ## Where Signal Information Appears @@ -79,3 +81,11 @@ In the app, signal data is shown in several places: ![Node entry showing SNR, RSSI values and colored signal bars](../../assets/screenshots/nodes_signal_info.png) +## Related Topics + +- [Nodes](nodes) — where signal bars appear in the node list +- [Node Metrics](node-metrics) — SNR/RSSI history and the per-node signal quality reference +- [Settings — Radio & User](settings-radio-user) — modem presets and their SNR limits + +--- + diff --git a/docs/en/user/tak.md b/docs/en/user/tak.md index 3a3118e48..2f61692cb 100644 --- a/docs/en/user/tak.md +++ b/docs/en/user/tak.md @@ -48,6 +48,15 @@ The TAK module allows Meshtastic nodes to: 2. Open ATAK and enable the Meshtastic plugin. 3. The plugin bridges messages between ATAK and your mesh network. +### Local TAK Server + +The app can also run a **local TAK server** so ATAK/iTAK clients on the same device or network can connect directly, without a remote TAK server. Open **Settings → Module Config → TAK → TAK Server**: + +![Local TAK Server settings with enable toggle and export option](../../assets/screenshots/tak_server_enabled.png) + +- **Enable Local TAK Server** — starts a local TLS server on port **8089** for ATAK/iTAK connections. +- **Export TAK Data Package** — generates a `.zip` data package that ATAK/iTAK can import to connect to this server. + ## TAK Roles Nodes configured with TAK-related roles behave differently from standard clients: diff --git a/docs/en/user/telemetry-and-sensors.md b/docs/en/user/telemetry-and-sensors.md index 5ca4e0ab3..0251011f7 100644 --- a/docs/en/user/telemetry-and-sensors.md +++ b/docs/en/user/telemetry-and-sensors.md @@ -103,11 +103,7 @@ Nodes with particulate matter or CO₂ sensors report air quality data: | PM10 | µg/m³ | Coarse particulate matter | | CO₂ | ppm | Carbon dioxide concentration | -The CO₂ reading is color-coded by severity: -- 🟢 **Good** (< 1000 ppm) — normal indoor levels -- 🟡 **Moderate** (1000–2000 ppm) — elevated, consider ventilation -- 🟠 **Poor** (2000–5000 ppm) — drowsiness, poor concentration -- 🔴 **Hazardous** (≥ 5000 ppm) — immediate health concern +The CO₂ reading is color-coded by severity (Good → Stuffy → Poor → Unsafe → Evacuate). See [Node Metrics — Air Quality](node-metrics#air-quality-metrics) for the exact ppm bands and colors. Air quality data can be viewed as info cards on the node detail screen, charted over time, and exported to CSV. diff --git a/docs/en/user/translate.md b/docs/en/user/translate.md index d97278855..bba05e820 100644 --- a/docs/en/user/translate.md +++ b/docs/en/user/translate.md @@ -2,7 +2,8 @@ title: Translate the App parent: User Guide nav_order: 17 -last_updated: 2026-05-13 +last_updated: 2026-06-25 +description: How the app and its documentation are translated via Crowdin, and guidelines for contributing translations. aliases: - translate - crowdin @@ -23,7 +24,7 @@ Contributing translations helps make Meshtastic accessible to a wider audience. | User Guide pages | `docs/user/*.md` | In-app documentation shown in Help & Documentation | | Fastlane metadata | `fastlane/metadata/android/en-US/` | App Store listing title, description, and changelogs | -> **Note — Developer Guide pages are English-only.** Code-focused documentation targeting contributors is not translated. +> ⚠️ **Note:** Developer Guide pages are English-only. Code-focused documentation targeting contributors is not translated. --- @@ -35,7 +36,7 @@ Contributing translations helps make Meshtastic accessible to a wider audience. 4. **Review context.** Many strings include screenshots or context comments — check these to understand where the text appears in the app. 5. **Submit.** Approved translations are automatically merged into the next release. -> **Tip — Keep translations short.** UI strings often appear in buttons, chips, or narrow columns. If a translation is significantly longer than the English original, consider abbreviating where the meaning stays clear. +> 💡 **Tip:** Keep translations short. UI strings often appear in buttons, chips, or narrow columns. If a translation is significantly longer than the English original, consider abbreviating where the meaning stays clear. --- @@ -68,15 +69,19 @@ In-app documentation follows a similar pattern under `docs/`: ``` docs/ -├── user/ ← English source (default) +├── en/user/ ← English source (default) │ ├── onboarding.md │ └── ... -├── fr/user/ ← French translations +├── fr-rFR/user/ ← French (France) │ ├── onboarding.md │ └── ... +├── de-rDE/user/ ← German (Germany) +│ └── ... └── ... ``` +Locale folders use the Android resource convention `{lang}-r{REGION}` (e.g. `fr-rFR`, `de-rDE`, `ja-rJP`), matching the `values-*` directories used for app strings. + The app automatically selects the correct locale based on your device's **Language & Region** settings. --- diff --git a/docs/en/user/units-and-locale.md b/docs/en/user/units-and-locale.md index 4fd350df2..2fc08d32c 100644 --- a/docs/en/user/units-and-locale.md +++ b/docs/en/user/units-and-locale.md @@ -3,6 +3,7 @@ title: Units, Measurement & Locale parent: User Guide nav_order: 16 last_updated: 2026-05-12 +description: How the app formats temperature, distance, speed, and other measurements based on your device locale. --- # Units, Measurement & Locale @@ -17,7 +18,7 @@ Meshtastic radios always transmit data in **metric units** (meters, °C, km/h, h On Android, your measurement preferences are determined by your system **Language & Region** settings. On Desktop (JVM), the app uses the JVM's default `Locale`. -> **Tip — You never need to toggle units inside the app.** Change your system measurement preferences and every screen in Meshtastic updates automatically — node details, telemetry charts, weather, altitude, and more. +> 💡 **Tip:** You never need to toggle units inside the app. Change your system measurement preferences and every screen in Meshtastic updates automatically — node details, telemetry charts, weather, altitude, and more. --- @@ -114,5 +115,13 @@ On Android, your measurement system (metric vs imperial) is tied to your region 2. Change your **Region** or **Measurement units** preference 3. Return to Meshtastic — values update immediately -> **Tip — The app uses `MetricFormatter` from `core:common`.** All measurement formatting is handled by a shared KMP utility that respects your platform's locale. Developers adding new measurement displays should use `MetricFormatter` rather than hard-coding unit conversions. +> 💡 **Tip:** The app uses `MetricFormatter` from `core:common`. All measurement formatting is handled by a shared KMP utility that respects your platform's locale. Developers adding new measurement displays should use `MetricFormatter` rather than hard-coding unit conversions. + +## Related Topics + +- [Node Metrics](node-metrics) — where temperature, distance, and sensor values are displayed +- [Telemetry & Sensors](telemetry-and-sensors) — the sensors that produce these measurements +- [Settings — Radio & User](settings-radio-user) — region setting that drives unit selection + +--- diff --git a/docs/en/user/widget.md b/docs/en/user/widget.md new file mode 100644 index 000000000..662bcc29a --- /dev/null +++ b/docs/en/user/widget.md @@ -0,0 +1,46 @@ +--- +title: Home Screen Widget +parent: User Guide +nav_order: 20 +last_updated: 2026-06-25 +description: Add the Meshtastic home screen widget to glance at your connected radio's local stats without opening the app. +aliases: + - widget + - home-screen-widget + - local-stats-widget +--- + +# Home Screen Widget + +On Android, Meshtastic provides a home screen **widget** that shows live local statistics from your connected radio at a glance — no need to open the app. + +## What It Shows + +The widget displays the **connected radio's** current local stats: + +- **Battery** — the radio's battery level, or *Powered* when running on external power +- **ChUtil** — channel utilization (how busy the LoRa channel is, as a percentage) +- **AirUtil** — airtime utilization (how much of the duty cycle your radio is transmitting) +- **Traffic** — packets transmitted / received, and duplicates seen +- **Relays** — packets relayed and relay cancellations (shown when the radio is relaying) + +Tap the widget to open the app, or use its refresh control to request fresh stats. + +> 💡 **Tip:** The values reflect the radio you are currently connected to. If the app isn't connected to a radio, the widget shows the last known stats until it reconnects. + +## Adding the Widget + +1. Long-press an empty area of your Android home screen. +2. Tap **Widgets**. +3. Find **Meshtastic** in the list and drag the **Local Stats** widget to your home screen. +4. Resize it as needed — the layout adapts to the available space. + +> ⚠️ **Note:** The widget is Android-only. It is not available on the Desktop or iOS builds. + +## Related Topics + +- [Node Metrics](node-metrics) — the full Signal Quality and Local Stats history inside the app +- [Connections](connections) — connect to a radio so the widget has stats to show +- [Discovery](discovery) — channel and airtime utilization across the mesh + +--- diff --git a/feature/connections/detekt-baseline.xml b/feature/connections/detekt-baseline.xml index 277dd5427..b1806ff2a 100644 --- a/feature/connections/detekt-baseline.xml +++ b/feature/connections/detekt-baseline.xml @@ -3,6 +3,7 @@ Kdoc:TcpDiscoveryHelpers.kt:/** * Shared helpers for TCP device discovery logic used by both [CommonGetDiscoveredDevicesUseCase] and the * Android-specific variant. */ + PreviewPublic:ConnectionsPreviews.kt:@PreviewLightDark @Composable fun BluetoothScanPreview PreviewPublic:ConnectionsPreviews.kt:@PreviewLightDark @Composable fun ConnectingDeviceInfoPreview PreviewPublic:ConnectionsPreviews.kt:@PreviewLightDark @Composable fun DeviceListItemPreview PreviewPublic:ConnectionsPreviews.kt:@PreviewLightDark @Composable fun DeviceSectionHeaderPreview diff --git a/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/component/ConnectionsPreviews.kt b/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/component/ConnectionsPreviews.kt index 2df7bc09f..bb2e9576c 100644 --- a/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/component/ConnectionsPreviews.kt +++ b/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/component/ConnectionsPreviews.kt @@ -16,8 +16,13 @@ */ package org.meshtastic.feature.connections.component +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -71,7 +76,43 @@ fun ConnectingDeviceInfoPreview() { @PreviewLightDark @Composable fun EmptyStateContentPreview() { - AppTheme { EmptyStateContent(text = "No devices found", imageVector = MeshtasticIcons.Search) } + // Bounded height so the docs reference is a tight crop of the empty-state block, not a full-screen frame + // (EmptyStateContent fills its parent to center its content). + AppTheme { + Surface(modifier = Modifier.fillMaxWidth().height(220.dp)) { + EmptyStateContent(text = "No devices found", imageVector = MeshtasticIcons.Search) + } + } +} + +// Real Connections-screen Bluetooth scan: the device list with the scan-in-progress header and a discovered radio. +// Replaces the old wifi-provision "Searching for device…" splash that was mislabeled as the BLE scan in the docs. +@PreviewLightDark +@Composable +fun BluetoothScanPreview() { + AppTheme { + Surface(modifier = Modifier.fillMaxWidth().height(320.dp)) { + DeviceList( + connectionState = ConnectionState.Disconnected, + selectedDevice = "", + bleDevices = + listOf( + DeviceListEntry.Ble(PreviewBleDevice(address = "AA:BB:CC:DD:EE:FF", name = "Meshtastic_abcd")), + ), + usbDevices = emptyList(), + discoveredTcpDevices = emptyList(), + recentTcpDevices = emptyList(), + isBleScanning = true, + isNetworkScanning = false, + activeTransport = DeviceType.BLE, + onSelectDevice = {}, + onToggleBleScan = {}, + onToggleNetworkScan = {}, + onAddManualAddress = { _, _ -> }, + onRemoveRecentAddress = {}, + ) + } + } } @PreviewLightDark diff --git a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwarePreviews.kt b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwarePreviews.kt index 47e8bbc28..c0c672c74 100644 --- a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwarePreviews.kt +++ b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwarePreviews.kt @@ -16,9 +16,7 @@ */ package org.meshtastic.feature.firmware -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Surface import androidx.compose.runtime.Composable @@ -29,16 +27,15 @@ import androidx.compose.ui.unit.dp import org.meshtastic.core.resources.UiText import org.meshtastic.core.ui.theme.AppTheme +// These previews intentionally wrap-content (no fillMaxSize) so the generated reference images are tight crops of +// the status block — the docs reference the status component itself, not the whole screen. See docs/assets/screenshots. + @PreviewLightDark @Composable fun VerifyingStatePreview() { AppTheme { Surface { - Column( - modifier = Modifier.fillMaxSize().padding(24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { + Column(modifier = Modifier.padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally) { VerifyingState() } } @@ -50,11 +47,7 @@ fun VerifyingStatePreview() { fun CheckingStatePreview() { AppTheme { Surface { - Column( - modifier = Modifier.fillMaxSize().padding(24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { + Column(modifier = Modifier.padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally) { CheckingState() } } @@ -66,11 +59,7 @@ fun CheckingStatePreview() { fun ErrorStatePreview() { AppTheme { Surface { - Column( - modifier = Modifier.fillMaxSize().padding(24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { + Column(modifier = Modifier.padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally) { ErrorState(error = UiText.DynamicString("Connection lost"), onRetry = {}) } } @@ -82,11 +71,7 @@ fun ErrorStatePreview() { fun SuccessStatePreview() { AppTheme { Surface { - Column( - modifier = Modifier.fillMaxSize().padding(24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { + Column(modifier = Modifier.padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally) { SuccessState(onDone = {}) } } diff --git a/screenshot-tests/build.gradle.kts b/screenshot-tests/build.gradle.kts index de011c43e..610fa2817 100644 --- a/screenshot-tests/build.gradle.kts +++ b/screenshot-tests/build.gradle.kts @@ -48,7 +48,6 @@ dependencies { implementation(project(":feature:wifi-provision")) implementation(project(":feature:connections")) implementation(project(":feature:settings")) - implementation(project(":feature:firmware")) implementation(project(":feature:intro")) implementation(project(":feature:map")) implementation(project(":feature:docs")) @@ -68,6 +67,8 @@ tasks.register("copyDocsScreenshots") { group = "documentation" val referenceDir = layout.projectDirectory.dir("src/screenshotTestDebug/reference") + // Doc-framed compositions live in the generate-only :docs-screenshots module; aggregate its references too. + val docsReferenceDir = rootProject.layout.projectDirectory.dir("docs-screenshots/src/screenshotTestDebug/reference") val manifestFile = layout.projectDirectory.file("docs-screenshots-manifest.txt") val aliasFile = layout.projectDirectory.file("docs-screenshot-aliases.properties") val outputDir = rootProject.layout.projectDirectory.dir("docs/assets/screenshots") @@ -83,6 +84,7 @@ tasks.register("copyDocsScreenshots") { } from(referenceDir) { include(manifestPatterns) } + from(docsReferenceDir) { include(manifestPatterns) } into(outputDir) // Build reverse alias map (CST name → semantic name) for renaming during copy. diff --git a/screenshot-tests/docs-screenshot-aliases.properties b/screenshot-tests/docs-screenshot-aliases.properties index 561dabcdb..bf89cf776 100644 --- a/screenshot-tests/docs-screenshot-aliases.properties +++ b/screenshot-tests/docs-screenshot-aliases.properties @@ -11,7 +11,7 @@ onboarding_welcome.png=ScreenshotWelcomeScreen_Light_b29dc7a7_0.png # Connections -connections_bluetooth_scan.png=ScreenshotScanningBle_Light_b29dc7a7_0.png +connections_bluetooth_scan.png=ScreenshotConnectionsBluetoothScan_Light_b29dc7a7_0.png connections_transport_filters.png=ScreenshotTransportSelector_Light_b29dc7a7_0.png connections_connecting.png=ScreenshotConnectingDeviceInfo_Light_b29dc7a7_0.png connections_disconnect.png=ScreenshotDisconnectButton_Light_b29dc7a7_0.png @@ -22,10 +22,12 @@ firmware_checking.png=ScreenshotFirmwareChecking_Light_b29dc7a7_0.png firmware_disclaimer.png=ScreenshotFirmwareDisclaimer_Light_b29dc7a7_0.png firmware_error.png=ScreenshotFirmwareError_Light_b29dc7a7_0.png firmware_success.png=ScreenshotFirmwareSuccess_Light_b29dc7a7_0.png +firmware_verifying.png=ScreenshotFirmwareVerifying_Light_b29dc7a7_0.png # Messages messages_quick_chat.png=ScreenshotQuickChatItem_Light_b29dc7a7_0.png messages_reaction.png=ScreenshotReactionItem_Light_b29dc7a7_0.png +messages_edit_quick_chat.png=ScreenshotEditQuickChatDialog_Light_b29dc7a7_0.png messages_search_bar.png=ScreenshotMessageSearchBar_Light_b29dc7a7_0.png messages-and-channels_channel_list.png=ScreenshotChannelItem_Light_b29dc7a7_0.png @@ -46,6 +48,7 @@ nodes_distance_info.png=ScreenshotDistanceInfo_Light_b29dc7a7_0.png # Node metrics node-metrics_telemetric_actions.png=ScreenshotTelemetricActionsSection_Light_b29dc7a7_0.png node-metrics_air_quality.png=ScreenshotAirQualityCards_Light_b29dc7a7_0.png +node-metrics_iaq_scale.png=ScreenshotIAQScale_Light_b29dc7a7_0.png # Discovery (Local Mesh Discovery scanner) discovery_preset_result.png=ScreenshotDiscoveryPresetResult_Light_b29dc7a7_0.png @@ -73,13 +76,11 @@ settings_password_field.png=ScreenshotEditPasswordPreference_Light_b29dc7a7_0.pn settings_text_field.png=ScreenshotEditTextPreference_Light_b29dc7a7_0.png settings_ipv4_field.png=ScreenshotEditIPv4Preference_Light_b29dc7a7_0.png +# TAK +tak_server_enabled.png=ScreenshotTakServerSectionEnabled_Light_b29dc7a7_0.png + # Docs browser docs-browser_toc.png=ScreenshotDocsBrowser_Light_b29dc7a7_0.png docs-browser_search.png=ScreenshotDocsSearchBarWithQuery_Light_b29dc7a7_0.png docs-browser_page.png=ScreenshotDocsPageContent_Light_b29dc7a7_0.png docs-browser_chirpy.png=ScreenshotChirpyAssistant_Light_b29dc7a7_0.png - -# NOTE: connections_wifi_scanning.png, connections_wifi_device_found.png, and -# connections_wifi_success.png are manual captures with no current CST source; -# they remain hand-maintained in docs/assets/screenshots/ until WifiProvision -# previews matching the documented flow exist. diff --git a/screenshot-tests/docs-screenshots-manifest.txt b/screenshot-tests/docs-screenshots-manifest.txt index 7bdca402c..8c3af438f 100644 --- a/screenshot-tests/docs-screenshots-manifest.txt +++ b/screenshot-tests/docs-screenshots-manifest.txt @@ -13,6 +13,8 @@ # Feature: Connections **/ConnectionsScreenshotTestsKt/Screenshot*_Light_*.png +# Feature: Connections — doc-framed compositions (generated by :docs-screenshots) +**/ConnectionsDocScreenshotTestsKt/Screenshot*_Light_*.png # Feature: Firmware **/FirmwareScreenshotTestsKt/Screenshot*_Light_*.png diff --git a/screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/ConnectionsScreenshotTests.kt b/screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/ConnectionsScreenshotTests.kt index e46919c36..f4d5198d0 100644 --- a/screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/ConnectionsScreenshotTests.kt +++ b/screenshot-tests/src/screenshotTest/kotlin/org/meshtastic/screenshots/feature/ConnectionsScreenshotTests.kt @@ -23,7 +23,6 @@ import org.meshtastic.feature.connections.component.ConnectingDeviceInfoPreview import org.meshtastic.feature.connections.component.DeviceListItemPreview import org.meshtastic.feature.connections.component.DeviceSectionHeaderPreview import org.meshtastic.feature.connections.component.DisconnectButtonPreview -import org.meshtastic.feature.connections.component.EmptyStateContentPreview import org.meshtastic.feature.connections.component.TransportSelectorPreview @PreviewTest @@ -47,13 +46,6 @@ fun ScreenshotConnectingDeviceInfo() { ConnectingDeviceInfoPreview() } -@PreviewTest -@PreviewLightDark -@Composable -fun ScreenshotEmptyStateContent() { - EmptyStateContentPreview() -} - @PreviewTest @PreviewLightDark @Composable diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotEmptyStateContent_Dark_d19fbf1f_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotEmptyStateContent_Dark_d19fbf1f_0.png deleted file mode 100644 index 32fab55bb..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotEmptyStateContent_Dark_d19fbf1f_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotEmptyStateContent_Light_b29dc7a7_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotEmptyStateContent_Light_b29dc7a7_0.png deleted file mode 100644 index 096d5690b..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotEmptyStateContent_Light_b29dc7a7_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Dark_d19fbf1f_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Dark_d19fbf1f_0.png deleted file mode 100644 index eb0889444..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Dark_d19fbf1f_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Light_b29dc7a7_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Light_b29dc7a7_0.png deleted file mode 100644 index a22d68b3b..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareChecking_Light_b29dc7a7_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Dark_d19fbf1f_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Dark_d19fbf1f_0.png deleted file mode 100644 index 677486d80..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Dark_d19fbf1f_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Light_b29dc7a7_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Light_b29dc7a7_0.png deleted file mode 100644 index 30c34d13e..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareError_Light_b29dc7a7_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Dark_d19fbf1f_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Dark_d19fbf1f_0.png deleted file mode 100644 index e28d5ddd8..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Dark_d19fbf1f_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Light_b29dc7a7_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Light_b29dc7a7_0.png deleted file mode 100644 index d6ac649ff..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareSuccess_Light_b29dc7a7_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Dark_d19fbf1f_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Dark_d19fbf1f_0.png deleted file mode 100644 index 152cbb341..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Dark_d19fbf1f_0.png and /dev/null differ diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Light_b29dc7a7_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Light_b29dc7a7_0.png deleted file mode 100644 index cf4807bc2..000000000 Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/FirmwareScreenshotTestsKt/ScreenshotFirmwareVerifying_Light_b29dc7a7_0.png and /dev/null differ diff --git a/settings.gradle.kts b/settings.gradle.kts index 4a0d54338..7aa3c4f83 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -134,5 +134,6 @@ include( ":core:barcode", ":feature:widget", ":screenshot-tests", + ":docs-screenshots", ":baselineprofile", )