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:
parent
de22791e86
commit
22a851b7e2
9 changed files with 319 additions and 19 deletions
|
|
@ -392,6 +392,36 @@
|
|||
"@approvalsExpiresInHours": { "placeholders": { "hours": { "type": "int" } } },
|
||||
|
||||
"doctorTitle": "Diagnose",
|
||||
"doctorSummaryModules": "Module",
|
||||
"doctorSummaryApprovals": "Freigaben",
|
||||
"doctorSummaryAudit": "Protokoll",
|
||||
"doctorSummaryServices": "Dienste",
|
||||
"doctorSummaryCapabilities": "{n} Capabilities",
|
||||
"@doctorSummaryCapabilities": { "placeholders": { "n": { "type": "int" } } },
|
||||
"doctorSummaryPending": "offen",
|
||||
"doctorSummaryChain": "{verified}/{total} Chain",
|
||||
"@doctorSummaryChain": {
|
||||
"placeholders": {
|
||||
"verified": { "type": "int" },
|
||||
"total": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"doctorSummaryDeclared": "deklariert",
|
||||
"doctorModulesPanelSummary": "{n} Module · {m} Capabilities",
|
||||
"@doctorModulesPanelSummary": {
|
||||
"placeholders": {
|
||||
"n": { "type": "int" },
|
||||
"m": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"doctorApprovalsNone": "Keine offenen Freigaben",
|
||||
"doctorApprovalsCount": "{n, plural, =1{1 Freigabe wartet auf Prüfung} other{{n} Freigaben warten auf Prüfung}}",
|
||||
"@doctorApprovalsCount": { "placeholders": { "n": { "type": "int" } } },
|
||||
"doctorApprovalsAttentionPill": "wichtig",
|
||||
"doctorServicesEmpty": "Keine Host-Dienste deklariert",
|
||||
"doctorServicesEmptyHint": "in ~/.fai/config.yaml unter services: hinzufügen",
|
||||
"doctorPillLoaded": "geladen",
|
||||
"doctorPillEmpty": "leer",
|
||||
"doctorRecheckTooltip": "Erneut prüfen",
|
||||
"doctorEventLogSection": "Audit-Log",
|
||||
"doctorModulesApprovalsSection": "Module & Freigaben",
|
||||
|
|
|
|||
|
|
@ -393,6 +393,36 @@
|
|||
"@approvalsExpiresInHours": { "placeholders": { "hours": { "type": "int" } } },
|
||||
|
||||
"doctorTitle": "Doctor",
|
||||
"doctorSummaryModules": "Modules",
|
||||
"doctorSummaryApprovals": "Approvals",
|
||||
"doctorSummaryAudit": "Audit",
|
||||
"doctorSummaryServices": "Services",
|
||||
"doctorSummaryCapabilities": "{n} capabilities",
|
||||
"@doctorSummaryCapabilities": { "placeholders": { "n": { "type": "int" } } },
|
||||
"doctorSummaryPending": "pending",
|
||||
"doctorSummaryChain": "{verified}/{total} chain",
|
||||
"@doctorSummaryChain": {
|
||||
"placeholders": {
|
||||
"verified": { "type": "int" },
|
||||
"total": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"doctorSummaryDeclared": "declared",
|
||||
"doctorModulesPanelSummary": "{n} modules · {m} capabilities",
|
||||
"@doctorModulesPanelSummary": {
|
||||
"placeholders": {
|
||||
"n": { "type": "int" },
|
||||
"m": { "type": "int" }
|
||||
}
|
||||
},
|
||||
"doctorApprovalsNone": "No pending approvals",
|
||||
"doctorApprovalsCount": "{n, plural, =1{1 approval awaiting review} other{{n} approvals awaiting review}}",
|
||||
"@doctorApprovalsCount": { "placeholders": { "n": { "type": "int" } } },
|
||||
"doctorApprovalsAttentionPill": "attention",
|
||||
"doctorServicesEmpty": "No host services declared",
|
||||
"doctorServicesEmptyHint": "add to ~/.fai/config.yaml under services:",
|
||||
"doctorPillLoaded": "loaded",
|
||||
"doctorPillEmpty": "empty",
|
||||
"doctorRecheckTooltip": "Re-check",
|
||||
"doctorEventLogSection": "Event log",
|
||||
"doctorModulesApprovalsSection": "Modules & approvals",
|
||||
|
|
|
|||
|
|
@ -1730,6 +1730,102 @@ abstract class AppLocalizations {
|
|||
/// **'Doctor'**
|
||||
String get doctorTitle;
|
||||
|
||||
/// No description provided for @doctorSummaryModules.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Modules'**
|
||||
String get doctorSummaryModules;
|
||||
|
||||
/// No description provided for @doctorSummaryApprovals.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Approvals'**
|
||||
String get doctorSummaryApprovals;
|
||||
|
||||
/// No description provided for @doctorSummaryAudit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Audit'**
|
||||
String get doctorSummaryAudit;
|
||||
|
||||
/// No description provided for @doctorSummaryServices.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Services'**
|
||||
String get doctorSummaryServices;
|
||||
|
||||
/// No description provided for @doctorSummaryCapabilities.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{n} capabilities'**
|
||||
String doctorSummaryCapabilities(int n);
|
||||
|
||||
/// No description provided for @doctorSummaryPending.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'pending'**
|
||||
String get doctorSummaryPending;
|
||||
|
||||
/// No description provided for @doctorSummaryChain.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{verified}/{total} chain'**
|
||||
String doctorSummaryChain(int verified, int total);
|
||||
|
||||
/// No description provided for @doctorSummaryDeclared.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'declared'**
|
||||
String get doctorSummaryDeclared;
|
||||
|
||||
/// No description provided for @doctorModulesPanelSummary.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{n} modules · {m} capabilities'**
|
||||
String doctorModulesPanelSummary(int n, int m);
|
||||
|
||||
/// No description provided for @doctorApprovalsNone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No pending approvals'**
|
||||
String get doctorApprovalsNone;
|
||||
|
||||
/// No description provided for @doctorApprovalsCount.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{n, plural, =1{1 approval awaiting review} other{{n} approvals awaiting review}}'**
|
||||
String doctorApprovalsCount(int n);
|
||||
|
||||
/// No description provided for @doctorApprovalsAttentionPill.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'attention'**
|
||||
String get doctorApprovalsAttentionPill;
|
||||
|
||||
/// No description provided for @doctorServicesEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No host services declared'**
|
||||
String get doctorServicesEmpty;
|
||||
|
||||
/// No description provided for @doctorServicesEmptyHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'add to ~/.fai/config.yaml under services:'**
|
||||
String get doctorServicesEmptyHint;
|
||||
|
||||
/// No description provided for @doctorPillLoaded.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'loaded'**
|
||||
String get doctorPillLoaded;
|
||||
|
||||
/// No description provided for @doctorPillEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'empty'**
|
||||
String get doctorPillEmpty;
|
||||
|
||||
/// No description provided for @doctorRecheckTooltip.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -978,6 +978,69 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get doctorTitle => 'Diagnose';
|
||||
|
||||
@override
|
||||
String get doctorSummaryModules => 'Module';
|
||||
|
||||
@override
|
||||
String get doctorSummaryApprovals => 'Freigaben';
|
||||
|
||||
@override
|
||||
String get doctorSummaryAudit => 'Protokoll';
|
||||
|
||||
@override
|
||||
String get doctorSummaryServices => 'Dienste';
|
||||
|
||||
@override
|
||||
String doctorSummaryCapabilities(int n) {
|
||||
return '$n Capabilities';
|
||||
}
|
||||
|
||||
@override
|
||||
String get doctorSummaryPending => 'offen';
|
||||
|
||||
@override
|
||||
String doctorSummaryChain(int verified, int total) {
|
||||
return '$verified/$total Chain';
|
||||
}
|
||||
|
||||
@override
|
||||
String get doctorSummaryDeclared => 'deklariert';
|
||||
|
||||
@override
|
||||
String doctorModulesPanelSummary(int n, int m) {
|
||||
return '$n Module · $m Capabilities';
|
||||
}
|
||||
|
||||
@override
|
||||
String get doctorApprovalsNone => 'Keine offenen Freigaben';
|
||||
|
||||
@override
|
||||
String doctorApprovalsCount(int n) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
n,
|
||||
locale: localeName,
|
||||
other: '$n Freigaben warten auf Prüfung',
|
||||
one: '1 Freigabe wartet auf Prüfung',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get doctorApprovalsAttentionPill => 'wichtig';
|
||||
|
||||
@override
|
||||
String get doctorServicesEmpty => 'Keine Host-Dienste deklariert';
|
||||
|
||||
@override
|
||||
String get doctorServicesEmptyHint =>
|
||||
'in ~/.fai/config.yaml unter services: hinzufügen';
|
||||
|
||||
@override
|
||||
String get doctorPillLoaded => 'geladen';
|
||||
|
||||
@override
|
||||
String get doctorPillEmpty => 'leer';
|
||||
|
||||
@override
|
||||
String get doctorRecheckTooltip => 'Erneut prüfen';
|
||||
|
||||
|
|
|
|||
|
|
@ -992,6 +992,69 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get doctorTitle => 'Doctor';
|
||||
|
||||
@override
|
||||
String get doctorSummaryModules => 'Modules';
|
||||
|
||||
@override
|
||||
String get doctorSummaryApprovals => 'Approvals';
|
||||
|
||||
@override
|
||||
String get doctorSummaryAudit => 'Audit';
|
||||
|
||||
@override
|
||||
String get doctorSummaryServices => 'Services';
|
||||
|
||||
@override
|
||||
String doctorSummaryCapabilities(int n) {
|
||||
return '$n capabilities';
|
||||
}
|
||||
|
||||
@override
|
||||
String get doctorSummaryPending => 'pending';
|
||||
|
||||
@override
|
||||
String doctorSummaryChain(int verified, int total) {
|
||||
return '$verified/$total chain';
|
||||
}
|
||||
|
||||
@override
|
||||
String get doctorSummaryDeclared => 'declared';
|
||||
|
||||
@override
|
||||
String doctorModulesPanelSummary(int n, int m) {
|
||||
return '$n modules · $m capabilities';
|
||||
}
|
||||
|
||||
@override
|
||||
String get doctorApprovalsNone => 'No pending approvals';
|
||||
|
||||
@override
|
||||
String doctorApprovalsCount(int n) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
n,
|
||||
locale: localeName,
|
||||
other: '$n approvals awaiting review',
|
||||
one: '1 approval awaiting review',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get doctorApprovalsAttentionPill => 'attention';
|
||||
|
||||
@override
|
||||
String get doctorServicesEmpty => 'No host services declared';
|
||||
|
||||
@override
|
||||
String get doctorServicesEmptyHint =>
|
||||
'add to ~/.fai/config.yaml under services:';
|
||||
|
||||
@override
|
||||
String get doctorPillLoaded => 'loaded';
|
||||
|
||||
@override
|
||||
String get doctorPillEmpty => 'empty';
|
||||
|
||||
@override
|
||||
String get doctorRecheckTooltip => 'Re-check';
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import 'widgets/widgets.dart';
|
|||
/// Studio's own build version. Bump on every UI commit so the
|
||||
/// running app self-identifies — visible in the sidebar header
|
||||
/// and quick-glance proof that you're seeing the current build.
|
||||
const String kStudioVersion = '0.33.0';
|
||||
const String kStudioVersion = '0.33.1';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -154,10 +154,18 @@ class FaiTheme {
|
|||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: scheme.surface,
|
||||
foregroundColor: scheme.onSurface,
|
||||
iconTheme: IconThemeData(color: scheme.onSurface),
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: false,
|
||||
titleTextStyle: textTheme.headlineSmall,
|
||||
// Bake the foreground colour straight into the title
|
||||
// style. `headlineSmall` is built from GoogleFonts.inter
|
||||
// with no color slot, and Material's "merge foreground
|
||||
// colour at draw time" path leaves it null in some
|
||||
// light-mode builds — the title rendered white-on-white.
|
||||
titleTextStyle: textTheme.headlineSmall?.copyWith(
|
||||
color: scheme.onSurface,
|
||||
),
|
||||
toolbarHeight: 64,
|
||||
),
|
||||
cardTheme: CardThemeData(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
name: fai_studio
|
||||
description: "F∆I Studio — desktop GUI for the F∆I hub"
|
||||
publish_to: 'none'
|
||||
version: 0.33.0
|
||||
version: 0.33.1
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.0-200.1.beta
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue