@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.
@@ -0,0 +1,289 @@
1
+ /**
2
+ * @fileoverview Watching a graph that is running, on the canvas its author drew on.
3
+ *
4
+ * **Why this is a component and not three.** A run needs rendering in at least three places — inside
5
+ * an Agent Run, inside the test harness, and on a standalone workflow-runs screen — and every one of
6
+ * them wants the identical thing: the graph, with live status, laid out legibly, with a node you can
7
+ * click. Three implementations would be three chances to render the same run differently, and the
8
+ * one that matters most (is that branch grey because it was *not taken*, or because it *broke*?) is
9
+ * exactly the distinction a second implementation gets wrong.
10
+ *
11
+ * **What it owns and what it doesn't.** It owns data acquisition — reading `MJ: Tasks` and
12
+ * `MJ: Task Dependencies` through the host's provider — and geometry. It does not navigate: a
13
+ * widgets-layer component cannot know whether it sits inside Explorer, so selecting a node emits an
14
+ * event and the host decides what "open the agent run" means. That boundary is the whole reason
15
+ * this can live beside the editor rather than in Explorer.
16
+ *
17
+ * @module @memberjunction/ng-task-graph-editor
18
+ */
19
+ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, } from '@angular/core';
20
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
21
+ import { LogError, RunView } from '@memberjunction/core';
22
+ import { GraphLayoutBounds, LayoutGraphNodes, ProjectTaskRowsToSpec, } from '@memberjunction/ai-core-plus';
23
+ import { BuildRuntimeStatus } from './task-graph-runtime-source';
24
+ import * as i0 from "@angular/core";
25
+ import * as i1 from "@memberjunction/ng-ui-components";
26
+ import * as i2 from "./task-graph-editor.component";
27
+ function TaskGraphRunViewComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
28
+ i0.ɵɵelement(0, "mj-alert", 0);
29
+ } if (rf & 2) {
30
+ const ctx_r0 = i0.ɵɵnextContext();
31
+ i0.ɵɵproperty("Message", ctx_r0.ErrorMessage);
32
+ } }
33
+ function TaskGraphRunViewComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
34
+ i0.ɵɵelementStart(0, "div", 1);
35
+ i0.ɵɵtext(1, "Loading the workflow\u2019s steps\u2026");
36
+ i0.ɵɵelementEnd();
37
+ } }
38
+ function TaskGraphRunViewComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
39
+ i0.ɵɵelement(0, "mj-empty-state", 2);
40
+ } }
41
+ function TaskGraphRunViewComponent_Conditional_3_Conditional_4_Template(rf, ctx) { if (rf & 1) {
42
+ i0.ɵɵelementStart(0, "span", 6);
43
+ i0.ɵɵtext(1);
44
+ i0.ɵɵelementEnd();
45
+ } if (rf & 2) {
46
+ const ctx_r0 = i0.ɵɵnextContext(2);
47
+ i0.ɵɵadvance();
48
+ i0.ɵɵtextInterpolate1("", ctx_r0.SkippedCount, " not taken");
49
+ } }
50
+ function TaskGraphRunViewComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
51
+ const _r2 = i0.ɵɵgetCurrentView();
52
+ i0.ɵɵelementStart(0, "div", 3)(1, "div", 4)(2, "span", 5);
53
+ i0.ɵɵtext(3);
54
+ i0.ɵɵelementEnd();
55
+ i0.ɵɵconditionalCreate(4, TaskGraphRunViewComponent_Conditional_3_Conditional_4_Template, 2, 1, "span", 6);
56
+ i0.ɵɵelementEnd();
57
+ i0.ɵɵelementStart(5, "div", 7)(6, "mj-task-graph-editor", 8);
58
+ i0.ɵɵlistener("SelectionChanged", function TaskGraphRunViewComponent_Conditional_3_Template_mj_task_graph_editor_SelectionChanged_6_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnSelectionChanged($event)); });
59
+ i0.ɵɵelementEnd()()();
60
+ } if (rf & 2) {
61
+ const ctx_r0 = i0.ɵɵnextContext();
62
+ i0.ɵɵadvance(3);
63
+ i0.ɵɵtextInterpolate2("", ctx_r0.CompletedCount, " of ", ctx_r0.TotalCount, " complete");
64
+ i0.ɵɵadvance();
65
+ i0.ɵɵconditional(ctx_r0.SkippedCount > 0 ? 4 : -1);
66
+ i0.ɵɵadvance();
67
+ i0.ɵɵstyleProp("height", ctx_r0.Height);
68
+ i0.ɵɵadvance();
69
+ i0.ɵɵproperty("Spec", ctx_r0.Spec)("RuntimeStatus", ctx_r0.RuntimeStatus)("NodePositions", ctx_r0.Positions)("ReadOnly", true)("ShowToolbar", false)("ShowPalette", false)("ShowProperties", false)("ShowMinimap", false)("ShowStatusBar", false);
70
+ } }
71
+ /** Statuses at which a graph has stopped moving, so polling can stop with it. */
72
+ const SETTLED = new Set(['Complete', 'Failed', 'Cancelled', 'Skipped']);
73
+ export class TaskGraphRunViewComponent extends BaseAngularComponent {
74
+ cdr;
75
+ /**
76
+ * The graph to watch — the parent `MJ: Tasks` row that represents it.
77
+ *
78
+ * Setter-based rather than `ngOnChanges`, matching the editor beside it: the reaction is
79
+ * explicit and costs nothing on unrelated change-detection passes.
80
+ */
81
+ set ParentTaskID(value) {
82
+ if (value === this.parentTaskID)
83
+ return;
84
+ this.parentTaskID = value;
85
+ void this.load();
86
+ }
87
+ get ParentTaskID() {
88
+ return this.parentTaskID;
89
+ }
90
+ /** Name shown on the compiled graph. Purely cosmetic; the rows carry the truth. */
91
+ WorkflowName = 'Workflow';
92
+ /**
93
+ * Keep re-reading while the graph is still moving.
94
+ *
95
+ * Off by default: most hosts show a finished run, and a component that polls by default would
96
+ * have every historical run quietly issuing queries forever.
97
+ */
98
+ LiveUpdates = false;
99
+ /** How often to re-read while live. Seconds, not milliseconds — this is a human-scale view. */
100
+ PollIntervalSeconds = 3;
101
+ /** Height of the canvas. Hosts embed this in very different amounts of space. */
102
+ Height = '320px';
103
+ NodeSelected = new EventEmitter();
104
+ /** Emitted once, when every step has reached a terminal status. */
105
+ Settled = new EventEmitter();
106
+ Spec = null;
107
+ RuntimeStatus = null;
108
+ Positions = new Map();
109
+ IsLoading = false;
110
+ ErrorMessage = null;
111
+ /** Rows by id, so a selection event can hand the host the whole task rather than an id. */
112
+ taskByID = new Map();
113
+ parentTaskID = null;
114
+ pollTimer = null;
115
+ destroyed = false;
116
+ constructor(cdr) {
117
+ super();
118
+ this.cdr = cdr;
119
+ }
120
+ ngOnDestroy() {
121
+ this.destroyed = true;
122
+ this.stopPolling();
123
+ }
124
+ /** Steps in a terminal state, for the host's summary line. */
125
+ get CompletedCount() {
126
+ return [...this.taskByID.values()].filter((t) => t.Status === 'Complete').length;
127
+ }
128
+ get TotalCount() {
129
+ return this.taskByID.size;
130
+ }
131
+ get SkippedCount() {
132
+ return [...this.taskByID.values()].filter((t) => t.Status === 'Skipped').length;
133
+ }
134
+ /**
135
+ * A node was selected on the canvas.
136
+ *
137
+ * The projected `tempId` IS the task id, so the row lookup is exact — and handing the host the
138
+ * whole task rather than an id spares every host from re-reading a row this component already
139
+ * holds.
140
+ */
141
+ OnSelectionChanged(args) {
142
+ const tempId = args?.Task?.tempId;
143
+ if (!tempId)
144
+ return;
145
+ this.NodeSelected.emit({ TaskID: tempId, Task: this.taskByID.get(tempId) ?? null });
146
+ }
147
+ /**
148
+ * Reads the graph and projects it onto the canvas.
149
+ *
150
+ * `BypassCache` throughout: a run view exists to show what is true *now*, and the whole point of
151
+ * the poll is to see a status that changed a second ago. A cached read would render a stale graph
152
+ * that looks authoritative.
153
+ */
154
+ async load() {
155
+ this.stopPolling();
156
+ if (!this.parentTaskID) {
157
+ this.Spec = null;
158
+ this.cdr.markForCheck();
159
+ return;
160
+ }
161
+ this.IsLoading = this.Spec === null; // only show the spinner on first load, not on every poll
162
+ this.ErrorMessage = null;
163
+ try {
164
+ const rv = RunView.FromMetadataProvider(this.ProviderToUse);
165
+ const [tasks, deps] = await rv.RunViews([
166
+ {
167
+ EntityName: 'MJ: Tasks',
168
+ ExtraFilter: `ParentID='${this.parentTaskID}'`,
169
+ // Started work in the order it ran, unstarted last, persist order as the
170
+ // tiebreak — the same rule GetAgentRunTree uses, so a graph reads identically
171
+ // wherever it is shown.
172
+ //
173
+ // `__mj_CreatedAt ASC` alone was persist order, which is the COMPILER's walk
174
+ // order, not the author's numbering and not what actually happened: a settled
175
+ // graph listed its steps in an order unrelated to their execution, and a skipped
176
+ // step could sit above work that ran.
177
+ OrderBy: 'CASE WHEN StartedAt IS NULL THEN 1 ELSE 0 END, StartedAt, __mj_CreatedAt',
178
+ ResultType: 'entity_object',
179
+ BypassCache: true,
180
+ },
181
+ {
182
+ EntityName: 'MJ: Task Dependencies',
183
+ ExtraFilter: `TaskID IN (SELECT ID FROM __mj.Task WHERE ParentID='${this.parentTaskID}')`,
184
+ ResultType: 'entity_object',
185
+ BypassCache: true,
186
+ },
187
+ ]);
188
+ if (this.destroyed)
189
+ return;
190
+ if (!tasks.Success) {
191
+ this.fail(`The workflow's steps could not be loaded: ${tasks.ErrorMessage}`);
192
+ return;
193
+ }
194
+ const taskRows = (tasks.Results ?? []);
195
+ const depRows = (deps.Success ? (deps.Results ?? []) : []);
196
+ this.project(taskRows, depRows);
197
+ if (this.LiveUpdates && !this.isSettled(taskRows))
198
+ this.schedulePoll();
199
+ else if (taskRows.length > 0)
200
+ this.Settled.emit();
201
+ }
202
+ catch (e) {
203
+ this.fail(e instanceof Error ? e.message : String(e));
204
+ }
205
+ finally {
206
+ this.IsLoading = false;
207
+ this.cdr.markForCheck();
208
+ }
209
+ }
210
+ /** Rows → spec + runtime + geometry. */
211
+ project(taskRows, depRows) {
212
+ this.taskByID = new Map(taskRows.map((t) => [t.ID, t]));
213
+ const projection = ProjectTaskRowsToSpec(this.WorkflowName, taskRows, depRows);
214
+ this.Spec = projection.Spec;
215
+ // Correlate by ID, not by name: a projected node's tempId IS the task id, which is the one
216
+ // case where an id match is exact. Name correlation exists for specs whose tempIds were
217
+ // never real, and would be needlessly lossy here.
218
+ const knownIDs = new Set(taskRows.map((t) => t.ID));
219
+ this.RuntimeStatus = BuildRuntimeStatus(taskRows, new Map(), knownIDs);
220
+ this.Positions = this.resolvePositions(projection.AuthoredPositions, taskRows, depRows);
221
+ }
222
+ /**
223
+ * The author's arrangement where there is one, a computed layout everywhere else.
224
+ *
225
+ * Mixing the two per-node rather than choosing one for the whole graph: a workflow whose author
226
+ * positioned some steps and left others where they fell should keep the positions they chose.
227
+ * A wholly unpositioned graph — the normal case for anything an agent emitted — is laid out
228
+ * entirely by the algorithm.
229
+ */
230
+ resolvePositions(authored, taskRows, depRows) {
231
+ const missing = taskRows.filter((t) => !authored.has(t.ID)).map((t) => t.ID);
232
+ if (missing.length === 0)
233
+ return authored;
234
+ const edges = depRows.map((d) => ({ From: d.DependsOnTaskID, To: d.TaskID }));
235
+ const computed = LayoutGraphNodes(taskRows.map((t) => t.ID), edges, { Direction: 'LR' });
236
+ const resolved = new Map(computed);
237
+ for (const [id, position] of authored)
238
+ resolved.set(id, position);
239
+ return resolved;
240
+ }
241
+ isSettled(taskRows) {
242
+ return taskRows.length > 0 && taskRows.every((t) => SETTLED.has(t.Status));
243
+ }
244
+ schedulePoll() {
245
+ this.pollTimer = setTimeout(() => void this.load(), Math.max(1, this.PollIntervalSeconds) * 1000);
246
+ }
247
+ stopPolling() {
248
+ if (this.pollTimer) {
249
+ clearTimeout(this.pollTimer);
250
+ this.pollTimer = null;
251
+ }
252
+ }
253
+ fail(message) {
254
+ LogError(`[TaskGraphRunView] ${message}`);
255
+ this.ErrorMessage = message;
256
+ this.cdr.markForCheck();
257
+ }
258
+ /** Canvas extent, so a host can size its container to the graph rather than guessing. */
259
+ get GraphBounds() {
260
+ const box = GraphLayoutBounds(this.Positions);
261
+ return { Width: box.Width, Height: box.Height };
262
+ }
263
+ static ɵfac = function TaskGraphRunViewComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TaskGraphRunViewComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
264
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TaskGraphRunViewComponent, selectors: [["mj-task-graph-run-view"]], inputs: { ParentTaskID: "ParentTaskID", WorkflowName: "WorkflowName", LiveUpdates: "LiveUpdates", PollIntervalSeconds: "PollIntervalSeconds", Height: "Height" }, outputs: { NodeSelected: "NodeSelected", Settled: "Settled" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 4, vars: 1, consts: [["Variant", "error", 3, "Message"], [1, "run-view-loading"], ["Icon", "fa-solid fa-diagram-project", "Title", "No steps yet", "Message", "This workflow has not created any steps."], [1, "run-view"], [1, "run-view-summary"], [1, "run-view-count"], [1, "run-view-skipped"], [1, "run-view-canvas"], [3, "SelectionChanged", "Spec", "RuntimeStatus", "NodePositions", "ReadOnly", "ShowToolbar", "ShowPalette", "ShowProperties", "ShowMinimap", "ShowStatusBar"]], template: function TaskGraphRunViewComponent_Template(rf, ctx) { if (rf & 1) {
265
+ i0.ɵɵconditionalCreate(0, TaskGraphRunViewComponent_Conditional_0_Template, 1, 1, "mj-alert", 0)(1, TaskGraphRunViewComponent_Conditional_1_Template, 2, 0, "div", 1)(2, TaskGraphRunViewComponent_Conditional_2_Template, 1, 0, "mj-empty-state", 2)(3, TaskGraphRunViewComponent_Conditional_3_Template, 7, 14, "div", 3);
266
+ } if (rf & 2) {
267
+ i0.ɵɵconditional(ctx.ErrorMessage ? 0 : ctx.IsLoading ? 1 : !ctx.Spec || ctx.Spec.tasks.length === 0 ? 2 : 3);
268
+ } }, dependencies: [i1.MJEmptyStateComponent, i1.MJAlertComponent, i2.TaskGraphEditorComponent], styles: ["[_nghost-%COMP%] { display: block; }\n\n.run-view[_ngcontent-%COMP%] { display: flex; flex-direction: column; gap: var(--mj-space-2); }\n\n.run-view-summary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-secondary);\n}\n\n.run-view-count[_ngcontent-%COMP%] { font-weight: 500; }\n\n\n\n\n.run-view-skipped[_ngcontent-%COMP%] { color: var(--mj-text-tertiary); }\n\n.run-view-canvas[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n background: var(--mj-bg-surface);\n}\n\n.run-view-loading[_ngcontent-%COMP%] {\n padding: var(--mj-space-4);\n color: var(--mj-text-secondary);\n font-size: var(--mj-font-size-sm);\n}"], changeDetection: 0 });
269
+ }
270
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TaskGraphRunViewComponent, [{
271
+ type: Component,
272
+ args: [{ standalone: false, selector: 'mj-task-graph-run-view', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (ErrorMessage) {\n <mj-alert Variant=\"error\" [Message]=\"ErrorMessage\"></mj-alert>\n} @else if (IsLoading) {\n <div class=\"run-view-loading\">Loading the workflow&rsquo;s steps&hellip;</div>\n} @else if (!Spec || Spec.tasks.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-diagram-project\"\n Title=\"No steps yet\"\n Message=\"This workflow has not created any steps.\">\n </mj-empty-state>\n} @else {\n <div class=\"run-view\">\n <div class=\"run-view-summary\">\n <span class=\"run-view-count\">{{ CompletedCount }} of {{ TotalCount }} complete</span>\n @if (SkippedCount > 0) {\n <span class=\"run-view-skipped\">{{ SkippedCount }} not taken</span>\n }\n </div>\n\n <div class=\"run-view-canvas\" [style.height]=\"Height\">\n <mj-task-graph-editor\n [Spec]=\"Spec\"\n [RuntimeStatus]=\"RuntimeStatus\"\n [NodePositions]=\"Positions\"\n [ReadOnly]=\"true\"\n [ShowToolbar]=\"false\"\n [ShowPalette]=\"false\"\n [ShowProperties]=\"false\"\n [ShowMinimap]=\"false\"\n [ShowStatusBar]=\"false\"\n (SelectionChanged)=\"OnSelectionChanged($event)\">\n </mj-task-graph-editor>\n </div>\n </div>\n}\n", styles: [":host { display: block; }\n\n.run-view { display: flex; flex-direction: column; gap: var(--mj-space-2); }\n\n.run-view-summary {\n display: flex;\n align-items: center;\n gap: var(--mj-space-3);\n font-size: var(--mj-font-size-sm);\n color: var(--mj-text-secondary);\n}\n\n.run-view-count { font-weight: 500; }\n\n/* Not-taken reads as neutral, never as a failure: a skipped branch is a normal outcome, and\n colouring it like an error sends people hunting for a bug that does not exist. */\n.run-view-skipped { color: var(--mj-text-tertiary); }\n\n.run-view-canvas {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n background: var(--mj-bg-surface);\n}\n\n.run-view-loading {\n padding: var(--mj-space-4);\n color: var(--mj-text-secondary);\n font-size: var(--mj-font-size-sm);\n}\n"] }]
273
+ }], () => [{ type: i0.ChangeDetectorRef }], { ParentTaskID: [{
274
+ type: Input
275
+ }], WorkflowName: [{
276
+ type: Input
277
+ }], LiveUpdates: [{
278
+ type: Input
279
+ }], PollIntervalSeconds: [{
280
+ type: Input
281
+ }], Height: [{
282
+ type: Input
283
+ }], NodeSelected: [{
284
+ type: Output
285
+ }], Settled: [{
286
+ type: Output
287
+ }] }); })();
288
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TaskGraphRunViewComponent, { className: "TaskGraphRunViewComponent", filePath: "src/lib/task-graph-run-view.component.ts", lineNumber: 60 }); })();
289
+ //# sourceMappingURL=task-graph-run-view.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-graph-run-view.component.js","sourceRoot":"","sources":["../../src/lib/task-graph-run-view.component.ts","../../src/lib/task-graph-run-view.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Watching a graph that is running, on the canvas its author drew on.\n *\n * **Why this is a component and not three.** A run needs rendering in at least three places — inside\n * an Agent Run, inside the test harness, and on a standalone workflow-runs screen — and every one of\n * them wants the identical thing: the graph, with live status, laid out legibly, with a node you can\n * click. Three implementations would be three chances to render the same run differently, and the\n * one that matters most (is that branch grey because it was *not taken*, or because it *broke*?) is\n * exactly the distinction a second implementation gets wrong.\n *\n * **What it owns and what it doesn't.** It owns data acquisition — reading `MJ: Tasks` and\n * `MJ: Task Dependencies` through the host's provider — and geometry. It does not navigate: a\n * widgets-layer component cannot know whether it sits inside Explorer, so selecting a node emits an\n * event and the host decides what \"open the agent run\" means. That boundary is the whole reason\n * this can live beside the editor rather than in Explorer.\n *\n * @module @memberjunction/ng-task-graph-editor\n */\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n Output,\n} from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { LogError, RunView } from '@memberjunction/core';\nimport { MJTaskEntity, MJTaskDependencyEntity } from '@memberjunction/core-entities';\nimport {\n GraphLayoutBounds,\n LayoutGraphNodes,\n ProjectTaskRowsToSpec,\n type GraphLayoutEdge,\n type GraphNodePosition,\n type TaskGraphSpec,\n} from '@memberjunction/ai-core-plus';\nimport { BuildRuntimeStatus } from './task-graph-runtime-source';\nimport type { TaskGraphRuntimeStatus } from './task-graph-canvas-adapter';\nimport type { TaskGraphSelectionChangedEventArgs } from './task-graph-editor-events';\n\n/** What the host learns when someone clicks a step. */\nexport type TaskGraphRunNodeSelectedEvent = {\n /** The `MJ: Tasks` row id. */\n TaskID: string;\n Task: MJTaskEntity | null;\n};\n\n/** Statuses at which a graph has stopped moving, so polling can stop with it. */\nconst SETTLED = new Set<MJTaskEntity['Status']>(['Complete', 'Failed', 'Cancelled', 'Skipped']);\n\n@Component({\n standalone: false,\n selector: 'mj-task-graph-run-view',\n templateUrl: './task-graph-run-view.component.html',\n styleUrls: ['./task-graph-run-view.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TaskGraphRunViewComponent extends BaseAngularComponent implements OnDestroy {\n /**\n * The graph to watch — the parent `MJ: Tasks` row that represents it.\n *\n * Setter-based rather than `ngOnChanges`, matching the editor beside it: the reaction is\n * explicit and costs nothing on unrelated change-detection passes.\n */\n @Input()\n public set ParentTaskID(value: string | null) {\n if (value === this.parentTaskID) return;\n this.parentTaskID = value;\n void this.load();\n }\n public get ParentTaskID(): string | null {\n return this.parentTaskID;\n }\n\n /** Name shown on the compiled graph. Purely cosmetic; the rows carry the truth. */\n @Input() public WorkflowName: string = 'Workflow';\n\n /**\n * Keep re-reading while the graph is still moving.\n *\n * Off by default: most hosts show a finished run, and a component that polls by default would\n * have every historical run quietly issuing queries forever.\n */\n @Input() public LiveUpdates: boolean = false;\n\n /** How often to re-read while live. Seconds, not milliseconds — this is a human-scale view. */\n @Input() public PollIntervalSeconds: number = 3;\n\n /** Height of the canvas. Hosts embed this in very different amounts of space. */\n @Input() public Height: string = '320px';\n\n @Output() public NodeSelected = new EventEmitter<TaskGraphRunNodeSelectedEvent>();\n /** Emitted once, when every step has reached a terminal status. */\n @Output() public Settled = new EventEmitter<void>();\n\n public Spec: TaskGraphSpec | null = null;\n public RuntimeStatus: TaskGraphRuntimeStatus | null = null;\n public Positions: Map<string, GraphNodePosition> = new Map();\n public IsLoading = false;\n public ErrorMessage: string | null = null;\n\n /** Rows by id, so a selection event can hand the host the whole task rather than an id. */\n private taskByID = new Map<string, MJTaskEntity>();\n private parentTaskID: string | null = null;\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private destroyed = false;\n\n constructor(private cdr: ChangeDetectorRef) {\n super();\n }\n\n public ngOnDestroy(): void {\n this.destroyed = true;\n this.stopPolling();\n }\n\n /** Steps in a terminal state, for the host's summary line. */\n public get CompletedCount(): number {\n return [...this.taskByID.values()].filter((t) => t.Status === 'Complete').length;\n }\n\n public get TotalCount(): number {\n return this.taskByID.size;\n }\n\n public get SkippedCount(): number {\n return [...this.taskByID.values()].filter((t) => t.Status === 'Skipped').length;\n }\n\n /**\n * A node was selected on the canvas.\n *\n * The projected `tempId` IS the task id, so the row lookup is exact — and handing the host the\n * whole task rather than an id spares every host from re-reading a row this component already\n * holds.\n */\n public OnSelectionChanged(args: TaskGraphSelectionChangedEventArgs): void {\n const tempId = args?.Task?.tempId;\n if (!tempId) return;\n this.NodeSelected.emit({ TaskID: tempId, Task: this.taskByID.get(tempId) ?? null });\n }\n\n /**\n * Reads the graph and projects it onto the canvas.\n *\n * `BypassCache` throughout: a run view exists to show what is true *now*, and the whole point of\n * the poll is to see a status that changed a second ago. A cached read would render a stale graph\n * that looks authoritative.\n */\n private async load(): Promise<void> {\n this.stopPolling();\n if (!this.parentTaskID) {\n this.Spec = null;\n this.cdr.markForCheck();\n return;\n }\n\n this.IsLoading = this.Spec === null; // only show the spinner on first load, not on every poll\n this.ErrorMessage = null;\n\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const [tasks, deps] = await rv.RunViews<MJTaskEntity | MJTaskDependencyEntity>([\n {\n EntityName: 'MJ: Tasks',\n ExtraFilter: `ParentID='${this.parentTaskID}'`,\n // Started work in the order it ran, unstarted last, persist order as the\n // tiebreak — the same rule GetAgentRunTree uses, so a graph reads identically\n // wherever it is shown.\n //\n // `__mj_CreatedAt ASC` alone was persist order, which is the COMPILER's walk\n // order, not the author's numbering and not what actually happened: a settled\n // graph listed its steps in an order unrelated to their execution, and a skipped\n // step could sit above work that ran.\n OrderBy: 'CASE WHEN StartedAt IS NULL THEN 1 ELSE 0 END, StartedAt, __mj_CreatedAt',\n ResultType: 'entity_object',\n BypassCache: true,\n },\n {\n EntityName: 'MJ: Task Dependencies',\n ExtraFilter: `TaskID IN (SELECT ID FROM __mj.Task WHERE ParentID='${this.parentTaskID}')`,\n ResultType: 'entity_object',\n BypassCache: true,\n },\n ]);\n\n if (this.destroyed) return;\n\n if (!tasks.Success) {\n this.fail(`The workflow's steps could not be loaded: ${tasks.ErrorMessage}`);\n return;\n }\n\n const taskRows = (tasks.Results ?? []) as MJTaskEntity[];\n const depRows = (deps.Success ? (deps.Results ?? []) : []) as MJTaskDependencyEntity[];\n this.project(taskRows, depRows);\n\n if (this.LiveUpdates && !this.isSettled(taskRows)) this.schedulePoll();\n else if (taskRows.length > 0) this.Settled.emit();\n } catch (e) {\n this.fail(e instanceof Error ? e.message : String(e));\n } finally {\n this.IsLoading = false;\n this.cdr.markForCheck();\n }\n }\n\n /** Rows → spec + runtime + geometry. */\n private project(taskRows: MJTaskEntity[], depRows: MJTaskDependencyEntity[]): void {\n this.taskByID = new Map(taskRows.map((t) => [t.ID, t]));\n\n const projection = ProjectTaskRowsToSpec(this.WorkflowName, taskRows, depRows);\n this.Spec = projection.Spec;\n\n // Correlate by ID, not by name: a projected node's tempId IS the task id, which is the one\n // case where an id match is exact. Name correlation exists for specs whose tempIds were\n // never real, and would be needlessly lossy here.\n const knownIDs = new Set(taskRows.map((t) => t.ID));\n this.RuntimeStatus = BuildRuntimeStatus(taskRows, new Map(), knownIDs);\n\n this.Positions = this.resolvePositions(projection.AuthoredPositions, taskRows, depRows);\n }\n\n /**\n * The author's arrangement where there is one, a computed layout everywhere else.\n *\n * Mixing the two per-node rather than choosing one for the whole graph: a workflow whose author\n * positioned some steps and left others where they fell should keep the positions they chose.\n * A wholly unpositioned graph — the normal case for anything an agent emitted — is laid out\n * entirely by the algorithm.\n */\n private resolvePositions(\n authored: Map<string, GraphNodePosition>,\n taskRows: MJTaskEntity[],\n depRows: MJTaskDependencyEntity[],\n ): Map<string, GraphNodePosition> {\n const missing = taskRows.filter((t) => !authored.has(t.ID)).map((t) => t.ID);\n if (missing.length === 0) return authored;\n\n const edges: GraphLayoutEdge[] = depRows.map((d) => ({ From: d.DependsOnTaskID, To: d.TaskID }));\n const computed = LayoutGraphNodes(taskRows.map((t) => t.ID), edges, { Direction: 'LR' });\n\n const resolved = new Map(computed);\n for (const [id, position] of authored) resolved.set(id, position);\n return resolved;\n }\n\n private isSettled(taskRows: MJTaskEntity[]): boolean {\n return taskRows.length > 0 && taskRows.every((t) => SETTLED.has(t.Status));\n }\n\n private schedulePoll(): void {\n this.pollTimer = setTimeout(() => void this.load(), Math.max(1, this.PollIntervalSeconds) * 1000);\n }\n\n private stopPolling(): void {\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n private fail(message: string): void {\n LogError(`[TaskGraphRunView] ${message}`);\n this.ErrorMessage = message;\n this.cdr.markForCheck();\n }\n\n /** Canvas extent, so a host can size its container to the graph rather than guessing. */\n public get GraphBounds(): { Width: number; Height: number } {\n const box = GraphLayoutBounds(this.Positions);\n return { Width: box.Width, Height: box.Height };\n }\n}\n","@if (ErrorMessage) {\n <mj-alert Variant=\"error\" [Message]=\"ErrorMessage\"></mj-alert>\n} @else if (IsLoading) {\n <div class=\"run-view-loading\">Loading the workflow&rsquo;s steps&hellip;</div>\n} @else if (!Spec || Spec.tasks.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-diagram-project\"\n Title=\"No steps yet\"\n Message=\"This workflow has not created any steps.\">\n </mj-empty-state>\n} @else {\n <div class=\"run-view\">\n <div class=\"run-view-summary\">\n <span class=\"run-view-count\">{{ CompletedCount }} of {{ TotalCount }} complete</span>\n @if (SkippedCount > 0) {\n <span class=\"run-view-skipped\">{{ SkippedCount }} not taken</span>\n }\n </div>\n\n <div class=\"run-view-canvas\" [style.height]=\"Height\">\n <mj-task-graph-editor\n [Spec]=\"Spec\"\n [RuntimeStatus]=\"RuntimeStatus\"\n [NodePositions]=\"Positions\"\n [ReadOnly]=\"true\"\n [ShowToolbar]=\"false\"\n [ShowPalette]=\"false\"\n [ShowProperties]=\"false\"\n [ShowMinimap]=\"false\"\n [ShowStatusBar]=\"false\"\n (SelectionChanged)=\"OnSelectionChanged($event)\">\n </mj-task-graph-editor>\n </div>\n </div>\n}\n"]}
@@ -3,5 +3,6 @@ export * from './lib/task-graph-editor.component';
3
3
  export * from './lib/task-graph-editor-events';
4
4
  export * from './lib/task-graph-canvas-adapter';
5
5
  export * from './lib/task-graph-properties-panel.component';
6
+ export * from './lib/task-graph-run-view.component';
6
7
  export * from './lib/task-graph-runtime-source';
7
8
  //# sourceMappingURL=public-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAQA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,iCAAiC,CAAC"}
1
+ {"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAQA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,qCAAqC,CAAC;AACpD,cAAc,iCAAiC,CAAC"}
@@ -11,5 +11,6 @@ export * from './lib/task-graph-editor.component';
11
11
  export * from './lib/task-graph-editor-events';
12
12
  export * from './lib/task-graph-canvas-adapter';
13
13
  export * from './lib/task-graph-properties-panel.component';
14
+ export * from './lib/task-graph-run-view.component';
14
15
  export * from './lib/task-graph-runtime-source';
15
16
  //# sourceMappingURL=public-api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,iCAAiC,CAAC","sourcesContent":["/*\n * Public API Surface of @memberjunction/ng-task-graph-editor\n *\n * A `widgets`-layer component for viewing and editing a `TaskGraphSpec` — the one graph contract\n * shared by design-time flows and runtime task graphs. Import `TaskGraphSpec` itself from\n * `@memberjunction/ai-core-plus`; this package deliberately does not re-export it (see the\n * no-cross-package-re-exports rule in `.claude/rules/typescript-style.md`).\n */\nexport * from './lib/task-graph-editor.module';\nexport * from './lib/task-graph-editor.component';\nexport * from './lib/task-graph-editor-events';\nexport * from './lib/task-graph-canvas-adapter';\nexport * from './lib/task-graph-properties-panel.component';\nexport * from './lib/task-graph-runtime-source';\n"]}
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,qCAAqC,CAAC;AACpD,cAAc,iCAAiC,CAAC","sourcesContent":["/*\n * Public API Surface of @memberjunction/ng-task-graph-editor\n *\n * A `widgets`-layer component for viewing and editing a `TaskGraphSpec` — the one graph contract\n * shared by design-time flows and runtime task graphs. Import `TaskGraphSpec` itself from\n * `@memberjunction/ai-core-plus`; this package deliberately does not re-export it (see the\n * no-cross-package-re-exports rule in `.claude/rules/typescript-style.md`).\n */\nexport * from './lib/task-graph-editor.module';\nexport * from './lib/task-graph-editor.component';\nexport * from './lib/task-graph-editor-events';\nexport * from './lib/task-graph-canvas-adapter';\nexport * from './lib/task-graph-properties-panel.component';\nexport * from './lib/task-graph-run-view.component';\nexport * from './lib/task-graph-runtime-source';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-task-graph-editor",
3
- "version": "6.1.0-edge.1",
3
+ "version": "6.1.0-edge.2",
4
4
  "description": "MemberJunction: View and edit a TaskGraphSpec on a canvas — the one graph contract shared by design-time flows and runtime task graphs",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -20,7 +20,7 @@
20
20
  "devDependencies": {
21
21
  "@angular/compiler": "21.1.3",
22
22
  "@angular/compiler-cli": "21.1.3",
23
- "@memberjunction/ng-test-utils": "6.1.0-edge.1",
23
+ "@memberjunction/ng-test-utils": "6.1.0-edge.2",
24
24
  "sass": "^1.97.3",
25
25
  "vitest": "^4.0.18"
26
26
  },
@@ -31,12 +31,13 @@
31
31
  "@angular/platform-browser": "^21.1.3"
32
32
  },
33
33
  "dependencies": {
34
- "@memberjunction/ai-core-plus": "6.1.0-edge.1",
35
- "@memberjunction/core": "6.1.0-edge.1",
36
- "@memberjunction/global": "6.1.0-edge.1",
37
- "@memberjunction/ng-base-types": "6.1.0-edge.1",
38
- "@memberjunction/ng-flow-editor": "6.1.0-edge.1",
39
- "@memberjunction/ng-ui-components": "6.1.0-edge.1",
34
+ "@memberjunction/ai-core-plus": "6.1.0-edge.2",
35
+ "@memberjunction/core": "6.1.0-edge.2",
36
+ "@memberjunction/core-entities": "6.1.0-edge.2",
37
+ "@memberjunction/global": "6.1.0-edge.2",
38
+ "@memberjunction/ng-base-types": "6.1.0-edge.2",
39
+ "@memberjunction/ng-flow-editor": "6.1.0-edge.2",
40
+ "@memberjunction/ng-ui-components": "6.1.0-edge.2",
40
41
  "rxjs": "^7.8.2",
41
42
  "tslib": "^2.8.1"
42
43
  },