feat(studio): brand-agnostic onboarding hints + clickable checklist + theme-picker clarity
Some checks failed
Security / Security check (push) Failing after 2s
Some checks failed
Security / Security check (push) Failing after 2s
Three changes:
1. Onboarding checklist hints no longer mention DeepWiki,
Semgrep, text.extract, or extract-summarize.yaml by
name. Brand- and file-specific copy moved to neutral
feature pointers ('add an MCP source', 'install a
module'). Brand names belong on the welcome page only
if they're load-bearing for understanding, which they
aren't.
2. Checklist rows are now clickable. Tapping a row navigates
to the page where the operator can complete it — MCP +
AI open Settings, Module opens Store, Flow opens Flows.
StudioShellState gains a public navigateTo(pageId) helper
so descendants can drive sidebar selection without
plumbing a controller through props.
3. Theme picker hint copy rewritten. Old hint suggested
'Custom' was a single-colour tweak; new copy spells out
that Material 3 derives a full palette (primary,
secondary, tertiary, surface, …) from one seed colour.
The Custom tile shows a small tune icon so its
open-the-picker semantics is visually distinct from the
apply-immediately plugin tiles.
Editor bumped to 0.12.0 (panel toggle + LabVIEW-style port
colours).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
c1c60d5434
commit
3db875c903
10 changed files with 105 additions and 31 deletions
|
|
@ -41,11 +41,11 @@
|
||||||
"welcomeChecklistAi": "System-AI konfigurieren",
|
"welcomeChecklistAi": "System-AI konfigurieren",
|
||||||
"welcomeChecklistAiHint": "Einstellungen → System-AI. Wird für die KI-Suche und Fehlererklärungen gebraucht.",
|
"welcomeChecklistAiHint": "Einstellungen → System-AI. Wird für die KI-Suche und Fehlererklärungen gebraucht.",
|
||||||
"welcomeChecklistMcp": "Öffentliche Capability-Quelle hinzufügen",
|
"welcomeChecklistMcp": "Öffentliche Capability-Quelle hinzufügen",
|
||||||
"welcomeChecklistMcpHint": "Store → Today-Hero, oder Einstellungen → MCP-Clients. DeepWiki und Semgrep sind je ein Klick.",
|
"welcomeChecklistMcpHint": "Im Store eine MCP-Quelle hinzufügen oder unter Einstellungen → MCP-Clients konfigurieren.",
|
||||||
"welcomeChecklistModule": "Ein Text-Modul installieren",
|
"welcomeChecklistModule": "Ein Text-Modul installieren",
|
||||||
"welcomeChecklistModuleHint": "Store → nach „text\" suchen → Installieren. text.extract für PDFs und DOCX ist ein guter Start.",
|
"welcomeChecklistModuleHint": "Im Store ein Modul aus der Kategorie deiner Wahl installieren. Module sind sandboxed F∆I-Komponenten.",
|
||||||
"welcomeChecklistFlow": "Einen gespeicherten Flow starten",
|
"welcomeChecklistFlow": "Einen gespeicherten Flow starten",
|
||||||
"welcomeChecklistFlowHint": "Flows → einen wählen → Starten. Der mitgelieferte extract-summarize.yaml ist ein guter erster Lauf.",
|
"welcomeChecklistFlowHint": "Flows → einen wählen → Starten. Studio bringt einige Demo-Flows mit; auch ein eigener tut's.",
|
||||||
"welcomeChecklistAllDone": "Alle vier Schritte erledigt.",
|
"welcomeChecklistAllDone": "Alle vier Schritte erledigt.",
|
||||||
"welcomeChecklistDismiss": "Checkliste ausblenden",
|
"welcomeChecklistDismiss": "Checkliste ausblenden",
|
||||||
"welcomeChecklistRefresh": "Aktualisieren",
|
"welcomeChecklistRefresh": "Aktualisieren",
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
"mcpServerEnLanguageBadge": "Server-Text in Englisch — Studio zeigt ihn wie geliefert.",
|
"mcpServerEnLanguageBadge": "Server-Text in Englisch — Studio zeigt ihn wie geliefert.",
|
||||||
"themePluginHeader": "Theme-Plugin",
|
"themePluginHeader": "Theme-Plugin",
|
||||||
"themePluginNone": "Standard (kein Plugin)",
|
"themePluginNone": "Standard (kein Plugin)",
|
||||||
"themePluginHint": "Installiertes studio.theme.*-Plugin auswählen, um das ganze Studio umzustylen. Erst eines aus dem Store installieren.",
|
"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.",
|
"themePluginEmpty": "Kein Theme-Plugin installiert. Im Store eines aus der Kategorie studio-plugin auswählen.",
|
||||||
"themePluginCustom": "Eigene Farbe",
|
"themePluginCustom": "Eigene Farbe",
|
||||||
"themePluginCustomDialogTitle": "Eigenes Theme — Seedfarbe wählen",
|
"themePluginCustomDialogTitle": "Eigenes Theme — Seedfarbe wählen",
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,11 @@
|
||||||
"welcomeChecklistAi": "Configure System AI",
|
"welcomeChecklistAi": "Configure System AI",
|
||||||
"welcomeChecklistAiHint": "Settings → System AI. Required for the AI search and inline failure explanations.",
|
"welcomeChecklistAiHint": "Settings → System AI. Required for the AI search and inline failure explanations.",
|
||||||
"welcomeChecklistMcp": "Add a public capability source",
|
"welcomeChecklistMcp": "Add a public capability source",
|
||||||
"welcomeChecklistMcpHint": "Store → Today hero, or Settings → MCP Clients. DeepWiki and Semgrep are one click each.",
|
"welcomeChecklistMcpHint": "Add an MCP source from the Store, or configure one under Settings → MCP Clients.",
|
||||||
"welcomeChecklistModule": "Install a text module",
|
"welcomeChecklistModule": "Install a text module",
|
||||||
"welcomeChecklistModuleHint": "Store → search \"text\" → Install. Try text.extract for PDFs and DOCX.",
|
"welcomeChecklistModuleHint": "Install a module from the Store — pick whichever category fits your work. Modules are sandboxed F∆I components.",
|
||||||
"welcomeChecklistFlow": "Run a saved flow",
|
"welcomeChecklistFlow": "Run a saved flow",
|
||||||
"welcomeChecklistFlowHint": "Flows → pick one → Run. The bundled extract-summarize.yaml is a good first run.",
|
"welcomeChecklistFlowHint": "Flows → pick one → Run. Studio ships a few demo flows; one of your own works too.",
|
||||||
"welcomeChecklistAllDone": "All four steps complete.",
|
"welcomeChecklistAllDone": "All four steps complete.",
|
||||||
"welcomeChecklistDismiss": "Hide checklist",
|
"welcomeChecklistDismiss": "Hide checklist",
|
||||||
"welcomeChecklistRefresh": "Refresh",
|
"welcomeChecklistRefresh": "Refresh",
|
||||||
|
|
@ -109,7 +109,7 @@
|
||||||
"mcpServerEnLanguageBadge": "Server text in English — Studio shows it as supplied.",
|
"mcpServerEnLanguageBadge": "Server text in English — Studio shows it as supplied.",
|
||||||
"themePluginHeader": "Theme plugin",
|
"themePluginHeader": "Theme plugin",
|
||||||
"themePluginNone": "Built-in (no 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.",
|
"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.",
|
"themePluginEmpty": "No theme plugins installed. Open the Store and pick one in the studio-plugin category.",
|
||||||
"themePluginCustom": "Custom colour",
|
"themePluginCustom": "Custom colour",
|
||||||
"themePluginCustomDialogTitle": "Custom theme — pick a seed colour",
|
"themePluginCustomDialogTitle": "Custom theme — pick a seed colour",
|
||||||
|
|
|
||||||
|
|
@ -305,7 +305,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @welcomeChecklistMcpHint.
|
/// No description provided for @welcomeChecklistMcpHint.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Store → Today hero, or Settings → MCP Clients. DeepWiki and Semgrep are one click each.'**
|
/// **'Add an MCP source from the Store, or configure one under Settings → MCP Clients.'**
|
||||||
String get welcomeChecklistMcpHint;
|
String get welcomeChecklistMcpHint;
|
||||||
|
|
||||||
/// No description provided for @welcomeChecklistModule.
|
/// No description provided for @welcomeChecklistModule.
|
||||||
|
|
@ -317,7 +317,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @welcomeChecklistModuleHint.
|
/// No description provided for @welcomeChecklistModuleHint.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Store → search \"text\" → Install. Try text.extract for PDFs and DOCX.'**
|
/// **'Install a module from the Store — pick whichever category fits your work. Modules are sandboxed F∆I components.'**
|
||||||
String get welcomeChecklistModuleHint;
|
String get welcomeChecklistModuleHint;
|
||||||
|
|
||||||
/// No description provided for @welcomeChecklistFlow.
|
/// No description provided for @welcomeChecklistFlow.
|
||||||
|
|
@ -329,7 +329,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @welcomeChecklistFlowHint.
|
/// No description provided for @welcomeChecklistFlowHint.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Flows → pick one → Run. The bundled extract-summarize.yaml is a good first run.'**
|
/// **'Flows → pick one → Run. Studio ships a few demo flows; one of your own works too.'**
|
||||||
String get welcomeChecklistFlowHint;
|
String get welcomeChecklistFlowHint;
|
||||||
|
|
||||||
/// No description provided for @welcomeChecklistAllDone.
|
/// No description provided for @welcomeChecklistAllDone.
|
||||||
|
|
@ -653,7 +653,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @themePluginHint.
|
/// No description provided for @themePluginHint.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Pick an installed studio.theme.* plugin to re-skin the whole app. Install one from the Store first.'**
|
/// **'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.'**
|
||||||
String get themePluginHint;
|
String get themePluginHint;
|
||||||
|
|
||||||
/// No description provided for @themePluginEmpty.
|
/// No description provided for @themePluginEmpty.
|
||||||
|
|
|
||||||
|
|
@ -128,21 +128,21 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistMcpHint =>
|
String get welcomeChecklistMcpHint =>
|
||||||
'Store → Today-Hero, oder Einstellungen → MCP-Clients. DeepWiki und Semgrep sind je ein Klick.';
|
'Im Store eine MCP-Quelle hinzufügen oder unter Einstellungen → MCP-Clients konfigurieren.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistModule => 'Ein Text-Modul installieren';
|
String get welcomeChecklistModule => 'Ein Text-Modul installieren';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistModuleHint =>
|
String get welcomeChecklistModuleHint =>
|
||||||
'Store → nach „text\" suchen → Installieren. text.extract für PDFs und DOCX ist ein guter Start.';
|
'Im Store ein Modul aus der Kategorie deiner Wahl installieren. Module sind sandboxed F∆I-Komponenten.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistFlow => 'Einen gespeicherten Flow starten';
|
String get welcomeChecklistFlow => 'Einen gespeicherten Flow starten';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistFlowHint =>
|
String get welcomeChecklistFlowHint =>
|
||||||
'Flows → einen wählen → Starten. Der mitgelieferte extract-summarize.yaml ist ein guter erster Lauf.';
|
'Flows → einen wählen → Starten. Studio bringt einige Demo-Flows mit; auch ein eigener tut\'s.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistAllDone => 'Alle vier Schritte erledigt.';
|
String get welcomeChecklistAllDone => 'Alle vier Schritte erledigt.';
|
||||||
|
|
@ -320,7 +320,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get themePluginHint =>
|
String get themePluginHint =>
|
||||||
'Installiertes studio.theme.*-Plugin auswählen, um das ganze Studio umzustylen. Erst eines aus dem Store installieren.';
|
'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.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get themePluginEmpty =>
|
String get themePluginEmpty =>
|
||||||
|
|
|
||||||
|
|
@ -128,21 +128,21 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistMcpHint =>
|
String get welcomeChecklistMcpHint =>
|
||||||
'Store → Today hero, or Settings → MCP Clients. DeepWiki and Semgrep are one click each.';
|
'Add an MCP source from the Store, or configure one under Settings → MCP Clients.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistModule => 'Install a text module';
|
String get welcomeChecklistModule => 'Install a text module';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistModuleHint =>
|
String get welcomeChecklistModuleHint =>
|
||||||
'Store → search \"text\" → Install. Try text.extract for PDFs and DOCX.';
|
'Install a module from the Store — pick whichever category fits your work. Modules are sandboxed F∆I components.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistFlow => 'Run a saved flow';
|
String get welcomeChecklistFlow => 'Run a saved flow';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistFlowHint =>
|
String get welcomeChecklistFlowHint =>
|
||||||
'Flows → pick one → Run. The bundled extract-summarize.yaml is a good first run.';
|
'Flows → pick one → Run. Studio ships a few demo flows; one of your own works too.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get welcomeChecklistAllDone => 'All four steps complete.';
|
String get welcomeChecklistAllDone => 'All four steps complete.';
|
||||||
|
|
@ -320,7 +320,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get themePluginHint =>
|
String get themePluginHint =>
|
||||||
'Pick an installed studio.theme.* plugin to re-skin the whole app. Install one from the Store first.';
|
'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.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get themePluginEmpty =>
|
String get themePluginEmpty =>
|
||||||
|
|
|
||||||
|
|
@ -227,15 +227,31 @@ class StudioShell extends StatefulWidget {
|
||||||
const StudioShell({super.key});
|
const StudioShell({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StudioShell> createState() => _StudioShellState();
|
State<StudioShell> createState() => StudioShellState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _StudioShellState extends State<StudioShell> {
|
class StudioShellState extends State<StudioShell> {
|
||||||
int _selectedIndex = 0;
|
int _selectedIndex = 0;
|
||||||
bool? _connected; // null = unknown, polled on a timer
|
bool? _connected; // null = unknown, polled on a timer
|
||||||
String? _activeChannel; // local / dev / beta / production
|
String? _activeChannel; // local / dev / beta / production
|
||||||
Timer? _healthPoll;
|
Timer? _healthPoll;
|
||||||
|
|
||||||
|
/// Switch the sidebar's selected page by its registered id
|
||||||
|
/// (`store`, `flows`, `audit`, etc.). Used by the welcome
|
||||||
|
/// page's onboarding checklist to make each item a real
|
||||||
|
/// navigation hop instead of a static hint. Silently
|
||||||
|
/// ignores unknown ids.
|
||||||
|
void navigateTo(String pageId) {
|
||||||
|
final idx = _pages.indexWhere((p) => p.id == pageId);
|
||||||
|
if (idx < 0) return;
|
||||||
|
setState(() => _selectedIndex = idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup helper — descendants call this to drive nav
|
||||||
|
/// without holding a controller reference.
|
||||||
|
static StudioShellState? of(BuildContext context) =>
|
||||||
|
context.findAncestorStateOfType<StudioShellState>();
|
||||||
|
|
||||||
static const _pages = <_NavPage>[
|
static const _pages = <_NavPage>[
|
||||||
_NavPage(
|
_NavPage(
|
||||||
id: 'welcome',
|
id: 'welcome',
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import '../data/hub.dart';
|
import '../data/hub.dart';
|
||||||
import '../data/system_actions.dart';
|
import '../data/system_actions.dart';
|
||||||
import '../l10n/app_localizations.dart';
|
import '../l10n/app_localizations.dart';
|
||||||
|
import '../main.dart' show StudioShellState;
|
||||||
import '../theme/theme.dart';
|
import '../theme/theme.dart';
|
||||||
import '../theme/tokens.dart';
|
import '../theme/tokens.dart';
|
||||||
import '../widgets/widgets.dart';
|
import '../widgets/widgets.dart';
|
||||||
|
|
@ -472,11 +473,36 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final l = AppLocalizations.of(context)!;
|
final l = AppLocalizations.of(context)!;
|
||||||
final allDone = _aiOk && _mcpOk && _moduleOk && _flowOk;
|
final allDone = _aiOk && _mcpOk && _moduleOk && _flowOk;
|
||||||
final items = <(bool, String, String)>[
|
// Each row gets an `onTap` that takes the operator to
|
||||||
(_aiOk, l.welcomeChecklistAi, l.welcomeChecklistAiHint),
|
// the page where they can actually complete the item.
|
||||||
(_mcpOk, l.welcomeChecklistMcp, l.welcomeChecklistMcpHint),
|
// Replaces the old "read the hint then guess where the
|
||||||
(_moduleOk, l.welcomeChecklistModule, l.welcomeChecklistModuleHint),
|
// feature lives" behaviour with a one-click hop.
|
||||||
(_flowOk, l.welcomeChecklistFlow, l.welcomeChecklistFlowHint),
|
final shell = StudioShellState.of(context);
|
||||||
|
final items = <(bool, String, String, VoidCallback?)>[
|
||||||
|
(
|
||||||
|
_aiOk,
|
||||||
|
l.welcomeChecklistAi,
|
||||||
|
l.welcomeChecklistAiHint,
|
||||||
|
() => FaiSettingsDialog.show(context),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
_mcpOk,
|
||||||
|
l.welcomeChecklistMcp,
|
||||||
|
l.welcomeChecklistMcpHint,
|
||||||
|
() => FaiSettingsDialog.show(context),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
_moduleOk,
|
||||||
|
l.welcomeChecklistModule,
|
||||||
|
l.welcomeChecklistModuleHint,
|
||||||
|
shell == null ? null : () => shell.navigateTo('store'),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
_flowOk,
|
||||||
|
l.welcomeChecklistFlow,
|
||||||
|
l.welcomeChecklistFlowHint,
|
||||||
|
shell == null ? null : () => shell.navigateTo('flows'),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: FaiSpace.xxl),
|
padding: const EdgeInsets.only(bottom: FaiSpace.xxl),
|
||||||
|
|
@ -533,6 +559,7 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
|
||||||
done: items[i].$1,
|
done: items[i].$1,
|
||||||
title: items[i].$2,
|
title: items[i].$2,
|
||||||
hint: items[i].$3,
|
hint: items[i].$3,
|
||||||
|
onTap: items[i].$4,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
@ -693,18 +720,23 @@ class _ChecklistRow extends StatelessWidget {
|
||||||
final bool done;
|
final bool done;
|
||||||
final String title;
|
final String title;
|
||||||
final String hint;
|
final String hint;
|
||||||
|
/// Tap handler — takes the operator to the page where
|
||||||
|
/// they can actually complete the item. null = no-op
|
||||||
|
/// (e.g. AI item when System AI was already configured).
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
const _ChecklistRow({
|
const _ChecklistRow({
|
||||||
required this.done,
|
required this.done,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.hint,
|
required this.hint,
|
||||||
|
this.onTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final l = AppLocalizations.of(context)!;
|
final l = AppLocalizations.of(context)!;
|
||||||
return Padding(
|
final row = Padding(
|
||||||
padding: const EdgeInsets.all(FaiSpace.lg),
|
padding: const EdgeInsets.all(FaiSpace.lg),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
@ -747,6 +779,11 @@ class _ChecklistRow extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// Wrap in InkWell only when a tap target exists so done
|
||||||
|
// rows (no action needed) don't shimmer hover-feedback
|
||||||
|
// they can't act on.
|
||||||
|
if (onTap == null) return row;
|
||||||
|
return InkWell(onTap: onTap, child: row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,10 @@ class _ThemePickerGridState extends State<ThemePickerGrid> {
|
||||||
final seed = seedHex != null
|
final seed = seedHex != null
|
||||||
? _parseHexColor(seedHex)
|
? _parseHexColor(seedHex)
|
||||||
: theme.colorScheme.primary;
|
: theme.colorScheme.primary;
|
||||||
|
// Material 3 derives a full ColorScheme from one seed —
|
||||||
|
// primary, secondary, tertiary, surface, outline, on-*.
|
||||||
|
// The custom tile previews that full palette so it reads
|
||||||
|
// visually as a complete theme, NOT a single colour.
|
||||||
final scheme = ColorScheme.fromSeed(
|
final scheme = ColorScheme.fromSeed(
|
||||||
seedColor: seed ?? theme.colorScheme.primary,
|
seedColor: seed ?? theme.colorScheme.primary,
|
||||||
brightness: theme.brightness,
|
brightness: theme.brightness,
|
||||||
|
|
@ -157,6 +161,11 @@ class _ThemePickerGridState extends State<ThemePickerGrid> {
|
||||||
label: l.themePluginCustom,
|
label: l.themePluginCustom,
|
||||||
swatches: [scheme.primary, scheme.secondary, scheme.tertiary],
|
swatches: [scheme.primary, scheme.secondary, scheme.tertiary],
|
||||||
selected: isCustom,
|
selected: isCustom,
|
||||||
|
// Slight visual cue that this tile is a different
|
||||||
|
// *source* of theme (seed-derived) compared to the
|
||||||
|
// plugin tiles (hand-curated palette). Same widget, the
|
||||||
|
// dashed border separates the concept.
|
||||||
|
editable: true,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final picked = await _showColorPickerDialog(
|
final picked = await _showColorPickerDialog(
|
||||||
context,
|
context,
|
||||||
|
|
@ -195,6 +204,11 @@ class _Tile extends StatelessWidget {
|
||||||
final List<Color> swatches;
|
final List<Color> swatches;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final bool loading;
|
final bool loading;
|
||||||
|
/// Adds a small tune icon at the top-right, telegraphing
|
||||||
|
/// "this tile opens an editor instead of applying
|
||||||
|
/// immediately". Used for the Custom tile so the operator
|
||||||
|
/// knows clicking it will open the colour-picker dialog.
|
||||||
|
final bool editable;
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
const _Tile({
|
const _Tile({
|
||||||
required this.label,
|
required this.label,
|
||||||
|
|
@ -202,6 +216,7 @@ class _Tile extends StatelessWidget {
|
||||||
required this.selected,
|
required this.selected,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
this.loading = false,
|
this.loading = false,
|
||||||
|
this.editable = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -273,6 +288,12 @@ class _Tile extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (editable && !selected)
|
||||||
|
Icon(
|
||||||
|
Icons.tune,
|
||||||
|
size: 14,
|
||||||
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
if (selected)
|
if (selected)
|
||||||
Icon(
|
Icon(
|
||||||
Icons.check_circle,
|
Icons.check_circle,
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,10 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: main
|
ref: main
|
||||||
resolved-ref: "7b256b5e35204771b8eeca3deb1f4071d86a035b"
|
resolved-ref: "1e9968496e7f04c04143a636b0cfe0b4c344dfa0"
|
||||||
url: "https://git.flemming.ai/fai/studio-flow-editor"
|
url: "https://git.flemming.ai/fai/studio-flow-editor"
|
||||||
source: git
|
source: git
|
||||||
version: "0.11.0"
|
version: "0.12.0"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
name: fai_studio
|
name: fai_studio
|
||||||
description: "F∆I Studio — desktop GUI for the F∆I hub"
|
description: "F∆I Studio — desktop GUI for the F∆I hub"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.58.0
|
version: 0.59.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.11.0-200.1.beta
|
sdk: ^3.11.0-200.1.beta
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue