diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 8fed805..6773789 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -104,7 +104,14 @@ "mcpSuggestionMemoryDesc": "Anthropic — persistenter Knowledge-Graph als Agenten-Gedächtnis.", "mcpSuggestionTimeDesc": "Anthropic — aktuelle Uhrzeit + Zeitzonen-Konvertierung.", "mcpServerEnLanguageBadge": "Server-Text in Englisch — Studio zeigt ihn wie geliefert.", - "themePluginHeader": "Theme-Plugin", + "themePluginHeader": "Theme-Plugin (wirkt sofort)", + "themeApplied": "Theme aktiv: {name}", + "@themeApplied": { + "placeholders": { + "name": {"type": "String"} + } + }, + "themePluginFailed": "Theme-Plugin konnte nicht geladen werden — vorheriges Theme bleibt.", "themePluginNone": "Standard (kein Plugin)", "themePluginHint": "Built-in- und Plugin-Kacheln bringen handkuratierte Paletten mit. Die Kachel „Eigene Farbe\" leitet aus einer gewählten Seedfarbe eine vollständige Material-3-Palette ab (primary, secondary, tertiary, surface, …) — das ist ein vollwertiges Theme, kein Einzelfarb-Tweak.", "themePluginEmpty": "Kein Theme-Plugin installiert. Im Store eines aus der Kategorie studio-plugin auswählen.", @@ -118,7 +125,7 @@ "settingsHost": "Host", "settingsPort": "Port", "settingsTls": "TLS", - "settingsSaveAndConnect": "Speichern & verbinden", + "settingsSaveAndConnect": "Mit Endpoint verbinden", "settingsLanguage": "Sprache", "settingsLanguageHint": "Sprache der App. Wirkt auf alle Seiten; mehrsprachige Store-Einträge folgen ebenfalls.", "settingsHubEndpointHint": "Wohin soll Studio sich verbinden? Standard ist der lokale Hub auf 127.0.0.1:50051.", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f943708..3a14c55 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -107,7 +107,14 @@ "mcpSuggestionMemoryDesc": "Anthropic — persistent knowledge graph for an agent's memory.", "mcpSuggestionTimeDesc": "Anthropic — current time + timezone conversion.", "mcpServerEnLanguageBadge": "Server text in English — Studio shows it as supplied.", - "themePluginHeader": "Theme plugin", + "themePluginHeader": "Theme plugin (applies instantly)", + "themeApplied": "Theme applied: {name}", + "@themeApplied": { + "placeholders": { + "name": {"type": "String"} + } + }, + "themePluginFailed": "Theme plugin could not load — keeping previous theme.", "themePluginNone": "Built-in (no plugin)", "themePluginHint": "Built-in and plugin tiles ship hand-curated palettes. The Custom tile derives a full Material 3 palette (primary, secondary, tertiary, surface, …) from one seed colour you pick — it's a complete theme, not a single-colour tweak.", "themePluginEmpty": "No theme plugins installed. Open the Store and pick one in the studio-plugin category.", @@ -121,7 +128,7 @@ "settingsHost": "Host", "settingsPort": "Port", "settingsTls": "TLS", - "settingsSaveAndConnect": "Save & connect", + "settingsSaveAndConnect": "Connect to endpoint", "settingsLanguage": "Language", "settingsLanguageHint": "App language. Affects every page; bilingual store entries also flip via this toggle.", "settingsHubEndpointHint": "Where should Studio connect? Default is the local hub at 127.0.0.1:50051.", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index f3f33a3..677b091 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -641,9 +641,21 @@ abstract class AppLocalizations { /// No description provided for @themePluginHeader. /// /// In en, this message translates to: - /// **'Theme plugin'** + /// **'Theme plugin (applies instantly)'** String get themePluginHeader; + /// No description provided for @themeApplied. + /// + /// In en, this message translates to: + /// **'Theme applied: {name}'** + String themeApplied(String name); + + /// No description provided for @themePluginFailed. + /// + /// In en, this message translates to: + /// **'Theme plugin could not load — keeping previous theme.'** + String get themePluginFailed; + /// No description provided for @themePluginNone. /// /// In en, this message translates to: @@ -725,7 +737,7 @@ abstract class AppLocalizations { /// No description provided for @settingsSaveAndConnect. /// /// In en, this message translates to: - /// **'Save & connect'** + /// **'Connect to endpoint'** String get settingsSaveAndConnect; /// No description provided for @settingsLanguage. diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 43aca8d..e7d74af 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -313,7 +313,16 @@ class AppLocalizationsDe extends AppLocalizations { 'Server-Text in Englisch — Studio zeigt ihn wie geliefert.'; @override - String get themePluginHeader => 'Theme-Plugin'; + String get themePluginHeader => 'Theme-Plugin (wirkt sofort)'; + + @override + String themeApplied(String name) { + return 'Theme aktiv: $name'; + } + + @override + String get themePluginFailed => + 'Theme-Plugin konnte nicht geladen werden — vorheriges Theme bleibt.'; @override String get themePluginNone => 'Standard (kein Plugin)'; @@ -357,7 +366,7 @@ class AppLocalizationsDe extends AppLocalizations { String get settingsTls => 'TLS'; @override - String get settingsSaveAndConnect => 'Speichern & verbinden'; + String get settingsSaveAndConnect => 'Mit Endpoint verbinden'; @override String get settingsLanguage => 'Sprache'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 90ed4b1..9c2441e 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -313,7 +313,16 @@ class AppLocalizationsEn extends AppLocalizations { 'Server text in English — Studio shows it as supplied.'; @override - String get themePluginHeader => 'Theme plugin'; + String get themePluginHeader => 'Theme plugin (applies instantly)'; + + @override + String themeApplied(String name) { + return 'Theme applied: $name'; + } + + @override + String get themePluginFailed => + 'Theme plugin could not load — keeping previous theme.'; @override String get themePluginNone => 'Built-in (no plugin)'; @@ -358,7 +367,7 @@ class AppLocalizationsEn extends AppLocalizations { String get settingsTls => 'TLS'; @override - String get settingsSaveAndConnect => 'Save & connect'; + String get settingsSaveAndConnect => 'Connect to endpoint'; @override String get settingsLanguage => 'Language'; diff --git a/lib/main.dart b/lib/main.dart index eebd850..d9943b3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'data/fai_log.dart'; import 'data/hub.dart'; import 'data/system_actions.dart'; import 'data/theme_plugin.dart'; @@ -26,7 +27,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.62.0'; +const String kStudioVersion = '0.62.1'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -178,10 +179,18 @@ class StudioAppState extends State { : themeDataFromScheme(schemes.light!), dark: schemes.dark == null ? null : themeDataFromScheme(schemes.dark!), ); - } catch (_) { - // Plugin unreachable, hub down, etc. Fall back to - // FaiTheme defaults silently — the Settings dialog is - // where the operator surfaces / fixes the problem. + } catch (e) { + // Plugin unreachable, hub down, manifest schema drift — + // any of these end up here. Log the failure so doctor + + // the in-app log viewer can surface it; the picker's + // own "applied" snackbar is replaced upstream by the + // friendlier failure message via [themePluginFailed]. + // ignore: discarded_futures + FaiLog.instance.error( + 'theme.plugin.load', + e, + context: 'capability=$capability', + ); return (light: null, dark: null); } } diff --git a/lib/widgets/fai_settings_dialog.dart b/lib/widgets/fai_settings_dialog.dart index a84a473..c52e597 100644 --- a/lib/widgets/fai_settings_dialog.dart +++ b/lib/widgets/fai_settings_dialog.dart @@ -1472,8 +1472,9 @@ class _AddN8nEndpointDialogState extends State<_AddN8nEndpointDialog> { ), FilledButton( onPressed: () { - if (_name.text.trim().isEmpty || _baseUrl.text.trim().isEmpty) + if (_name.text.trim().isEmpty || _baseUrl.text.trim().isEmpty) { return; + } Navigator.pop( context, N8nEndpointDraft( diff --git a/lib/widgets/theme_picker_grid.dart b/lib/widgets/theme_picker_grid.dart index f4108c6..e7c781d 100644 --- a/lib/widgets/theme_picker_grid.dart +++ b/lib/widgets/theme_picker_grid.dart @@ -109,11 +109,29 @@ class _ThemePickerGridState extends State { selected: active == null, onTap: () { StudioApp.of(context)?.setThemePlugin(null); + _confirmApplied(l, l.themePluginNone); setState(() {}); }, ); } + /// Brief snackbar acknowledging the theme switch. The picker + /// applies themes instantly (no Save), so the operator needs + /// a visible cue that the click was registered — without it, + /// a slow theme load looks like "the click did nothing". + void _confirmApplied(AppLocalizations l, String displayName) { + final messenger = ScaffoldMessenger.maybeOf(context); + if (messenger == null) return; + messenger.removeCurrentSnackBar(); + messenger.showSnackBar( + SnackBar( + content: Text(l.themeApplied(displayName)), + duration: const Duration(seconds: 2), + behavior: SnackBarBehavior.floating, + ), + ); + } + Widget _pluginTile(ThemeData theme, String cap, String? active) { // Pretty name: studio.theme.sunflower → "Sunflower". final display = _displayName(cap); @@ -138,6 +156,7 @@ class _ThemePickerGridState extends State { loading: preview == null, onTap: () { StudioApp.of(context)?.setThemePlugin(cap); + _confirmApplied(AppLocalizations.of(context)!, display); setState(() {}); }, ); @@ -176,6 +195,7 @@ class _ThemePickerGridState extends State { // Strip alpha component — we only persist the RGB. final rgb = hex.substring(2).toUpperCase(); StudioApp.of(context)?.setThemePlugin('custom:#$rgb'); + _confirmApplied(l, '#$rgb'); setState(() {}); }, ); diff --git a/pubspec.yaml b/pubspec.yaml index 9919880..11bad1f 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.62.0 +version: 0.62.1 environment: sdk: ^3.11.0-200.1.beta