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",
|
||||
"welcomeChecklistAiHint": "Einstellungen → System-AI. Wird für die KI-Suche und Fehlererklärungen gebraucht.",
|
||||
"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",
|
||||
"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",
|
||||
"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.",
|
||||
"welcomeChecklistDismiss": "Checkliste ausblenden",
|
||||
"welcomeChecklistRefresh": "Aktualisieren",
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
"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.",
|
||||
"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.",
|
||||
"themePluginCustom": "Eigene Farbe",
|
||||
"themePluginCustomDialogTitle": "Eigenes Theme — Seedfarbe wählen",
|
||||
|
|
|
|||
|
|
@ -44,11 +44,11 @@
|
|||
"welcomeChecklistAi": "Configure System AI",
|
||||
"welcomeChecklistAiHint": "Settings → System AI. Required for the AI search and inline failure explanations.",
|
||||
"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",
|
||||
"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",
|
||||
"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.",
|
||||
"welcomeChecklistDismiss": "Hide checklist",
|
||||
"welcomeChecklistRefresh": "Refresh",
|
||||
|
|
@ -109,7 +109,7 @@
|
|||
"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.",
|
||||
"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.",
|
||||
"themePluginCustom": "Custom colour",
|
||||
"themePluginCustomDialogTitle": "Custom theme — pick a seed colour",
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ abstract class AppLocalizations {
|
|||
/// No description provided for @welcomeChecklistMcpHint.
|
||||
///
|
||||
/// 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;
|
||||
|
||||
/// No description provided for @welcomeChecklistModule.
|
||||
|
|
@ -317,7 +317,7 @@ abstract class AppLocalizations {
|
|||
/// No description provided for @welcomeChecklistModuleHint.
|
||||
///
|
||||
/// 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;
|
||||
|
||||
/// No description provided for @welcomeChecklistFlow.
|
||||
|
|
@ -329,7 +329,7 @@ abstract class AppLocalizations {
|
|||
/// No description provided for @welcomeChecklistFlowHint.
|
||||
///
|
||||
/// 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;
|
||||
|
||||
/// No description provided for @welcomeChecklistAllDone.
|
||||
|
|
@ -653,7 +653,7 @@ abstract class AppLocalizations {
|
|||
/// 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.'**
|
||||
/// **'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;
|
||||
|
||||
/// No description provided for @themePluginEmpty.
|
||||
|
|
|
|||
|
|
@ -128,21 +128,21 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
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
|
||||
String get welcomeChecklistModule => 'Ein Text-Modul installieren';
|
||||
|
||||
@override
|
||||
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
|
||||
String get welcomeChecklistFlow => 'Einen gespeicherten Flow starten';
|
||||
|
||||
@override
|
||||
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
|
||||
String get welcomeChecklistAllDone => 'Alle vier Schritte erledigt.';
|
||||
|
|
@ -320,7 +320,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
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
|
||||
String get themePluginEmpty =>
|
||||
|
|
|
|||
|
|
@ -128,21 +128,21 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
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
|
||||
String get welcomeChecklistModule => 'Install a text module';
|
||||
|
||||
@override
|
||||
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
|
||||
String get welcomeChecklistFlow => 'Run a saved flow';
|
||||
|
||||
@override
|
||||
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
|
||||
String get welcomeChecklistAllDone => 'All four steps complete.';
|
||||
|
|
@ -320,7 +320,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
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
|
||||
String get themePluginEmpty =>
|
||||
|
|
|
|||
|
|
@ -227,15 +227,31 @@ class StudioShell extends StatefulWidget {
|
|||
const StudioShell({super.key});
|
||||
|
||||
@override
|
||||
State<StudioShell> createState() => _StudioShellState();
|
||||
State<StudioShell> createState() => StudioShellState();
|
||||
}
|
||||
|
||||
class _StudioShellState extends State<StudioShell> {
|
||||
class StudioShellState extends State<StudioShell> {
|
||||
int _selectedIndex = 0;
|
||||
bool? _connected; // null = unknown, polled on a timer
|
||||
String? _activeChannel; // local / dev / beta / production
|
||||
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>[
|
||||
_NavPage(
|
||||
id: 'welcome',
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
import '../data/hub.dart';
|
||||
import '../data/system_actions.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import '../main.dart' show StudioShellState;
|
||||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
import '../widgets/widgets.dart';
|
||||
|
|
@ -472,11 +473,36 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
|
|||
final theme = Theme.of(context);
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final allDone = _aiOk && _mcpOk && _moduleOk && _flowOk;
|
||||
final items = <(bool, String, String)>[
|
||||
(_aiOk, l.welcomeChecklistAi, l.welcomeChecklistAiHint),
|
||||
(_mcpOk, l.welcomeChecklistMcp, l.welcomeChecklistMcpHint),
|
||||
(_moduleOk, l.welcomeChecklistModule, l.welcomeChecklistModuleHint),
|
||||
(_flowOk, l.welcomeChecklistFlow, l.welcomeChecklistFlowHint),
|
||||
// Each row gets an `onTap` that takes the operator to
|
||||
// the page where they can actually complete the item.
|
||||
// Replaces the old "read the hint then guess where the
|
||||
// feature lives" behaviour with a one-click hop.
|
||||
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(
|
||||
padding: const EdgeInsets.only(bottom: FaiSpace.xxl),
|
||||
|
|
@ -533,6 +559,7 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
|
|||
done: items[i].$1,
|
||||
title: items[i].$2,
|
||||
hint: items[i].$3,
|
||||
onTap: items[i].$4,
|
||||
),
|
||||
],
|
||||
],
|
||||
|
|
@ -693,18 +720,23 @@ class _ChecklistRow extends StatelessWidget {
|
|||
final bool done;
|
||||
final String title;
|
||||
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({
|
||||
required this.done,
|
||||
required this.title,
|
||||
required this.hint,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return Padding(
|
||||
final row = Padding(
|
||||
padding: const EdgeInsets.all(FaiSpace.lg),
|
||||
child: Row(
|
||||
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
|
||||
? _parseHexColor(seedHex)
|
||||
: 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(
|
||||
seedColor: seed ?? theme.colorScheme.primary,
|
||||
brightness: theme.brightness,
|
||||
|
|
@ -157,6 +161,11 @@ class _ThemePickerGridState extends State<ThemePickerGrid> {
|
|||
label: l.themePluginCustom,
|
||||
swatches: [scheme.primary, scheme.secondary, scheme.tertiary],
|
||||
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 {
|
||||
final picked = await _showColorPickerDialog(
|
||||
context,
|
||||
|
|
@ -195,6 +204,11 @@ class _Tile extends StatelessWidget {
|
|||
final List<Color> swatches;
|
||||
final bool selected;
|
||||
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;
|
||||
const _Tile({
|
||||
required this.label,
|
||||
|
|
@ -202,6 +216,7 @@ class _Tile extends StatelessWidget {
|
|||
required this.selected,
|
||||
required this.onTap,
|
||||
this.loading = false,
|
||||
this.editable = false,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -273,6 +288,12 @@ class _Tile extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (editable && !selected)
|
||||
Icon(
|
||||
Icons.tune,
|
||||
size: 14,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
if (selected)
|
||||
Icon(
|
||||
Icons.check_circle,
|
||||
|
|
|
|||
|
|
@ -125,10 +125,10 @@ packages:
|
|||
description:
|
||||
path: "."
|
||||
ref: main
|
||||
resolved-ref: "7b256b5e35204771b8eeca3deb1f4071d86a035b"
|
||||
resolved-ref: "1e9968496e7f04c04143a636b0cfe0b4c344dfa0"
|
||||
url: "https://git.flemming.ai/fai/studio-flow-editor"
|
||||
source: git
|
||||
version: "0.11.0"
|
||||
version: "0.12.0"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
name: fai_studio
|
||||
description: "F∆I Studio — desktop GUI for the F∆I hub"
|
||||
publish_to: 'none'
|
||||
version: 0.58.0
|
||||
version: 0.59.0
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.0-200.1.beta
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue