feat(studio): theme-plugin picker + translate-hook wrapper
Some checks failed
Security / Security check (push) Failing after 2s

Two pieces of the Studio plugin host:

- `data/theme_plugin.dart`: discovery, persistence, ColorScheme
  translation. Plugins listed via list_capabilities filtered
  to studio.theme.*; selection persisted in SharedPreferences;
  loadThemePluginSchemes builds a ColorScheme pair from the
  plugin's 14 ARGB tokens per brightness.
- `main.dart`: StudioApp gains themePluginNotifier +
  setThemePlugin(). MaterialApp wraps a FutureBuilder that
  re-fetches the plugin's ColorSchemes whenever the
  capability flips; falls back to FaiTheme.light/dark on
  null or load failure.
- `fai_settings_dialog.dart`: new `_ThemePluginPanel` shows a
  dropdown of installed studio.theme.* capabilities + a
  "Built-in" entry. Switching applies immediately.
- HubService.invokePluginTranslate added as the typed wrapper
  for the new gRPC RPC — ready for the FaiEnBadge swap-out
  in the next iteration.
- 4 new l10n keys (themePluginHeader / None / Hint / Empty),
  EN+DE.

flutter analyze + flutter test (widget + friendly_error): both
green. The integration test for the theme picker is the
existing fai_runtime plugin_theme + Studio-side
invokePluginTheme path that round-trips through the new
mirror-installable studio-theme-solarized bundle.

Signed-off-by: flemming-it <sf@flemming.it>
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-25 23:02:21 +02:00
parent a5bcde6446
commit 91706cea2b
9 changed files with 430 additions and 13 deletions

View file

@ -95,6 +95,11 @@
"mcpSuggestionTimeDesc": "Anthropic — aktuelle Uhrzeit + Zeitzonen-Konvertierung.",
"mcpServerEnLanguageBadge": "Server-Text in Englisch — Studio zeigt ihn wie geliefert.",
"themePluginHeader": "Theme-Plugin",
"themePluginNone": "Standard (kein Plugin)",
"themePluginHint": "Installiertes studio.theme.*-Plugin auswählen, um das ganze Studio umzustylen. Erst eines aus dem Store installieren.",
"themePluginEmpty": "Kein Theme-Plugin installiert. Im Store eines aus der Kategorie studio-plugin auswählen.",
"settingsTitle": "Hub-Endpunkt",
"settingsHost": "Host",
"settingsPort": "Port",

View file

@ -96,6 +96,11 @@
"mcpSuggestionTimeDesc": "Anthropic — current time + timezone conversion.",
"mcpServerEnLanguageBadge": "Server text in English — Studio shows it as supplied.",
"themePluginHeader": "Theme plugin",
"themePluginNone": "Built-in (no plugin)",
"themePluginHint": "Pick an installed studio.theme.* plugin to re-skin the whole app. Install one from the Store first.",
"themePluginEmpty": "No theme plugins installed. Open the Store and pick one in the studio-plugin category.",
"settingsTitle": "Hub endpoint",
"settingsHost": "Host",
"settingsPort": "Port",

View file

@ -620,6 +620,30 @@ abstract class AppLocalizations {
/// **'Server text in English — Studio shows it as supplied.'**
String get mcpServerEnLanguageBadge;
/// No description provided for @themePluginHeader.
///
/// In en, this message translates to:
/// **'Theme plugin'**
String get themePluginHeader;
/// No description provided for @themePluginNone.
///
/// In en, this message translates to:
/// **'Built-in (no plugin)'**
String get themePluginNone;
/// No description provided for @themePluginHint.
///
/// In en, this message translates to:
/// **'Pick an installed studio.theme.* plugin to re-skin the whole app. Install one from the Store first.'**
String get themePluginHint;
/// No description provided for @themePluginEmpty.
///
/// In en, this message translates to:
/// **'No theme plugins installed. Open the Store and pick one in the studio-plugin category.'**
String get themePluginEmpty;
/// No description provided for @settingsTitle.
///
/// In en, this message translates to:

View file

@ -302,6 +302,20 @@ class AppLocalizationsDe extends AppLocalizations {
String get mcpServerEnLanguageBadge =>
'Server-Text in Englisch — Studio zeigt ihn wie geliefert.';
@override
String get themePluginHeader => 'Theme-Plugin';
@override
String get themePluginNone => 'Standard (kein Plugin)';
@override
String get themePluginHint =>
'Installiertes studio.theme.*-Plugin auswählen, um das ganze Studio umzustylen. Erst eines aus dem Store installieren.';
@override
String get themePluginEmpty =>
'Kein Theme-Plugin installiert. Im Store eines aus der Kategorie studio-plugin auswählen.';
@override
String get settingsTitle => 'Hub-Endpunkt';

View file

@ -302,6 +302,20 @@ class AppLocalizationsEn extends AppLocalizations {
String get mcpServerEnLanguageBadge =>
'Server text in English — Studio shows it as supplied.';
@override
String get themePluginHeader => 'Theme plugin';
@override
String get themePluginNone => 'Built-in (no plugin)';
@override
String get themePluginHint =>
'Pick an installed studio.theme.* plugin to re-skin the whole app. Install one from the Store first.';
@override
String get themePluginEmpty =>
'No theme plugins installed. Open the Store and pick one in the studio-plugin category.';
@override
String get settingsTitle => 'Hub endpoint';