# TrueScreen Android Design Documentation

This folder contains a static design-system documentation page for the Android app. Open `index.html` directly in a browser; no dev server is required for normal use.

## Safety Rules

- Check `git status` before editing. This repository often has an unclean worktree.
- Do not overwrite, revert, or reformat unrelated user changes.
- Keep `designDoc/` static and self-contained.
- Do not change production component behavior just to improve documentation.
- Add documentation previews in the screenshot-test source set unless the user explicitly asks to add previews to production component files.

## Source Of Truth

- Page shell: `designDoc/index.html`.
- Page data and renderers: `designDoc/app.js`.
- Page styling: `designDoc/styles.css`.
- Design-doc contract check: `designDoc/validate-design-doc.js`.
- Documentation screenshots: `designDoc/assets/components/screenshots/`.
- Legacy placeholder SVGs: `designDoc/assets/components/*.svg`.
- Website icon: `designDoc/assets/app-icon.svg` and PNG fallback `designDoc/assets/app-icon.png`, adapted from `core/resources/src/official/res/mipmap/ic_launcher.xml`.
- Maintenance section: `index.html#maintenance` loads this README directly from `README.md` only after the collapsed panel is expanded, renders it in the panel, and keeps a separate open-file link.
- Shared agent skill: `designDoc/skills/android-design-update/`.
- Colors: `core/designsystem/src/main/java/com/truescreen/core/designsystem/color/Palette.kt` and `ColorScheme.kt`.
- Semantic accessors: `core/designsystem/src/main/java/com/truescreen/core/designsystem/theme/DesignTokens.kt`.
- Typography, spacing, radii: `TSTheme.typography`, `TSTheme.spacing`, `TSTheme.radius`, and `TSTheme.shapes`.
- Reusable components: `core/designsystem/src/main/java/com/truescreen/core/designsystem/components/`.
- Design-doc screenshot previews: `core/designsystem/src/screenshotTest/kotlin/com/truescreen/core/designsystem/`.
- Android guidance source: `https://design-system.qzb1wg.easypanel.host/documentation-app.html#android`.
- Whitelabel flavors: `build-logic/convention/src/main/kotlin/com/truescreen/android/configuration/Flavor.kt`.
- Whitelabel app names: `core/resources/src/{flavor}/res/values/strings.xml`.
- Whitelabel Compose colors: flavor-specific overrides in `Theme.kt`, inherited defaults in `ColorScheme.kt`, and flavor list in `Flavor.kt`.

## Shared Agent Skill

The repo includes a shareable agent skill at `designDoc/skills/android-design-update/`. It contains the workflow and scanner for refreshing this folder when Android design-system components, tokens, previews, or screenshots change.

Codex discovers local skills from:

```bash
${CODEX_HOME:-$HOME/.codex}/skills/android-design-update
```

Claude Code discovers skills from project and personal folders:

```bash
.claude/skills/android-design-update
~/.claude/skills/android-design-update
```

Use the project folder when the skill should travel with this repository. Use the personal folder only when the user wants the skill available across many projects.

### Codex Install Prompt

Paste this prompt into Codex from the repository root:

```text
Install the TrueScreen design docs skill from designDoc/skills/android-design-update into my Codex skills folder at ${CODEX_HOME:-$HOME/.codex}/skills/android-design-update. Preserve SKILL.md, agents/openai.yaml, and scripts/scan_design_doc.py. After installing, tell me to restart Codex so the skill can be discovered.
```

### Claude Code Install Prompt

Paste this prompt into Claude Code from the repository root:

```text
Install the TrueScreen design docs skill from designDoc/skills/android-design-update as a Claude Code project skill at .claude/skills/android-design-update. Preserve SKILL.md and scripts/scan_design_doc.py. Then list available skills and confirm android-design-update is discoverable. Use ~/.claude/skills/android-design-update only if I ask for a personal/global skill.
```

### Refresh Prompt

After the skill is installed, use this prompt when the design docs need to catch up with code changes:

```text
Use the android-design-update skill. Check git status first, scan the TrueScreen Android design system, add any new reusable visual components or changed tokens to designDoc/, refresh @DesignDoc screenshot previews and stable light/dark screenshots when needed, run the design-doc validator, and summarize exactly what changed.
```

The scanner can also be run directly from the repo:

```bash
python3 designDoc/skills/android-design-update/scripts/scan_design_doc.py .
```

## Official Screenshot Testing Setup

The project uses AndroidX Compose Preview Screenshot Testing.

- Plugin: `com.android.compose.screenshot`.
- Version catalog key: `libs.plugins.screenshot`.
- Required property: `android.experimental.enableScreenshotTest=true` in `gradle.properties`.
- Required module flag: `experimentalProperties["android.experimental.enableScreenshotTest"] = true` in `core/designsystem/build.gradle.kts`.
- Screenshot source set: `core/designsystem/src/screenshotTest/kotlin/`.
- Reference images: `core/designsystem/src/screenshotTestDevelopmentOfficialDebug/reference/`.

Focused commands:

```bash
./gradlew :core:designsystem:compileDevelopmentOfficialDebugScreenshotTestSources
./gradlew :core:designsystem:updateDevelopmentOfficialDebugScreenshotTest
./gradlew :core:designsystem:validateDevelopmentOfficialDebugScreenshotTest
./gradlew :core:designsystem:runKtlintCheckOverScreenshotTestSourceSet
```

## `@DesignDoc` Preview Contract

`@DesignDoc` marks screenshot-test previews that are used by this static docs page. Kotlin annotations cannot target "only composables with previews" directly, so the repository enforces that contract through `designDoc/validate-design-doc.js`.

Rules:

- Apply `@DesignDoc` only to functions that are also `@Composable`.
- The same function must also have `@PreviewTest`.
- The same function must also have `@Preview` or a multi-preview annotation such as `@ComponentLightDarkScreenshot`.
- `id` must match the `components[].id` value in `designDoc/app.js`.
- `assetPrefix` must match the stable image prefix in `designDoc/assets/components/screenshots/`.
- For visual cards, `components[].preview` in `app.js` must use the same value as `assetPrefix`.
- A documented visual component should have a component card in `app.js`, an `@DesignDoc` preview, official reference PNGs, and stable docs PNG copies.
- Runtime-only or service-backed helpers can be listed in `app.js` without `@DesignDoc`, but the card must explain why a screenshot is unavailable.

Find current docs previews with:

```bash
rg -n "@DesignDoc|fun .*ScreenshotPreview" core/designsystem/src/screenshotTest/kotlin
node designDoc/validate-design-doc.js
```

## Screenshot Refresh Workflow

1. Update or add the `@DesignDoc` preview in `core/designsystem/src/screenshotTest/kotlin/`.
2. Run:

   ```bash
   ./gradlew :core:designsystem:updateDevelopmentOfficialDebugScreenshotTest
   ```

3. Find generated references in:

   ```text
   core/designsystem/src/screenshotTestDevelopmentOfficialDebug/reference/com/truescreen/core/designsystem/ComponentScreenshotPreviewsKt/
   ```

4. Copy the generated light and dark PNGs to stable docs names:

   ```text
   designDoc/assets/components/screenshots/{assetPrefix}-light.png
   designDoc/assets/components/screenshots/{assetPrefix}-dark.png
   ```

5. Update the component card's `preview` value and `screenshotAssets` in `designDoc/app.js` when a new `assetPrefix` is introduced.
6. Run validation:

   ```bash
   ./gradlew :core:designsystem:validateDevelopmentOfficialDebugScreenshotTest
   ```

Do not rename `@PreviewTest` functions casually. AndroidX reference filenames include the function name, so renaming a docs preview requires regenerating references and updating copied docs assets.

## Page Update Workflow

1. Check `git status`.
2. Check whether source tokens changed:

   ```bash
   rg -n "Palette|ColorScheme|TSColors|TSTheme|Spacing|Radius|Typography" core/designsystem/src/main/java/com/truescreen/core/designsystem
   ```

3. Check whether whitelabel flavors, app names, or Compose theme color mappings changed:

   ```bash
   rg -n "WhitelabelFlavors|appName|#[0-9A-Fa-f]{3,8}|DefaultTrueScreenTheme|lightColorScheme|darkColorScheme|lightCustomColorScheme|darkCustomColorScheme" build-logic/convention/src/main/kotlin/com/truescreen/android/configuration core/resources/src/*/res/values/strings.xml core/designsystem/src/main/java/com/truescreen/core/designsystem/theme/Theme.kt core/designsystem/src/main/java/com/truescreen/core/designsystem/color/ColorScheme.kt
   ```

4. Check whether component APIs changed:

   ```bash
   rg -n "^fun |^class |^object |^enum class |^interface " core/designsystem/src/main/java/com/truescreen/core/designsystem/components -g "*.kt"
   ```

5. Update `guidance`, `typography`, `spacing`, `radii`, `colorSections`, `whitelabelColors`, `components`, and `screenshotAssets` in `designDoc/app.js`.
6. Update `designDoc/styles.css` only when the page layout or component-card rendering needs to change.
7. Update this README when a new docs card, source file, component class, composable, flavor, whitelabel color, or screenshot preview is added.
8. Do not copy or embed this README inside `designDoc/index.html`; `app.js` loads `README.md` directly and `validate-design-doc.js` rejects embedded README copies.
9. Run static checks:

   ```bash
   node --check designDoc/app.js
   node designDoc/validate-design-doc.js
   ```

10. Open `designDoc/index.html` and verify navigation, anchors, color swatches, the whitelabel color selector, the Light/Dark color mode selector, component cards, README loading, image loading, and responsive layout. Loading the page must not request or download `README.md`; the README should only be fetched when the maintenance panel is expanded. Some browsers block JavaScript reads from `file://`; in that case the README panel should show a non-downloading error message, while the Download text link in the panel header should still point at the same file. Do not use an iframe, object, embed, or automatic navigation to `README.md`; some browsers treat Markdown files as downloads.

## Whitelabel Color Map

The Colors page includes a horizontal selector for every entry in `WhitelabelFlavors`: `official`, `federpol`, `protos`, `surveytech`, `camcomrf`, `giuffre`, and `zurich`.

`colorSections` in `designDoc/app.js` is the single visible source for palette, light scheme, dark scheme, custom semantic colors, and semantic accessors. Do not copy that full color list into `whitelabelColors`. The whitelabel selector must update the existing color tables below it by applying the selected flavor's override map.

The Colors page also has a Light/Dark mode selector. Keep `colorModes`, `selectColorMode`, `renderColorSections(selected, selectedColorMode)`, and `colorOverrideKey(sectionTitle, token, mode)` aligned. Palette and `TSTheme.colors semantic accessors` stay visible in both modes; only the matching Material scheme and light/dark-specific custom rows should be shown for the selected mode.

Each whitelabel selector chip must use that flavor's `primary` property. Set it to the flavor's resolved light `primary` color from `Theme.kt`; for `official`, use the default light `primary` from `ColorScheme.kt`.

Update `colorSections`, `colorModes`, `colorOverrideKey`, or `whitelabelColors` in `designDoc/app.js` when:

- A flavor is added, removed, or renamed in `WhitelabelFlavors`.
- A flavor `appName` string changes.
- `ColorScheme.kt` adds, removes, or changes a default Material or custom color token.
- `Theme.kt` adds, removes, or changes a flavor-specific `lightColorScheme`, `darkColorScheme`, `lightCustomColorScheme`, or `darkCustomColorScheme` override.

Keep `whitelabelColors` limited to flavor metadata, `primary`, `sources`, and changed override values. Inherited values should stay in `colorSections`; flavor overrides should stay in the selected flavor's `overrides` object. Preserve alpha values explicitly, for example `#55A6FE / 30% alpha`.

## Component Page Inventory

Every reusable visual component should be represented here. When a source file changes, update the matching `components` card in `designDoc/app.js`, update or add the `@DesignDoc` preview, regenerate screenshots, and refresh stable PNG copies.

| Card id | Source files | Classes/composables to track | Docs preview |
| --- | --- | --- | --- |
| `buttons` | `NewButtons.kt`, `Buttons.kt` | `ButtonSize`, `ButtonType`, `PrimaryButton`, `PrimarySmallButton`, `PrimaryTinyButton`, `SecondaryButton`, `SecondarySmallButton`, `SecondaryTinyButton`, `GhostButton`, `GhostSmallButton`, `GhostTinyButton`, `OutlineButton`, `OutlineSmallButton`, `OutlineTinyButton`, `ErrorButton`, `CustomButton`, `CustomOutlineButton`, `CtaButton`, `WarningCtaButton`, `LowEmphasisCtaButton`, `OutlinedCtaButton`, `TextCtaButton`, `SmallTextCtaButton`, `TinyCtaButton`, `TinyOutlinedCtaButton`, `LowEmphasisTinyCtaButton` | `@DesignDoc(id = "buttons", assetPrefix = "buttons")` |
| `surfaces` | `Surfaces.kt` | `surfaceContainerColor`, `surfacePrimaryContainerColor`, `surfaceContentColor`, `surfacePrimaryContentColor`, `surfaceContainerBorderColor`, `surfaceContainerShape`, `surfaceContainerBorder`, `ContentSurface`, `ContentPrimarySurface`, `ContentCustomSurface`, `CheckboxSurface` | `@DesignDoc(id = "surfaces", assetPrefix = "surfaces")` |
| `truescreen-page` | `ScreenPage.kt` | `TrueScreenPageType`, `TrueScreenAppBar`, `TrueScreenPage`, `TrueScreenPageDialog`, `TrueScreenEdgeToEdgeDialog`, `TrueScreenDialog` | `@DesignDoc(id = "truescreen-page", assetPrefix = "truescreen-page")` |
| `dialogs` | `Dialogs.kt`, `ConfirmationDialog.kt`, `ScreenPage.kt` | `DialogContainer`, `ConfirmationDialog`, `TrueScreenPageDialog`, `TrueScreenDialog`, `TrueScreenEdgeToEdgeDialog` | `@DesignDoc(id = "dialogs", assetPrefix = "dialogs")` |
| `full-row-icon` | `FullRowIcon.kt` | `FullRowIconButtonGroup`, `FullRowIconButton`, `FullRowIconToggle` | `@DesignDoc(id = "full-row-icon", assetPrefix = "full-row-icon")` |
| `inputs` | `TextField.kt`, `PhoneNumberComponent.kt`, `Checkbox.kt` | `ValueTextField`, `CountryFlag`, `PhoneNumberTextField`, `CountryCodesDialog`, `CheckboxComponent`, `Pair<Int?, String>.phoneNumber`, `RegionPrefixData?.numberFormatted` | `@DesignDoc(id = "inputs", assetPrefix = "inputs")` |
| `feedback` | `ErrorState.kt`, `AppLoader.kt`, `AppLoaderOverlay.kt`, `CircularLoader.kt`, `LoaderImage.kt`, `LottieIcon.kt` | `ErrorStatePage`, `ErrorState`, `ErrorStateDialog`, `ErrorStateDynamicHeight`, `ErrorStateFullHeight`, `AppLoader`, `AppLoaderOverlay`, `CircularLoader`, `LoaderImage`, `LottieIcon`, `LottieIconType` | `@DesignDoc(id = "feedback", assetPrefix = "feedback")` |
| `media-cards` | `MediaPreviewContainer.kt`, `FeatureCard.kt`, `PageBanner.kt`, `LabelIcon.kt`, `TextWithLinks.kt` | `MediaDialog`, `ImagePreviewDialog`, `ImagePreviewContainer`, `FeatureCard`, `PageBanner`, `LabelIcon`, `TextWithLinks` | `@DesignDoc(id = "media-cards", assetPrefix = "media-labels")` |
| `scroll-layout` | `ScrollableColumn.kt`, `ScrollableLazyColumn.kt`, `ScrollableLazyRow.kt`, `ScrollableLazyGrid.kt`, `EndlessLazyColumn.kt`, `FlexRow.kt` | `ScrollableColumn`, `ScrollableLazyColumn`, `ScrollableLazyRow`, `ScrollableLazyVerticalGrid`, `EndlessLazyVerticalGrid`, `EndlessLazyHorizontalGrid`, `EndlessLazyHorizontalDynamicRow`, `FlexRow` | `@DesignDoc(id = "scroll-layout", assetPrefix = "scroll-layout")` |
| `emphatic` | `EmphaticCircleButton.kt`, `EmphaticRecorderButton.kt` | `EmphaticCircleButton`, `EmphaticRecorderButton` | `@DesignDoc(id = "emphatic", assetPrefix = "emphatic-actions")` |
| `runtime` | `MapPreview.kt`, `LocationPermissionHandler.kt`, `LocationRequester.kt`, `ModalBottomSheet.kt`, `SupportedPhoneRegions.kt` | `MapCustomMarker`, `MapPreview`, `PermissionHandler`, `rememberPermissionHandler`, `rememberNotificationPermissionHandler`, `rememberLocationPermissionHandler`, `rememberLocationRequester`, `RememberBottomSheetController`, `RememberBottomSheetScrollableController`, `BottomSheetController`, `SupportedPhoneRegions`, `phoneNumber`, `regionCode`, `valid` | No screenshot unless a reliable preview-safe fake is added. Keep the unavailable reason in `app.js`. |
| `small-utilities` | `FlexRow.kt`, `LabelIcon.kt`, `TextWithLinks.kt`, `SupportedPhoneRegions.kt` | `FlexRow`, `LabelIcon`, `TextWithLinks`, `phoneNumber`, `regionCode`, `valid` | Usually covered inside other cards. Split into a visual card only if these utilities grow into standalone design-system components. |

## Known Extension Candidates

These items are currently grouped into broad cards. If a future change makes one of them important enough to inspect independently, add a dedicated card and `@DesignDoc` preview.

- Button matrix: add separate screenshots for icon-only, custom-color, multiline, and disabled variants if button behavior changes.
- Phone input: split `PhoneNumberTextField` and `CountryCodesDialog` out of `inputs` if country selection UX changes.
- Loader components: split `AppLoader`, `AppLoaderOverlay`, `CircularLoader`, `LoaderImage`, and `LottieIcon` if loading states become visually distinct.
- Error states: split `ErrorStatePage`, `ErrorStateDialog`, and `ErrorStateFullHeight` if empty/error UX changes.
- Media preview: add real fake-image coverage for `MediaDialog`, `ImagePreviewDialog`, and `ImagePreviewContainer`.
- Scroll containers: add loading and paging-state coverage for `EndlessLazyVerticalGrid`, `EndlessLazyHorizontalGrid`, and `EndlessLazyHorizontalDynamicRow`.
- Bottom sheets: add a dedicated preview only if `RememberBottomSheetController` can be represented without runtime-only controller behavior.
- Maps and permissions: keep runtime-only documentation unless a deterministic fake map or permission state is introduced.

## Adding A New Component

1. Add the component to the inventory table above.
2. Add or update the matching object in `components` inside `designDoc/app.js`.
3. If the component is visual and preview-safe, add a new `@DesignDoc` screenshot preview in `ComponentScreenshotPreviews.kt` or a nearby screenshot-test file.
4. Add a `screenshotAssets` entry using the same `assetPrefix`.
5. Run the screenshot update task.
6. Copy generated references to stable docs image names.
7. Run screenshot validation and static page checks.
8. Run `node designDoc/validate-design-doc.js` and fix any contract mismatch.
9. Open `index.html` and inspect the card in light and dark mode.

## Asset Naming

- Stable docs screenshots use kebab-case.
- Light and dark files are paired:

  ```text
  {assetPrefix}-light.png
  {assetPrefix}-dark.png
  ```

- `assetPrefix` must match the `@DesignDoc` annotation.
- Keep generated AndroidX hashed reference filenames in the screenshot-test reference folder. Keep stable names only under `designDoc/assets/components/screenshots/`.
