Skip to content

feat: event-theme fonts — capture + apply app-wide (Google flavor) with sheet opt-out#6163

Merged
jamesarich merged 6 commits into
mainfrom
claude/event-firmware-fonts
Jul 9, 2026
Merged

feat: event-theme fonts — capture + apply app-wide (Google flavor) with sheet opt-out#6163
jamesarich merged 6 commits into
mainfrom
claude/event-firmware-fonts

Conversation

@jamesarich

@jamesarich jamesarich commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Why

The live /resource/eventFirmware (v2) ships theme.fonts (Google Font family names — e.g. DEFCON's {heading: Lato, body: Atkinson Hyperlegible}), which the model was dropping. This captures the field and renders it as the app-wide typeface while a device is on that event firmware. Follow-up to #6162; surfaced while auditing the payload for design#120.

🌟 What it does

  • Capture: EventFirmwareTheme.fonts ({heading, body}) added to the model; persists for free via the existing JSON theme column (no schema change).
  • Apply app-wide: while connected to event firmware (fonts available + not opted out), the full M3 typescale is remapped — heading font → display/headline/title, body font → body/label — via AppTheme reading LocalEventTypographyFonts.
  • Opt-out: a "Use event fonts" switch in the event info sheet, shown only when fonts are actually applicable (Google flavor + edition ships fonts), backed by a new eventFontsEnabled UiPrefs boolean (default on).

🛠️ Flavor seam (mirrors the ApiService split)

  • core:ui EventFonts.kt: EventFontResolver fun interface + EventFonts + Typography.withEventFonts + the two CompositionLocals.
  • Google flavor binds GoogleFontsEventFontResolver (androidx.compose.ui:ui-text-google-fonts + GoogleFont.Provider); F-Droid binds a no-op resolver → default typeface. Resolved via Koin, provided at MainActivity next to LocalEventBranding.
  • Ships androidApp/src/google/res/values/font_certs.xml (canonical AOSP cert array) in the google source set only — F-Droid stays Google-free.

⚠️ Caveats

  • Google flavor only by design (F-Droid has no Play font provider).
  • Downloadable fonts load async — brief fallback→swap on first display — and fetch from Google's font provider (consistent with Maps/Firebase on this flavor).
  • Runtime font rendering not verified here (needs a device); everything compiles + DI-verifies on both flavors.

Testing

spotlessCheck, detekt, :core:ui:build, :core:prefs:allTests, :androidApp:{compile,test}{Google,Fdroid}DebugKotlin/UnitTest, :core:data:jvmTest all green — including KoinVerificationTest on both flavors and v2FieldsRoundTripThroughCache (theme.fonts survives the DB round-trip).

Summary by CodeRabbit

  • New Features
    • Added event-specific typography support for firmware themes using custom heading/body fonts.
    • Introduced an app-wide “Use event fonts” toggle (default enabled) with UI control in the event info sheet.
    • Enabled Google Fonts provisioning for the Google build flavor; the FDroid flavor disables event font provisioning.
  • Bug Fixes
    • Updated v2 themed firmware cache round-trip to include and preserve heading/body font values.
  • Chores
    • Added the new UI string and included the required Google Fonts certificate resources.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds event font metadata, platform-specific font resolution, app-wide enablement state, and a UI toggle for applying event fonts, with repository coverage updated for the new theme fields.

Changes

Event fonts

Layer / File(s) Summary
Font model and theme contract
core/model/src/commonMain/kotlin/org/meshtastic/core/model/EventFirmware.kt
Adds EventFirmwareFonts, adds fonts to EventFirmwareTheme, and updates related KDoc.
Platform font resolution
androidApp/build.gradle.kts, gradle/libs.versions.toml, androidApp/src/google/.../FlavorModule.kt, androidApp/src/fdroid/.../FlavorModule.kt, androidApp/src/google/.../GoogleFontsEventFontResolver.kt, androidApp/src/google/res/values/font_certs.xml
Adds Google Fonts dependency wiring, font certificate resources, and flavor-specific event font resolvers.
Preference and theme state
core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/AppPreferences.kt, core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/ui/UiPrefsImpl.kt, core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeAppPreferences.kt, core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt, androidApp/src/main/kotlin/org/meshtastic/app/MainActivity.kt, core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/EventFonts.kt, core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt
Adds event-font enablement state, theme composition locals, typography application, and wiring from preferences through the app theme.
Event fonts toggle UI
core/resources/src/commonMain/composeResources/values/strings.xml, .skills/compose-ui/strings-index.txt, core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EventInfoSheet.kt
Adds the localized label and bottom-sheet toggle for enabling event fonts.
Repository cache coverage
core/data/src/jvmTest/kotlin/org/meshtastic/core/data/repository/EventFirmwareRepositoryImplTest.kt
Extends the v2 round-trip test to verify fonts.heading and fonts.body survive cache retrieval.

Sequence Diagram(s)

sequenceDiagram
  participant FlavorModule
  participant EventFontResolver
  participant GoogleFontsEventFontResolver
  participant GoogleFontProvider
  participant AppCompositionLocals
  participant AppTheme
  FlavorModule->>EventFontResolver: provide resolver
  AppCompositionLocals->>EventFontResolver: resolve event fonts
  EventFontResolver->>GoogleFontsEventFontResolver: map firmware font names
  GoogleFontsEventFontResolver->>GoogleFontProvider: build FontFamily values
  AppCompositionLocals->>AppTheme: provide LocalEventTypographyFonts
  AppTheme->>AppTheme: apply event fonts to typography
Loading
sequenceDiagram
  participant UiPrefs
  participant UIViewModel
  participant MainActivity
  participant EventInfoSheet
  UiPrefs->>UIViewModel: eventFontsEnabled state
  MainActivity->>UIViewModel: read / update toggle state
  EventInfoSheet->>UIViewModel: user toggles switch
  UIViewModel->>UiPrefs: persist eventFontsEnabled
Loading

Estimated code review effort: 4 (Complex) | ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: capturing event fonts, applying them app-wide, and providing a sheet opt-out for Google builds.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/event-firmware-fonts

Comment @coderabbitai help to get the list of available commands.

The live /resource/eventFirmware v2 populates `theme.fonts` for at least the
DEFCON edition (`{"heading":"Lato","body":"Atkinson Hyperlegible"}`), which the
model was silently dropping via @JsonIgnoreUnknownKeys.

- core:model — add `fonts: EventFirmwareFonts?` (new data class with
  `heading`/`body`) to EventFirmwareTheme. These are Google Font *family names*,
  not URLs. Persists for free: theme is cached as a JSON column, so fonts rides
  the existing round-trip (no entity/DAO/schema change).
- Extended v2FieldsRoundTripThroughCache to assert theme.fonts survives the
  seed → DB → getEdition path.

Captures the data only. Actually rendering with these fonts is deliberately out
of scope — a downloadable-font provider is Google-flavor only (F-Droid has none)
and would need a KMP expect/actual for cosmetic sheet typography; consumers fall
back to the app typeface until/unless that's deemed worth it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added enhancement New feature or request repo Repository maintenance labels Jul 9, 2026
@jamesarich
jamesarich force-pushed the claude/event-firmware-fonts branch from fe3107f to 3d6dbf9 Compare July 9, 2026 00:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/EventFirmwareRepositoryImpl.kt (1)

93-104: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Optional: cache the seeded flag to avoid a DB count() on every call.

ensureSeeded() re-queries localDataSource.count() on every getEdition() call even after the table has been seeded. A cheap in-memory @Volatile seeded flag would avoid the extra round trip on the hot path once seeding has completed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/EventFirmwareRepositoryImpl.kt`
around lines 93 - 104, `ensureSeeded()` is still doing a database `count()`
check on every `getEdition()` path even after seeding has already completed. Add
an in-memory `@Volatile` seeded flag to `EventFirmwareRepositoryImpl`, use it as
the fast path in `ensureSeeded()`, and set it to true only after
`localDataSource.upsertAll(...)` succeeds inside the `writeMutex.withLock` block
so the existing double-check behavior remains safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/EventFirmwareRepositoryImpl.kt`:
- Around line 93-104: `ensureSeeded()` is still doing a database `count()` check
on every `getEdition()` path even after seeding has already completed. Add an
in-memory `@Volatile` seeded flag to `EventFirmwareRepositoryImpl`, use it as
the fast path in `ensureSeeded()`, and set it to true only after
`localDataSource.upsertAll(...)` succeeds inside the `writeMutex.withLock` block
so the existing double-check behavior remains safe.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: efa42723-7aee-4fcc-9d96-6cbd2cd50631

📥 Commits

Reviewing files that changed from the base of the PR and between 8e8cc5b and fe3107f.

📒 Files selected for processing (23)
  • .github/workflows/scheduled-updates.yml
  • .skills/compose-ui/strings-index.txt
  • androidApp/src/fdroid/kotlin/org/meshtastic/app/di/FDroidNetworkModule.kt
  • androidApp/src/main/assets/event_firmware.json
  • core/data/src/commonMain/kotlin/org/meshtastic/core/data/datasource/EventFirmwareEditionLocalDataSource.kt
  • core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/EventFirmwareRepositoryImpl.kt
  • core/data/src/jvmTest/kotlin/org/meshtastic/core/data/repository/DeviceLinkRepositoryImplTest.kt
  • core/data/src/jvmTest/kotlin/org/meshtastic/core/data/repository/EventFirmwareRepositoryImplTest.kt
  • core/data/src/jvmTest/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepositoryImplTest.kt
  • core/database/schemas/org.meshtastic.core.database.MeshtasticDatabase/47.json
  • core/database/src/commonMain/kotlin/org/meshtastic/core/database/MeshtasticDatabase.kt
  • core/database/src/commonMain/kotlin/org/meshtastic/core/database/dao/EventFirmwareEditionDao.kt
  • core/database/src/commonMain/kotlin/org/meshtastic/core/database/entity/EventFirmwareEditionEntity.kt
  • core/model/src/commonMain/kotlin/org/meshtastic/core/model/EventFirmware.kt
  • core/network/src/commonMain/kotlin/org/meshtastic/core/network/EventFirmwareRemoteDataSource.kt
  • core/network/src/commonMain/kotlin/org/meshtastic/core/network/service/ApiService.kt
  • core/resources/src/commonMain/composeResources/values/strings.xml
  • core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EventInfoSheet.kt
  • core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/MainAppBar.kt
  • core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/LocalEventBranding.kt
  • core/ui/src/commonTest/kotlin/org/meshtastic/core/ui/util/EventBrandingTest.kt
  • feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/ConnectionsScreen.kt
  • schemas/event_firmware.schema.json
💤 Files with no reviewable changes (1)
  • schemas/event_firmware.schema.json

…pt-out

Renders an event edition's theme.fonts (Google Font family names, e.g. DEFCON's
Lato/Atkinson Hyperlegible) as the app-wide typeface while connected to that
event firmware, via downloadable Google Fonts. Google flavor only — F-Droid has
no Play font provider and keeps the default typeface.

🌟 New Features
- App-wide typography swap: AppTheme reads LocalEventTypographyFonts and remaps
  the full M3 typescale (heading → display/headline/title, body → body/label)
  when an event edition is active, fonts resolve, and the user hasn't opted out.
- Opt-out toggle ("Use event fonts") in the event info sheet, shown only when the
  fonts are actually available (Google flavor + edition ships fonts). Backed by a
  new eventFontsEnabled UiPrefs boolean (default on).

🛠️ Architecture (flavor seam, mirrors ApiService)
- core:ui EventFonts.kt: EventFontResolver fun interface + EventFonts +
  Typography.withEventFonts + LocalEventTypographyFonts / LocalEventFontsToggle.
- Google flavor binds GoogleFontsEventFontResolver (ui-text-google-fonts +
  GoogleFont.Provider); F-Droid binds a no-op resolver. Resolved via Koin,
  provided into the composition at MainActivity next to LocalEventBranding.
- Ships androidApp/src/google/res/values/font_certs.xml (canonical AOSP cert
  array) for the font provider — google source set only, F-Droid stays clean.

Caveats: downloadable fonts load async (brief fallback→swap on first show) and
fetch from Google's font provider (google flavor only, consistent with Maps).
Runtime rendering not verifiable here (needs a device); compiles + DI-verifies on
both flavors.

Testing: spotlessCheck, detekt, :core:ui:build, :core:prefs:allTests,
:androidApp:{compile,test}{Google,Fdroid}DebugKotlin/UnitTest, :core:data:jvmTest
all green — incl. KoinVerificationTest on both flavors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jamesarich jamesarich changed the title feat: capture event-theme fonts (heading/body) in the model feat: event-theme fonts — capture + apply app-wide (Google flavor) with sheet opt-out Jul 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EventInfoSheet.kt (1)

103-114: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Make the whole row toggle the switch, not just the Switch itself.

Currently only tapping the small Switch toggles the preference; the label Text occupies most of the row's width but isn't clickable. Consider applying Modifier.toggleable(value = fontsToggle.enabled, onValueChange = fontsToggle.onChange, role = Role.Switch) on the Row and passing onCheckedChange = null to the Switch to avoid double-toggling, matching common Material list-row patterns and improving touch-target size/accessibility.

♻️ Suggested refactor
                     Row(
-                        modifier = Modifier.fillMaxWidth(),
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .toggleable(
+                                value = fontsToggle.enabled,
+                                onValueChange = fontsToggle.onChange,
+                                role = Role.Switch,
+                            ),
                         horizontalArrangement = Arrangement.spacedBy(12.dp),
                         verticalAlignment = Alignment.CenterVertically,
                     ) {
                         Text(
                             text = stringResource(Res.string.event_use_event_fonts),
                             style = MaterialTheme.typography.bodyMedium,
                             modifier = Modifier.weight(1f),
                         )
-                        Switch(checked = fontsToggle.enabled, onCheckedChange = fontsToggle.onChange)
+                        Switch(checked = fontsToggle.enabled, onCheckedChange = null)
                     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EventInfoSheet.kt`
around lines 103 - 114, The `EventInfoSheet` row currently only toggles via the
`Switch`, so make the entire `Row` act as the toggle target. Update the `Row`
that contains the `Text` label and `Switch` to use `Modifier.toggleable` with
`fontsToggle.enabled`, `fontsToggle.onChange`, and `Role.Switch`, and make the
`Switch` non-interactive by passing `onCheckedChange = null` so it doesn’t
double-fire. Keep the existing `fontsToggle` wiring intact and ensure the label
and switch still reflect the same state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EventInfoSheet.kt`:
- Around line 103-114: The `EventInfoSheet` row currently only toggles via the
`Switch`, so make the entire `Row` act as the toggle target. Update the `Row`
that contains the `Text` label and `Switch` to use `Modifier.toggleable` with
`fontsToggle.enabled`, `fontsToggle.onChange`, and `Role.Switch`, and make the
`Switch` non-interactive by passing `onCheckedChange = null` so it doesn’t
double-fire. Keep the existing `fontsToggle` wiring intact and ensure the label
and switch still reflect the same state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dfa9c20f-fe31-495b-9f91-88d341c07ae8

📥 Commits

Reviewing files that changed from the base of the PR and between 3d6dbf9 and 4dff4d7.

📒 Files selected for processing (16)
  • .skills/compose-ui/strings-index.txt
  • androidApp/build.gradle.kts
  • androidApp/src/fdroid/kotlin/org/meshtastic/app/di/FlavorModule.kt
  • androidApp/src/google/kotlin/org/meshtastic/app/di/FlavorModule.kt
  • androidApp/src/google/kotlin/org/meshtastic/app/theme/GoogleFontsEventFontResolver.kt
  • androidApp/src/google/res/values/font_certs.xml
  • androidApp/src/main/kotlin/org/meshtastic/app/MainActivity.kt
  • core/prefs/src/commonMain/kotlin/org/meshtastic/core/prefs/ui/UiPrefsImpl.kt
  • core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/AppPreferences.kt
  • core/resources/src/commonMain/composeResources/values/strings.xml
  • core/testing/src/commonMain/kotlin/org/meshtastic/core/testing/FakeAppPreferences.kt
  • core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EventInfoSheet.kt
  • core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/EventFonts.kt
  • core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt
  • core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt
  • gradle/libs.versions.toml
✅ Files skipped from review due to trivial changes (3)
  • core/resources/src/commonMain/composeResources/values/strings.xml
  • androidApp/build.gradle.kts
  • .skills/compose-ui/strings-index.txt

jamesarich and others added 2 commits July 8, 2026 20:59
CodeRabbit nitpick: the whole row (label + switch) now toggles the pref via
Modifier.toggleable(role = Role.Switch); the Switch is visual-only
(onCheckedChange = null) so it doesn't double-fire. Bigger touch target,
one semantics node.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Runtime verification on a Pixel 6a showed the app-wide event typography swap was
wired correctly (the themed Text read fontFamily=GoogleFont("Atkinson
Hyperlegible")/Lato) but rendered the Roboto fallback with zero font-provider
requests. Root cause: Compose does NOT auto-fetch a downloadable (Google) font
that is referenced only through the theme's Typography — it silently uses the
fallback. The documented fix is to preload the font into the resolver cache.

- AppTheme now preloads the active event heading/body FontFamily via
  LocalFontFamilyResolver.preload() in a LaunchedEffect keyed on the fonts.
  No-op when there are no event fonts (desktop / F-Droid / non-event / opted
  out). Once cached, the themed text re-resolves to the real typeface.

Verified end-to-end on device (DEFCON replay): before the fix ON vs OFF was
pixel-identical (Roboto); after, "DEF CON 34" renders in Lato and body text in
Atkinson Hyperlegible, and toggling "Use event fonts" visibly switches the
typeface app-wide. GMS FontLog confirms the fonts are fetched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt`:
- Around line 143-147: The font preloading logic in LaunchedEffect for Theme.kt
currently uses runCatching around fontResolver.preload, which can swallow
CancellationException and hide preload failures. Update the
eventFonts/fontResolver block to catch exceptions explicitly in the preload
loop, re-throw CancellationException immediately, and handle other failures with
logging or error reporting so the preload issue is visible. Keep the fix
localized to the LaunchedEffect that iterates over fonts.heading and fonts.body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e1d24926-bef4-4a6a-b8c3-97a6d0bc13c4

📥 Commits

Reviewing files that changed from the base of the PR and between f955ed2 and e1364af.

📒 Files selected for processing (1)
  • core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt

Comment thread core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/theme/Theme.kt
jamesarich and others added 2 commits July 8, 2026 21:33
CodeRabbit: runCatching around the suspending fontResolver.preload() also
catches CancellationException, so a cancelled LaunchedEffect would continue to
the next font instead of stopping (structured-concurrency violation), and preload
failures were dropped silently. Replaced with an explicit try/catch that
re-throws CancellationException and logs other exceptions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The opt-out toggle previously governed only the app-wide fonts, while the app-bar
accent wash was always-on. Consolidate them: one composition local carries the
whole ambient event theme and one toggle governs both.

- core:ui EventFonts.kt: new EventTheme(accent, fonts); LocalEventTypographyFonts
  → LocalEventTheme; EventFontsToggle → EventThemeToggle (dropped the unused
  `available` field — the sheet only opens for an active event, which always has
  an accent, so the toggle is always meaningful).
- MainAppBar reads the accent wash from LocalEventTheme (now gated by the toggle)
  instead of unconditionally from the edition. The branding icon + info sheet
  still read LocalEventBranding, so they stay available even when opted out.
- AppTheme reads LocalEventTheme.fonts.
- Pref eventFontsEnabled → eventThemeEnabled (key event-theme-enabled); string
  event_use_event_fonts → event_use_event_theme ("Use event theme").
- MainActivity provides EventTheme{accent, fonts} only while on event firmware
  and not opted out.

Decided against app-wide color-scheme recolor: there is no built-in Compose M3
arbitrary-seed→ColorScheme API (dynamicColorScheme seeds only from the system
wallpaper), and slotting raw brand hex into primary/secondary breaks on-color
contrast. Accent-wash-only keeps it safe with no new dependency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jamesarich
jamesarich enabled auto-merge July 9, 2026 03:02
@jamesarich
jamesarich added this pull request to the merge queue Jul 9, 2026
Merged via the queue into main with commit bd14228 Jul 9, 2026
17 checks passed
@jamesarich
jamesarich deleted the claude/event-firmware-fonts branch July 9, 2026 03:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request repo Repository maintenance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant