@memberjunction/ng-core-entity-forms 2.74.0 → 2.76.0
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/README.md +124 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-diagram.component.d.ts +109 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-diagram.component.d.ts.map +1 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-diagram.component.js +2020 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-diagram.component.js.map +1 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-form-section.component.d.ts +32 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-form-section.component.d.ts.map +1 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-form-section.component.js +413 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-form-section.component.js.map +1 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/step-info-control.component.d.ts +9 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/step-info-control.component.d.ts.map +1 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/step-info-control.component.js +84 -0
- package/dist/lib/custom/AIAgents/FlowAgentType/step-info-control.component.js.map +1 -0
- package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts +34 -6
- package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/ai-agent-form.component.js +656 -520
- package/dist/lib/custom/AIAgents/ai-agent-form.component.js.map +1 -1
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts +11 -6
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts.map +1 -1
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js +459 -448
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js.map +1 -1
- package/dist/lib/custom/Actions/action-execution-log-form.component.js +51 -49
- package/dist/lib/custom/Actions/action-execution-log-form.component.js.map +1 -1
- package/dist/lib/custom/Actions/action-test-harness.component.d.ts.map +1 -1
- package/dist/lib/custom/Actions/action-test-harness.component.js +5 -3
- package/dist/lib/custom/Actions/action-test-harness.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.d.ts +35 -7
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.js +235 -219
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.d.ts +37 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.d.ts.map +1 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.js +117 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.js.map +1 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.d.ts +49 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.d.ts.map +1 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js +211 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js.map +1 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.d.ts +33 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.d.ts.map +1 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.js +265 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.js.map +1 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.js +0 -8
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts +4 -8
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js +47 -189
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-visualization.component.d.ts +71 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-visualization.component.d.ts.map +1 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-visualization.component.js +931 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-visualization.component.js.map +1 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts +10 -4
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js +257 -295
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js.map +1 -1
- package/dist/lib/custom/custom-forms.module.d.ts +30 -25
- package/dist/lib/custom/custom-forms.module.d.ts.map +1 -1
- package/dist/lib/custom/custom-forms.module.js +31 -4
- package/dist/lib/custom/custom-forms.module.js.map +1 -1
- package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.js +34 -14
- package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/AIAgentStep/aiagentstep.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/AIAgentStep/aiagentstep.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIAgentStep/aiagentstep.form.component.js +80 -0
- package/dist/lib/generated/Entities/AIAgentStep/aiagentstep.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIAgentStep/sections/details.component.d.ts +11 -0
- package/dist/lib/generated/Entities/AIAgentStep/sections/details.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIAgentStep/sections/details.component.js +277 -0
- package/dist/lib/generated/Entities/AIAgentStep/sections/details.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIAgentStepPath/aiagentsteppath.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/AIAgentStepPath/aiagentsteppath.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIAgentStepPath/aiagentsteppath.form.component.js +59 -0
- package/dist/lib/generated/Entities/AIAgentStepPath/aiagentsteppath.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIAgentStepPath/sections/details.component.d.ts +11 -0
- package/dist/lib/generated/Entities/AIAgentStepPath/sections/details.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIAgentStepPath/sections/details.component.js +147 -0
- package/dist/lib/generated/Entities/AIAgentStepPath/sections/details.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIAgentType/sections/details.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/AIAgentType/sections/details.component.js +31 -4
- package/dist/lib/generated/Entities/AIAgentType/sections/details.component.js.map +1 -1
- package/dist/lib/generated/Entities/AIPrompt/aiprompt.form.component.js +16 -6
- package/dist/lib/generated/Entities/AIPrompt/aiprompt.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/AIPromptRun/sections/details.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/AIPromptRun/sections/details.component.js +15 -4
- package/dist/lib/generated/Entities/AIPromptRun/sections/details.component.js.map +1 -1
- package/dist/lib/generated/Entities/Action/action.form.component.js +19 -9
- package/dist/lib/generated/Entities/Action/action.form.component.js.map +1 -1
- package/dist/lib/generated/generated-forms.module.d.ts +294 -285
- package/dist/lib/generated/generated-forms.module.d.ts.map +1 -1
- package/dist/lib/generated/generated-forms.module.js +183 -110
- package/dist/lib/generated/generated-forms.module.js.map +1 -1
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +21 -17
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
import { Component, Input, ViewChild } from '@angular/core';
|
|
2
|
+
import { Subject, combineLatest } from 'rxjs';
|
|
3
|
+
import { takeUntil } from 'rxjs/operators';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
import * as i1 from "./ai-agent-run-data.service";
|
|
6
|
+
import * as i2 from "@progress/kendo-angular-layout";
|
|
7
|
+
import * as i3 from "./ai-agent-run-step-detail.component";
|
|
8
|
+
const _c0 = ["svgContainer"];
|
|
9
|
+
function AIAgentRunVisualizationComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
10
|
+
i0.ɵɵelementStart(0, "div", 2);
|
|
11
|
+
i0.ɵɵelement(1, "i", 4);
|
|
12
|
+
i0.ɵɵelementStart(2, "span");
|
|
13
|
+
i0.ɵɵtext(3);
|
|
14
|
+
i0.ɵɵelementEnd()();
|
|
15
|
+
} if (rf & 2) {
|
|
16
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
17
|
+
i0.ɵɵadvance(3);
|
|
18
|
+
i0.ɵɵtextInterpolate(ctx_r0.error);
|
|
19
|
+
} }
|
|
20
|
+
function AIAgentRunVisualizationComponent_Conditional_2_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
21
|
+
i0.ɵɵelementStart(0, "div", 18);
|
|
22
|
+
i0.ɵɵelement(1, "i", 20);
|
|
23
|
+
i0.ɵɵelementStart(2, "span");
|
|
24
|
+
i0.ɵɵtext(3, "Loading visualization...");
|
|
25
|
+
i0.ɵɵelementEnd()();
|
|
26
|
+
} }
|
|
27
|
+
function AIAgentRunVisualizationComponent_Conditional_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
28
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
29
|
+
i0.ɵɵelementStart(0, "kendo-splitter-pane", 19)(1, "mj-ai-agent-run-step-detail", 21);
|
|
30
|
+
i0.ɵɵlistener("closePanel", function AIAgentRunVisualizationComponent_Conditional_2_Conditional_18_Template_mj_ai_agent_run_step_detail_closePanel_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.closeDetailPane()); })("navigateToActionLog", function AIAgentRunVisualizationComponent_Conditional_2_Conditional_18_Template_mj_ai_agent_run_step_detail_navigateToActionLog_1_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.navigateToActionLog($event)); })("copyToClipboard", function AIAgentRunVisualizationComponent_Conditional_2_Conditional_18_Template_mj_ai_agent_run_step_detail_copyToClipboard_1_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.copyToClipboard($event)); });
|
|
31
|
+
i0.ɵɵelementEnd()();
|
|
32
|
+
} if (rf & 2) {
|
|
33
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
34
|
+
i0.ɵɵproperty("size", "400px")("min", "300px")("max", "600px")("collapsible", true);
|
|
35
|
+
i0.ɵɵadvance();
|
|
36
|
+
i0.ɵɵproperty("selectedTimelineItem", ctx_r0.selectedItem);
|
|
37
|
+
} }
|
|
38
|
+
function AIAgentRunVisualizationComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
39
|
+
const _r2 = i0.ɵɵgetCurrentView();
|
|
40
|
+
i0.ɵɵelementStart(0, "kendo-splitter", 3)(1, "kendo-splitter-pane", 5)(2, "div", 6, 0)(4, "div", 7)(5, "div", 8);
|
|
41
|
+
i0.ɵɵelement(6, "i", 9);
|
|
42
|
+
i0.ɵɵtext(7, " Agent Execution Flow ");
|
|
43
|
+
i0.ɵɵelementEnd();
|
|
44
|
+
i0.ɵɵelementStart(8, "div", 10)(9, "button", 11);
|
|
45
|
+
i0.ɵɵlistener("click", function AIAgentRunVisualizationComponent_Conditional_2_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.zoomIn()); });
|
|
46
|
+
i0.ɵɵelement(10, "i", 12);
|
|
47
|
+
i0.ɵɵelementEnd();
|
|
48
|
+
i0.ɵɵelementStart(11, "button", 13);
|
|
49
|
+
i0.ɵɵlistener("click", function AIAgentRunVisualizationComponent_Conditional_2_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.zoomOut()); });
|
|
50
|
+
i0.ɵɵelement(12, "i", 14);
|
|
51
|
+
i0.ɵɵelementEnd();
|
|
52
|
+
i0.ɵɵelementStart(13, "button", 15);
|
|
53
|
+
i0.ɵɵlistener("click", function AIAgentRunVisualizationComponent_Conditional_2_Template_button_click_13_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.resetZoom()); });
|
|
54
|
+
i0.ɵɵelement(14, "i", 16);
|
|
55
|
+
i0.ɵɵelementEnd();
|
|
56
|
+
i0.ɵɵelementStart(15, "span", 17);
|
|
57
|
+
i0.ɵɵtext(16);
|
|
58
|
+
i0.ɵɵelementEnd()()();
|
|
59
|
+
i0.ɵɵtemplate(17, AIAgentRunVisualizationComponent_Conditional_2_Conditional_17_Template, 4, 0, "div", 18);
|
|
60
|
+
i0.ɵɵelementEnd()();
|
|
61
|
+
i0.ɵɵtemplate(18, AIAgentRunVisualizationComponent_Conditional_2_Conditional_18_Template, 2, 5, "kendo-splitter-pane", 19);
|
|
62
|
+
i0.ɵɵelementEnd();
|
|
63
|
+
} if (rf & 2) {
|
|
64
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
65
|
+
i0.ɵɵadvance();
|
|
66
|
+
i0.ɵɵproperty("collapsible", false);
|
|
67
|
+
i0.ɵɵadvance(15);
|
|
68
|
+
i0.ɵɵtextInterpolate1("", (ctx_r0.panZoom.scale * 100).toFixed(0), "%");
|
|
69
|
+
i0.ɵɵadvance();
|
|
70
|
+
i0.ɵɵconditional(ctx_r0.loading ? 17 : -1);
|
|
71
|
+
i0.ɵɵadvance();
|
|
72
|
+
i0.ɵɵconditional(ctx_r0.selectedItem ? 18 : -1);
|
|
73
|
+
} }
|
|
74
|
+
export class AIAgentRunVisualizationComponent {
|
|
75
|
+
constructor(cdr, dataService) {
|
|
76
|
+
this.cdr = cdr;
|
|
77
|
+
this.dataService = dataService;
|
|
78
|
+
this.destroy$ = new Subject();
|
|
79
|
+
this.viewInitialized = false;
|
|
80
|
+
this.pendingData = null;
|
|
81
|
+
this.loading = false; // Start with false so the container renders
|
|
82
|
+
this.error = null;
|
|
83
|
+
this.dataLoading = true; // Track data loading separately
|
|
84
|
+
this.selectedItem = null;
|
|
85
|
+
// Node management
|
|
86
|
+
this.nodes = new Map();
|
|
87
|
+
this.scopes = new Map();
|
|
88
|
+
this.connections = [];
|
|
89
|
+
// Pan and zoom state
|
|
90
|
+
this.panZoom = {
|
|
91
|
+
scale: 1,
|
|
92
|
+
translateX: 0,
|
|
93
|
+
translateY: 0,
|
|
94
|
+
isPanning: false,
|
|
95
|
+
startX: 0,
|
|
96
|
+
startY: 0
|
|
97
|
+
};
|
|
98
|
+
// Drag state
|
|
99
|
+
this.dragState = {
|
|
100
|
+
isDragging: false,
|
|
101
|
+
element: null,
|
|
102
|
+
nodeId: null,
|
|
103
|
+
startX: 0,
|
|
104
|
+
startY: 0,
|
|
105
|
+
startTransform: { x: 0, y: 0 }
|
|
106
|
+
};
|
|
107
|
+
this.onMouseMove = (event) => {
|
|
108
|
+
if (!this.dragState.isDragging || !this.dragState.element)
|
|
109
|
+
return;
|
|
110
|
+
const svg = this.dragState.element.ownerSVGElement;
|
|
111
|
+
if (!svg)
|
|
112
|
+
return;
|
|
113
|
+
const pt = svg.createSVGPoint();
|
|
114
|
+
pt.x = event.clientX;
|
|
115
|
+
pt.y = event.clientY;
|
|
116
|
+
const svgP = pt.matrixTransform(svg.getScreenCTM()?.inverse());
|
|
117
|
+
const dx = svgP.x - this.dragState.startX;
|
|
118
|
+
const dy = svgP.y - this.dragState.startY;
|
|
119
|
+
const newX = this.dragState.startTransform.x + dx;
|
|
120
|
+
const newY = this.dragState.startTransform.y + dy;
|
|
121
|
+
// Update element position
|
|
122
|
+
this.dragState.element.setAttribute('transform', `translate(${newX}, ${newY})`);
|
|
123
|
+
// Update node data
|
|
124
|
+
const node = this.nodes.get(this.dragState.nodeId);
|
|
125
|
+
if (node) {
|
|
126
|
+
node.x = newX;
|
|
127
|
+
node.y = newY;
|
|
128
|
+
}
|
|
129
|
+
// Update connections
|
|
130
|
+
this.drawConnections();
|
|
131
|
+
};
|
|
132
|
+
this.onMouseUp = (event) => {
|
|
133
|
+
if (!this.dragState.isDragging || !this.dragState.element)
|
|
134
|
+
return;
|
|
135
|
+
this.dragState.element.classList.remove('dragging');
|
|
136
|
+
// Clean up
|
|
137
|
+
this.dragState = {
|
|
138
|
+
isDragging: false,
|
|
139
|
+
element: null,
|
|
140
|
+
nodeId: null,
|
|
141
|
+
startX: 0,
|
|
142
|
+
startY: 0,
|
|
143
|
+
startTransform: { x: 0, y: 0 }
|
|
144
|
+
};
|
|
145
|
+
document.removeEventListener('mousemove', this.onMouseMove);
|
|
146
|
+
document.removeEventListener('mouseup', this.onMouseUp);
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
ngOnInit() {
|
|
150
|
+
if (this.aiAgentRunId) {
|
|
151
|
+
// Subscribe to data from service
|
|
152
|
+
combineLatest([
|
|
153
|
+
this.dataService.steps$,
|
|
154
|
+
this.dataService.subRuns$,
|
|
155
|
+
this.dataService.actionLogs$,
|
|
156
|
+
this.dataService.promptRuns$,
|
|
157
|
+
this.dataService.loading$
|
|
158
|
+
]).pipe(takeUntil(this.destroy$)).subscribe(([steps, subRuns, actionLogs, promptRuns, loading]) => {
|
|
159
|
+
if (!loading && steps && steps.length > 0) {
|
|
160
|
+
console.log('Visualization: Received data from service', {
|
|
161
|
+
steps: steps.length,
|
|
162
|
+
subRuns: subRuns.length,
|
|
163
|
+
viewInitialized: this.viewInitialized
|
|
164
|
+
});
|
|
165
|
+
if (this.viewInitialized && this.svgContainer?.nativeElement) {
|
|
166
|
+
// View is ready, build immediately
|
|
167
|
+
this.buildVisualization(steps, subRuns, actionLogs, promptRuns);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
// Store data for when view is ready
|
|
171
|
+
console.log('View not ready, storing data for later');
|
|
172
|
+
this.pendingData = { steps, subRuns, actionLogs, promptRuns };
|
|
173
|
+
this.loading = false; // Show the container so it can render
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else if (!loading && (!steps || steps.length === 0)) {
|
|
177
|
+
console.log('Visualization: No steps available');
|
|
178
|
+
this.loading = false;
|
|
179
|
+
this.error = null;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
// Subscribe to error state
|
|
183
|
+
this.dataService.error$.pipe(takeUntil(this.destroy$)).subscribe(error => {
|
|
184
|
+
if (error) {
|
|
185
|
+
this.error = error;
|
|
186
|
+
this.loading = false;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
console.error('Visualization: No agent run ID provided');
|
|
192
|
+
this.error = 'No agent run ID provided';
|
|
193
|
+
this.loading = false;
|
|
194
|
+
this.dataLoading = false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
ngAfterViewInit() {
|
|
198
|
+
console.log('Visualization ngAfterViewInit');
|
|
199
|
+
this.viewInitialized = true;
|
|
200
|
+
// Initialize SVG
|
|
201
|
+
if (this.svgContainer && this.svgContainer.nativeElement) {
|
|
202
|
+
this.initializeSVG();
|
|
203
|
+
// If we have pending data, process it now
|
|
204
|
+
if (this.pendingData) {
|
|
205
|
+
console.log('Processing pending data after view init');
|
|
206
|
+
this.buildVisualization(this.pendingData.steps, this.pendingData.subRuns, this.pendingData.actionLogs, this.pendingData.promptRuns);
|
|
207
|
+
this.pendingData = null;
|
|
208
|
+
}
|
|
209
|
+
this.cdr.detectChanges();
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
console.error('SVG container not found in ngAfterViewInit');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
ngOnDestroy() {
|
|
216
|
+
this.destroy$.next();
|
|
217
|
+
this.destroy$.complete();
|
|
218
|
+
// Clean up drag listeners
|
|
219
|
+
if (this.dragState.isDragging) {
|
|
220
|
+
document.removeEventListener('mousemove', this.onMouseMove);
|
|
221
|
+
document.removeEventListener('mouseup', this.onMouseUp);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
initializeSVG() {
|
|
225
|
+
const container = this.svgContainer?.nativeElement;
|
|
226
|
+
if (!container) {
|
|
227
|
+
console.error('SVG container element not found');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// Create SVG element if it doesn't exist
|
|
231
|
+
let svg = container.querySelector('svg');
|
|
232
|
+
if (!svg) {
|
|
233
|
+
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
234
|
+
svg.setAttribute('class', 'visualization-svg');
|
|
235
|
+
svg.setAttribute('width', '100%');
|
|
236
|
+
svg.setAttribute('height', '100%');
|
|
237
|
+
// Add event listeners
|
|
238
|
+
svg.addEventListener('wheel', (e) => this.onWheel(e));
|
|
239
|
+
svg.addEventListener('mousedown', (e) => this.onSvgMouseDown(e));
|
|
240
|
+
svg.addEventListener('mousemove', (e) => this.onSvgMouseMove(e));
|
|
241
|
+
svg.addEventListener('mouseup', (e) => this.onSvgMouseUp(e));
|
|
242
|
+
container.appendChild(svg);
|
|
243
|
+
}
|
|
244
|
+
// Add arrow marker definition
|
|
245
|
+
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
|
|
246
|
+
const marker = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
|
|
247
|
+
marker.setAttribute('id', 'arrowhead-viz');
|
|
248
|
+
marker.setAttribute('markerWidth', '10');
|
|
249
|
+
marker.setAttribute('markerHeight', '7');
|
|
250
|
+
marker.setAttribute('refX', '9');
|
|
251
|
+
marker.setAttribute('refY', '3.5');
|
|
252
|
+
marker.setAttribute('orient', 'auto');
|
|
253
|
+
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
254
|
+
polygon.setAttribute('points', '0 0, 10 3.5, 0 7');
|
|
255
|
+
polygon.setAttribute('fill', '#4a90e2');
|
|
256
|
+
marker.appendChild(polygon);
|
|
257
|
+
defs.appendChild(marker);
|
|
258
|
+
svg.appendChild(defs);
|
|
259
|
+
console.log('SVG initialized successfully');
|
|
260
|
+
}
|
|
261
|
+
async buildVisualization(steps, subRuns, actionLogs, promptRuns) {
|
|
262
|
+
console.log('buildVisualization called', {
|
|
263
|
+
steps: steps?.length || 0,
|
|
264
|
+
hasContainer: !!this.svgContainer,
|
|
265
|
+
containerElement: this.svgContainer?.nativeElement
|
|
266
|
+
});
|
|
267
|
+
if (!this.svgContainer || !this.svgContainer.nativeElement) {
|
|
268
|
+
console.error('SVG container not available');
|
|
269
|
+
this.loading = false;
|
|
270
|
+
this.error = 'Visualization container not ready';
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// If no steps, show empty state
|
|
274
|
+
if (!steps || steps.length === 0) {
|
|
275
|
+
console.log('No steps to visualize');
|
|
276
|
+
this.loading = false;
|
|
277
|
+
this.error = null;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
this.loading = true;
|
|
281
|
+
this.error = null;
|
|
282
|
+
try {
|
|
283
|
+
// Wait a tick for the view to be fully ready
|
|
284
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
285
|
+
// Clear existing elements
|
|
286
|
+
this.clearVisualization();
|
|
287
|
+
// Build nodes and scopes
|
|
288
|
+
await this.buildNodesAndScopes(steps, subRuns, promptRuns);
|
|
289
|
+
// Arrange layout
|
|
290
|
+
this.arrangeLayout();
|
|
291
|
+
// Draw connections
|
|
292
|
+
this.drawConnections();
|
|
293
|
+
// Fit to view
|
|
294
|
+
this.fitToView();
|
|
295
|
+
this.loading = false;
|
|
296
|
+
console.log('Visualization built successfully');
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
this.error = 'Failed to build visualization';
|
|
300
|
+
console.error('Visualization error:', error);
|
|
301
|
+
this.loading = false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
clearVisualization() {
|
|
305
|
+
const svg = this.svgContainer?.nativeElement?.querySelector('svg');
|
|
306
|
+
if (!svg)
|
|
307
|
+
return;
|
|
308
|
+
const mainGroup = svg.querySelector('.main-group');
|
|
309
|
+
if (mainGroup) {
|
|
310
|
+
// Remove all child elements except defs
|
|
311
|
+
while (mainGroup.firstChild) {
|
|
312
|
+
mainGroup.removeChild(mainGroup.firstChild);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
this.nodes.clear();
|
|
316
|
+
this.scopes.clear();
|
|
317
|
+
this.connections = [];
|
|
318
|
+
}
|
|
319
|
+
async buildNodesAndScopes(steps, subRuns, promptRuns) {
|
|
320
|
+
const svg = this.svgContainer?.nativeElement?.querySelector('svg');
|
|
321
|
+
if (!svg)
|
|
322
|
+
return;
|
|
323
|
+
let mainGroup = svg.querySelector('.main-group');
|
|
324
|
+
if (!mainGroup) {
|
|
325
|
+
mainGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
326
|
+
mainGroup.setAttribute('class', 'main-group');
|
|
327
|
+
svg.appendChild(mainGroup);
|
|
328
|
+
}
|
|
329
|
+
// Create nodes for each step
|
|
330
|
+
for (let i = 0; i < steps.length; i++) {
|
|
331
|
+
const step = steps[i];
|
|
332
|
+
if (step.StepType === 'Sub-Agent' && step.TargetLogID) {
|
|
333
|
+
// Create a scope for sub-agent
|
|
334
|
+
const subRun = subRuns.find(sr => sr.ID === step.TargetLogID);
|
|
335
|
+
const scopeElement = await this.createScopeElement(step, subRun);
|
|
336
|
+
mainGroup.appendChild(scopeElement);
|
|
337
|
+
const scope = {
|
|
338
|
+
id: step.ID,
|
|
339
|
+
name: subRun?.Agent || 'Sub-Agent',
|
|
340
|
+
x: 0,
|
|
341
|
+
y: 0,
|
|
342
|
+
width: 300,
|
|
343
|
+
height: 200,
|
|
344
|
+
expanded: false,
|
|
345
|
+
nodes: [],
|
|
346
|
+
element: scopeElement
|
|
347
|
+
};
|
|
348
|
+
this.scopes.set(step.ID, scope);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
// Create regular node
|
|
352
|
+
const nodeElement = this.createNodeElement(step, promptRuns);
|
|
353
|
+
mainGroup.appendChild(nodeElement);
|
|
354
|
+
const nodeData = {
|
|
355
|
+
step: step,
|
|
356
|
+
x: 0,
|
|
357
|
+
y: 0,
|
|
358
|
+
width: 180,
|
|
359
|
+
height: 80,
|
|
360
|
+
element: nodeElement
|
|
361
|
+
};
|
|
362
|
+
this.nodes.set(step.ID, nodeData);
|
|
363
|
+
}
|
|
364
|
+
// Add connection info
|
|
365
|
+
if (i > 0) {
|
|
366
|
+
this.connections.push({
|
|
367
|
+
from: steps[i - 1].ID,
|
|
368
|
+
to: step.ID
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
createNodeElement(step, promptRuns) {
|
|
374
|
+
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
375
|
+
g.setAttribute('class', 'step-node');
|
|
376
|
+
g.setAttribute('data-step-id', step.ID);
|
|
377
|
+
// Add event handlers
|
|
378
|
+
g.addEventListener('click', (e) => this.onNodeClick(e, step));
|
|
379
|
+
g.addEventListener('mousedown', (e) => this.onNodeMouseDown(e, step.ID));
|
|
380
|
+
g.style.cursor = 'pointer';
|
|
381
|
+
const nodeWidth = 180;
|
|
382
|
+
const nodeHeight = 80;
|
|
383
|
+
// Background rectangle
|
|
384
|
+
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
385
|
+
rect.setAttribute('width', nodeWidth.toString());
|
|
386
|
+
rect.setAttribute('height', nodeHeight.toString());
|
|
387
|
+
rect.setAttribute('rx', '8');
|
|
388
|
+
rect.setAttribute('fill', 'white');
|
|
389
|
+
rect.setAttribute('stroke', this.getStepColor(step));
|
|
390
|
+
rect.setAttribute('stroke-width', '2');
|
|
391
|
+
g.appendChild(rect);
|
|
392
|
+
// Status indicator
|
|
393
|
+
const statusCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
394
|
+
statusCircle.setAttribute('cx', '12');
|
|
395
|
+
statusCircle.setAttribute('cy', '12');
|
|
396
|
+
statusCircle.setAttribute('r', '6');
|
|
397
|
+
statusCircle.setAttribute('fill', this.getStatusColor(step.Status));
|
|
398
|
+
g.appendChild(statusCircle);
|
|
399
|
+
// Icon
|
|
400
|
+
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
401
|
+
icon.setAttribute('x', '35');
|
|
402
|
+
icon.setAttribute('y', '20');
|
|
403
|
+
icon.setAttribute('font-size', '18');
|
|
404
|
+
icon.setAttribute('fill', this.getStepColor(step));
|
|
405
|
+
icon.textContent = this.getStepEmoji(step.StepType);
|
|
406
|
+
g.appendChild(icon);
|
|
407
|
+
// Title
|
|
408
|
+
const title = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
409
|
+
title.setAttribute('x', '60');
|
|
410
|
+
title.setAttribute('y', '20');
|
|
411
|
+
title.setAttribute('font-size', '14');
|
|
412
|
+
title.setAttribute('font-weight', '600');
|
|
413
|
+
title.setAttribute('fill', '#2c3e50');
|
|
414
|
+
title.textContent = this.truncateText(step.StepName || `Step ${step.StepNumber}`, 15);
|
|
415
|
+
g.appendChild(title);
|
|
416
|
+
// Step type
|
|
417
|
+
const type = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
418
|
+
type.setAttribute('x', '12');
|
|
419
|
+
type.setAttribute('y', '40');
|
|
420
|
+
type.setAttribute('font-size', '12');
|
|
421
|
+
type.setAttribute('fill', '#6c757d');
|
|
422
|
+
type.textContent = step.StepType;
|
|
423
|
+
g.appendChild(type);
|
|
424
|
+
// Model info for prompts
|
|
425
|
+
if (step.StepType === 'Prompt' && step.TargetLogID && promptRuns) {
|
|
426
|
+
const promptRun = promptRuns.find(pr => pr.ID === step.TargetLogID);
|
|
427
|
+
if (promptRun) {
|
|
428
|
+
const model = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
429
|
+
model.setAttribute('x', '12');
|
|
430
|
+
model.setAttribute('y', '55');
|
|
431
|
+
model.setAttribute('font-size', '11');
|
|
432
|
+
model.setAttribute('fill', '#868e96');
|
|
433
|
+
model.textContent = `${promptRun.Model || 'Unknown'}`;
|
|
434
|
+
g.appendChild(model);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// Duration
|
|
438
|
+
if (step.CompletedAt) {
|
|
439
|
+
const duration = this.calculateDuration(step.StartedAt, step.CompletedAt);
|
|
440
|
+
const durationText = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
441
|
+
durationText.setAttribute('x', '12');
|
|
442
|
+
durationText.setAttribute('y', '70');
|
|
443
|
+
durationText.setAttribute('font-size', '11');
|
|
444
|
+
durationText.setAttribute('fill', '#868e96');
|
|
445
|
+
durationText.textContent = `⏱️ ${duration}`;
|
|
446
|
+
g.appendChild(durationText);
|
|
447
|
+
}
|
|
448
|
+
return g;
|
|
449
|
+
}
|
|
450
|
+
async createScopeElement(step, subRun) {
|
|
451
|
+
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
452
|
+
g.setAttribute('class', 'scope-container');
|
|
453
|
+
g.setAttribute('data-scope-id', step.ID);
|
|
454
|
+
const scopeWidth = 300;
|
|
455
|
+
const scopeHeight = 200;
|
|
456
|
+
const headerHeight = 40;
|
|
457
|
+
// Background
|
|
458
|
+
const bg = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
459
|
+
bg.setAttribute('width', scopeWidth.toString());
|
|
460
|
+
bg.setAttribute('height', scopeHeight.toString());
|
|
461
|
+
bg.setAttribute('rx', '12');
|
|
462
|
+
bg.setAttribute('fill', '#f8f9fa');
|
|
463
|
+
bg.setAttribute('stroke', '#4a90e2');
|
|
464
|
+
bg.setAttribute('stroke-width', '2');
|
|
465
|
+
bg.setAttribute('stroke-dasharray', '5,5');
|
|
466
|
+
g.appendChild(bg);
|
|
467
|
+
// Header background
|
|
468
|
+
const headerBg = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
469
|
+
headerBg.setAttribute('width', scopeWidth.toString());
|
|
470
|
+
headerBg.setAttribute('height', headerHeight.toString());
|
|
471
|
+
headerBg.setAttribute('rx', '12');
|
|
472
|
+
headerBg.setAttribute('fill', '#e3f2fd');
|
|
473
|
+
g.appendChild(headerBg);
|
|
474
|
+
// Fix bottom corners
|
|
475
|
+
const headerFix = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
476
|
+
headerFix.setAttribute('y', (headerHeight - 12).toString());
|
|
477
|
+
headerFix.setAttribute('width', scopeWidth.toString());
|
|
478
|
+
headerFix.setAttribute('height', '12');
|
|
479
|
+
headerFix.setAttribute('fill', '#e3f2fd');
|
|
480
|
+
g.appendChild(headerFix);
|
|
481
|
+
// Robot icon
|
|
482
|
+
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
483
|
+
icon.setAttribute('x', '12');
|
|
484
|
+
icon.setAttribute('y', '26');
|
|
485
|
+
icon.setAttribute('font-size', '20');
|
|
486
|
+
icon.textContent = '🤖';
|
|
487
|
+
g.appendChild(icon);
|
|
488
|
+
// Title
|
|
489
|
+
const title = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
490
|
+
title.setAttribute('x', '40');
|
|
491
|
+
title.setAttribute('y', '26');
|
|
492
|
+
title.setAttribute('font-size', '14');
|
|
493
|
+
title.setAttribute('font-weight', '600');
|
|
494
|
+
title.setAttribute('fill', '#1976d2');
|
|
495
|
+
title.textContent = subRun?.Agent || 'Sub-Agent';
|
|
496
|
+
g.appendChild(title);
|
|
497
|
+
// Expand/collapse button
|
|
498
|
+
const expandBtn = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
499
|
+
expandBtn.setAttribute('class', 'expand-button');
|
|
500
|
+
expandBtn.style.cursor = 'pointer';
|
|
501
|
+
expandBtn.setAttribute('transform', `translate(${scopeWidth - 30}, 10)`);
|
|
502
|
+
const expandBg = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
503
|
+
expandBg.setAttribute('cx', '10');
|
|
504
|
+
expandBg.setAttribute('cy', '10');
|
|
505
|
+
expandBg.setAttribute('r', '10');
|
|
506
|
+
expandBg.setAttribute('fill', 'white');
|
|
507
|
+
expandBg.setAttribute('stroke', '#4a90e2');
|
|
508
|
+
expandBtn.appendChild(expandBg);
|
|
509
|
+
const expandIcon = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
510
|
+
expandIcon.setAttribute('x', '10');
|
|
511
|
+
expandIcon.setAttribute('y', '15');
|
|
512
|
+
expandIcon.setAttribute('text-anchor', 'middle');
|
|
513
|
+
expandIcon.setAttribute('font-size', '12');
|
|
514
|
+
expandIcon.setAttribute('fill', '#4a90e2');
|
|
515
|
+
expandIcon.textContent = '+';
|
|
516
|
+
expandBtn.appendChild(expandIcon);
|
|
517
|
+
expandBtn.addEventListener('click', (e) => this.onScopeExpandClick(e, step.ID));
|
|
518
|
+
g.appendChild(expandBtn);
|
|
519
|
+
// Click handler for scope
|
|
520
|
+
g.addEventListener('click', (e) => {
|
|
521
|
+
if (!e.target.closest('.expand-button')) {
|
|
522
|
+
this.onNodeClick(e, step);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
return g;
|
|
526
|
+
}
|
|
527
|
+
arrangeLayout() {
|
|
528
|
+
const nodeSpacingX = 250;
|
|
529
|
+
const nodeSpacingY = 120;
|
|
530
|
+
const startX = 50;
|
|
531
|
+
const startY = 50;
|
|
532
|
+
let currentY = startY;
|
|
533
|
+
let index = 0;
|
|
534
|
+
// Arrange nodes and scopes sequentially
|
|
535
|
+
const allItems = [...this.nodes.values(), ...Array.from(this.scopes.values()).map(s => ({
|
|
536
|
+
step: { ID: s.id },
|
|
537
|
+
x: 0,
|
|
538
|
+
y: 0,
|
|
539
|
+
width: s.width,
|
|
540
|
+
height: s.height
|
|
541
|
+
}))];
|
|
542
|
+
// Sort by step number if available
|
|
543
|
+
allItems.sort((a, b) => {
|
|
544
|
+
const aStep = a.step;
|
|
545
|
+
const bStep = b.step;
|
|
546
|
+
return (aStep.StepNumber || 0) - (bStep.StepNumber || 0);
|
|
547
|
+
});
|
|
548
|
+
for (const item of allItems) {
|
|
549
|
+
const nodeData = this.nodes.get(item.step.ID);
|
|
550
|
+
const scopeData = this.scopes.get(item.step.ID);
|
|
551
|
+
if (nodeData) {
|
|
552
|
+
nodeData.x = startX;
|
|
553
|
+
nodeData.y = currentY;
|
|
554
|
+
if (nodeData.element) {
|
|
555
|
+
nodeData.element.setAttribute('transform', `translate(${nodeData.x}, ${nodeData.y})`);
|
|
556
|
+
}
|
|
557
|
+
currentY += nodeSpacingY;
|
|
558
|
+
}
|
|
559
|
+
else if (scopeData) {
|
|
560
|
+
scopeData.x = startX;
|
|
561
|
+
scopeData.y = currentY;
|
|
562
|
+
if (scopeData.element) {
|
|
563
|
+
scopeData.element.setAttribute('transform', `translate(${scopeData.x}, ${scopeData.y})`);
|
|
564
|
+
}
|
|
565
|
+
currentY += scopeData.height + nodeSpacingY;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
drawConnections() {
|
|
570
|
+
const svg = this.svgContainer?.nativeElement?.querySelector('svg');
|
|
571
|
+
if (!svg)
|
|
572
|
+
return;
|
|
573
|
+
const mainGroup = svg.querySelector('.main-group');
|
|
574
|
+
if (!mainGroup)
|
|
575
|
+
return;
|
|
576
|
+
// Create connections group if not exists
|
|
577
|
+
let connectionsGroup = mainGroup.querySelector('.connections-group');
|
|
578
|
+
if (!connectionsGroup) {
|
|
579
|
+
connectionsGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
580
|
+
connectionsGroup.setAttribute('class', 'connections-group');
|
|
581
|
+
mainGroup.insertBefore(connectionsGroup, mainGroup.firstChild);
|
|
582
|
+
}
|
|
583
|
+
// Clear existing connections
|
|
584
|
+
while (connectionsGroup.firstChild) {
|
|
585
|
+
connectionsGroup.removeChild(connectionsGroup.firstChild);
|
|
586
|
+
}
|
|
587
|
+
// Draw each connection
|
|
588
|
+
for (const conn of this.connections) {
|
|
589
|
+
const fromNode = this.nodes.get(conn.from);
|
|
590
|
+
const fromScope = this.scopes.get(conn.from);
|
|
591
|
+
const toNode = this.nodes.get(conn.to);
|
|
592
|
+
const toScope = this.scopes.get(conn.to);
|
|
593
|
+
let fromX = 0, fromY = 0, toX = 0, toY = 0;
|
|
594
|
+
if (fromNode) {
|
|
595
|
+
fromX = fromNode.x + fromNode.width / 2;
|
|
596
|
+
fromY = fromNode.y + fromNode.height;
|
|
597
|
+
}
|
|
598
|
+
else if (fromScope) {
|
|
599
|
+
fromX = fromScope.x + fromScope.width / 2;
|
|
600
|
+
fromY = fromScope.y + fromScope.height;
|
|
601
|
+
}
|
|
602
|
+
if (toNode) {
|
|
603
|
+
toX = toNode.x + toNode.width / 2;
|
|
604
|
+
toY = toNode.y;
|
|
605
|
+
}
|
|
606
|
+
else if (toScope) {
|
|
607
|
+
toX = toScope.x + toScope.width / 2;
|
|
608
|
+
toY = toScope.y;
|
|
609
|
+
}
|
|
610
|
+
if (fromX && fromY && toX && toY) {
|
|
611
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
612
|
+
const d = `M ${fromX} ${fromY} L ${toX} ${toY}`;
|
|
613
|
+
path.setAttribute('d', d);
|
|
614
|
+
path.setAttribute('fill', 'none');
|
|
615
|
+
path.setAttribute('stroke', '#4a90e2');
|
|
616
|
+
path.setAttribute('stroke-width', '2');
|
|
617
|
+
path.setAttribute('marker-end', 'url(#arrowhead-viz)');
|
|
618
|
+
path.setAttribute('opacity', '0.6');
|
|
619
|
+
connectionsGroup.appendChild(path);
|
|
620
|
+
conn.path = path;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
fitToView() {
|
|
625
|
+
const svg = this.svgContainer?.nativeElement?.querySelector('svg');
|
|
626
|
+
const mainGroup = svg?.querySelector('.main-group');
|
|
627
|
+
if (!svg || !mainGroup)
|
|
628
|
+
return;
|
|
629
|
+
const bbox = mainGroup.getBBox();
|
|
630
|
+
const padding = 50;
|
|
631
|
+
const viewBox = `${bbox.x - padding} ${bbox.y - padding} ${bbox.width + 2 * padding} ${bbox.height + 2 * padding}`;
|
|
632
|
+
svg.setAttribute('viewBox', viewBox);
|
|
633
|
+
}
|
|
634
|
+
getStepColor(step) {
|
|
635
|
+
const colorMap = {
|
|
636
|
+
'Prompt': '#2196f3',
|
|
637
|
+
'Actions': '#4caf50',
|
|
638
|
+
'Sub-Agent': '#ff9800',
|
|
639
|
+
'Tool': '#9c27b0',
|
|
640
|
+
'Decision': '#f44336'
|
|
641
|
+
};
|
|
642
|
+
return colorMap[step.StepType] || '#757575';
|
|
643
|
+
}
|
|
644
|
+
getStepEmoji(stepType) {
|
|
645
|
+
const emojiMap = {
|
|
646
|
+
'Prompt': '💬',
|
|
647
|
+
'Actions': '⚙️',
|
|
648
|
+
'Sub-Agent': '🤖',
|
|
649
|
+
'Tool': '🔧',
|
|
650
|
+
'Decision': '🔀'
|
|
651
|
+
};
|
|
652
|
+
return emojiMap[stepType] || '⚪';
|
|
653
|
+
}
|
|
654
|
+
getStatusColor(status) {
|
|
655
|
+
const colorMap = {
|
|
656
|
+
'Running': '#1976d2',
|
|
657
|
+
'Completed': '#388e3c',
|
|
658
|
+
'Failed': '#d32f2f',
|
|
659
|
+
'Cancelled': '#f57c00',
|
|
660
|
+
'Paused': '#757575'
|
|
661
|
+
};
|
|
662
|
+
return colorMap[status] || '#9e9e9e';
|
|
663
|
+
}
|
|
664
|
+
truncateText(text, maxLength) {
|
|
665
|
+
if (text.length <= maxLength)
|
|
666
|
+
return text;
|
|
667
|
+
return text.substring(0, maxLength - 3) + '...';
|
|
668
|
+
}
|
|
669
|
+
calculateDuration(start, end) {
|
|
670
|
+
if (!end)
|
|
671
|
+
return 'Running...';
|
|
672
|
+
const ms = new Date(end).getTime() - new Date(start).getTime();
|
|
673
|
+
if (ms < 1000)
|
|
674
|
+
return `${ms}ms`;
|
|
675
|
+
if (ms < 60000)
|
|
676
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
677
|
+
if (ms < 3600000)
|
|
678
|
+
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
679
|
+
return `${Math.floor(ms / 3600000)}h ${Math.floor((ms % 3600000) / 60000)}m`;
|
|
680
|
+
}
|
|
681
|
+
// Event handlers
|
|
682
|
+
onNodeClick(event, step) {
|
|
683
|
+
event.stopPropagation();
|
|
684
|
+
// Create timeline item from step
|
|
685
|
+
const item = {
|
|
686
|
+
id: step.ID,
|
|
687
|
+
type: 'step',
|
|
688
|
+
title: step.StepName || `Step ${step.StepNumber}`,
|
|
689
|
+
subtitle: `Type: ${step.StepType}`,
|
|
690
|
+
status: step.Status,
|
|
691
|
+
startTime: step.StartedAt,
|
|
692
|
+
endTime: step.CompletedAt || undefined,
|
|
693
|
+
duration: this.calculateDuration(step.StartedAt, step.CompletedAt),
|
|
694
|
+
icon: this.getStepIcon(step.StepType),
|
|
695
|
+
color: this.getStatusColorName(step.Status),
|
|
696
|
+
data: step,
|
|
697
|
+
level: 0
|
|
698
|
+
};
|
|
699
|
+
this.selectedItem = item;
|
|
700
|
+
this.cdr.detectChanges();
|
|
701
|
+
}
|
|
702
|
+
getStepIcon(stepType) {
|
|
703
|
+
const iconMap = {
|
|
704
|
+
'Prompt': 'fa-microchip',
|
|
705
|
+
'Tool': 'fa-tools',
|
|
706
|
+
'Sub-Agent': 'fa-robot',
|
|
707
|
+
'Decision': 'fa-code-branch',
|
|
708
|
+
'Actions': 'fa-cog'
|
|
709
|
+
};
|
|
710
|
+
return iconMap[stepType] || 'fa-circle';
|
|
711
|
+
}
|
|
712
|
+
getStatusColorName(status) {
|
|
713
|
+
const colorMap = {
|
|
714
|
+
'Running': 'info',
|
|
715
|
+
'Completed': 'success',
|
|
716
|
+
'Failed': 'error',
|
|
717
|
+
'Cancelled': 'warning',
|
|
718
|
+
'Paused': 'secondary'
|
|
719
|
+
};
|
|
720
|
+
return colorMap[status] || 'secondary';
|
|
721
|
+
}
|
|
722
|
+
async onScopeExpandClick(event, scopeId) {
|
|
723
|
+
event.stopPropagation();
|
|
724
|
+
const scope = this.scopes.get(scopeId);
|
|
725
|
+
if (!scope)
|
|
726
|
+
return;
|
|
727
|
+
scope.expanded = !scope.expanded;
|
|
728
|
+
if (scope.expanded) {
|
|
729
|
+
// Load sub-agent steps
|
|
730
|
+
await this.loadScopeContents(scopeId);
|
|
731
|
+
}
|
|
732
|
+
// Update UI
|
|
733
|
+
this.updateScopeVisual(scope);
|
|
734
|
+
this.arrangeLayout();
|
|
735
|
+
this.drawConnections();
|
|
736
|
+
}
|
|
737
|
+
async loadScopeContents(scopeId) {
|
|
738
|
+
const scope = this.scopes.get(scopeId);
|
|
739
|
+
if (!scope)
|
|
740
|
+
return;
|
|
741
|
+
// Find the step
|
|
742
|
+
const step = Array.from(this.nodes.values()).find(n => n.step.ID === scopeId)?.step
|
|
743
|
+
|| Array.from(this.scopes.values()).find(s => s.id === scopeId);
|
|
744
|
+
if (!step || !step.TargetLogID)
|
|
745
|
+
return;
|
|
746
|
+
const targetLogId = step.TargetLogID;
|
|
747
|
+
try {
|
|
748
|
+
// Load sub-agent data through service
|
|
749
|
+
const data = await this.dataService.loadSubAgentData(targetLogId);
|
|
750
|
+
if (data.steps && data.steps.length > 0) {
|
|
751
|
+
// Create nodes from data
|
|
752
|
+
this.createScopeNodes(scope, data.steps, data.promptRuns);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
catch (error) {
|
|
756
|
+
console.error('Failed to load scope contents:', error);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
createScopeNodes(scope, steps, promptRuns) {
|
|
760
|
+
const svg = this.svgContainer?.nativeElement?.querySelector('svg');
|
|
761
|
+
if (!svg)
|
|
762
|
+
return;
|
|
763
|
+
const mainGroup = svg.querySelector('.main-group');
|
|
764
|
+
if (!mainGroup || !scope.element)
|
|
765
|
+
return;
|
|
766
|
+
// Create nodes inside scope
|
|
767
|
+
const nodeSpacingY = 60;
|
|
768
|
+
const startX = 20;
|
|
769
|
+
const startY = 60;
|
|
770
|
+
for (let i = 0; i < steps.length; i++) {
|
|
771
|
+
const step = steps[i];
|
|
772
|
+
const nodeElement = this.createNodeElement(step, promptRuns);
|
|
773
|
+
// Add node to scope group
|
|
774
|
+
scope.element.appendChild(nodeElement);
|
|
775
|
+
const nodeData = {
|
|
776
|
+
step: step,
|
|
777
|
+
x: startX,
|
|
778
|
+
y: startY + i * nodeSpacingY,
|
|
779
|
+
width: 160,
|
|
780
|
+
height: 60,
|
|
781
|
+
element: nodeElement
|
|
782
|
+
};
|
|
783
|
+
nodeElement.setAttribute('transform', `translate(${nodeData.x}, ${nodeData.y})`);
|
|
784
|
+
scope.nodes.push(nodeData);
|
|
785
|
+
this.nodes.set(step.ID, nodeData);
|
|
786
|
+
}
|
|
787
|
+
// Update scope height
|
|
788
|
+
scope.height = Math.max(200, startY + steps.length * nodeSpacingY + 20);
|
|
789
|
+
}
|
|
790
|
+
updateScopeVisual(scope) {
|
|
791
|
+
if (!scope.element)
|
|
792
|
+
return;
|
|
793
|
+
// Update background height
|
|
794
|
+
const bg = scope.element.querySelector('rect');
|
|
795
|
+
if (bg) {
|
|
796
|
+
bg.setAttribute('height', scope.height.toString());
|
|
797
|
+
}
|
|
798
|
+
// Update expand button
|
|
799
|
+
const expandIcon = scope.element.querySelector('.expand-button text');
|
|
800
|
+
if (expandIcon) {
|
|
801
|
+
expandIcon.textContent = scope.expanded ? '−' : '+';
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
// Drag handling
|
|
805
|
+
onNodeMouseDown(event, nodeId) {
|
|
806
|
+
event.preventDefault();
|
|
807
|
+
event.stopPropagation();
|
|
808
|
+
const node = this.nodes.get(nodeId);
|
|
809
|
+
if (!node || !node.element)
|
|
810
|
+
return;
|
|
811
|
+
// Get current transform
|
|
812
|
+
const transform = node.element.getAttribute('transform');
|
|
813
|
+
const match = transform?.match(/translate\(([-\d.]+),\s*([-\d.]+)\)/);
|
|
814
|
+
if (!match)
|
|
815
|
+
return;
|
|
816
|
+
// Get SVG coordinates
|
|
817
|
+
const svg = node.element.ownerSVGElement;
|
|
818
|
+
if (!svg)
|
|
819
|
+
return;
|
|
820
|
+
const pt = svg.createSVGPoint();
|
|
821
|
+
pt.x = event.clientX;
|
|
822
|
+
pt.y = event.clientY;
|
|
823
|
+
const svgP = pt.matrixTransform(svg.getScreenCTM()?.inverse());
|
|
824
|
+
this.dragState = {
|
|
825
|
+
isDragging: true,
|
|
826
|
+
element: node.element,
|
|
827
|
+
nodeId: nodeId,
|
|
828
|
+
startX: svgP.x,
|
|
829
|
+
startY: svgP.y,
|
|
830
|
+
startTransform: {
|
|
831
|
+
x: parseFloat(match[1]),
|
|
832
|
+
y: parseFloat(match[2])
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
node.element.classList.add('dragging');
|
|
836
|
+
// Add document-level event listeners
|
|
837
|
+
document.addEventListener('mousemove', this.onMouseMove);
|
|
838
|
+
document.addEventListener('mouseup', this.onMouseUp);
|
|
839
|
+
}
|
|
840
|
+
// Pan and zoom
|
|
841
|
+
onWheel(event) {
|
|
842
|
+
event.preventDefault();
|
|
843
|
+
const delta = event.deltaY > 0 ? 0.9 : 1.1;
|
|
844
|
+
this.panZoom.scale = Math.max(0.3, Math.min(3, this.panZoom.scale * delta));
|
|
845
|
+
this.updateTransform();
|
|
846
|
+
}
|
|
847
|
+
onSvgMouseDown(event) {
|
|
848
|
+
if (event.button === 0 && event.target === event.currentTarget) {
|
|
849
|
+
this.panZoom.isPanning = true;
|
|
850
|
+
this.panZoom.startX = event.clientX - this.panZoom.translateX;
|
|
851
|
+
this.panZoom.startY = event.clientY - this.panZoom.translateY;
|
|
852
|
+
event.preventDefault();
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
onSvgMouseMove(event) {
|
|
856
|
+
if (this.panZoom.isPanning) {
|
|
857
|
+
this.panZoom.translateX = event.clientX - this.panZoom.startX;
|
|
858
|
+
this.panZoom.translateY = event.clientY - this.panZoom.startY;
|
|
859
|
+
this.updateTransform();
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
onSvgMouseUp(event) {
|
|
863
|
+
this.panZoom.isPanning = false;
|
|
864
|
+
}
|
|
865
|
+
updateTransform() {
|
|
866
|
+
const svg = this.svgContainer?.nativeElement?.querySelector('svg');
|
|
867
|
+
if (svg) {
|
|
868
|
+
const mainGroup = svg.querySelector('.main-group');
|
|
869
|
+
if (mainGroup) {
|
|
870
|
+
mainGroup.setAttribute('transform', `translate(${this.panZoom.translateX}, ${this.panZoom.translateY}) scale(${this.panZoom.scale})`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
// Toolbar actions
|
|
875
|
+
zoomIn() {
|
|
876
|
+
this.panZoom.scale = Math.min(this.panZoom.scale * 1.2, 3);
|
|
877
|
+
this.updateTransform();
|
|
878
|
+
}
|
|
879
|
+
zoomOut() {
|
|
880
|
+
this.panZoom.scale = Math.max(this.panZoom.scale / 1.2, 0.3);
|
|
881
|
+
this.updateTransform();
|
|
882
|
+
}
|
|
883
|
+
resetZoom() {
|
|
884
|
+
this.panZoom.scale = 1;
|
|
885
|
+
this.panZoom.translateX = 0;
|
|
886
|
+
this.panZoom.translateY = 0;
|
|
887
|
+
this.updateTransform();
|
|
888
|
+
}
|
|
889
|
+
// Detail pane handlers
|
|
890
|
+
closeDetailPane() {
|
|
891
|
+
this.selectedItem = null;
|
|
892
|
+
this.cdr.detectChanges();
|
|
893
|
+
}
|
|
894
|
+
navigateToActionLog(logId) {
|
|
895
|
+
// Emit event to parent component
|
|
896
|
+
// Parent will handle navigation
|
|
897
|
+
}
|
|
898
|
+
async copyToClipboard(text) {
|
|
899
|
+
try {
|
|
900
|
+
await navigator.clipboard.writeText(text);
|
|
901
|
+
}
|
|
902
|
+
catch (err) {
|
|
903
|
+
console.error('Failed to copy to clipboard:', err);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
static { this.ɵfac = function AIAgentRunVisualizationComponent_Factory(t) { return new (t || AIAgentRunVisualizationComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.AIAgentRunDataService)); }; }
|
|
907
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AIAgentRunVisualizationComponent, selectors: [["mj-ai-agent-run-visualization"]], viewQuery: function AIAgentRunVisualizationComponent_Query(rf, ctx) { if (rf & 1) {
|
|
908
|
+
i0.ɵɵviewQuery(_c0, 5);
|
|
909
|
+
} if (rf & 2) {
|
|
910
|
+
let _t;
|
|
911
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.svgContainer = _t.first);
|
|
912
|
+
} }, inputs: { aiAgentRunId: "aiAgentRunId" }, decls: 3, vars: 1, consts: [["svgContainer", ""], [1, "visualization-container"], [1, "error-state"], ["orientation", "horizontal", 2, "height", "100%"], [1, "fa-solid", "fa-exclamation-triangle"], [3, "collapsible"], [1, "diagram-container"], [1, "diagram-toolbar"], [1, "toolbar-title"], [1, "fa-solid", "fa-diagram-project"], [1, "zoom-controls"], ["title", "Zoom in", 3, "click"], [1, "fa-solid", "fa-search-plus"], ["title", "Zoom out", 3, "click"], [1, "fa-solid", "fa-search-minus"], ["title", "Reset zoom", 3, "click"], [1, "fa-solid", "fa-compress"], [1, "zoom-level"], [1, "loading-overlay"], [3, "size", "min", "max", "collapsible"], [1, "fa-solid", "fa-spinner", "fa-spin"], [3, "closePanel", "navigateToActionLog", "copyToClipboard", "selectedTimelineItem"]], template: function AIAgentRunVisualizationComponent_Template(rf, ctx) { if (rf & 1) {
|
|
913
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
914
|
+
i0.ɵɵtemplate(1, AIAgentRunVisualizationComponent_Conditional_1_Template, 4, 1, "div", 2)(2, AIAgentRunVisualizationComponent_Conditional_2_Template, 19, 4, "kendo-splitter", 3);
|
|
915
|
+
i0.ɵɵelementEnd();
|
|
916
|
+
} if (rf & 2) {
|
|
917
|
+
i0.ɵɵadvance();
|
|
918
|
+
i0.ɵɵconditional(ctx.error ? 1 : 2);
|
|
919
|
+
} }, dependencies: [i2.SplitterComponent, i2.SplitterPaneComponent, i3.AIAgentRunStepDetailComponent], styles: [".visualization-container[_ngcontent-%COMP%] {\n width: 100%;\n height: 600px;\n min-height: 400px;\n position: relative;\n background: #f5f5f5;\n border: 1px solid #ddd;\n border-radius: 4px;\n overflow: hidden;\n}\n\n.loading-state[_ngcontent-%COMP%], .error-state[_ngcontent-%COMP%], .empty-state[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 0.5rem;\n color: #666;\n}\n\n.error-state[_ngcontent-%COMP%] {\n color: #c00;\n}\n\n.empty-state[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 1rem;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 2rem;\n opacity: 0.5;\n}\n\n.loading-overlay[_ngcontent-%COMP%] {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: rgba(255, 255, 255, 0.9);\n padding: 1rem 2rem;\n border-radius: 4px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n z-index: 10;\n}\n\n.diagram-container[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n background: #fafafa;\n}\n\n.diagram-toolbar[_ngcontent-%COMP%] {\n position: absolute;\n top: 10px;\n right: 10px;\n left: 10px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: white;\n padding: 8px 12px;\n border-radius: 4px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n z-index: 10;\n}\n\n.toolbar-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 600;\n color: #2c3e50;\n}\n\n.zoom-controls[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n align-items: center;\n}\n\n.zoom-controls[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n padding: 6px 10px;\n border: 1px solid #ddd;\n background: white;\n border-radius: 3px;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n transition: all 0.2s;\n}\n\n.zoom-controls[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:hover {\n background: #f0f0f0;\n border-color: #4a90e2;\n}\n\n.zoom-level[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666;\n margin-left: 8px;\n font-family: monospace;\n}\n\n.visualization-svg[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n cursor: grab;\n}\n\n.visualization-svg[_ngcontent-%COMP%]:active {\n cursor: grabbing;\n}\n\n\n\n[_nghost-%COMP%] .step-node {\n cursor: pointer;\n transition: transform 0.2s;\n}\n\n[_nghost-%COMP%] .step-node:hover {\n transform: scale(1.02);\n}\n\n[_nghost-%COMP%] .step-node.dragging {\n opacity: 0.8;\n cursor: move;\n}\n\n[_nghost-%COMP%] .step-node rect {\n filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\n}\n\n[_nghost-%COMP%] .step-node:hover rect {\n filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15));\n}\n\n[_nghost-%COMP%] .scope-container {\n cursor: pointer;\n}\n\n[_nghost-%COMP%] .scope-container rect {\n filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\n}\n\n[_nghost-%COMP%] .expand-button:hover circle {\n fill: #e3f2fd;\n}\n\n[_nghost-%COMP%] .connections-group path {\n transition: stroke-width 0.2s;\n}\n\n[_nghost-%COMP%] .connections-group path:hover {\n stroke-width: 3;\n}\n\n\n\n[_nghost-%COMP%] .k-splitter {\n border: none;\n}\n\n[_nghost-%COMP%] .k-splitter-pane {\n overflow: visible;\n}\n\n[_nghost-%COMP%] .k-splitbar {\n background: #e0e6ed;\n width: 4px;\n}\n\n[_nghost-%COMP%] .k-splitbar:hover {\n background: #c1c9d2;\n}\n\n[_nghost-%COMP%] .k-splitbar-draggable-horizontal {\n cursor: col-resize;\n}\n\n\n\n[_nghost-%COMP%] .step-node[data-type=\"prompt\"] rect {\n fill: #e3f2fd;\n}\n\n[_nghost-%COMP%] .step-node[data-type=\"action\"] rect {\n fill: #e8f5e9;\n}\n\n[_nghost-%COMP%] .step-node[data-type=\"subagent\"] rect {\n fill: #fff3e0;\n}\n\n[_nghost-%COMP%] .step-node[data-type=\"tool\"] rect {\n fill: #f3e5f5;\n}\n\n[_nghost-%COMP%] .step-node[data-type=\"decision\"] rect {\n fill: #ffebee;\n}"] }); }
|
|
920
|
+
}
|
|
921
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AIAgentRunVisualizationComponent, [{
|
|
922
|
+
type: Component,
|
|
923
|
+
args: [{ selector: 'mj-ai-agent-run-visualization', template: "<div class=\"visualization-container\">\n @if (error) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ error }}</span>\n </div>\n } @else {\n <kendo-splitter orientation=\"horizontal\" style=\"height: 100%;\">\n <!-- Diagram Pane -->\n <kendo-splitter-pane [collapsible]=\"false\">\n <div class=\"diagram-container\" #svgContainer>\n <div class=\"diagram-toolbar\">\n <div class=\"toolbar-title\">\n <i class=\"fa-solid fa-diagram-project\"></i>\n Agent Execution Flow\n </div>\n <div class=\"zoom-controls\">\n <button (click)=\"zoomIn()\" title=\"Zoom in\">\n <i class=\"fa-solid fa-search-plus\"></i>\n </button>\n <button (click)=\"zoomOut()\" title=\"Zoom out\">\n <i class=\"fa-solid fa-search-minus\"></i>\n </button>\n <button (click)=\"resetZoom()\" title=\"Reset zoom\">\n <i class=\"fa-solid fa-compress\"></i>\n </button>\n <span class=\"zoom-level\">{{ (panZoom.scale * 100).toFixed(0) }}%</span>\n </div>\n </div>\n @if (loading) {\n <div class=\"loading-overlay\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading visualization...</span>\n </div>\n }\n </div>\n </kendo-splitter-pane>\n \n <!-- Detail Pane -->\n @if (selectedItem) {\n <kendo-splitter-pane [size]=\"'400px'\" [min]=\"'300px'\" [max]=\"'600px'\" [collapsible]=\"true\">\n <mj-ai-agent-run-step-detail\n [selectedTimelineItem]=\"selectedItem\"\n (closePanel)=\"closeDetailPane()\"\n (navigateToActionLog)=\"navigateToActionLog($event)\"\n (copyToClipboard)=\"copyToClipboard($event)\">\n </mj-ai-agent-run-step-detail>\n </kendo-splitter-pane>\n }\n </kendo-splitter>\n }\n</div>", styles: [".visualization-container {\n width: 100%;\n height: 600px;\n min-height: 400px;\n position: relative;\n background: #f5f5f5;\n border: 1px solid #ddd;\n border-radius: 4px;\n overflow: hidden;\n}\n\n.loading-state, .error-state, .empty-state {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 0.5rem;\n color: #666;\n}\n\n.error-state {\n color: #c00;\n}\n\n.empty-state {\n flex-direction: column;\n gap: 1rem;\n}\n\n.empty-state i {\n font-size: 2rem;\n opacity: 0.5;\n}\n\n.loading-overlay {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: rgba(255, 255, 255, 0.9);\n padding: 1rem 2rem;\n border-radius: 4px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n z-index: 10;\n}\n\n.diagram-container {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n background: #fafafa;\n}\n\n.diagram-toolbar {\n position: absolute;\n top: 10px;\n right: 10px;\n left: 10px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: white;\n padding: 8px 12px;\n border-radius: 4px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n z-index: 10;\n}\n\n.toolbar-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 600;\n color: #2c3e50;\n}\n\n.zoom-controls {\n display: flex;\n gap: 4px;\n align-items: center;\n}\n\n.zoom-controls button {\n padding: 6px 10px;\n border: 1px solid #ddd;\n background: white;\n border-radius: 3px;\n cursor: pointer;\n font-size: 12px;\n display: flex;\n align-items: center;\n transition: all 0.2s;\n}\n\n.zoom-controls button:hover {\n background: #f0f0f0;\n border-color: #4a90e2;\n}\n\n.zoom-level {\n font-size: 12px;\n color: #666;\n margin-left: 8px;\n font-family: monospace;\n}\n\n.visualization-svg {\n width: 100%;\n height: 100%;\n cursor: grab;\n}\n\n.visualization-svg:active {\n cursor: grabbing;\n}\n\n/* SVG Styles */\n:host ::ng-deep .step-node {\n cursor: pointer;\n transition: transform 0.2s;\n}\n\n:host ::ng-deep .step-node:hover {\n transform: scale(1.02);\n}\n\n:host ::ng-deep .step-node.dragging {\n opacity: 0.8;\n cursor: move;\n}\n\n:host ::ng-deep .step-node rect {\n filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\n}\n\n:host ::ng-deep .step-node:hover rect {\n filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15));\n}\n\n:host ::ng-deep .scope-container {\n cursor: pointer;\n}\n\n:host ::ng-deep .scope-container rect {\n filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));\n}\n\n:host ::ng-deep .expand-button:hover circle {\n fill: #e3f2fd;\n}\n\n:host ::ng-deep .connections-group path {\n transition: stroke-width 0.2s;\n}\n\n:host ::ng-deep .connections-group path:hover {\n stroke-width: 3;\n}\n\n/* Splitter customization */\n:host ::ng-deep .k-splitter {\n border: none;\n}\n\n:host ::ng-deep .k-splitter-pane {\n overflow: visible;\n}\n\n:host ::ng-deep .k-splitbar {\n background: #e0e6ed;\n width: 4px;\n}\n\n:host ::ng-deep .k-splitbar:hover {\n background: #c1c9d2;\n}\n\n:host ::ng-deep .k-splitbar-draggable-horizontal {\n cursor: col-resize;\n}\n\n/* Node type colors */\n:host ::ng-deep .step-node[data-type=\"prompt\"] rect {\n fill: #e3f2fd;\n}\n\n:host ::ng-deep .step-node[data-type=\"action\"] rect {\n fill: #e8f5e9;\n}\n\n:host ::ng-deep .step-node[data-type=\"subagent\"] rect {\n fill: #fff3e0;\n}\n\n:host ::ng-deep .step-node[data-type=\"tool\"] rect {\n fill: #f3e5f5;\n}\n\n:host ::ng-deep .step-node[data-type=\"decision\"] rect {\n fill: #ffebee;\n}"] }]
|
|
924
|
+
}], () => [{ type: i0.ChangeDetectorRef }, { type: i1.AIAgentRunDataService }], { svgContainer: [{
|
|
925
|
+
type: ViewChild,
|
|
926
|
+
args: ['svgContainer', { static: false }]
|
|
927
|
+
}], aiAgentRunId: [{
|
|
928
|
+
type: Input
|
|
929
|
+
}] }); })();
|
|
930
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AIAgentRunVisualizationComponent, { className: "AIAgentRunVisualizationComponent", filePath: "src/lib/custom/ai-agent-run/ai-agent-run-visualization.component.ts", lineNumber: 34 }); })();
|
|
931
|
+
//# sourceMappingURL=ai-agent-run-visualization.component.js.map
|