Some checks failed
Security / Security check (push) Failing after 1s
FlowsPage now passes onInstallCapability to the editor — the editor's 'Install <cap>' button on an unknown-capability diagnostic calls HubService.installModule, refreshes the capability list, and returns it so the editor reanalyzes and clears the issue. On install failure: friendly SnackBar surfaces the error. The operator never gets stuck on the analyzer reporting a capability they just clicked to install. Studio bumped to 0.64.0; editor path-override pulls in 0.17.0. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
118 lines
4 KiB
Dart
118 lines
4 KiB
Dart
// FlowsPage — Studio's flow surface. Thin host wrapper around
|
|
// the swappable fai_studio_flow_editor package: loads the
|
|
// hub's installed capabilities, hands them + a Studio-side
|
|
// FlowRunDriver to the editor, and lets the editor own
|
|
// everything else (file list, three-tab body, properties
|
|
// panel, run progress, …).
|
|
//
|
|
// Pre-0.52 versions split file-listing and run-dialog state
|
|
// across this page. v0.52 moves all of that into the editor
|
|
// package so a swap is one pubspec change — Studio doesn't
|
|
// need to know what's inside the editor any more.
|
|
|
|
import 'package:fai_studio_flow_editor/fai_studio_flow_editor.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../data/flow_run_driver.dart';
|
|
import '../data/hub.dart';
|
|
|
|
class FlowsPage extends StatefulWidget {
|
|
/// Pre-load this flow when the editor first builds. Studio
|
|
/// keeps the parameter so the Cmd+K palette + welcome page
|
|
/// can deep-link straight into a specific flow.
|
|
final String? initialFlowName;
|
|
|
|
const FlowsPage({super.key, this.initialFlowName});
|
|
|
|
@override
|
|
State<FlowsPage> createState() => _FlowsPageState();
|
|
}
|
|
|
|
class _FlowsPageState extends State<FlowsPage> {
|
|
late Future<List<String>> _capabilities;
|
|
late final StudioFlowRunDriver _driver;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_driver = StudioFlowRunDriver();
|
|
_capabilities = _loadCapabilities();
|
|
}
|
|
|
|
/// Pulls the capability identifiers the picker dialog
|
|
/// offers when adding a step. `<capability>@<version>` so
|
|
/// the operator can see exactly what they'll be wiring in,
|
|
/// matching the format the `use:` field expects. Soft-fails
|
|
/// to an empty list if the hub is unreachable — the editor
|
|
/// falls back to a free-form text field in that case.
|
|
Future<List<String>> _loadCapabilities() async {
|
|
try {
|
|
final caps = await HubService.instance.allCapabilities();
|
|
final entries =
|
|
caps.map((c) => '${c.capability}@${c.version}').toSet().toList()
|
|
..sort();
|
|
return entries;
|
|
} catch (_) {
|
|
return const <String>[];
|
|
}
|
|
}
|
|
|
|
/// Quick-fix handler — invoked when the operator clicks
|
|
/// `Install <capability>` on an analyzer issue. Parses the
|
|
/// editor-supplied `provider/name@version` spec into the
|
|
/// Hub's install API and returns the refreshed capability
|
|
/// list so the editor can re-analyze without waiting for the
|
|
/// parent widget rebuild.
|
|
Future<List<String>?> _onInstallCapability(String capability) async {
|
|
try {
|
|
// Editor passes the raw `use:` value (e.g.
|
|
// `text.classify@^1`). HubService.installModule resolves
|
|
// any version spec the hub's registry accepts.
|
|
await HubService.instance.installModule(source: capability);
|
|
final caps = await HubService.instance.allCapabilities();
|
|
final updated = caps
|
|
.map((c) => '${c.capability}@${c.version}')
|
|
.toSet()
|
|
.toList()
|
|
..sort();
|
|
if (mounted) {
|
|
// Refresh the cached capability future so subsequent
|
|
// FutureBuilder rebuilds also see the new list.
|
|
setState(() => _capabilities = Future.value(updated));
|
|
}
|
|
return updated;
|
|
} catch (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).removeCurrentSnackBar();
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Install failed: $e'),
|
|
behavior: SnackBarBehavior.floating,
|
|
),
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final locale = Localizations.localeOf(context);
|
|
final editorLocale = locale.languageCode == 'de'
|
|
? FlowEditorLocale.de
|
|
: FlowEditorLocale.en;
|
|
return FutureBuilder<List<String>>(
|
|
future: _capabilities,
|
|
builder: (context, snap) {
|
|
final caps = snap.data ?? const <String>[];
|
|
return FlowEditorPage(
|
|
initialFlowName: widget.initialFlowName,
|
|
locale: editorLocale,
|
|
runDriver: _driver,
|
|
availableCapabilities: caps,
|
|
onInstallCapability: _onInstallCapability,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|