@memberjunction/ng-task-graph-editor 6.1.0-edge.1 → 6.1.0-edge.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,14 +24,16 @@
24
24
  *
25
25
  * @module @memberjunction/ng-task-graph-editor
26
26
  */
27
- import { EventEmitter } from '@angular/core';
27
+ import { EventEmitter, OnDestroy } from '@angular/core';
28
28
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
29
29
  import { type TaskGraphSpec, type TaskGraphSpecNode, type TaskGraphValidationError } from '@memberjunction/ai-core-plus';
30
- import type { FlowConnection, FlowConnectionCreatedEvent, FlowLayoutDirection, FlowNode, FlowNodeTypeConfig } from '@memberjunction/ng-flow-editor';
30
+ import { FlowEditorComponent } from '@memberjunction/ng-flow-editor';
31
+ import type { FlowConnection, FlowConnectionCreatedEvent, FlowLayoutDirection, FlowNode, FlowNodeAddedEvent, FlowNodeMovedEvent, FlowNodeTypeConfig, FlowPosition } from '@memberjunction/ng-flow-editor';
31
32
  import { type TaskGraphRuntimeStatus } from './task-graph-canvas-adapter';
33
+ import type { DependencyConditionChangeRequestedEventArgs, TaskPropertyChangeRequestedEventArgs } from './task-graph-properties-panel.component';
32
34
  import { AfterDependencyAddedEventArgs, AfterDependencyRemovedEventArgs, AfterTaskAddedEventArgs, AfterTaskRemovedEventArgs, AfterTaskUpdatedEventArgs, AgentOpenRequestedEventArgs, BeforeDependencyAddedEventArgs, BeforeDependencyRemovedEventArgs, BeforeTaskAddedEventArgs, BeforeTaskRemovedEventArgs, BeforeTaskUpdatedEventArgs, RecordOpenRequestedEventArgs, TaskGraphSelectionChangedEventArgs, TaskGraphSpecChangedEventArgs, TaskGraphValidationChangedEventArgs } from './task-graph-editor-events';
33
35
  import * as i0 from "@angular/core";
34
- export declare class TaskGraphEditorComponent extends BaseAngularComponent {
36
+ export declare class TaskGraphEditorComponent extends BaseAngularComponent implements OnDestroy {
35
37
  /**
36
38
  * The graph to show. Setter-based rather than `ngOnChanges` (repo convention): the reaction is
37
39
  * explicit, runs only when this property changes, and costs nothing on other change-detection
@@ -46,6 +48,22 @@ export declare class TaskGraphEditorComponent extends BaseAngularComponent {
46
48
  */
47
49
  set RuntimeStatus(value: TaskGraphRuntimeStatus | null);
48
50
  get RuntimeStatus(): TaskGraphRuntimeStatus | null;
51
+ /**
52
+ * Geometry for the nodes, keyed by `tempId`. Supplying it places the graph instead of laying it
53
+ * out.
54
+ *
55
+ * **Why a host must be able to supply this.** The spec has no layout field, so without it every
56
+ * node projects to the origin — all of them, stacked in one place — and the canvas is left to
57
+ * rescue the situation with a deferred Dagre pass. That pass is fine in the editor, where the
58
+ * author is present and can rearrange. It was not fine in the RUN views, which held the real
59
+ * geometry (a workflow's authored positions, or a computed layout) and had no way to hand it
60
+ * over: every run rendered its steps piled on the origin, and the zoom-to-fit that followed
61
+ * fitted a bounding box one node wide and blew the viewport up past 250%.
62
+ *
63
+ * Applied as the starting geometry only. Once the canvas reports positions of its own — a drag,
64
+ * an arrange — those win, because the person moving a node is the authority on where it goes.
65
+ */
66
+ set NodePositions(value: ReadonlyMap<string, FlowPosition> | null);
49
67
  /** Read-only mode. The same component is the viewer — there is no second, weaker renderer. */
50
68
  ReadOnly: boolean;
51
69
  ShowToolbar: boolean;
@@ -53,6 +71,22 @@ export declare class TaskGraphEditorComponent extends BaseAngularComponent {
53
71
  ShowMinimap: boolean;
54
72
  ShowStatusBar: boolean;
55
73
  AutoLayoutDirection: FlowLayoutDirection;
74
+ /**
75
+ * Whether the properties panel rides alongside the canvas.
76
+ *
77
+ * On by default, because without it a step added from the palette can never be named or
78
+ * assigned — the canvas draws structure, the panel supplies content, and one without the other
79
+ * is a graph the author can build but not finish. Hosts embedding the read-only viewer in a chat
80
+ * card turn it off.
81
+ */
82
+ ShowProperties: boolean;
83
+ /**
84
+ * Agent names offered when assigning a step. Supplied by the host, which owns data access —
85
+ * this is a widgets-layer component and does not query.
86
+ */
87
+ AvailableAgentNames: readonly string[];
88
+ /** Action names offered when assigning a step. Same ownership rule as `AvailableAgentNames`. */
89
+ AvailableActionNames: readonly string[];
56
90
  /** Shown when there is nothing to draw yet. */
57
91
  EmptyStateMessage: string;
58
92
  BeforeTaskAdded: EventEmitter<BeforeTaskAddedEventArgs>;
@@ -78,6 +112,7 @@ export declare class TaskGraphEditorComponent extends BaseAngularComponent {
78
112
  SelectedTask: TaskGraphSpecNode | null;
79
113
  ValidationErrors: readonly TaskGraphValidationError[];
80
114
  IsValid: boolean;
115
+ protected canvas: FlowEditorComponent | undefined;
81
116
  private currentSpec;
82
117
  private currentRuntime;
83
118
  get IsEmpty(): boolean;
@@ -113,6 +148,25 @@ export declare class TaskGraphEditorComponent extends BaseAngularComponent {
113
148
  /** Asks the host to open a record the graph references. */
114
149
  RequestRecordOpen(entityName: string, recordID: string): void;
115
150
  OnNodeSelected(node: FlowNode | null): void;
151
+ /**
152
+ * A palette entry was clicked or dragged onto the canvas.
153
+ *
154
+ * **This binding is the bug.** The canvas has always emitted `NodeAdded` for a palette drop, and
155
+ * this component simply never listened — so the node the canvas announced was thrown away, the
156
+ * spec never gained a task, and the author was told "a task graph must contain at least one
157
+ * task" no matter how many times they tried to add one. The canvas does not mutate its own
158
+ * `Nodes` on purpose (the host owns the model); an unheard event is therefore a silent no-op
159
+ * rather than a visible failure, which is why it survived.
160
+ *
161
+ * The new step is selected immediately: it lands unnamed and, for an agent or action step with
162
+ * nothing available to default to, unassigned — so the properties panel is where the author has
163
+ * to go next, and putting them there beats making them find it.
164
+ */
165
+ OnNodeAdded(event: FlowNodeAddedEvent): void;
166
+ /** Applies a properties-panel edit through the same vetoable path a canvas edit takes. */
167
+ OnTaskPropertyChangeRequested(args: TaskPropertyChangeRequestedEventArgs): void;
168
+ /** Applies a properties-panel edge-condition edit. */
169
+ OnDependencyConditionChangeRequested(args: DependencyConditionChangeRequestedEventArgs): void;
116
170
  OnConnectionCreated(event: FlowConnectionCreatedEvent): void;
117
171
  OnConnectionRemoved(connection: FlowConnection): void;
118
172
  OnNodeRemoved(node: FlowNode): void;
@@ -122,7 +176,55 @@ export declare class TaskGraphEditorComponent extends BaseAngularComponent {
122
176
  private commit;
123
177
  /** Re-derives the canvas from the spec. Validation rides along so the two never disagree. */
124
178
  private project;
179
+ /**
180
+ * Lays the graph out ONCE — when it arrives with no geometry of its own.
181
+ *
182
+ * A `TaskGraphSpec` carries no positions, so a spec opened for the first time projects with
183
+ * every node at the origin and needs Dagre to make it readable. After that the author's layout
184
+ * is the layout: `knownPositions` carries it across re-projections, and re-arranging again would
185
+ * throw away the arrangement they just made.
186
+ *
187
+ * It must also not run on every edit, because `AutoArrange` ends in `ZoomToFit` — so arranging
188
+ * per change meant the viewport snapped to fit after every added step and every drawn
189
+ * connection, which on a one-node graph zooms to maximum. That is the behaviour being fixed
190
+ * here; the rule mirrors the Flow Agent editor's (`flow-agent-editor.component.ts`), which has
191
+ * always arranged only when every node sits at the origin.
192
+ */
193
+ private arrangeIfNeverLaidOut;
194
+ /**
195
+ * Fits the viewport to the graph, once the canvas has drawn it.
196
+ *
197
+ * Deferred for the same reason the layout pass is: `fitToScreen` measures the rendered nodes, so
198
+ * calling it in the same turn as the projection fits whatever was on screen a moment ago. With
199
+ * every node still at the origin that bounding box is a single node wide, and "fit" means zoom
200
+ * to ~265% — the symptom that made a four-step workflow look like one enormous box.
201
+ */
202
+ private zoomToFitSoon;
203
+ /**
204
+ * The canvas is the authority on geometry, so remember what it reports.
205
+ *
206
+ * Without this the spec — which has no geometry field — is the only survivor of a re-projection,
207
+ * and every edit silently moved every node back to the origin. That is what forced a re-arrange
208
+ * (and therefore a re-zoom) on each change.
209
+ */
210
+ OnNodesChanged(nodes: FlowNode[]): void;
211
+ /** A single node was dragged. Same authority, narrower event. */
212
+ OnNodeMoved(event: FlowNodeMovedEvent): void;
213
+ ngOnDestroy(): void;
214
+ /** The topology the current layout was computed for; '' when nothing has been laid out. */
215
+ /**
216
+ * Node geometry, which the spec cannot hold.
217
+ *
218
+ * `TaskGraphSpec` is an execution contract with no layout field, so a re-projection would
219
+ * otherwise return every node to the origin. Keyed by `tempId`; written from the canvas
220
+ * (`NodesChanged` / `NodeMoved`) and from the drop position of a newly added node, and read back
221
+ * by `SpecToNodes` on every projection.
222
+ */
223
+ private readonly knownPositions;
224
+ /** Whether the one-time Dagre pass has run (or been made unnecessary by a hand-placed node). */
225
+ private hasLaidOut;
226
+ private pendingLayout;
125
227
  static ɵfac: i0.ɵɵFactoryDeclaration<TaskGraphEditorComponent, never>;
126
- static ɵcmp: i0.ɵɵComponentDeclaration<TaskGraphEditorComponent, "mj-task-graph-editor", never, { "Spec": { "alias": "Spec"; "required": false; }; "RuntimeStatus": { "alias": "RuntimeStatus"; "required": false; }; "ReadOnly": { "alias": "ReadOnly"; "required": false; }; "ShowToolbar": { "alias": "ShowToolbar"; "required": false; }; "ShowPalette": { "alias": "ShowPalette"; "required": false; }; "ShowMinimap": { "alias": "ShowMinimap"; "required": false; }; "ShowStatusBar": { "alias": "ShowStatusBar"; "required": false; }; "AutoLayoutDirection": { "alias": "AutoLayoutDirection"; "required": false; }; "EmptyStateMessage": { "alias": "EmptyStateMessage"; "required": false; }; }, { "BeforeTaskAdded": "BeforeTaskAdded"; "AfterTaskAdded": "AfterTaskAdded"; "BeforeTaskRemoved": "BeforeTaskRemoved"; "AfterTaskRemoved": "AfterTaskRemoved"; "BeforeTaskUpdated": "BeforeTaskUpdated"; "AfterTaskUpdated": "AfterTaskUpdated"; "BeforeDependencyAdded": "BeforeDependencyAdded"; "AfterDependencyAdded": "AfterDependencyAdded"; "BeforeDependencyRemoved": "BeforeDependencyRemoved"; "AfterDependencyRemoved": "AfterDependencyRemoved"; "SpecChanged": "SpecChanged"; "SelectionChanged": "SelectionChanged"; "ValidationChanged": "ValidationChanged"; "AgentOpenRequested": "AgentOpenRequested"; "RecordOpenRequested": "RecordOpenRequested"; }, never, never, false, never>;
228
+ static ɵcmp: i0.ɵɵComponentDeclaration<TaskGraphEditorComponent, "mj-task-graph-editor", never, { "Spec": { "alias": "Spec"; "required": false; }; "RuntimeStatus": { "alias": "RuntimeStatus"; "required": false; }; "NodePositions": { "alias": "NodePositions"; "required": false; }; "ReadOnly": { "alias": "ReadOnly"; "required": false; }; "ShowToolbar": { "alias": "ShowToolbar"; "required": false; }; "ShowPalette": { "alias": "ShowPalette"; "required": false; }; "ShowMinimap": { "alias": "ShowMinimap"; "required": false; }; "ShowStatusBar": { "alias": "ShowStatusBar"; "required": false; }; "AutoLayoutDirection": { "alias": "AutoLayoutDirection"; "required": false; }; "ShowProperties": { "alias": "ShowProperties"; "required": false; }; "AvailableAgentNames": { "alias": "AvailableAgentNames"; "required": false; }; "AvailableActionNames": { "alias": "AvailableActionNames"; "required": false; }; "EmptyStateMessage": { "alias": "EmptyStateMessage"; "required": false; }; }, { "BeforeTaskAdded": "BeforeTaskAdded"; "AfterTaskAdded": "AfterTaskAdded"; "BeforeTaskRemoved": "BeforeTaskRemoved"; "AfterTaskRemoved": "AfterTaskRemoved"; "BeforeTaskUpdated": "BeforeTaskUpdated"; "AfterTaskUpdated": "AfterTaskUpdated"; "BeforeDependencyAdded": "BeforeDependencyAdded"; "AfterDependencyAdded": "AfterDependencyAdded"; "BeforeDependencyRemoved": "BeforeDependencyRemoved"; "AfterDependencyRemoved": "AfterDependencyRemoved"; "SpecChanged": "SpecChanged"; "SelectionChanged": "SelectionChanged"; "ValidationChanged": "ValidationChanged"; "AgentOpenRequested": "AgentOpenRequested"; "RecordOpenRequested": "RecordOpenRequested"; }, never, never, false, never>;
127
229
  }
128
230
  //# sourceMappingURL=task-graph-editor.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"task-graph-editor.component.d.ts","sourceRoot":"","sources":["../../src/lib/task-graph-editor.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"task-graph-editor.component.d.ts","sourceRoot":"","sources":["../../src/lib/task-graph-editor.component.ts"],"names":[],"mappings":"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"}
@@ -24,14 +24,16 @@
24
24
  *
25
25
  * @module @memberjunction/ng-task-graph-editor
26
26
  */
27
- import { Component, EventEmitter, Input, Output } from '@angular/core';
27
+ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
28
28
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
29
- import { ValidateTaskGraphSpec, } from '@memberjunction/ai-core-plus';
30
- import { AddDependency, AddTask, GetDependents, NextTempId, RemoveDependency, RemoveTask, SpecToConnections, SpecToNodes, TASK_GRAPH_NODE_TYPES, UpdateTask, WouldCreateCycle, } from './task-graph-canvas-adapter';
29
+ import { ConfigOf, ValidateTaskGraphSpec, } from '@memberjunction/ai-core-plus';
30
+ import { FlowEditorComponent } from '@memberjunction/ng-flow-editor';
31
+ import { AddDependency, AddTask, GetDependents, GetNodeTypeConfig, IsAuthorableNodeType, NewTaskFromNodeType, NextTempId, RemoveDependency, RemoveTask, SpecToConnections, SpecToNodes, TASK_GRAPH_NODE_TYPES, UpdateTask, WouldCreateCycle, } from './task-graph-canvas-adapter';
31
32
  import { AfterDependencyAddedEventArgs, AfterDependencyRemovedEventArgs, AfterTaskAddedEventArgs, AfterTaskRemovedEventArgs, AfterTaskUpdatedEventArgs, AgentOpenRequestedEventArgs, BeforeDependencyAddedEventArgs, BeforeDependencyRemovedEventArgs, BeforeTaskAddedEventArgs, BeforeTaskRemovedEventArgs, BeforeTaskUpdatedEventArgs, RecordOpenRequestedEventArgs, TaskGraphSelectionChangedEventArgs, TaskGraphSpecChangedEventArgs, TaskGraphValidationChangedEventArgs, } from './task-graph-editor-events';
32
33
  import * as i0 from "@angular/core";
33
34
  import * as i1 from "@memberjunction/ng-flow-editor";
34
35
  import * as i2 from "@memberjunction/ng-ui-components";
36
+ import * as i3 from "./task-graph-properties-panel.component";
35
37
  const _forTrack0 = ($index, $item) => $item.Code + ($item.TempId ?? "");
36
38
  function TaskGraphEditorComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
37
39
  i0.ɵɵelement(0, "mj-empty-state", 1);
@@ -39,7 +41,13 @@ function TaskGraphEditorComponent_Conditional_1_Template(rf, ctx) { if (rf & 1)
39
41
  const ctx_r0 = i0.ɵɵnextContext();
40
42
  i0.ɵɵproperty("Message", ctx_r0.EmptyStateMessage);
41
43
  } }
42
- function TaskGraphEditorComponent_Conditional_2_Conditional_0_For_5_Template(rf, ctx) { if (rf & 1) {
44
+ function TaskGraphEditorComponent_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
45
+ i0.ɵɵelement(0, "mj-alert", 2);
46
+ } if (rf & 2) {
47
+ const ctx_r0 = i0.ɵɵnextContext(2);
48
+ i0.ɵɵproperty("Message", ctx_r0.EmptyStateMessage);
49
+ } }
50
+ function TaskGraphEditorComponent_Conditional_2_Conditional_1_For_5_Template(rf, ctx) { if (rf & 1) {
43
51
  i0.ɵɵelementStart(0, "li");
44
52
  i0.ɵɵtext(1);
45
53
  i0.ɵɵelementEnd();
@@ -48,12 +56,12 @@ function TaskGraphEditorComponent_Conditional_2_Conditional_0_For_5_Template(rf,
48
56
  i0.ɵɵadvance();
49
57
  i0.ɵɵtextInterpolate(err_r3.Message);
50
58
  } }
51
- function TaskGraphEditorComponent_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
52
- i0.ɵɵelementStart(0, "mj-alert", 2)(1, "strong");
59
+ function TaskGraphEditorComponent_Conditional_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
60
+ i0.ɵɵelementStart(0, "mj-alert", 3)(1, "strong");
53
61
  i0.ɵɵtext(2);
54
62
  i0.ɵɵelementEnd();
55
- i0.ɵɵelementStart(3, "ul", 4);
56
- i0.ɵɵrepeaterCreate(4, TaskGraphEditorComponent_Conditional_2_Conditional_0_For_5_Template, 2, 1, "li", null, _forTrack0);
63
+ i0.ɵɵelementStart(3, "ul", 7);
64
+ i0.ɵɵrepeaterCreate(4, TaskGraphEditorComponent_Conditional_2_Conditional_1_For_5_Template, 2, 1, "li", null, _forTrack0);
57
65
  i0.ɵɵelementEnd()();
58
66
  } if (rf & 2) {
59
67
  const ctx_r0 = i0.ɵɵnextContext(2);
@@ -62,17 +70,33 @@ function TaskGraphEditorComponent_Conditional_2_Conditional_0_Template(rf, ctx)
62
70
  i0.ɵɵadvance(2);
63
71
  i0.ɵɵrepeater(ctx_r0.ValidationErrors);
64
72
  } }
73
+ function TaskGraphEditorComponent_Conditional_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
74
+ const _r4 = i0.ɵɵgetCurrentView();
75
+ i0.ɵɵelementStart(0, "mj-task-graph-properties", 8);
76
+ i0.ɵɵlistener("TaskPropertyChangeRequested", function TaskGraphEditorComponent_Conditional_2_Conditional_4_Template_mj_task_graph_properties_TaskPropertyChangeRequested_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnTaskPropertyChangeRequested($event)); })("DependencyConditionChangeRequested", function TaskGraphEditorComponent_Conditional_2_Conditional_4_Template_mj_task_graph_properties_DependencyConditionChangeRequested_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OnDependencyConditionChangeRequested($event)); });
77
+ i0.ɵɵelementEnd();
78
+ } if (rf & 2) {
79
+ const ctx_r0 = i0.ɵɵnextContext(2);
80
+ i0.ɵɵproperty("Task", ctx_r0.SelectedTask)("Spec", ctx_r0.Spec)("ReadOnly", ctx_r0.ReadOnly)("AvailableAgentNames", ctx_r0.AvailableAgentNames)("AvailableActionNames", ctx_r0.AvailableActionNames);
81
+ } }
65
82
  function TaskGraphEditorComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
66
83
  const _r2 = i0.ɵɵgetCurrentView();
67
- i0.ɵɵconditionalCreate(0, TaskGraphEditorComponent_Conditional_2_Conditional_0_Template, 6, 2, "mj-alert", 2);
68
- i0.ɵɵelementStart(1, "mj-flow-editor", 3);
69
- i0.ɵɵlistener("NodeSelected", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_NodeSelected_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnNodeSelected($event)); })("NodeRemoved", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_NodeRemoved_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnNodeRemoved($event)); })("ConnectionCreated", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_ConnectionCreated_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnConnectionCreated($event)); })("ConnectionRemoved", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_ConnectionRemoved_1_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnConnectionRemoved($event)); });
84
+ i0.ɵɵconditionalCreate(0, TaskGraphEditorComponent_Conditional_2_Conditional_0_Template, 1, 1, "mj-alert", 2);
85
+ i0.ɵɵconditionalCreate(1, TaskGraphEditorComponent_Conditional_2_Conditional_1_Template, 6, 2, "mj-alert", 3);
86
+ i0.ɵɵelementStart(2, "div", 4)(3, "mj-flow-editor", 5);
87
+ i0.ɵɵlistener("NodeSelected", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_NodeSelected_3_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnNodeSelected($event)); })("NodeAdded", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_NodeAdded_3_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnNodeAdded($event)); })("NodeRemoved", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_NodeRemoved_3_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnNodeRemoved($event)); })("NodesChanged", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_NodesChanged_3_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnNodesChanged($event)); })("NodeMoved", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_NodeMoved_3_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnNodeMoved($event)); })("ConnectionCreated", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_ConnectionCreated_3_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnConnectionCreated($event)); })("ConnectionRemoved", function TaskGraphEditorComponent_Conditional_2_Template_mj_flow_editor_ConnectionRemoved_3_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnConnectionRemoved($event)); });
88
+ i0.ɵɵelementEnd();
89
+ i0.ɵɵconditionalCreate(4, TaskGraphEditorComponent_Conditional_2_Conditional_4_Template, 1, 5, "mj-task-graph-properties", 6);
70
90
  i0.ɵɵelementEnd();
71
91
  } if (rf & 2) {
72
92
  const ctx_r0 = i0.ɵɵnextContext();
73
- i0.ɵɵconditional(!ctx_r0.IsValid && ctx_r0.ValidationErrors.length > 0 ? 0 : -1);
93
+ i0.ɵɵconditional(ctx_r0.IsEmpty ? 0 : -1);
74
94
  i0.ɵɵadvance();
95
+ i0.ɵɵconditional(!ctx_r0.IsValid && ctx_r0.ValidationErrors.length > 0 ? 1 : -1);
96
+ i0.ɵɵadvance(2);
75
97
  i0.ɵɵproperty("Nodes", ctx_r0.Nodes)("Connections", ctx_r0.Connections)("NodeTypes", ctx_r0.NodeTypes)("ReadOnly", ctx_r0.ReadOnly)("ShowToolbar", ctx_r0.ShowToolbar)("ShowPalette", ctx_r0.ShowPalette && !ctx_r0.ReadOnly)("ShowMinimap", ctx_r0.ShowMinimap)("ShowStatusBar", ctx_r0.ShowStatusBar)("AutoLayoutDirection", ctx_r0.AutoLayoutDirection);
98
+ i0.ɵɵadvance();
99
+ i0.ɵɵconditional(ctx_r0.ShowProperties && !ctx_r0.ReadOnly ? 4 : -1);
76
100
  } }
77
101
  export class TaskGraphEditorComponent extends BaseAngularComponent {
78
102
  // ── Inputs ───────────────────────────────────────────────────────────────
@@ -100,6 +124,31 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
100
124
  get RuntimeStatus() {
101
125
  return this.currentRuntime;
102
126
  }
127
+ /**
128
+ * Geometry for the nodes, keyed by `tempId`. Supplying it places the graph instead of laying it
129
+ * out.
130
+ *
131
+ * **Why a host must be able to supply this.** The spec has no layout field, so without it every
132
+ * node projects to the origin — all of them, stacked in one place — and the canvas is left to
133
+ * rescue the situation with a deferred Dagre pass. That pass is fine in the editor, where the
134
+ * author is present and can rearrange. It was not fine in the RUN views, which held the real
135
+ * geometry (a workflow's authored positions, or a computed layout) and had no way to hand it
136
+ * over: every run rendered its steps piled on the origin, and the zoom-to-fit that followed
137
+ * fitted a bounding box one node wide and blew the viewport up past 250%.
138
+ *
139
+ * Applied as the starting geometry only. Once the canvas reports positions of its own — a drag,
140
+ * an arrange — those win, because the person moving a node is the authority on where it goes.
141
+ */
142
+ set NodePositions(value) {
143
+ if (!value || value.size === 0)
144
+ return;
145
+ for (const [id, position] of value)
146
+ this.knownPositions.set(id, { ...position });
147
+ // Real geometry means the one-time Dagre pass has nothing to rescue.
148
+ this.hasLaidOut = true;
149
+ this.project();
150
+ this.zoomToFitSoon();
151
+ }
103
152
  /** Read-only mode. The same component is the viewer — there is no second, weaker renderer. */
104
153
  ReadOnly = false;
105
154
  ShowToolbar = true;
@@ -107,6 +156,22 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
107
156
  ShowMinimap = true;
108
157
  ShowStatusBar = true;
109
158
  AutoLayoutDirection = 'vertical';
159
+ /**
160
+ * Whether the properties panel rides alongside the canvas.
161
+ *
162
+ * On by default, because without it a step added from the palette can never be named or
163
+ * assigned — the canvas draws structure, the panel supplies content, and one without the other
164
+ * is a graph the author can build but not finish. Hosts embedding the read-only viewer in a chat
165
+ * card turn it off.
166
+ */
167
+ ShowProperties = true;
168
+ /**
169
+ * Agent names offered when assigning a step. Supplied by the host, which owns data access —
170
+ * this is a widgets-layer component and does not query.
171
+ */
172
+ AvailableAgentNames = [];
173
+ /** Action names offered when assigning a step. Same ownership rule as `AvailableAgentNames`. */
174
+ AvailableActionNames = [];
110
175
  /** Shown when there is nothing to draw yet. */
111
176
  EmptyStateMessage = 'No steps yet. Add one to start building this workflow.';
112
177
  // ── Outputs ──────────────────────────────────────────────────────────────
@@ -130,10 +195,11 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
130
195
  // ── Rendered state ───────────────────────────────────────────────────────
131
196
  Nodes = [];
132
197
  Connections = [];
133
- NodeTypes = TASK_GRAPH_NODE_TYPES;
198
+ NodeTypes = [...TASK_GRAPH_NODE_TYPES];
134
199
  SelectedTask = null;
135
200
  ValidationErrors = [];
136
201
  IsValid = true;
202
+ canvas;
137
203
  currentSpec = null;
138
204
  currentRuntime = null;
139
205
  get IsEmpty() {
@@ -160,13 +226,18 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
160
226
  AddTask(partial = {}) {
161
227
  if (this.ReadOnly || !this.currentSpec)
162
228
  return null;
229
+ // Kind and configuration travel together — a partial that supplies one without the other
230
+ // would be a node the engine cannot run, so an unspecified partial defaults to an unassigned
231
+ // Agent step and the validator says so immediately.
163
232
  const task = {
164
233
  tempId: partial.tempId ?? NextTempId(this.currentSpec),
165
234
  name: partial.name ?? 'New step',
166
235
  description: partial.description ?? '',
167
- agentName: partial.agentName,
168
- assignToUser: partial.assignToUser,
236
+ kind: partial.kind ?? 'Agent',
237
+ configuration: partial.configuration ?? { agentName: '' },
169
238
  dependsOn: partial.dependsOn ?? [],
239
+ policy: partial.policy,
240
+ layout: partial.layout,
170
241
  inputPayload: partial.inputPayload,
171
242
  };
172
243
  const before = new BeforeTaskAddedEventArgs(task);
@@ -261,8 +332,9 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
261
332
  }
262
333
  /** Asks the host to open the agent behind a task. */
263
334
  RequestAgentOpen(task) {
264
- if (task.agentName) {
265
- this.AgentOpenRequested.emit(new AgentOpenRequestedEventArgs(task.agentName, task));
335
+ const agentName = ConfigOf(task, 'Agent')?.agentName;
336
+ if (agentName) {
337
+ this.AgentOpenRequested.emit(new AgentOpenRequestedEventArgs(agentName, task));
266
338
  }
267
339
  }
268
340
  /** Asks the host to open a record the graph references. */
@@ -273,6 +345,51 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
273
345
  OnNodeSelected(node) {
274
346
  this.selectTask(node ? this.findTask(node.ID) : null);
275
347
  }
348
+ /**
349
+ * A palette entry was clicked or dragged onto the canvas.
350
+ *
351
+ * **This binding is the bug.** The canvas has always emitted `NodeAdded` for a palette drop, and
352
+ * this component simply never listened — so the node the canvas announced was thrown away, the
353
+ * spec never gained a task, and the author was told "a task graph must contain at least one
354
+ * task" no matter how many times they tried to add one. The canvas does not mutate its own
355
+ * `Nodes` on purpose (the host owns the model); an unheard event is therefore a silent no-op
356
+ * rather than a visible failure, which is why it survived.
357
+ *
358
+ * The new step is selected immediately: it lands unnamed and, for an agent or action step with
359
+ * nothing available to default to, unassigned — so the properties panel is where the author has
360
+ * to go next, and putting them there beats making them find it.
361
+ */
362
+ OnNodeAdded(event) {
363
+ if (this.ReadOnly || !this.currentSpec)
364
+ return;
365
+ const type = GetNodeTypeConfig(event.Node.Type)?.Type;
366
+ // Only an authorable shape can be dropped from the palette. The render set is wider, and a
367
+ // display-only kind arriving here would mean the palette offered something with no editor.
368
+ if (!type || !IsAuthorableNodeType(type))
369
+ return;
370
+ const added = this.AddTask(NewTaskFromNodeType(this.currentSpec, type, {
371
+ agentName: this.AvailableAgentNames[0],
372
+ actionName: this.AvailableActionNames[0],
373
+ }));
374
+ if (!added)
375
+ return;
376
+ // Remember where the canvas put it BEFORE anything re-projects. The spec has no geometry
377
+ // field, so this map is the only record that the author dropped (or clicked) it here — and
378
+ // without it the node would snap back to the origin on the very next edit.
379
+ this.knownPositions.set(added.tempId, { ...event.Node.Position });
380
+ // A graph that has received a hand-placed node is laid out, by definition. Marking it here
381
+ // stops the one-time Dagre pass from firing later and discarding that placement.
382
+ this.hasLaidOut = true;
383
+ this.selectTask(added);
384
+ }
385
+ /** Applies a properties-panel edit through the same vetoable path a canvas edit takes. */
386
+ OnTaskPropertyChangeRequested(args) {
387
+ this.UpdateTask(args.TempId, args.Next);
388
+ }
389
+ /** Applies a properties-panel edge-condition edit. */
390
+ OnDependencyConditionChangeRequested(args) {
391
+ this.SetDependencyCondition(args.FromTempId, args.ToTempId, args.Condition);
392
+ }
276
393
  OnConnectionCreated(event) {
277
394
  this.AddDependency(event.SourceNodeID, event.TargetNodeID);
278
395
  }
@@ -303,29 +420,125 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
303
420
  this.Connections = [];
304
421
  this.ValidationErrors = [];
305
422
  this.IsValid = true;
423
+ this.hasLaidOut = false;
424
+ this.knownPositions.clear();
306
425
  return;
307
426
  }
308
- this.Nodes = SpecToNodes(this.currentSpec, this.currentRuntime ?? undefined);
427
+ this.Nodes = SpecToNodes(this.currentSpec, this.currentRuntime ?? undefined, this.knownPositions);
309
428
  this.Connections = SpecToConnections(this.currentSpec);
310
429
  this.Validate();
430
+ this.arrangeIfNeverLaidOut();
431
+ }
432
+ /**
433
+ * Lays the graph out ONCE — when it arrives with no geometry of its own.
434
+ *
435
+ * A `TaskGraphSpec` carries no positions, so a spec opened for the first time projects with
436
+ * every node at the origin and needs Dagre to make it readable. After that the author's layout
437
+ * is the layout: `knownPositions` carries it across re-projections, and re-arranging again would
438
+ * throw away the arrangement they just made.
439
+ *
440
+ * It must also not run on every edit, because `AutoArrange` ends in `ZoomToFit` — so arranging
441
+ * per change meant the viewport snapped to fit after every added step and every drawn
442
+ * connection, which on a one-node graph zooms to maximum. That is the behaviour being fixed
443
+ * here; the rule mirrors the Flow Agent editor's (`flow-agent-editor.component.ts`), which has
444
+ * always arranged only when every node sits at the origin.
445
+ */
446
+ arrangeIfNeverLaidOut() {
447
+ if (this.Nodes.length === 0)
448
+ return;
449
+ if (this.hasLaidOut)
450
+ return;
451
+ // Nothing to rescue a layout from: a spec whose nodes all sit at the origin has never been
452
+ // arranged. One node at the origin is the legitimate starting case too.
453
+ const allAtOrigin = this.Nodes.every((n) => n.Position.X === 0 && n.Position.Y === 0);
454
+ if (!allAtOrigin) {
455
+ this.hasLaidOut = true;
456
+ return;
457
+ }
458
+ this.hasLaidOut = true;
459
+ // Deferred one turn: the canvas has to render the nodes before Dagre can measure them.
460
+ // Cleared on destroy so a pending layout cannot run against a torn-down view.
461
+ if (this.pendingLayout !== null)
462
+ clearTimeout(this.pendingLayout);
463
+ this.pendingLayout = setTimeout(() => {
464
+ this.pendingLayout = null;
465
+ this.canvas?.AutoArrange(this.AutoLayoutDirection);
466
+ });
467
+ }
468
+ /**
469
+ * Fits the viewport to the graph, once the canvas has drawn it.
470
+ *
471
+ * Deferred for the same reason the layout pass is: `fitToScreen` measures the rendered nodes, so
472
+ * calling it in the same turn as the projection fits whatever was on screen a moment ago. With
473
+ * every node still at the origin that bounding box is a single node wide, and "fit" means zoom
474
+ * to ~265% — the symptom that made a four-step workflow look like one enormous box.
475
+ */
476
+ zoomToFitSoon() {
477
+ if (this.pendingLayout !== null)
478
+ clearTimeout(this.pendingLayout);
479
+ this.pendingLayout = setTimeout(() => {
480
+ this.pendingLayout = null;
481
+ this.canvas?.ZoomToFit();
482
+ });
483
+ }
484
+ /**
485
+ * The canvas is the authority on geometry, so remember what it reports.
486
+ *
487
+ * Without this the spec — which has no geometry field — is the only survivor of a re-projection,
488
+ * and every edit silently moved every node back to the origin. That is what forced a re-arrange
489
+ * (and therefore a re-zoom) on each change.
490
+ */
491
+ OnNodesChanged(nodes) {
492
+ for (const n of nodes)
493
+ this.knownPositions.set(n.ID, { ...n.Position });
494
+ }
495
+ /** A single node was dragged. Same authority, narrower event. */
496
+ OnNodeMoved(event) {
497
+ this.knownPositions.set(event.NodeID, { ...event.NewPosition });
498
+ }
499
+ ngOnDestroy() {
500
+ if (this.pendingLayout !== null) {
501
+ clearTimeout(this.pendingLayout);
502
+ this.pendingLayout = null;
503
+ }
311
504
  }
505
+ /** The topology the current layout was computed for; '' when nothing has been laid out. */
506
+ /**
507
+ * Node geometry, which the spec cannot hold.
508
+ *
509
+ * `TaskGraphSpec` is an execution contract with no layout field, so a re-projection would
510
+ * otherwise return every node to the origin. Keyed by `tempId`; written from the canvas
511
+ * (`NodesChanged` / `NodeMoved`) and from the drop position of a newly added node, and read back
512
+ * by `SpecToNodes` on every projection.
513
+ */
514
+ knownPositions = new Map();
515
+ /** Whether the one-time Dagre pass has run (or been made unnecessary by a hand-placed node). */
516
+ hasLaidOut = false;
517
+ pendingLayout = null;
312
518
  static ɵfac = /*@__PURE__*/ (() => { let ɵTaskGraphEditorComponent_BaseFactory; return function TaskGraphEditorComponent_Factory(__ngFactoryType__) { return (ɵTaskGraphEditorComponent_BaseFactory || (ɵTaskGraphEditorComponent_BaseFactory = i0.ɵɵgetInheritedFactory(TaskGraphEditorComponent)))(__ngFactoryType__ || TaskGraphEditorComponent); }; })();
313
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TaskGraphEditorComponent, selectors: [["mj-task-graph-editor"]], inputs: { Spec: "Spec", RuntimeStatus: "RuntimeStatus", ReadOnly: "ReadOnly", ShowToolbar: "ShowToolbar", ShowPalette: "ShowPalette", ShowMinimap: "ShowMinimap", ShowStatusBar: "ShowStatusBar", AutoLayoutDirection: "AutoLayoutDirection", EmptyStateMessage: "EmptyStateMessage" }, outputs: { BeforeTaskAdded: "BeforeTaskAdded", AfterTaskAdded: "AfterTaskAdded", BeforeTaskRemoved: "BeforeTaskRemoved", AfterTaskRemoved: "AfterTaskRemoved", BeforeTaskUpdated: "BeforeTaskUpdated", AfterTaskUpdated: "AfterTaskUpdated", BeforeDependencyAdded: "BeforeDependencyAdded", AfterDependencyAdded: "AfterDependencyAdded", BeforeDependencyRemoved: "BeforeDependencyRemoved", AfterDependencyRemoved: "AfterDependencyRemoved", SpecChanged: "SpecChanged", SelectionChanged: "SelectionChanged", ValidationChanged: "ValidationChanged", AgentOpenRequested: "AgentOpenRequested", RecordOpenRequested: "RecordOpenRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 1, consts: [[1, "mj-tge"], ["Icon", "fa-diagram-project", "Title", "Nothing here yet", 3, "Message"], ["Variant", "warning", 1, "mj-tge__validation"], [1, "mj-tge__canvas", 3, "NodeSelected", "NodeRemoved", "ConnectionCreated", "ConnectionRemoved", "Nodes", "Connections", "NodeTypes", "ReadOnly", "ShowToolbar", "ShowPalette", "ShowMinimap", "ShowStatusBar", "AutoLayoutDirection"], [1, "mj-tge__validation-list"]], template: function TaskGraphEditorComponent_Template(rf, ctx) { if (rf & 1) {
519
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TaskGraphEditorComponent, selectors: [["mj-task-graph-editor"]], viewQuery: function TaskGraphEditorComponent_Query(rf, ctx) { if (rf & 1) {
520
+ i0.ɵɵviewQuery(FlowEditorComponent, 5);
521
+ } if (rf & 2) {
522
+ let _t;
523
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.canvas = _t.first);
524
+ } }, inputs: { Spec: "Spec", RuntimeStatus: "RuntimeStatus", NodePositions: "NodePositions", ReadOnly: "ReadOnly", ShowToolbar: "ShowToolbar", ShowPalette: "ShowPalette", ShowMinimap: "ShowMinimap", ShowStatusBar: "ShowStatusBar", AutoLayoutDirection: "AutoLayoutDirection", ShowProperties: "ShowProperties", AvailableAgentNames: "AvailableAgentNames", AvailableActionNames: "AvailableActionNames", EmptyStateMessage: "EmptyStateMessage" }, outputs: { BeforeTaskAdded: "BeforeTaskAdded", AfterTaskAdded: "AfterTaskAdded", BeforeTaskRemoved: "BeforeTaskRemoved", AfterTaskRemoved: "AfterTaskRemoved", BeforeTaskUpdated: "BeforeTaskUpdated", AfterTaskUpdated: "AfterTaskUpdated", BeforeDependencyAdded: "BeforeDependencyAdded", AfterDependencyAdded: "AfterDependencyAdded", BeforeDependencyRemoved: "BeforeDependencyRemoved", AfterDependencyRemoved: "AfterDependencyRemoved", SpecChanged: "SpecChanged", SelectionChanged: "SelectionChanged", ValidationChanged: "ValidationChanged", AgentOpenRequested: "AgentOpenRequested", RecordOpenRequested: "RecordOpenRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 1, consts: [[1, "mj-tge"], ["Icon", "fa-diagram-project", "Title", "Nothing here yet", 3, "Message"], ["Variant", "info", 1, "mj-tge__empty-hint", 3, "Message"], ["Variant", "warning", 1, "mj-tge__validation"], [1, "mj-tge__workspace"], [1, "mj-tge__canvas", 3, "NodeSelected", "NodeAdded", "NodeRemoved", "NodesChanged", "NodeMoved", "ConnectionCreated", "ConnectionRemoved", "Nodes", "Connections", "NodeTypes", "ReadOnly", "ShowToolbar", "ShowPalette", "ShowMinimap", "ShowStatusBar", "AutoLayoutDirection"], [1, "mj-tge__properties", 3, "Task", "Spec", "ReadOnly", "AvailableAgentNames", "AvailableActionNames"], [1, "mj-tge__validation-list"], [1, "mj-tge__properties", 3, "TaskPropertyChangeRequested", "DependencyConditionChangeRequested", "Task", "Spec", "ReadOnly", "AvailableAgentNames", "AvailableActionNames"]], template: function TaskGraphEditorComponent_Template(rf, ctx) { if (rf & 1) {
314
525
  i0.ɵɵelementStart(0, "div", 0);
315
- i0.ɵɵconditionalCreate(1, TaskGraphEditorComponent_Conditional_1_Template, 1, 1, "mj-empty-state", 1)(2, TaskGraphEditorComponent_Conditional_2_Template, 2, 10);
526
+ i0.ɵɵconditionalCreate(1, TaskGraphEditorComponent_Conditional_1_Template, 1, 1, "mj-empty-state", 1)(2, TaskGraphEditorComponent_Conditional_2_Template, 5, 12);
316
527
  i0.ɵɵelementEnd();
317
528
  } if (rf & 2) {
318
529
  i0.ɵɵadvance();
319
- i0.ɵɵconditional(ctx.IsEmpty ? 1 : 2);
320
- } }, dependencies: [i1.FlowEditorComponent, i2.MJEmptyStateComponent, i2.MJAlertComponent], styles: ["\n\n.mj-tge[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-page);\n}\n\n.mj-tge__validation[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n margin: var(--mj-space-sm, 0.5rem);\n}\n\n.mj-tge__validation-list[_ngcontent-%COMP%] {\n margin: var(--mj-space-xs, 0.25rem) 0 0;\n padding-inline-start: 1.25rem;\n}\n\n\n\n.mj-tge__canvas[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-height: 0;\n}"] });
530
+ i0.ɵɵconditional(ctx.IsEmpty && ctx.ReadOnly ? 1 : 2);
531
+ } }, dependencies: [i1.FlowEditorComponent, i2.MJEmptyStateComponent, i2.MJAlertComponent, i3.TaskGraphPropertiesPanelComponent], styles: ["\n\n.mj-tge[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-page);\n}\n\n.mj-tge__validation[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n margin: var(--mj-space-sm, 0.5rem);\n}\n\n.mj-tge__validation-list[_ngcontent-%COMP%] {\n margin: var(--mj-space-xs, 0.25rem) 0 0;\n padding-inline-start: 1.25rem;\n}\n\n\n\n.mj-tge__workspace[_ngcontent-%COMP%] {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n\n\n.mj-tge__canvas[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n}\n\n.mj-tge__properties[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n width: 300px;\n min-height: 0;\n overflow-y: auto;\n border-left: 1px solid var(--mj-border-default);\n}\n\n@media (max-width: 900px) {\n \n\n .mj-tge__workspace[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .mj-tge__properties[_ngcontent-%COMP%] {\n width: 100%;\n max-height: 40%;\n border-left: 0;\n border-top: 1px solid var(--mj-border-default);\n }\n}\n\n\n\n.mj-tge__empty-hint[_ngcontent-%COMP%] {\n margin: var(--mj-space-2) var(--mj-space-3) 0;\n}"] });
321
532
  }
322
533
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TaskGraphEditorComponent, [{
323
534
  type: Component,
324
- args: [{ standalone: false, selector: 'mj-task-graph-editor', template: "<div class=\"mj-tge\">\n @if (IsEmpty) {\n <mj-empty-state\n Icon=\"fa-diagram-project\"\n Title=\"Nothing here yet\"\n [Message]=\"EmptyStateMessage\">\n </mj-empty-state>\n } @else {\n @if (!IsValid && ValidationErrors.length > 0) {\n <!-- Author-time validation comes from the SAME function the engine runs at submission, so a\n graph that looks fine here cannot be rejected later for a reason the canvas never showed. -->\n <mj-alert Variant=\"warning\" class=\"mj-tge__validation\">\n <strong>{{ ValidationErrors.length }} problem{{ ValidationErrors.length === 1 ? '' : 's' }} to fix</strong>\n <ul class=\"mj-tge__validation-list\">\n @for (err of ValidationErrors; track err.Code + (err.TempId ?? '')) {\n <li>{{ err.Message }}</li>\n }\n </ul>\n </mj-alert>\n }\n\n <mj-flow-editor\n class=\"mj-tge__canvas\"\n [Nodes]=\"Nodes\"\n [Connections]=\"Connections\"\n [NodeTypes]=\"NodeTypes\"\n [ReadOnly]=\"ReadOnly\"\n [ShowToolbar]=\"ShowToolbar\"\n [ShowPalette]=\"ShowPalette && !ReadOnly\"\n [ShowMinimap]=\"ShowMinimap\"\n [ShowStatusBar]=\"ShowStatusBar\"\n [AutoLayoutDirection]=\"AutoLayoutDirection\"\n (NodeSelected)=\"OnNodeSelected($event)\"\n (NodeRemoved)=\"OnNodeRemoved($event)\"\n (ConnectionCreated)=\"OnConnectionCreated($event)\"\n (ConnectionRemoved)=\"OnConnectionRemoved($event)\">\n </mj-flow-editor>\n }\n</div>\n", styles: ["/* Design tokens only \u2014 no hardcoded colors (see .claude/rules/design-tokens.md). */\n.mj-tge {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-page);\n}\n\n.mj-tge__validation {\n flex: 0 0 auto;\n margin: var(--mj-space-sm, 0.5rem);\n}\n\n.mj-tge__validation-list {\n margin: var(--mj-space-xs, 0.25rem) 0 0;\n padding-inline-start: 1.25rem;\n}\n\n/* The canvas takes the remaining height; min-height:0 is what lets it actually shrink inside flex. */\n.mj-tge__canvas {\n flex: 1 1 auto;\n min-height: 0;\n}\n"] }]
535
+ args: [{ standalone: false, selector: 'mj-task-graph-editor', template: "<div class=\"mj-tge\">\n <!-- An EDITABLE empty canvas must still render the editor, because the palette is the only way to\n add a step \u2014 showing a bare empty state instead made \"Add one to start building this workflow\"\n an instruction the user could not follow. The hint stays, as a banner above a usable canvas. -->\n @if (IsEmpty && ReadOnly) {\n <mj-empty-state\n Icon=\"fa-diagram-project\"\n Title=\"Nothing here yet\"\n [Message]=\"EmptyStateMessage\">\n </mj-empty-state>\n } @else {\n @if (IsEmpty) {\n <mj-alert Variant=\"info\" class=\"mj-tge__empty-hint\" [Message]=\"EmptyStateMessage\"></mj-alert>\n }\n @if (!IsValid && ValidationErrors.length > 0) {\n <!-- Author-time validation comes from the SAME function the engine runs at submission, so a\n graph that looks fine here cannot be rejected later for a reason the canvas never showed. -->\n <mj-alert Variant=\"warning\" class=\"mj-tge__validation\">\n <strong>{{ ValidationErrors.length }} problem{{ ValidationErrors.length === 1 ? '' : 's' }} to fix</strong>\n <ul class=\"mj-tge__validation-list\">\n @for (err of ValidationErrors; track err.Code + (err.TempId ?? '')) {\n <li>{{ err.Message }}</li>\n }\n </ul>\n </mj-alert>\n }\n\n <div class=\"mj-tge__workspace\">\n <mj-flow-editor\n class=\"mj-tge__canvas\"\n [Nodes]=\"Nodes\"\n [Connections]=\"Connections\"\n [NodeTypes]=\"NodeTypes\"\n [ReadOnly]=\"ReadOnly\"\n [ShowToolbar]=\"ShowToolbar\"\n [ShowPalette]=\"ShowPalette && !ReadOnly\"\n [ShowMinimap]=\"ShowMinimap\"\n [ShowStatusBar]=\"ShowStatusBar\"\n [AutoLayoutDirection]=\"AutoLayoutDirection\"\n (NodeSelected)=\"OnNodeSelected($event)\"\n (NodeAdded)=\"OnNodeAdded($event)\"\n (NodeRemoved)=\"OnNodeRemoved($event)\"\n (NodesChanged)=\"OnNodesChanged($event)\"\n (NodeMoved)=\"OnNodeMoved($event)\"\n (ConnectionCreated)=\"OnConnectionCreated($event)\"\n (ConnectionRemoved)=\"OnConnectionRemoved($event)\">\n </mj-flow-editor>\n\n <!-- The canvas draws structure; this supplies content. A step added from the palette arrives\n unnamed and (with nothing to default to) unassigned, so without this panel the author can\n create a step but never finish one. -->\n @if (ShowProperties && !ReadOnly) {\n <mj-task-graph-properties\n class=\"mj-tge__properties\"\n [Task]=\"SelectedTask\"\n [Spec]=\"Spec\"\n [ReadOnly]=\"ReadOnly\"\n [AvailableAgentNames]=\"AvailableAgentNames\"\n [AvailableActionNames]=\"AvailableActionNames\"\n (TaskPropertyChangeRequested)=\"OnTaskPropertyChangeRequested($event)\"\n (DependencyConditionChangeRequested)=\"OnDependencyConditionChangeRequested($event)\">\n </mj-task-graph-properties>\n }\n </div>\n }\n</div>\n", styles: ["/* Design tokens only \u2014 no hardcoded colors (see .claude/rules/design-tokens.md). */\n.mj-tge {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n background: var(--mj-bg-page);\n}\n\n.mj-tge__validation {\n flex: 0 0 auto;\n margin: var(--mj-space-sm, 0.5rem);\n}\n\n.mj-tge__validation-list {\n margin: var(--mj-space-xs, 0.25rem) 0 0;\n padding-inline-start: 1.25rem;\n}\n\n/* Canvas + properties side by side; the row takes the remaining height. */\n.mj-tge__workspace {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/* The canvas takes the remaining width; min-width:0 is what lets it actually shrink inside flex. */\n.mj-tge__canvas {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n}\n\n.mj-tge__properties {\n flex: 0 0 auto;\n width: 300px;\n min-height: 0;\n overflow-y: auto;\n border-left: 1px solid var(--mj-border-default);\n}\n\n@media (max-width: 900px) {\n /* Below this the two side by side leave neither usable; the panel drops under the canvas. */\n .mj-tge__workspace {\n flex-direction: column;\n }\n\n .mj-tge__properties {\n width: 100%;\n max-height: 40%;\n border-left: 0;\n border-top: 1px solid var(--mj-border-default);\n }\n}\n\n/* Shown above a usable, empty canvas \u2014 the palette stays reachable so the hint's advice is followable. */\n.mj-tge__empty-hint {\n margin: var(--mj-space-2) var(--mj-space-3) 0;\n}\n"] }]
325
536
  }], null, { Spec: [{
326
537
  type: Input
327
538
  }], RuntimeStatus: [{
328
539
  type: Input
540
+ }], NodePositions: [{
541
+ type: Input
329
542
  }], ReadOnly: [{
330
543
  type: Input
331
544
  }], ShowToolbar: [{
@@ -338,6 +551,12 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
338
551
  type: Input
339
552
  }], AutoLayoutDirection: [{
340
553
  type: Input
554
+ }], ShowProperties: [{
555
+ type: Input
556
+ }], AvailableAgentNames: [{
557
+ type: Input
558
+ }], AvailableActionNames: [{
559
+ type: Input
341
560
  }], EmptyStateMessage: [{
342
561
  type: Input
343
562
  }], BeforeTaskAdded: [{
@@ -370,6 +589,9 @@ export class TaskGraphEditorComponent extends BaseAngularComponent {
370
589
  type: Output
371
590
  }], RecordOpenRequested: [{
372
591
  type: Output
592
+ }], canvas: [{
593
+ type: ViewChild,
594
+ args: [FlowEditorComponent]
373
595
  }] }); })();
374
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TaskGraphEditorComponent, { className: "TaskGraphEditorComponent", filePath: "src/lib/task-graph-editor.component.ts", lineNumber: 80 }); })();
596
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TaskGraphEditorComponent, { className: "TaskGraphEditorComponent", filePath: "src/lib/task-graph-editor.component.ts", lineNumber: 92 }); })();
375
597
  //# sourceMappingURL=task-graph-editor.component.js.map