fix(studio): doctor i18n + light-mode AppBar contrast (v0.33.1)

Two reports:

1. "In Diagnose ist viel nicht übersetzt, wie pending, chain
   etc." The Doctor summary strip (Modules / Approvals / Audit
   / Services tiles) and the modules panel still rendered
   English wire labels: "pending", "chain", "declared",
   "loaded", "empty", "attention", "No pending approvals",
   "No host services declared", and the matching "{n} modules
   · {m} capabilities" / "{n} approvals awaiting review"
   strings.

   Fix: 13 new ARB keys covering the summary tiles, modules
   panel, and services panel; doctor.dart now reads them via
   AppLocalizations. German operators no longer see English
   labels on Diagnose.

2. "Im Light mode ist der kontrast zur überschrift falsch,
   store ist weiß auf weiß." The AppBar title rendered
   white-on-white in light mode.

   Cause: `appBarTheme.titleTextStyle = textTheme.headlineSmall`
   passed a TextStyle built fresh from GoogleFonts.inter(...)
   with `color: null`. Material's "merge foregroundColor at
   draw time" path didn't always populate it — depended on
   build configuration. The colour fell through to whatever
   the surrounding DefaultTextStyle had, which on the
   light-mode AppBar was the surface colour.

   Fix: bake the foreground colour straight into the title
   style via `copyWith(color: scheme.onSurface)`. Also pin
   `iconTheme: IconThemeData(color: scheme.onSurface)` so
   actions-row icons get the same treatment defensively.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-08 13:43:17 +02:00
parent de22791e86
commit 22a851b7e2
9 changed files with 319 additions and 19 deletions

View file

@ -143,19 +143,20 @@ class _SummaryStrip extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l = AppLocalizations.of(context)!;
return Row(
children: [
_StatTile(
label: 'Modules',
label: l.doctorSummaryModules,
value: snapshot.moduleCount.toString(),
subtitle: '${snapshot.capabilityCount} capabilities',
subtitle: l.doctorSummaryCapabilities(snapshot.capabilityCount),
icon: Icons.extension_outlined,
),
const SizedBox(width: FaiSpace.md),
_StatTile(
label: 'Approvals',
label: l.doctorSummaryApprovals,
value: snapshot.pendingApprovals.toString(),
subtitle: 'pending',
subtitle: l.doctorSummaryPending,
icon: Icons.inbox_outlined,
tone: snapshot.pendingApprovals > 0
? FaiPillTone.warning
@ -163,10 +164,12 @@ class _SummaryStrip extends StatelessWidget {
),
const SizedBox(width: FaiSpace.md),
_StatTile(
label: 'Audit',
label: l.doctorSummaryAudit,
value: snapshot.chainHealthy ? '' : '',
subtitle:
'${snapshot.eventChainVerified}/${snapshot.eventChainTotal} chain',
subtitle: l.doctorSummaryChain(
snapshot.eventChainVerified,
snapshot.eventChainTotal,
),
icon: Icons.shield_outlined,
tone: snapshot.chainHealthy
? FaiPillTone.success
@ -174,9 +177,9 @@ class _SummaryStrip extends StatelessWidget {
),
const SizedBox(width: FaiSpace.md),
_StatTile(
label: 'Services',
label: l.doctorSummaryServices,
value: snapshot.services.length.toString(),
subtitle: 'declared',
subtitle: l.doctorSummaryDeclared,
icon: Icons.dns_outlined,
),
],
@ -639,6 +642,7 @@ class _ModulesPanel extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final l = AppLocalizations.of(context)!;
return FaiCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -652,12 +656,17 @@ class _ModulesPanel extends StatelessWidget {
),
const SizedBox(width: FaiSpace.sm),
Text(
'${snapshot.moduleCount} modules · ${snapshot.capabilityCount} capabilities',
l.doctorModulesPanelSummary(
snapshot.moduleCount,
snapshot.capabilityCount,
),
style: theme.textTheme.bodyMedium,
),
const Spacer(),
FaiPill(
label: snapshot.moduleCount > 0 ? 'loaded' : 'empty',
label: snapshot.moduleCount > 0
? l.doctorPillLoaded
: l.doctorPillEmpty,
tone: snapshot.moduleCount > 0
? FaiPillTone.success
: FaiPillTone.neutral,
@ -675,14 +684,14 @@ class _ModulesPanel extends StatelessWidget {
const SizedBox(width: FaiSpace.sm),
Text(
snapshot.pendingApprovals == 0
? 'No pending approvals'
: '${snapshot.pendingApprovals} approval${snapshot.pendingApprovals == 1 ? '' : 's'} awaiting review',
? l.doctorApprovalsNone
: l.doctorApprovalsCount(snapshot.pendingApprovals),
style: theme.textTheme.bodyMedium,
),
const Spacer(),
if (snapshot.pendingApprovals > 0)
FaiPill(
label: 'attention',
label: l.doctorApprovalsAttentionPill,
tone: FaiPillTone.warning,
),
],
@ -701,6 +710,7 @@ class _ServicesPanel extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final l = AppLocalizations.of(context)!;
if (snapshot.services.isEmpty) {
return FaiCard(
child: Row(
@ -712,14 +722,14 @@ class _ServicesPanel extends StatelessWidget {
),
const SizedBox(width: FaiSpace.sm),
Text(
'No host services declared',
l.doctorServicesEmpty,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
const Spacer(),
Text(
'add to ~/.fai/config.yaml under services:',
l.doctorServicesEmptyHint,
style: FaiTheme.mono(
size: 11,
color: theme.colorScheme.onSurfaceVariant,