diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index d21e106..18c8b71 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -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", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9958d33..5186dcf 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -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", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 4e2a321..d5a1d54 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -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: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index b59bf66..234ed67 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -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'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 0d94baf..7632cae 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -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'; diff --git a/lib/main.dart b/lib/main.dart index 85d4747..73d57b4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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 main() async { WidgetsFlutterBinding.ensureInitialized(); diff --git a/lib/pages/doctor.dart b/lib/pages/doctor.dart index 89cf83d..79b2929 100644 --- a/lib/pages/doctor.dart +++ b/lib/pages/doctor.dart @@ -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, diff --git a/lib/theme/theme.dart b/lib/theme/theme.dart index 506f1d6..ed68f2b 100644 --- a/lib/theme/theme.dart +++ b/lib/theme/theme.dart @@ -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( diff --git a/pubspec.yaml b/pubspec.yaml index 1973096..88aada3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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