feat(ui): Flows page (5th destination) + keyboard shortcuts
Two operator-DX additions:
1. **Flows** is the new third destination in the sidebar.
Lists saved flows from the hub via the new HubAdmin
ListFlows RPC. Each row has a Run button that opens an
input dialog (key=value pairs, one per line, all values
sent as text payloads — the binary-input case stays in
`fai run` CLI). Flow runs in a non-dismissable progress
dialog; output is shown per-key with monospace
selectable text. Errors render with the exception detail
for diagnosis.
2. **Keyboard shortcuts** at the shell level:
- Cmd+1 / Cmd+2 / Cmd+3 / Cmd+4 / Cmd+5 jump to the
matching destination (Doctor / Modules / Flows / Audit
/ Approvals).
- Cmd+, opens the Settings dialog (macOS convention).
Implemented via Flutter's Shortcuts/Actions/Intent triple
so the bindings are discoverable in IDE devtools and
composable with platform-specific overrides later.
Bumps fai_studio 0.7.3 -> 0.8.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
2f32023322
commit
753db4fc60
5 changed files with 508 additions and 24 deletions
|
|
@ -106,6 +106,49 @@ class HubService {
|
|||
);
|
||||
}
|
||||
|
||||
/// Saved flows known to the hub.
|
||||
Future<List<SavedFlow>> listFlows() async {
|
||||
final flows = await _client.listFlows();
|
||||
return flows
|
||||
.map(
|
||||
(f) => SavedFlow(
|
||||
name: f.name,
|
||||
path: f.path,
|
||||
sizeBytes: f.sizeBytes.toInt(),
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
..sort((a, b) => a.name.compareTo(b.name));
|
||||
}
|
||||
|
||||
/// Run a saved flow with the supplied text inputs. Returns
|
||||
/// the named outputs as a map of UI-friendly strings.
|
||||
Future<Map<String, String>> runSavedFlow({
|
||||
required String name,
|
||||
required Map<String, String> textInputs,
|
||||
}) async {
|
||||
final r = await _client.runSavedFlow(
|
||||
name: name,
|
||||
textInputs: textInputs,
|
||||
);
|
||||
return {
|
||||
for (final entry in r.outputs.entries)
|
||||
entry.key: _payloadToText(entry.value),
|
||||
};
|
||||
}
|
||||
|
||||
String _payloadToText(Payload p) {
|
||||
if (p.hasText()) return p.text;
|
||||
if (p.hasJson()) return p.json.toString();
|
||||
if (p.hasBytes()) {
|
||||
return '<bytes: ${p.bytes.data.length} bytes, ${p.bytes.mimeType}>';
|
||||
}
|
||||
if (p.hasFile()) {
|
||||
return '<file: ${p.file.uri}>';
|
||||
}
|
||||
return '<unknown payload variant>';
|
||||
}
|
||||
|
||||
Future<List<AuditEvent>> recentEvents({
|
||||
int limit = 50,
|
||||
List<String> types = const [],
|
||||
|
|
@ -264,6 +307,18 @@ class ModuleSummary {
|
|||
});
|
||||
}
|
||||
|
||||
class SavedFlow {
|
||||
final String name;
|
||||
final String path;
|
||||
final int sizeBytes;
|
||||
|
||||
const SavedFlow({
|
||||
required this.name,
|
||||
required this.path,
|
||||
required this.sizeBytes,
|
||||
});
|
||||
}
|
||||
|
||||
class ModuleDetail {
|
||||
final String name;
|
||||
final String version;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue