feat(editor): graph-pulse on errors + store-aware install / add-source
Three connected improvements to the analyzer-driven diagnostics:
- **Pulsing halo on broken graph nodes**. Every step / pseudo-
node (inputs / outputs) carrying an analyzer issue now
breathes a red (error) or amber (warning) halo on the graph
tab — operator sees the problem on the canvas without
flipping to text. Honours prefers-reduced-motion: reduce-
motion users get a static halo at the same intensity. The
canvas reads severity via a new `stepIssueSeverity` map on
FlowEditorController; pseudo-nodes use `__inputs__` /
`__outputs__` sentinel ids.
- **Store-aware install button**. The analyzer now takes a
second closure, `storeCapabilities`, listing what the public
store can install. Unknown-capability issues only carry the
"Install …" quick-fix when the bare cap is in that list;
otherwise the issue carries an "Add source for …" fix
instead. Resolves the asymmetry the operator reported: the
Store didn't show `htw.digiscout/onet.lookup` but the editor
happily offered to install it (and would have failed). The
install path no longer lies about itself.
- **Did-you-mean suggestion**. When the unknown cap is within
edit-distance two of an installed or store cap (different
spelling — distance-0 stays hidden because that's an install
case, not a typo), the analyzer emits a `ReplaceLineValueFix`
suggesting the closest match. Preserves the version
constraint by reusing the installed spec when present
(e.g. `text.echi@^0.1` → `text.echo@^0.1`).
New public surface:
- `AddModuleSourceFix` + `AddModuleSourceCallback`
- `FlowEditorPage.storeCapabilities` + `onAddModuleSource`
- `FlowAnalyzer.stepSeverity` + the `kInputsNodeId` /
`kOutputsNodeId` sentinels
- `FlowIssueSeverity` enum + `FlowNode.issueSeverity`
Tests:
- Install fix only when in store
- AddModuleSourceFix as fallback when not in store
- Did-you-mean replaces install when a near miss exists
- stepSeverity populated for both step ids and pseudo-nodes
All 36 editor tests green. Bumped to 0.18.0.
Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
f43c1ac6cf
commit
efdfa7dd79
10 changed files with 497 additions and 45 deletions
|
|
@ -85,6 +85,21 @@ class FlowEditorPage extends StatefulWidget {
|
|||
/// action (the diagnostic remains visible without it).
|
||||
final InstallCapabilityCallback? onInstallCapability;
|
||||
|
||||
/// Host-side "register a new module source" handler. Invoked
|
||||
/// when the operator clicks "Add source for <capability>…" on
|
||||
/// an unknown-capability issue that isn't in the public store.
|
||||
/// Same callback contract as [onInstallCapability]; the host
|
||||
/// is expected to prompt the operator for a path / URL and
|
||||
/// then call the Hub install API.
|
||||
final AddModuleSourceCallback? onAddModuleSource;
|
||||
|
||||
/// Capabilities the public store knows how to install. The
|
||||
/// analyzer uses this to decide whether to show "Install …"
|
||||
/// (in store) or "Add source for …" (not in store) as the
|
||||
/// quick-fix on an unknown `use:` line. Empty list = store
|
||||
/// silent — no install button offered.
|
||||
final List<String> storeCapabilities;
|
||||
|
||||
const FlowEditorPage({
|
||||
super.key,
|
||||
this.initialFlowName,
|
||||
|
|
@ -93,6 +108,8 @@ class FlowEditorPage extends StatefulWidget {
|
|||
this.availableCapabilities = const [],
|
||||
this.style,
|
||||
this.onInstallCapability,
|
||||
this.onAddModuleSource,
|
||||
this.storeCapabilities = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -117,8 +134,9 @@ class _FlowEditorPageState extends State<FlowEditorPage>
|
|||
super.initState();
|
||||
_l = FlowEditorStrings(widget.locale);
|
||||
_controller = FlowEditorController();
|
||||
_controller.codeController.setAvailableCapabilities(
|
||||
() => widget.availableCapabilities,
|
||||
_controller.codeController.setCapabilityProviders(
|
||||
available: () => widget.availableCapabilities,
|
||||
store: () => widget.storeCapabilities,
|
||||
);
|
||||
_controller.addListener(_onCtrlChanged);
|
||||
_controller.codeController.hoverRequest.addListener(_onHoverChanged);
|
||||
|
|
@ -184,7 +202,22 @@ class _FlowEditorPageState extends State<FlowEditorPage>
|
|||
if (handler == null) return;
|
||||
final newCaps = await handler(capability);
|
||||
if (newCaps != null && mounted) {
|
||||
_controller.codeController.setAvailableCapabilities(() => newCaps);
|
||||
_controller.codeController.setCapabilityProviders(
|
||||
available: () => newCaps,
|
||||
store: () => widget.storeCapabilities,
|
||||
);
|
||||
await _controller.codeController.reanalyze();
|
||||
}
|
||||
break;
|
||||
case AddModuleSourceFix(:final capability):
|
||||
final handler = widget.onAddModuleSource;
|
||||
if (handler == null) return;
|
||||
final newCaps = await handler(capability);
|
||||
if (newCaps != null && mounted) {
|
||||
_controller.codeController.setCapabilityProviders(
|
||||
available: () => newCaps,
|
||||
store: () => widget.storeCapabilities,
|
||||
);
|
||||
await _controller.codeController.reanalyze();
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue