@memberjunction/ng-task-graph-editor 0.0.0 → 6.1.0-edge.1
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.
- package/LICENSE +7 -0
- package/dist/lib/task-graph-canvas-adapter.d.ts +98 -0
- package/dist/lib/task-graph-canvas-adapter.d.ts.map +1 -0
- package/dist/lib/task-graph-canvas-adapter.js +244 -0
- package/dist/lib/task-graph-canvas-adapter.js.map +1 -0
- package/dist/lib/task-graph-editor-events.d.ts +137 -0
- package/dist/lib/task-graph-editor-events.d.ts.map +1 -0
- package/dist/lib/task-graph-editor-events.js +180 -0
- package/dist/lib/task-graph-editor-events.js.map +1 -0
- package/dist/lib/task-graph-editor.component.d.ts +128 -0
- package/dist/lib/task-graph-editor.component.d.ts.map +1 -0
- package/dist/lib/task-graph-editor.component.js +375 -0
- package/dist/lib/task-graph-editor.component.js.map +1 -0
- package/dist/lib/task-graph-editor.module.d.ts +13 -0
- package/dist/lib/task-graph-editor.module.d.ts.map +1 -0
- package/dist/lib/task-graph-editor.module.js +23 -0
- package/dist/lib/task-graph-editor.module.js.map +1 -0
- package/dist/lib/task-graph-properties-panel.component.d.ts +71 -0
- package/dist/lib/task-graph-properties-panel.component.d.ts.map +1 -0
- package/dist/lib/task-graph-properties-panel.component.js +238 -0
- package/dist/lib/task-graph-properties-panel.component.js.map +1 -0
- package/dist/lib/task-graph-runtime-source.d.ts +55 -0
- package/dist/lib/task-graph-runtime-source.d.ts.map +1 -0
- package/dist/lib/task-graph-runtime-source.js +66 -0
- package/dist/lib/task-graph-runtime-source.js.map +1 -0
- package/dist/public-api.d.ts +7 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +15 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +52 -7
- package/README.md +0 -45
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview View and edit a `TaskGraphSpec` on a canvas.
|
|
3
|
+
*
|
|
4
|
+
* **What makes this reusable is its subject.** It edits `TaskGraphSpec` — the one fully-qualified
|
|
5
|
+
* graph contract every producer in the program already authors against (D16) — rather than any
|
|
6
|
+
* particular persistence. A design-time flow, a graph an agent emitted at runtime, a stored workflow
|
|
7
|
+
* definition and a graph a person is drawing from scratch are all the same type here, so one
|
|
8
|
+
* component serves the Flow Agent editor, the Agent Run admin view, conversation plan cards, the
|
|
9
|
+
* Tasks view, and anything downstream.
|
|
10
|
+
*
|
|
11
|
+
* The alternative — teaching the existing agent editor a second dialect — is precisely how the two
|
|
12
|
+
* graph models drifted apart before Phase 4 pulled them onto one traversal engine.
|
|
13
|
+
*
|
|
14
|
+
* **Layer: `widgets` (L1/L2).** No `@angular/router`, no `@memberjunction/ng-shared`, no
|
|
15
|
+
* `NavigationService`. Route-derived state arrives as `@Input()`; navigation *intent* leaves as an
|
|
16
|
+
* `@Output()` the host acts on, because a widget cannot know whether it is inside Explorer, a
|
|
17
|
+
* downstream app, or an embedded panel. See `guides/UI_LAYERING_GUIDE.md`.
|
|
18
|
+
*
|
|
19
|
+
* **Validation is the engine's.** Cycles, unknown dependency refs and assignment conflicts are
|
|
20
|
+
* reported by calling `ValidateTaskGraphSpec` from `@memberjunction/ai-core-plus` — the same
|
|
21
|
+
* function `LoopAgentType` and `TaskGraphService.Submit` call. A second implementation here would be
|
|
22
|
+
* a second definition of "valid", and the graph that passes on the canvas would be free to fail at
|
|
23
|
+
* submission.
|
|
24
|
+
*
|
|
25
|
+
* @module @memberjunction/ng-task-graph-editor
|
|
26
|
+
*/
|
|
27
|
+
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
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';
|
|
31
|
+
import { AfterDependencyAddedEventArgs, AfterDependencyRemovedEventArgs, AfterTaskAddedEventArgs, AfterTaskRemovedEventArgs, AfterTaskUpdatedEventArgs, AgentOpenRequestedEventArgs, BeforeDependencyAddedEventArgs, BeforeDependencyRemovedEventArgs, BeforeTaskAddedEventArgs, BeforeTaskRemovedEventArgs, BeforeTaskUpdatedEventArgs, RecordOpenRequestedEventArgs, TaskGraphSelectionChangedEventArgs, TaskGraphSpecChangedEventArgs, TaskGraphValidationChangedEventArgs, } from './task-graph-editor-events';
|
|
32
|
+
import * as i0 from "@angular/core";
|
|
33
|
+
import * as i1 from "@memberjunction/ng-flow-editor";
|
|
34
|
+
import * as i2 from "@memberjunction/ng-ui-components";
|
|
35
|
+
const _forTrack0 = ($index, $item) => $item.Code + ($item.TempId ?? "");
|
|
36
|
+
function TaskGraphEditorComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
37
|
+
i0.ɵɵelement(0, "mj-empty-state", 1);
|
|
38
|
+
} if (rf & 2) {
|
|
39
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
40
|
+
i0.ɵɵproperty("Message", ctx_r0.EmptyStateMessage);
|
|
41
|
+
} }
|
|
42
|
+
function TaskGraphEditorComponent_Conditional_2_Conditional_0_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
43
|
+
i0.ɵɵelementStart(0, "li");
|
|
44
|
+
i0.ɵɵtext(1);
|
|
45
|
+
i0.ɵɵelementEnd();
|
|
46
|
+
} if (rf & 2) {
|
|
47
|
+
const err_r3 = ctx.$implicit;
|
|
48
|
+
i0.ɵɵadvance();
|
|
49
|
+
i0.ɵɵtextInterpolate(err_r3.Message);
|
|
50
|
+
} }
|
|
51
|
+
function TaskGraphEditorComponent_Conditional_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
52
|
+
i0.ɵɵelementStart(0, "mj-alert", 2)(1, "strong");
|
|
53
|
+
i0.ɵɵtext(2);
|
|
54
|
+
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);
|
|
57
|
+
i0.ɵɵelementEnd()();
|
|
58
|
+
} if (rf & 2) {
|
|
59
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
60
|
+
i0.ɵɵadvance(2);
|
|
61
|
+
i0.ɵɵtextInterpolate2("", ctx_r0.ValidationErrors.length, " problem", ctx_r0.ValidationErrors.length === 1 ? "" : "s", " to fix");
|
|
62
|
+
i0.ɵɵadvance(2);
|
|
63
|
+
i0.ɵɵrepeater(ctx_r0.ValidationErrors);
|
|
64
|
+
} }
|
|
65
|
+
function TaskGraphEditorComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
66
|
+
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)); });
|
|
70
|
+
i0.ɵɵelementEnd();
|
|
71
|
+
} if (rf & 2) {
|
|
72
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
73
|
+
i0.ɵɵconditional(!ctx_r0.IsValid && ctx_r0.ValidationErrors.length > 0 ? 0 : -1);
|
|
74
|
+
i0.ɵɵadvance();
|
|
75
|
+
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);
|
|
76
|
+
} }
|
|
77
|
+
export class TaskGraphEditorComponent extends BaseAngularComponent {
|
|
78
|
+
// ── Inputs ───────────────────────────────────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* The graph to show. Setter-based rather than `ngOnChanges` (repo convention): the reaction is
|
|
81
|
+
* explicit, runs only when this property changes, and costs nothing on other change-detection
|
|
82
|
+
* passes.
|
|
83
|
+
*/
|
|
84
|
+
set Spec(value) {
|
|
85
|
+
this.currentSpec = value;
|
|
86
|
+
this.project();
|
|
87
|
+
}
|
|
88
|
+
get Spec() {
|
|
89
|
+
return this.currentSpec;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Live per-task state, keyed by `tempId`. Supplying it turns the canvas into a runtime view of
|
|
93
|
+
* the same graph — the convergence point the program was aiming at: one renderer for design time
|
|
94
|
+
* and run time, rather than an editor and a separate Gantt that can disagree.
|
|
95
|
+
*/
|
|
96
|
+
set RuntimeStatus(value) {
|
|
97
|
+
this.currentRuntime = value;
|
|
98
|
+
this.project();
|
|
99
|
+
}
|
|
100
|
+
get RuntimeStatus() {
|
|
101
|
+
return this.currentRuntime;
|
|
102
|
+
}
|
|
103
|
+
/** Read-only mode. The same component is the viewer — there is no second, weaker renderer. */
|
|
104
|
+
ReadOnly = false;
|
|
105
|
+
ShowToolbar = true;
|
|
106
|
+
ShowPalette = true;
|
|
107
|
+
ShowMinimap = true;
|
|
108
|
+
ShowStatusBar = true;
|
|
109
|
+
AutoLayoutDirection = 'vertical';
|
|
110
|
+
/** Shown when there is nothing to draw yet. */
|
|
111
|
+
EmptyStateMessage = 'No steps yet. Add one to start building this workflow.';
|
|
112
|
+
// ── Outputs ──────────────────────────────────────────────────────────────
|
|
113
|
+
BeforeTaskAdded = new EventEmitter();
|
|
114
|
+
AfterTaskAdded = new EventEmitter();
|
|
115
|
+
BeforeTaskRemoved = new EventEmitter();
|
|
116
|
+
AfterTaskRemoved = new EventEmitter();
|
|
117
|
+
BeforeTaskUpdated = new EventEmitter();
|
|
118
|
+
AfterTaskUpdated = new EventEmitter();
|
|
119
|
+
BeforeDependencyAdded = new EventEmitter();
|
|
120
|
+
AfterDependencyAdded = new EventEmitter();
|
|
121
|
+
BeforeDependencyRemoved = new EventEmitter();
|
|
122
|
+
AfterDependencyRemoved = new EventEmitter();
|
|
123
|
+
/** Informational — no `Before` pair, because these report what already happened. */
|
|
124
|
+
SpecChanged = new EventEmitter();
|
|
125
|
+
SelectionChanged = new EventEmitter();
|
|
126
|
+
ValidationChanged = new EventEmitter();
|
|
127
|
+
/** Intent-only — the host navigates; this widget has no Router and must not acquire one. */
|
|
128
|
+
AgentOpenRequested = new EventEmitter();
|
|
129
|
+
RecordOpenRequested = new EventEmitter();
|
|
130
|
+
// ── Rendered state ───────────────────────────────────────────────────────
|
|
131
|
+
Nodes = [];
|
|
132
|
+
Connections = [];
|
|
133
|
+
NodeTypes = TASK_GRAPH_NODE_TYPES;
|
|
134
|
+
SelectedTask = null;
|
|
135
|
+
ValidationErrors = [];
|
|
136
|
+
IsValid = true;
|
|
137
|
+
currentSpec = null;
|
|
138
|
+
currentRuntime = null;
|
|
139
|
+
get IsEmpty() {
|
|
140
|
+
return (this.currentSpec?.tasks?.length ?? 0) === 0;
|
|
141
|
+
}
|
|
142
|
+
// ── Public methods ───────────────────────────────────────────────────────
|
|
143
|
+
//
|
|
144
|
+
// Imperative entry points exist for exactly the case the cancelable-event contract cannot serve:
|
|
145
|
+
// a host that needs to `await` something (a confirm dialog) before the action proceeds. A
|
|
146
|
+
// `Before*` handler cannot await — control returns to the emitter before the handler sets
|
|
147
|
+
// `Cancel` — so the host awaits first and then calls one of these.
|
|
148
|
+
/** Re-runs validation and emits the result. Idempotent. */
|
|
149
|
+
Validate() {
|
|
150
|
+
const result = this.currentSpec
|
|
151
|
+
? ValidateTaskGraphSpec(this.currentSpec)
|
|
152
|
+
: { Valid: true, Errors: [] };
|
|
153
|
+
this.IsValid = result.Valid;
|
|
154
|
+
this.ValidationErrors = result.Errors;
|
|
155
|
+
const args = new TaskGraphValidationChangedEventArgs(result.Valid, result.Errors);
|
|
156
|
+
this.ValidationChanged.emit(args);
|
|
157
|
+
return args;
|
|
158
|
+
}
|
|
159
|
+
/** Adds a task. Returns the new task, or null when a host vetoed it. */
|
|
160
|
+
AddTask(partial = {}) {
|
|
161
|
+
if (this.ReadOnly || !this.currentSpec)
|
|
162
|
+
return null;
|
|
163
|
+
const task = {
|
|
164
|
+
tempId: partial.tempId ?? NextTempId(this.currentSpec),
|
|
165
|
+
name: partial.name ?? 'New step',
|
|
166
|
+
description: partial.description ?? '',
|
|
167
|
+
agentName: partial.agentName,
|
|
168
|
+
assignToUser: partial.assignToUser,
|
|
169
|
+
dependsOn: partial.dependsOn ?? [],
|
|
170
|
+
inputPayload: partial.inputPayload,
|
|
171
|
+
};
|
|
172
|
+
const before = new BeforeTaskAddedEventArgs(task);
|
|
173
|
+
this.BeforeTaskAdded.emit(before);
|
|
174
|
+
if (before.Cancel)
|
|
175
|
+
return null;
|
|
176
|
+
this.commit(AddTask(this.currentSpec, task), 'TaskAdded');
|
|
177
|
+
this.AfterTaskAdded.emit(new AfterTaskAddedEventArgs(task, this.currentSpec));
|
|
178
|
+
return task;
|
|
179
|
+
}
|
|
180
|
+
/** Removes a task and every edge into it. Returns false when a host vetoed it. */
|
|
181
|
+
RemoveTask(tempId) {
|
|
182
|
+
if (this.ReadOnly || !this.currentSpec)
|
|
183
|
+
return false;
|
|
184
|
+
const task = this.findTask(tempId);
|
|
185
|
+
if (!task)
|
|
186
|
+
return false;
|
|
187
|
+
// The blast radius travels with the event: a host deciding whether to veto needs to know
|
|
188
|
+
// what else breaks, not merely which box was clicked.
|
|
189
|
+
const before = new BeforeTaskRemovedEventArgs(task, GetDependents(this.currentSpec, tempId));
|
|
190
|
+
this.BeforeTaskRemoved.emit(before);
|
|
191
|
+
if (before.Cancel)
|
|
192
|
+
return false;
|
|
193
|
+
this.commit(RemoveTask(this.currentSpec, tempId), 'TaskRemoved');
|
|
194
|
+
if (this.SelectedTask?.tempId === tempId)
|
|
195
|
+
this.selectTask(null);
|
|
196
|
+
this.AfterTaskRemoved.emit(new AfterTaskRemovedEventArgs(task, this.currentSpec));
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
/** Replaces a task's properties. Returns false when a host vetoed it. */
|
|
200
|
+
UpdateTask(tempId, next) {
|
|
201
|
+
if (this.ReadOnly || !this.currentSpec)
|
|
202
|
+
return false;
|
|
203
|
+
const previous = this.findTask(tempId);
|
|
204
|
+
if (!previous)
|
|
205
|
+
return false;
|
|
206
|
+
const before = new BeforeTaskUpdatedEventArgs(previous, next);
|
|
207
|
+
this.BeforeTaskUpdated.emit(before);
|
|
208
|
+
if (before.Cancel)
|
|
209
|
+
return false;
|
|
210
|
+
this.commit(UpdateTask(this.currentSpec, tempId, next), 'TaskUpdated');
|
|
211
|
+
if (this.SelectedTask?.tempId === tempId)
|
|
212
|
+
this.SelectedTask = next;
|
|
213
|
+
this.AfterTaskUpdated.emit(new AfterTaskUpdatedEventArgs(next, this.currentSpec));
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Adds a dependency edge. Returns false when it was vetoed **or would create a cycle**.
|
|
218
|
+
*
|
|
219
|
+
* The cycle is refused unconditionally, not merely reported: a cyclic graph can never execute —
|
|
220
|
+
* nothing would ever become eligible — so allowing the canvas to draw one would let a user build
|
|
221
|
+
* something the engine must then reject. Better to refuse the stroke than to accept a graph that
|
|
222
|
+
* cannot run.
|
|
223
|
+
*/
|
|
224
|
+
AddDependency(fromTempId, toTempId, condition) {
|
|
225
|
+
if (this.ReadOnly || !this.currentSpec)
|
|
226
|
+
return false;
|
|
227
|
+
const cycle = WouldCreateCycle(this.currentSpec, fromTempId, toTempId);
|
|
228
|
+
const before = new BeforeDependencyAddedEventArgs(fromTempId, toTempId, cycle);
|
|
229
|
+
this.BeforeDependencyAdded.emit(before);
|
|
230
|
+
if (before.Cancel || cycle)
|
|
231
|
+
return false;
|
|
232
|
+
this.commit(AddDependency(this.currentSpec, fromTempId, toTempId, condition), 'DependencyAdded');
|
|
233
|
+
this.AfterDependencyAdded.emit(new AfterDependencyAddedEventArgs(fromTempId, toTempId, this.currentSpec));
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
/** Removes a dependency edge. Returns false when a host vetoed it. */
|
|
237
|
+
RemoveDependency(fromTempId, toTempId) {
|
|
238
|
+
if (this.ReadOnly || !this.currentSpec)
|
|
239
|
+
return false;
|
|
240
|
+
const before = new BeforeDependencyRemovedEventArgs(fromTempId, toTempId);
|
|
241
|
+
this.BeforeDependencyRemoved.emit(before);
|
|
242
|
+
if (before.Cancel)
|
|
243
|
+
return false;
|
|
244
|
+
this.commit(RemoveDependency(this.currentSpec, fromTempId, toTempId), 'DependencyRemoved');
|
|
245
|
+
this.AfterDependencyRemoved.emit(new AfterDependencyRemovedEventArgs(fromTempId, toTempId, this.currentSpec));
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Sets — or with an empty string, clears — a dependency edge's condition.
|
|
250
|
+
*
|
|
251
|
+
* Implemented as remove-then-add so it travels the same `Before*`/`After*` path as any other
|
|
252
|
+
* edge change. A separate silent mutation would be a second write path into the spec, and the
|
|
253
|
+
* veto contract would then be right in one place and wrong in the other.
|
|
254
|
+
*/
|
|
255
|
+
SetDependencyCondition(fromTempId, toTempId, condition) {
|
|
256
|
+
if (this.ReadOnly || !this.currentSpec)
|
|
257
|
+
return false;
|
|
258
|
+
if (!this.RemoveDependency(fromTempId, toTempId))
|
|
259
|
+
return false;
|
|
260
|
+
return this.AddDependency(fromTempId, toTempId, condition.trim() || undefined);
|
|
261
|
+
}
|
|
262
|
+
/** Asks the host to open the agent behind a task. */
|
|
263
|
+
RequestAgentOpen(task) {
|
|
264
|
+
if (task.agentName) {
|
|
265
|
+
this.AgentOpenRequested.emit(new AgentOpenRequestedEventArgs(task.agentName, task));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/** Asks the host to open a record the graph references. */
|
|
269
|
+
RequestRecordOpen(entityName, recordID) {
|
|
270
|
+
this.RecordOpenRequested.emit(new RecordOpenRequestedEventArgs(entityName, recordID));
|
|
271
|
+
}
|
|
272
|
+
// ── Canvas handlers ──────────────────────────────────────────────────────
|
|
273
|
+
OnNodeSelected(node) {
|
|
274
|
+
this.selectTask(node ? this.findTask(node.ID) : null);
|
|
275
|
+
}
|
|
276
|
+
OnConnectionCreated(event) {
|
|
277
|
+
this.AddDependency(event.SourceNodeID, event.TargetNodeID);
|
|
278
|
+
}
|
|
279
|
+
OnConnectionRemoved(connection) {
|
|
280
|
+
this.RemoveDependency(connection.SourceNodeID, connection.TargetNodeID);
|
|
281
|
+
}
|
|
282
|
+
OnNodeRemoved(node) {
|
|
283
|
+
this.RemoveTask(node.ID);
|
|
284
|
+
}
|
|
285
|
+
// ── Internals ────────────────────────────────────────────────────────────
|
|
286
|
+
findTask(tempId) {
|
|
287
|
+
return (this.currentSpec?.tasks ?? []).find((t) => t.tempId === tempId) ?? null;
|
|
288
|
+
}
|
|
289
|
+
selectTask(task) {
|
|
290
|
+
this.SelectedTask = task;
|
|
291
|
+
this.SelectionChanged.emit(new TaskGraphSelectionChangedEventArgs(task));
|
|
292
|
+
}
|
|
293
|
+
/** Applies a new spec, re-projects, re-validates, and tells the host — in that order. */
|
|
294
|
+
commit(spec, reason) {
|
|
295
|
+
this.currentSpec = spec;
|
|
296
|
+
this.project();
|
|
297
|
+
this.SpecChanged.emit(new TaskGraphSpecChangedEventArgs(spec, reason));
|
|
298
|
+
}
|
|
299
|
+
/** Re-derives the canvas from the spec. Validation rides along so the two never disagree. */
|
|
300
|
+
project() {
|
|
301
|
+
if (!this.currentSpec) {
|
|
302
|
+
this.Nodes = [];
|
|
303
|
+
this.Connections = [];
|
|
304
|
+
this.ValidationErrors = [];
|
|
305
|
+
this.IsValid = true;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
this.Nodes = SpecToNodes(this.currentSpec, this.currentRuntime ?? undefined);
|
|
309
|
+
this.Connections = SpecToConnections(this.currentSpec);
|
|
310
|
+
this.Validate();
|
|
311
|
+
}
|
|
312
|
+
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) {
|
|
314
|
+
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);
|
|
316
|
+
i0.ɵɵelementEnd();
|
|
317
|
+
} if (rf & 2) {
|
|
318
|
+
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}"] });
|
|
321
|
+
}
|
|
322
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TaskGraphEditorComponent, [{
|
|
323
|
+
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"] }]
|
|
325
|
+
}], null, { Spec: [{
|
|
326
|
+
type: Input
|
|
327
|
+
}], RuntimeStatus: [{
|
|
328
|
+
type: Input
|
|
329
|
+
}], ReadOnly: [{
|
|
330
|
+
type: Input
|
|
331
|
+
}], ShowToolbar: [{
|
|
332
|
+
type: Input
|
|
333
|
+
}], ShowPalette: [{
|
|
334
|
+
type: Input
|
|
335
|
+
}], ShowMinimap: [{
|
|
336
|
+
type: Input
|
|
337
|
+
}], ShowStatusBar: [{
|
|
338
|
+
type: Input
|
|
339
|
+
}], AutoLayoutDirection: [{
|
|
340
|
+
type: Input
|
|
341
|
+
}], EmptyStateMessage: [{
|
|
342
|
+
type: Input
|
|
343
|
+
}], BeforeTaskAdded: [{
|
|
344
|
+
type: Output
|
|
345
|
+
}], AfterTaskAdded: [{
|
|
346
|
+
type: Output
|
|
347
|
+
}], BeforeTaskRemoved: [{
|
|
348
|
+
type: Output
|
|
349
|
+
}], AfterTaskRemoved: [{
|
|
350
|
+
type: Output
|
|
351
|
+
}], BeforeTaskUpdated: [{
|
|
352
|
+
type: Output
|
|
353
|
+
}], AfterTaskUpdated: [{
|
|
354
|
+
type: Output
|
|
355
|
+
}], BeforeDependencyAdded: [{
|
|
356
|
+
type: Output
|
|
357
|
+
}], AfterDependencyAdded: [{
|
|
358
|
+
type: Output
|
|
359
|
+
}], BeforeDependencyRemoved: [{
|
|
360
|
+
type: Output
|
|
361
|
+
}], AfterDependencyRemoved: [{
|
|
362
|
+
type: Output
|
|
363
|
+
}], SpecChanged: [{
|
|
364
|
+
type: Output
|
|
365
|
+
}], SelectionChanged: [{
|
|
366
|
+
type: Output
|
|
367
|
+
}], ValidationChanged: [{
|
|
368
|
+
type: Output
|
|
369
|
+
}], AgentOpenRequested: [{
|
|
370
|
+
type: Output
|
|
371
|
+
}], RecordOpenRequested: [{
|
|
372
|
+
type: Output
|
|
373
|
+
}] }); })();
|
|
374
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TaskGraphEditorComponent, { className: "TaskGraphEditorComponent", filePath: "src/lib/task-graph-editor.component.ts", lineNumber: 80 }); })();
|
|
375
|
+
//# sourceMappingURL=task-graph-editor.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graph-editor.component.js","sourceRoot":"","sources":["../../src/lib/task-graph-editor.component.ts","../../src/lib/task-graph-editor.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview View and edit a `TaskGraphSpec` on a canvas.\n *\n * **What makes this reusable is its subject.** It edits `TaskGraphSpec` — the one fully-qualified\n * graph contract every producer in the program already authors against (D16) — rather than any\n * particular persistence. A design-time flow, a graph an agent emitted at runtime, a stored workflow\n * definition and a graph a person is drawing from scratch are all the same type here, so one\n * component serves the Flow Agent editor, the Agent Run admin view, conversation plan cards, the\n * Tasks view, and anything downstream.\n *\n * The alternative — teaching the existing agent editor a second dialect — is precisely how the two\n * graph models drifted apart before Phase 4 pulled them onto one traversal engine.\n *\n * **Layer: `widgets` (L1/L2).** No `@angular/router`, no `@memberjunction/ng-shared`, no\n * `NavigationService`. Route-derived state arrives as `@Input()`; navigation *intent* leaves as an\n * `@Output()` the host acts on, because a widget cannot know whether it is inside Explorer, a\n * downstream app, or an embedded panel. See `guides/UI_LAYERING_GUIDE.md`.\n *\n * **Validation is the engine's.** Cycles, unknown dependency refs and assignment conflicts are\n * reported by calling `ValidateTaskGraphSpec` from `@memberjunction/ai-core-plus` — the same\n * function `LoopAgentType` and `TaskGraphService.Submit` call. A second implementation here would be\n * a second definition of \"valid\", and the graph that passes on the canvas would be free to fail at\n * submission.\n *\n * @module @memberjunction/ng-task-graph-editor\n */\nimport { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport {\n ValidateTaskGraphSpec,\n type TaskGraphSpec,\n type TaskGraphSpecNode,\n type TaskGraphValidationError,\n} from '@memberjunction/ai-core-plus';\nimport type {\n FlowConnection,\n FlowConnectionCreatedEvent,\n FlowLayoutDirection,\n FlowNode,\n FlowNodeTypeConfig,\n} from '@memberjunction/ng-flow-editor';\nimport {\n AddDependency,\n AddTask,\n GetDependents,\n NextTempId,\n RemoveDependency,\n RemoveTask,\n SpecToConnections,\n SpecToNodes,\n TASK_GRAPH_NODE_TYPES,\n UpdateTask,\n WouldCreateCycle,\n type TaskGraphRuntimeStatus,\n} from './task-graph-canvas-adapter';\nimport {\n AfterDependencyAddedEventArgs,\n AfterDependencyRemovedEventArgs,\n AfterTaskAddedEventArgs,\n AfterTaskRemovedEventArgs,\n AfterTaskUpdatedEventArgs,\n AgentOpenRequestedEventArgs,\n BeforeDependencyAddedEventArgs,\n BeforeDependencyRemovedEventArgs,\n BeforeTaskAddedEventArgs,\n BeforeTaskRemovedEventArgs,\n BeforeTaskUpdatedEventArgs,\n RecordOpenRequestedEventArgs,\n TaskGraphSelectionChangedEventArgs,\n TaskGraphSpecChangedEventArgs,\n TaskGraphValidationChangedEventArgs,\n} from './task-graph-editor-events';\n\n@Component({\n standalone: false,\n selector: 'mj-task-graph-editor',\n templateUrl: './task-graph-editor.component.html',\n styleUrls: ['./task-graph-editor.component.css'],\n})\nexport class TaskGraphEditorComponent extends BaseAngularComponent {\n // ── Inputs ───────────────────────────────────────────────────────────────\n\n /**\n * The graph to show. Setter-based rather than `ngOnChanges` (repo convention): the reaction is\n * explicit, runs only when this property changes, and costs nothing on other change-detection\n * passes.\n */\n @Input()\n public set Spec(value: TaskGraphSpec | null) {\n this.currentSpec = value;\n this.project();\n }\n public get Spec(): TaskGraphSpec | null {\n return this.currentSpec;\n }\n\n /**\n * Live per-task state, keyed by `tempId`. Supplying it turns the canvas into a runtime view of\n * the same graph — the convergence point the program was aiming at: one renderer for design time\n * and run time, rather than an editor and a separate Gantt that can disagree.\n */\n @Input()\n public set RuntimeStatus(value: TaskGraphRuntimeStatus | null) {\n this.currentRuntime = value;\n this.project();\n }\n public get RuntimeStatus(): TaskGraphRuntimeStatus | null {\n return this.currentRuntime;\n }\n\n /** Read-only mode. The same component is the viewer — there is no second, weaker renderer. */\n @Input() public ReadOnly: boolean = false;\n\n @Input() public ShowToolbar: boolean = true;\n @Input() public ShowPalette: boolean = true;\n @Input() public ShowMinimap: boolean = true;\n @Input() public ShowStatusBar: boolean = true;\n @Input() public AutoLayoutDirection: FlowLayoutDirection = 'vertical';\n\n /** Shown when there is nothing to draw yet. */\n @Input() public EmptyStateMessage: string = 'No steps yet. Add one to start building this workflow.';\n\n // ── Outputs ──────────────────────────────────────────────────────────────\n\n @Output() public BeforeTaskAdded = new EventEmitter<BeforeTaskAddedEventArgs>();\n @Output() public AfterTaskAdded = new EventEmitter<AfterTaskAddedEventArgs>();\n @Output() public BeforeTaskRemoved = new EventEmitter<BeforeTaskRemovedEventArgs>();\n @Output() public AfterTaskRemoved = new EventEmitter<AfterTaskRemovedEventArgs>();\n @Output() public BeforeTaskUpdated = new EventEmitter<BeforeTaskUpdatedEventArgs>();\n @Output() public AfterTaskUpdated = new EventEmitter<AfterTaskUpdatedEventArgs>();\n @Output() public BeforeDependencyAdded = new EventEmitter<BeforeDependencyAddedEventArgs>();\n @Output() public AfterDependencyAdded = new EventEmitter<AfterDependencyAddedEventArgs>();\n @Output() public BeforeDependencyRemoved = new EventEmitter<BeforeDependencyRemovedEventArgs>();\n @Output() public AfterDependencyRemoved = new EventEmitter<AfterDependencyRemovedEventArgs>();\n\n /** Informational — no `Before` pair, because these report what already happened. */\n @Output() public SpecChanged = new EventEmitter<TaskGraphSpecChangedEventArgs>();\n @Output() public SelectionChanged = new EventEmitter<TaskGraphSelectionChangedEventArgs>();\n @Output() public ValidationChanged = new EventEmitter<TaskGraphValidationChangedEventArgs>();\n\n /** Intent-only — the host navigates; this widget has no Router and must not acquire one. */\n @Output() public AgentOpenRequested = new EventEmitter<AgentOpenRequestedEventArgs>();\n @Output() public RecordOpenRequested = new EventEmitter<RecordOpenRequestedEventArgs>();\n\n // ── Rendered state ───────────────────────────────────────────────────────\n\n public Nodes: FlowNode[] = [];\n public Connections: FlowConnection[] = [];\n public NodeTypes: FlowNodeTypeConfig[] = TASK_GRAPH_NODE_TYPES;\n public SelectedTask: TaskGraphSpecNode | null = null;\n public ValidationErrors: readonly TaskGraphValidationError[] = [];\n public IsValid: boolean = true;\n\n private currentSpec: TaskGraphSpec | null = null;\n private currentRuntime: TaskGraphRuntimeStatus | null = null;\n\n public get IsEmpty(): boolean {\n return (this.currentSpec?.tasks?.length ?? 0) === 0;\n }\n\n // ── Public methods ───────────────────────────────────────────────────────\n //\n // Imperative entry points exist for exactly the case the cancelable-event contract cannot serve:\n // a host that needs to `await` something (a confirm dialog) before the action proceeds. A\n // `Before*` handler cannot await — control returns to the emitter before the handler sets\n // `Cancel` — so the host awaits first and then calls one of these.\n\n /** Re-runs validation and emits the result. Idempotent. */\n public Validate(): TaskGraphValidationChangedEventArgs {\n const result = this.currentSpec\n ? ValidateTaskGraphSpec(this.currentSpec)\n : { Valid: true, Errors: [] as TaskGraphValidationError[] };\n\n this.IsValid = result.Valid;\n this.ValidationErrors = result.Errors;\n\n const args = new TaskGraphValidationChangedEventArgs(result.Valid, result.Errors);\n this.ValidationChanged.emit(args);\n return args;\n }\n\n /** Adds a task. Returns the new task, or null when a host vetoed it. */\n public AddTask(partial: Partial<TaskGraphSpecNode> = {}): TaskGraphSpecNode | null {\n if (this.ReadOnly || !this.currentSpec) return null;\n\n const task: TaskGraphSpecNode = {\n tempId: partial.tempId ?? NextTempId(this.currentSpec),\n name: partial.name ?? 'New step',\n description: partial.description ?? '',\n agentName: partial.agentName,\n assignToUser: partial.assignToUser,\n dependsOn: partial.dependsOn ?? [],\n inputPayload: partial.inputPayload,\n };\n\n const before = new BeforeTaskAddedEventArgs(task);\n this.BeforeTaskAdded.emit(before);\n if (before.Cancel) return null;\n\n this.commit(AddTask(this.currentSpec, task), 'TaskAdded');\n this.AfterTaskAdded.emit(new AfterTaskAddedEventArgs(task, this.currentSpec!));\n return task;\n }\n\n /** Removes a task and every edge into it. Returns false when a host vetoed it. */\n public RemoveTask(tempId: string): boolean {\n if (this.ReadOnly || !this.currentSpec) return false;\n const task = this.findTask(tempId);\n if (!task) return false;\n\n // The blast radius travels with the event: a host deciding whether to veto needs to know\n // what else breaks, not merely which box was clicked.\n const before = new BeforeTaskRemovedEventArgs(task, GetDependents(this.currentSpec, tempId));\n this.BeforeTaskRemoved.emit(before);\n if (before.Cancel) return false;\n\n this.commit(RemoveTask(this.currentSpec, tempId), 'TaskRemoved');\n if (this.SelectedTask?.tempId === tempId) this.selectTask(null);\n this.AfterTaskRemoved.emit(new AfterTaskRemovedEventArgs(task, this.currentSpec!));\n return true;\n }\n\n /** Replaces a task's properties. Returns false when a host vetoed it. */\n public UpdateTask(tempId: string, next: TaskGraphSpecNode): boolean {\n if (this.ReadOnly || !this.currentSpec) return false;\n const previous = this.findTask(tempId);\n if (!previous) return false;\n\n const before = new BeforeTaskUpdatedEventArgs(previous, next);\n this.BeforeTaskUpdated.emit(before);\n if (before.Cancel) return false;\n\n this.commit(UpdateTask(this.currentSpec, tempId, next), 'TaskUpdated');\n if (this.SelectedTask?.tempId === tempId) this.SelectedTask = next;\n this.AfterTaskUpdated.emit(new AfterTaskUpdatedEventArgs(next, this.currentSpec!));\n return true;\n }\n\n /**\n * Adds a dependency edge. Returns false when it was vetoed **or would create a cycle**.\n *\n * The cycle is refused unconditionally, not merely reported: a cyclic graph can never execute —\n * nothing would ever become eligible — so allowing the canvas to draw one would let a user build\n * something the engine must then reject. Better to refuse the stroke than to accept a graph that\n * cannot run.\n */\n public AddDependency(fromTempId: string, toTempId: string, condition?: string): boolean {\n if (this.ReadOnly || !this.currentSpec) return false;\n\n const cycle = WouldCreateCycle(this.currentSpec, fromTempId, toTempId);\n const before = new BeforeDependencyAddedEventArgs(fromTempId, toTempId, cycle);\n this.BeforeDependencyAdded.emit(before);\n if (before.Cancel || cycle) return false;\n\n this.commit(AddDependency(this.currentSpec, fromTempId, toTempId, condition), 'DependencyAdded');\n this.AfterDependencyAdded.emit(new AfterDependencyAddedEventArgs(fromTempId, toTempId, this.currentSpec!));\n return true;\n }\n\n /** Removes a dependency edge. Returns false when a host vetoed it. */\n public RemoveDependency(fromTempId: string, toTempId: string): boolean {\n if (this.ReadOnly || !this.currentSpec) return false;\n\n const before = new BeforeDependencyRemovedEventArgs(fromTempId, toTempId);\n this.BeforeDependencyRemoved.emit(before);\n if (before.Cancel) return false;\n\n this.commit(RemoveDependency(this.currentSpec, fromTempId, toTempId), 'DependencyRemoved');\n this.AfterDependencyRemoved.emit(new AfterDependencyRemovedEventArgs(fromTempId, toTempId, this.currentSpec!));\n return true;\n }\n\n /**\n * Sets — or with an empty string, clears — a dependency edge's condition.\n *\n * Implemented as remove-then-add so it travels the same `Before*`/`After*` path as any other\n * edge change. A separate silent mutation would be a second write path into the spec, and the\n * veto contract would then be right in one place and wrong in the other.\n */\n public SetDependencyCondition(fromTempId: string, toTempId: string, condition: string): boolean {\n if (this.ReadOnly || !this.currentSpec) return false;\n if (!this.RemoveDependency(fromTempId, toTempId)) return false;\n return this.AddDependency(fromTempId, toTempId, condition.trim() || undefined);\n }\n\n /** Asks the host to open the agent behind a task. */\n public RequestAgentOpen(task: TaskGraphSpecNode): void {\n if (task.agentName) {\n this.AgentOpenRequested.emit(new AgentOpenRequestedEventArgs(task.agentName, task));\n }\n }\n\n /** Asks the host to open a record the graph references. */\n public RequestRecordOpen(entityName: string, recordID: string): void {\n this.RecordOpenRequested.emit(new RecordOpenRequestedEventArgs(entityName, recordID));\n }\n\n // ── Canvas handlers ──────────────────────────────────────────────────────\n\n public OnNodeSelected(node: FlowNode | null): void {\n this.selectTask(node ? this.findTask(node.ID) : null);\n }\n\n public OnConnectionCreated(event: FlowConnectionCreatedEvent): void {\n this.AddDependency(event.SourceNodeID, event.TargetNodeID);\n }\n\n public OnConnectionRemoved(connection: FlowConnection): void {\n this.RemoveDependency(connection.SourceNodeID, connection.TargetNodeID);\n }\n\n public OnNodeRemoved(node: FlowNode): void {\n this.RemoveTask(node.ID);\n }\n\n // ── Internals ────────────────────────────────────────────────────────────\n\n private findTask(tempId: string): TaskGraphSpecNode | null {\n return (this.currentSpec?.tasks ?? []).find((t) => t.tempId === tempId) ?? null;\n }\n\n private selectTask(task: TaskGraphSpecNode | null): void {\n this.SelectedTask = task;\n this.SelectionChanged.emit(new TaskGraphSelectionChangedEventArgs(task));\n }\n\n /** Applies a new spec, re-projects, re-validates, and tells the host — in that order. */\n private commit(spec: TaskGraphSpec, reason: TaskGraphSpecChangedEventArgs['Reason']): void {\n this.currentSpec = spec;\n this.project();\n this.SpecChanged.emit(new TaskGraphSpecChangedEventArgs(spec, reason));\n }\n\n /** Re-derives the canvas from the spec. Validation rides along so the two never disagree. */\n private project(): void {\n if (!this.currentSpec) {\n this.Nodes = [];\n this.Connections = [];\n this.ValidationErrors = [];\n this.IsValid = true;\n return;\n }\n this.Nodes = SpecToNodes(this.currentSpec, this.currentRuntime ?? undefined);\n this.Connections = SpecToConnections(this.currentSpec);\n this.Validate();\n }\n}\n","<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"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import * as i1 from "./task-graph-editor.component";
|
|
3
|
+
import * as i2 from "./task-graph-properties-panel.component";
|
|
4
|
+
import * as i3 from "@angular/common";
|
|
5
|
+
import * as i4 from "@angular/forms";
|
|
6
|
+
import * as i5 from "@memberjunction/ng-flow-editor";
|
|
7
|
+
import * as i6 from "@memberjunction/ng-ui-components";
|
|
8
|
+
export declare class TaskGraphEditorModule {
|
|
9
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TaskGraphEditorModule, never>;
|
|
10
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<TaskGraphEditorModule, [typeof i1.TaskGraphEditorComponent, typeof i2.TaskGraphPropertiesPanelComponent], [typeof i3.CommonModule, typeof i4.FormsModule, typeof i5.FlowEditorModule, typeof i6.MJEmptyStateComponent, typeof i6.MJAlertComponent, typeof i6.MJButtonDirective], [typeof i1.TaskGraphEditorComponent, typeof i2.TaskGraphPropertiesPanelComponent]>;
|
|
11
|
+
static ɵinj: i0.ɵɵInjectorDeclaration<TaskGraphEditorModule>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=task-graph-editor.module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graph-editor.module.d.ts","sourceRoot":"","sources":["../../src/lib/task-graph-editor.module.ts"],"names":[],"mappings":";;;;;;;AASA,qBAKa,qBAAqB;yCAArB,qBAAqB;0CAArB,qBAAqB;0CAArB,qBAAqB;CAAG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NgModule } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
import { FlowEditorModule } from '@memberjunction/ng-flow-editor';
|
|
5
|
+
import { MJEmptyStateComponent, MJAlertComponent, MJButtonDirective } from '@memberjunction/ng-ui-components';
|
|
6
|
+
import { TaskGraphEditorComponent } from './task-graph-editor.component';
|
|
7
|
+
import { TaskGraphPropertiesPanelComponent } from './task-graph-properties-panel.component';
|
|
8
|
+
import * as i0 from "@angular/core";
|
|
9
|
+
export class TaskGraphEditorModule {
|
|
10
|
+
static ɵfac = function TaskGraphEditorModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TaskGraphEditorModule)(); };
|
|
11
|
+
static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: TaskGraphEditorModule });
|
|
12
|
+
static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [CommonModule, FormsModule, FlowEditorModule, MJEmptyStateComponent, MJAlertComponent] });
|
|
13
|
+
}
|
|
14
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TaskGraphEditorModule, [{
|
|
15
|
+
type: NgModule,
|
|
16
|
+
args: [{
|
|
17
|
+
declarations: [TaskGraphEditorComponent, TaskGraphPropertiesPanelComponent],
|
|
18
|
+
imports: [CommonModule, FormsModule, FlowEditorModule, MJEmptyStateComponent, MJAlertComponent, MJButtonDirective],
|
|
19
|
+
exports: [TaskGraphEditorComponent, TaskGraphPropertiesPanelComponent],
|
|
20
|
+
}]
|
|
21
|
+
}], null, null); })();
|
|
22
|
+
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(TaskGraphEditorModule, { declarations: [TaskGraphEditorComponent, TaskGraphPropertiesPanelComponent], imports: [CommonModule, FormsModule, FlowEditorModule, MJEmptyStateComponent, MJAlertComponent, MJButtonDirective], exports: [TaskGraphEditorComponent, TaskGraphPropertiesPanelComponent] }); })();
|
|
23
|
+
//# sourceMappingURL=task-graph-editor.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graph-editor.module.js","sourceRoot":"","sources":["../../src/lib/task-graph-editor.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAE9G,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;;AAO5F,MAAM,OAAO,qBAAqB;+GAArB,qBAAqB;4DAArB,qBAAqB;gEAHtB,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,gBAAgB;;iFAGnF,qBAAqB;cALjC,QAAQ;eAAC;gBACR,YAAY,EAAE,CAAC,wBAAwB,EAAE,iCAAiC,CAAC;gBAC3E,OAAO,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,CAAC;gBAClH,OAAO,EAAE,CAAC,wBAAwB,EAAE,iCAAiC,CAAC;aACvE;;wFACY,qBAAqB,mBAJjB,wBAAwB,EAAE,iCAAiC,aAChE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,aACvG,wBAAwB,EAAE,iCAAiC","sourcesContent":["import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { FlowEditorModule } from '@memberjunction/ng-flow-editor';\nimport { MJEmptyStateComponent, MJAlertComponent, MJButtonDirective } from '@memberjunction/ng-ui-components';\n\nimport { TaskGraphEditorComponent } from './task-graph-editor.component';\nimport { TaskGraphPropertiesPanelComponent } from './task-graph-properties-panel.component';\n\n@NgModule({\n declarations: [TaskGraphEditorComponent, TaskGraphPropertiesPanelComponent],\n imports: [CommonModule, FormsModule, FlowEditorModule, MJEmptyStateComponent, MJAlertComponent, MJButtonDirective],\n exports: [TaskGraphEditorComponent, TaskGraphPropertiesPanelComponent],\n})\nexport class TaskGraphEditorModule {}\n"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Edits the selected task's properties, plus the graph-level traversal settings.
|
|
3
|
+
*
|
|
4
|
+
* Split out of the canvas component for the reason panels usually are — the canvas is about
|
|
5
|
+
* *structure* (what connects to what) and this is about *content* (what a step actually does). But
|
|
6
|
+
* also for a sharper reason: a host embedding the read-only viewer in a chat card or a run history
|
|
7
|
+
* pane wants the graph without a form beside it, and a panel welded into the canvas cannot be
|
|
8
|
+
* declined.
|
|
9
|
+
*
|
|
10
|
+
* Emits intent rather than mutating: the canvas component owns the spec, so every edit here leaves
|
|
11
|
+
* as a request the parent applies through the same `Before*`/`After*` path a drag or a delete takes.
|
|
12
|
+
* Two write paths into one spec would be two places for the veto contract to be wrong.
|
|
13
|
+
*
|
|
14
|
+
* @module @memberjunction/ng-task-graph-editor
|
|
15
|
+
*/
|
|
16
|
+
import { EventEmitter } from '@angular/core';
|
|
17
|
+
import type { TaskGraphSpec, TaskGraphSpecNode } from '@memberjunction/ai-core-plus';
|
|
18
|
+
import * as i0 from "@angular/core";
|
|
19
|
+
/** A requested change to one task. The parent applies it; this panel never writes. */
|
|
20
|
+
export declare class TaskPropertyChangeRequestedEventArgs {
|
|
21
|
+
readonly TempId: string;
|
|
22
|
+
readonly Next: TaskGraphSpecNode;
|
|
23
|
+
constructor(TempId: string, Next: TaskGraphSpecNode);
|
|
24
|
+
}
|
|
25
|
+
/** A requested change to a dependency edge's condition. */
|
|
26
|
+
export declare class DependencyConditionChangeRequestedEventArgs {
|
|
27
|
+
readonly FromTempId: string;
|
|
28
|
+
readonly ToTempId: string;
|
|
29
|
+
/** Empty string clears the condition, making the edge unconditional again. */
|
|
30
|
+
readonly Condition: string;
|
|
31
|
+
constructor(FromTempId: string, ToTempId: string,
|
|
32
|
+
/** Empty string clears the condition, making the edge unconditional again. */
|
|
33
|
+
Condition: string);
|
|
34
|
+
}
|
|
35
|
+
export declare class TaskGraphPropertiesPanelComponent {
|
|
36
|
+
/** The task being edited. Null shows the empty state rather than a form bound to nothing. */
|
|
37
|
+
set Task(value: TaskGraphSpecNode | null);
|
|
38
|
+
get Task(): TaskGraphSpecNode | null;
|
|
39
|
+
/** The whole graph, for resolving the selected task's incoming edges. */
|
|
40
|
+
Spec: TaskGraphSpec | null;
|
|
41
|
+
/** Agent names offered in the assignment dropdown. Supplied by the host, which owns data access. */
|
|
42
|
+
AvailableAgentNames: readonly string[];
|
|
43
|
+
ReadOnly: boolean;
|
|
44
|
+
TaskPropertyChangeRequested: EventEmitter<TaskPropertyChangeRequestedEventArgs>;
|
|
45
|
+
DependencyConditionChangeRequested: EventEmitter<DependencyConditionChangeRequestedEventArgs>;
|
|
46
|
+
Draft: TaskGraphSpecNode | null;
|
|
47
|
+
private currentTask;
|
|
48
|
+
/**
|
|
49
|
+
* Whether this step waits on a person.
|
|
50
|
+
*
|
|
51
|
+
* Derived from the absence of an agent rather than stored separately, because the spec's own
|
|
52
|
+
* rule is that a task has exactly one assignee. A separate boolean could disagree with
|
|
53
|
+
* `agentName` and the validator would then reject a graph the form said was fine.
|
|
54
|
+
*/
|
|
55
|
+
get IsHumanTask(): boolean;
|
|
56
|
+
/** The edges into this task, so their conditions are editable where the step is. */
|
|
57
|
+
get IncomingEdges(): Array<{
|
|
58
|
+
FromTempId: string;
|
|
59
|
+
FromName: string;
|
|
60
|
+
Condition: string;
|
|
61
|
+
}>;
|
|
62
|
+
/** Switches the step between an agent and a person, keeping the spec's xor rule intact. */
|
|
63
|
+
SetAssignment(kind: 'agent' | 'human', agentName?: string): void;
|
|
64
|
+
/** Asks the parent to apply the draft. */
|
|
65
|
+
Commit(): void;
|
|
66
|
+
/** Asks the parent to change (or, with an empty string, clear) an edge's condition. */
|
|
67
|
+
CommitCondition(fromTempId: string, condition: string): void;
|
|
68
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TaskGraphPropertiesPanelComponent, never>;
|
|
69
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<TaskGraphPropertiesPanelComponent, "mj-task-graph-properties", never, { "Task": { "alias": "Task"; "required": false; }; "Spec": { "alias": "Spec"; "required": false; }; "AvailableAgentNames": { "alias": "AvailableAgentNames"; "required": false; }; "ReadOnly": { "alias": "ReadOnly"; "required": false; }; }, { "TaskPropertyChangeRequested": "TaskPropertyChangeRequested"; "DependencyConditionChangeRequested": "DependencyConditionChangeRequested"; }, never, never, false, never>;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=task-graph-properties-panel.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graph-properties-panel.component.d.ts","sourceRoot":"","sources":["../../src/lib/task-graph-properties-panel.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAa,YAAY,EAAiB,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;;AAGrF,sFAAsF;AACtF,qBAAa,oCAAoC;aAEzB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,iBAAiB;gBADvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,iBAAiB;CAE9C;AAED,2DAA2D;AAC3D,qBAAa,2CAA2C;aAEhC,UAAU,EAAE,MAAM;aAClB,QAAQ,EAAE,MAAM;IAChC,8EAA8E;aAC9D,SAAS,EAAE,MAAM;gBAHjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM;IAChC,8EAA8E;IAC9D,SAAS,EAAE,MAAM;CAExC;AAED,qBAMa,iCAAiC;IAC1C,6FAA6F;IAC7F,IACW,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAK9C;IACD,IAAW,IAAI,IAAI,iBAAiB,GAAG,IAAI,CAE1C;IAED,yEAAyE;IACzD,IAAI,EAAE,aAAa,GAAG,IAAI,CAAQ;IAElD,oGAAoG;IACpF,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAM;IAE5C,QAAQ,EAAE,OAAO,CAAS;IAEzB,2BAA2B,qDAA4D;IACvF,kCAAkC,4DAAmE;IAE/G,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAE9C,OAAO,CAAC,WAAW,CAAkC;IAErD;;;;;;OAMG;IACH,IAAW,WAAW,IAAI,OAAO,CAEhC;IAED,oFAAoF;IACpF,IAAW,aAAa,IAAI,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAQ7F;IAED,2FAA2F;IACpF,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAQvE,0CAA0C;IACnC,MAAM,IAAI,IAAI;IAOrB,uFAAuF;IAChF,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;yCApE1D,iCAAiC;2CAAjC,iCAAiC;CA0E7C"}
|