@memberjunction/ng-ai-test-harness
Version:
MemberJunction AI Test Harness - A reusable component for testing AI agents and prompts with beautiful UX
686 lines (676 loc) • 54.8 kB
JavaScript
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
import * as i0 from "@angular/core";
function ExecutionNodeComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
const _r1 = i0.ɵɵgetCurrentView();
i0.ɵɵelementStart(0, "i", 19);
i0.ɵɵlistener("click", function ExecutionNodeComponent_Conditional_2_Template_i_click_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onToggleChildren($event)); });
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext();
i0.ɵɵclassProp("fa-chevron-down", ctx_r1.expanded)("fa-chevron-right", !ctx_r1.expanded);
} }
function ExecutionNodeComponent_Case_4_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 4);
} }
function ExecutionNodeComponent_Case_5_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 5);
} }
function ExecutionNodeComponent_Case_6_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 6);
} }
function ExecutionNodeComponent_Case_7_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 7);
} }
function ExecutionNodeComponent_Case_9_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 9);
} }
function ExecutionNodeComponent_Case_10_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 10);
} }
function ExecutionNodeComponent_Case_11_Conditional_0_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i");
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵclassMap(ctx_r1.getActionIconClass());
} }
function ExecutionNodeComponent_Case_11_Conditional_1_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 21);
} }
function ExecutionNodeComponent_Case_11_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵtemplate(0, ExecutionNodeComponent_Case_11_Conditional_0_Template, 1, 2, "i", 20)(1, ExecutionNodeComponent_Case_11_Conditional_1_Template, 1, 0, "i", 21);
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext();
i0.ɵɵconditional(ctx_r1.getActionIconClass() ? 0 : 1);
} }
function ExecutionNodeComponent_Case_12_Conditional_0_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "img", 22);
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵproperty("src", ctx_r1.getAgentLogoURL(), i0.ɵɵsanitizeUrl)("alt", ctx_r1.getAgentName() || "Agent");
} }
function ExecutionNodeComponent_Case_12_Conditional_1_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i");
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵclassMap(ctx_r1.getAgentIconClass());
} }
function ExecutionNodeComponent_Case_12_Conditional_2_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 23);
} }
function ExecutionNodeComponent_Case_12_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵtemplate(0, ExecutionNodeComponent_Case_12_Conditional_0_Template, 1, 2, "img", 22)(1, ExecutionNodeComponent_Case_12_Conditional_1_Template, 1, 2, "i", 20)(2, ExecutionNodeComponent_Case_12_Conditional_2_Template, 1, 0, "i", 23);
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext();
i0.ɵɵconditional(ctx_r1.getAgentLogoURL() ? 0 : ctx_r1.getAgentIconClass() ? 1 : 2);
} }
function ExecutionNodeComponent_Case_13_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 11);
} }
function ExecutionNodeComponent_Case_14_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "i", 12);
} }
function ExecutionNodeComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "small", 14);
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext();
i0.ɵɵadvance();
i0.ɵɵtextInterpolate1("[Level ", ctx_r1.depth, "]");
} }
function ExecutionNodeComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "span", 15);
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext();
i0.ɵɵadvance();
i0.ɵɵtextInterpolate(ctx_r1.formatDuration(ctx_r1.getDuration()));
} }
function ExecutionNodeComponent_Conditional_19_Conditional_1_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "span", 24);
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵadvance();
i0.ɵɵtextInterpolate1("", ctx_r1.getTokensUsed(), " tokens");
} }
function ExecutionNodeComponent_Conditional_19_Conditional_2_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "span", 25);
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵadvance();
i0.ɵɵtextInterpolate1("$", ctx_r1.getCost().toFixed(4), "");
} }
function ExecutionNodeComponent_Conditional_19_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "span", 16);
i0.ɵɵtemplate(1, ExecutionNodeComponent_Conditional_19_Conditional_1_Template, 2, 1, "span", 24)(2, ExecutionNodeComponent_Conditional_19_Conditional_2_Template, 2, 1, "span", 25);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext();
i0.ɵɵadvance();
i0.ɵɵconditional(ctx_r1.getTokensUsed() ? 1 : -1);
i0.ɵɵadvance();
i0.ɵɵconditional(ctx_r1.getCost() ? 2 : -1);
} }
function ExecutionNodeComponent_Conditional_20_Template(rf, ctx) { if (rf & 1) {
const _r3 = i0.ɵɵgetCurrentView();
i0.ɵɵelementStart(0, "button", 26);
i0.ɵɵlistener("click", function ExecutionNodeComponent_Conditional_20_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onToggleDetails($event)); });
i0.ɵɵelement(1, "i", 27);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext();
i0.ɵɵproperty("title", ctx_r1.detailsExpanded ? "Hide details" : "Show details");
i0.ɵɵadvance();
i0.ɵɵclassProp("fa-info", !ctx_r1.detailsExpanded)("fa-times", ctx_r1.detailsExpanded);
} }
function ExecutionNodeComponent_Conditional_21_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 31);
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(3);
i0.ɵɵadvance();
i0.ɵɵtextInterpolate(ctx_r1.step.StepName);
} }
function ExecutionNodeComponent_Conditional_21_Conditional_0_Conditional_2_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelement(0, "div", 32);
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(3);
i0.ɵɵproperty("innerHTML", ctx_r1.formatMarkdown(ctx_r1.getDetailsMarkdown()), i0.ɵɵsanitizeHtml);
} }
function ExecutionNodeComponent_Conditional_21_Conditional_0_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 28);
i0.ɵɵtemplate(1, ExecutionNodeComponent_Conditional_21_Conditional_0_Conditional_1_Template, 2, 1, "div", 31)(2, ExecutionNodeComponent_Conditional_21_Conditional_0_Conditional_2_Template, 1, 1, "div", 32);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵadvance();
i0.ɵɵconditional(ctx_r1.isNameTruncated() ? 1 : -1);
i0.ɵɵadvance();
i0.ɵɵconditional(ctx_r1.getDetailsMarkdown() ? 2 : -1);
} }
function ExecutionNodeComponent_Conditional_21_Conditional_2_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 29)(1, "div", 33);
i0.ɵɵelement(2, "i", 34);
i0.ɵɵtext(3, " Error ");
i0.ɵɵelementEnd();
i0.ɵɵelementStart(4, "div", 35);
i0.ɵɵtext(5);
i0.ɵɵelementEnd()();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵadvance(5);
i0.ɵɵtextInterpolate(ctx_r1.step.ErrorMessage);
} }
function ExecutionNodeComponent_Conditional_21_Conditional_3_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 30)(1, "div", 33);
i0.ɵɵelement(2, "i", 36);
i0.ɵɵtext(3, " Input ");
i0.ɵɵelementEnd();
i0.ɵɵelementStart(4, "div", 35);
i0.ɵɵtext(5);
i0.ɵɵelementEnd()();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵadvance(5);
i0.ɵɵtextInterpolate(ctx_r1.getInputPreview());
} }
function ExecutionNodeComponent_Conditional_21_Conditional_4_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 30)(1, "div", 33);
i0.ɵɵelement(2, "i", 37);
i0.ɵɵtext(3, " Output ");
i0.ɵɵelementEnd();
i0.ɵɵelementStart(4, "div", 35);
i0.ɵɵtext(5);
i0.ɵɵelementEnd()();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext(2);
i0.ɵɵadvance(5);
i0.ɵɵtextInterpolate(ctx_r1.getOutputPreview());
} }
function ExecutionNodeComponent_Conditional_21_Conditional_5_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 30)(1, "div", 35);
i0.ɵɵtext(2, "No additional details available for this step.");
i0.ɵɵelementEnd()();
} }
function ExecutionNodeComponent_Conditional_21_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵtemplate(0, ExecutionNodeComponent_Conditional_21_Conditional_0_Template, 3, 2, "div", 28);
i0.ɵɵelementStart(1, "div", 18);
i0.ɵɵtemplate(2, ExecutionNodeComponent_Conditional_21_Conditional_2_Template, 6, 1, "div", 29)(3, ExecutionNodeComponent_Conditional_21_Conditional_3_Template, 6, 1, "div", 30)(4, ExecutionNodeComponent_Conditional_21_Conditional_4_Template, 6, 1, "div", 30)(5, ExecutionNodeComponent_Conditional_21_Conditional_5_Template, 3, 0, "div", 30);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r1 = i0.ɵɵnextContext();
i0.ɵɵconditional(ctx_r1.getDetailsMarkdown() || ctx_r1.isNameTruncated() ? 0 : -1);
i0.ɵɵadvance(2);
i0.ɵɵconditional(ctx_r1.step.ErrorMessage ? 2 : -1);
i0.ɵɵadvance();
i0.ɵɵconditional(ctx_r1.getInputPreview() ? 3 : -1);
i0.ɵɵadvance();
i0.ɵɵconditional(ctx_r1.getOutputPreview() ? 4 : -1);
i0.ɵɵadvance();
i0.ɵɵconditional(!ctx_r1.step.ErrorMessage && !ctx_r1.getInputPreview() && !ctx_r1.getOutputPreview() && !ctx_r1.getDetailsMarkdown() && !ctx_r1.isNameTruncated() ? 5 : -1);
} }
export class ExecutionNodeComponent {
constructor() {
this.depth = 0;
this.agentPath = [];
this.expanded = false;
this.detailsExpanded = false;
this.toggleNode = new EventEmitter();
this.toggleDetails = new EventEmitter();
this.userInteracted = new EventEmitter();
}
hasChildren() {
return this.step.StepType === 'Sub-Agent' &&
!!this.step.SubAgentRun?.Steps &&
this.step.SubAgentRun.Steps.length > 0;
}
onToggleChildren(event) {
if (event) {
event.stopPropagation();
}
if (this.hasChildren()) {
this.toggleNode.emit();
this.userInteracted.emit();
}
}
onToggleDetails(event) {
if (event) {
event.stopPropagation();
}
if (this.hasNodeDetails()) {
this.toggleDetails.emit();
this.userInteracted.emit();
}
}
onDoubleClick() {
if (this.hasChildren()) {
this.toggleNode.emit();
this.userInteracted.emit();
}
}
hasNodeDetails() {
return !!this.step.InputData ||
!!this.step.OutputData ||
!!this.step.ErrorMessage ||
!!this.getDetailsMarkdown() ||
this.isNameTruncated();
}
getTruncatedName() {
const maxLength = 120;
const name = this.getStepName();
if (name.length <= maxLength) {
return name;
}
return name.substring(0, maxLength) + '...';
}
isNameTruncated() {
return this.step.StepName.length > 120;
}
formatDuration(ms) {
if (ms < 1000)
return `${ms}ms`;
if (ms < 60000)
return `${(ms / 1000).toFixed(1)}s`;
const minutes = Math.floor(ms / 60000);
const seconds = Math.floor((ms % 60000) / 1000);
return `${minutes}m ${seconds}s`;
}
getNodeTitle() {
if (this.step.StepType === 'Sub-Agent' && this.getAgentName()) {
return `Sub-agent: ${this.getAgentName()}`;
}
if (this.step.StepType === 'Actions' && this.getActionName()) {
return `Action: ${this.getActionName()}`;
}
return this.step.StepType;
}
// Getter methods for step data
getStepName() {
// Extract just the first line if the name contains markdown
const lines = this.step.StepName.split('\n');
return lines[0].trim();
}
getStepTypeClass() {
const typeMap = {
'Validation': 'validation',
'Prompt': 'prompt',
'Actions': 'action',
'Sub-Agent': 'sub-agent',
'Decision': 'decision',
'Chat': 'chat'
};
return typeMap[this.step.StepType] || 'prompt';
}
getStatusClass() {
const statusMap = {
'Pending': 'pending',
'Running': 'running',
'Completed': 'completed',
'Failed': 'failed',
'Cancelled': 'failed',
'Paused': 'pending'
};
// Use override if provided, otherwise use actual status
const status = this.overrideDisplayStatus || this.step.Status;
return statusMap[status] || 'pending';
}
getDuration() {
if (!this.step.StartedAt || !this.step.CompletedAt)
return 0;
return new Date(this.step.CompletedAt).getTime() - new Date(this.step.StartedAt).getTime();
}
getTokensUsed() {
// Check if this is a prompt step with token data
if (this.step.StepType === 'Prompt' && this.step.PromptRun) {
return this.step.PromptRun.TokensUsed || undefined;
}
return undefined;
}
getCost() {
// Check if this is a prompt step with cost data
if (this.step.StepType === 'Prompt' && this.step.PromptRun) {
return this.step.PromptRun.TotalCost || undefined;
}
return undefined;
}
getDetailsMarkdown() {
// Check if the step name contains markdown details after the first line
const lines = this.step.StepName.split('\n');
if (lines.length > 1) {
return lines.slice(1).join('\n').trim();
}
return undefined;
}
getInputPreview() {
if (!this.step.InputData)
return undefined;
try {
const parsed = JSON.parse(this.step.InputData);
// Extract meaningful preview
if (parsed.promptName)
return `Prompt: ${parsed.promptName}`;
if (parsed.actionName)
return `Action: ${parsed.actionName}`;
if (parsed.subAgentName)
return `Sub-agent: ${parsed.subAgentName}`;
if (parsed.message)
return parsed.message;
if (parsed.userMessage)
return parsed.userMessage;
// Fallback to stringified preview
const str = JSON.stringify(parsed, null, 2);
return str.length > 500 ? str.substring(0, 500) + '...' : str;
}
catch {
return typeof this.step.InputData === 'string' ? this.step.InputData : JSON.stringify(this.step.InputData);
}
}
getOutputPreview() {
if (!this.step.OutputData)
return undefined;
try {
const parsed = JSON.parse(this.step.OutputData);
// Show action results clearly
if (parsed.actionResult) {
const result = parsed.actionResult;
let preview = '';
if (result.success !== undefined) {
preview += `Success: ${result.success}\n`;
}
if (result.resultCode) {
preview += `Result Code: ${result.resultCode}\n`;
}
if (result.message) {
preview += `Message: ${result.message}\n`;
}
if (result.result) {
preview += `Result: ${typeof result.result === 'object' ? JSON.stringify(result.result, null, 2) : result.result}`;
}
return preview.trim();
}
// Show prompt results
if (parsed.promptResult) {
const result = parsed.promptResult;
let preview = '';
if (result.success !== undefined) {
preview += `Success: ${result.success}\n`;
}
if (result.content) {
preview += `Content: ${result.content}`;
}
return preview;
}
// Fallback
const str = JSON.stringify(parsed, null, 2);
return str.length > 500 ? str.substring(0, 500) + '...' : str;
}
catch {
return typeof this.step.OutputData === 'string' ? this.step.OutputData : JSON.stringify(this.step.OutputData);
}
}
// Methods to extract metadata from input/output data
getAgentName() {
if (this.step.StepType === 'Sub-Agent' && this.step.SubAgentRun) {
return this.step.SubAgentRun.Agent || undefined;
}
return this.parseMetadata('subAgentName');
}
getAgentIconClass() {
return this.parseMetadata('subAgentIconClass') || this.parseMetadata('agentIconClass');
}
getAgentLogoURL() {
return this.parseMetadata('subAgentLogoURL') || this.parseMetadata('agentLogoURL');
}
getActionName() {
if (this.step.StepType === 'Actions' && this.step.ActionExecutionLog) {
return this.step.ActionExecutionLog.Action;
}
return this.parseMetadata('actionName');
}
getActionIconClass() {
return this.parseMetadata('actionIconClass');
}
parseMetadata(key) {
if (!this.step.InputData)
return undefined;
try {
const parsed = JSON.parse(this.step.InputData);
return parsed[key];
}
catch {
return undefined;
}
}
formatMarkdown(markdown) {
// Basic markdown formatting
let html = markdown;
// Headers
html = html.replace(/^### (.*$)/gim, '<h4>$1</h4>');
html = html.replace(/^## (.*$)/gim, '<h3>$1</h3>');
html = html.replace(/^# (.*$)/gim, '<h2>$1</h2>');
// Bold
html = html.replace(/\*\*(.*)\*\*/g, '<strong>$1</strong>');
// Italic
html = html.replace(/\*(.*)\*/g, '<em>$1</em>');
// Detect and linkify URLs
const urlRegex = /(?:https?:\/\/|www\.)[^\s<]+/gi;
html = html.replace(urlRegex, (url) => {
const href = url.startsWith('http') ? url : `https://${url}`;
return `<a href="${href}" target="_blank" rel="noopener noreferrer" style="color: #2196f3; text-decoration: underline;">${url}</a>`;
});
// Line breaks
html = html.replace(/\n/g, '<br>');
// Lists
html = html.replace(/^\* (.+)$/gim, '<li>$1</li>');
html = html.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>');
// Code blocks
html = html.replace(/```(.*?)```/gs, '<pre><code>$1</code></pre>');
// Inline code
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
return html;
}
static { this.ɵfac = function ExecutionNodeComponent_Factory(t) { return new (t || ExecutionNodeComponent)(); }; }
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ExecutionNodeComponent, selectors: [["mj-execution-node"]], inputs: { step: "step", depth: "depth", agentPath: "agentPath", expanded: "expanded", detailsExpanded: "detailsExpanded", overrideDisplayStatus: "overrideDisplayStatus" }, outputs: { toggleNode: "toggleNode", toggleDetails: "toggleDetails", userInteracted: "userInteracted" }, decls: 22, vars: 20, consts: [[1, "tree-node"], [1, "node-header", 3, "dblclick"], ["title", "Toggle children", 1, "expand-icon", "fa-solid", 3, "fa-chevron-down", "fa-chevron-right"], [1, "status-icon"], [1, "fa-regular", "fa-circle"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-check-circle"], [1, "fa-solid", "fa-times-circle"], [1, "type-icon", 3, "title"], [1, "fa-solid", "fa-shield-halved"], [1, "fa-solid", "fa-brain"], [1, "fa-solid", "fa-code-branch"], [1, "fa-solid", "fa-comments"], [1, "node-name"], [2, "color", "#666", "margin-right", "8px"], [1, "node-duration"], [1, "node-metrics"], [1, "details-toggle-btn", 3, "title"], [1, "node-details"], ["title", "Toggle children", 1, "expand-icon", "fa-solid", 3, "click"], [3, "class"], [1, "fa-solid", "fa-bolt"], [1, "agent-logo-icon", 3, "src", "alt"], [1, "fa-solid", "fa-sitemap"], [1, "tokens"], [1, "cost"], [1, "details-toggle-btn", 3, "click", "title"], [1, "fa-solid"], [1, "markdown-details"], [1, "detail-section", "error"], [1, "detail-section"], [1, "full-name"], [1, "detail-content", "markdown", 3, "innerHTML"], [1, "detail-label"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "detail-content"], [1, "fa-solid", "fa-sign-in-alt"], [1, "fa-solid", "fa-sign-out-alt"]], template: function ExecutionNodeComponent_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1);
i0.ɵɵlistener("dblclick", function ExecutionNodeComponent_Template_div_dblclick_1_listener() { return ctx.onDoubleClick(); });
i0.ɵɵtemplate(2, ExecutionNodeComponent_Conditional_2_Template, 1, 4, "i", 2);
i0.ɵɵelementStart(3, "span", 3);
i0.ɵɵtemplate(4, ExecutionNodeComponent_Case_4_Template, 1, 0, "i", 4)(5, ExecutionNodeComponent_Case_5_Template, 1, 0, "i", 5)(6, ExecutionNodeComponent_Case_6_Template, 1, 0, "i", 6)(7, ExecutionNodeComponent_Case_7_Template, 1, 0, "i", 7);
i0.ɵɵelementEnd();
i0.ɵɵelementStart(8, "span", 8);
i0.ɵɵtemplate(9, ExecutionNodeComponent_Case_9_Template, 1, 0, "i", 9)(10, ExecutionNodeComponent_Case_10_Template, 1, 0, "i", 10)(11, ExecutionNodeComponent_Case_11_Template, 2, 1)(12, ExecutionNodeComponent_Case_12_Template, 3, 1)(13, ExecutionNodeComponent_Case_13_Template, 1, 0, "i", 11)(14, ExecutionNodeComponent_Case_14_Template, 1, 0, "i", 12);
i0.ɵɵelementEnd();
i0.ɵɵelementStart(15, "span", 13);
i0.ɵɵtemplate(16, ExecutionNodeComponent_Conditional_16_Template, 2, 1, "small", 14);
i0.ɵɵtext(17);
i0.ɵɵelementEnd();
i0.ɵɵtemplate(18, ExecutionNodeComponent_Conditional_18_Template, 2, 1, "span", 15)(19, ExecutionNodeComponent_Conditional_19_Template, 3, 2, "span", 16)(20, ExecutionNodeComponent_Conditional_20_Template, 2, 5, "button", 17);
i0.ɵɵelementEnd();
i0.ɵɵtemplate(21, ExecutionNodeComponent_Conditional_21_Template, 6, 5, "div", 18);
i0.ɵɵelementEnd();
} if (rf & 2) {
let tmp_6_0;
let tmp_8_0;
i0.ɵɵclassMap("depth-" + ctx.depth + " type-" + ctx.getStepTypeClass());
i0.ɵɵclassProp("expanded", ctx.expanded)("has-children", ctx.hasChildren())("details-expanded", ctx.detailsExpanded);
i0.ɵɵadvance(2);
i0.ɵɵconditional(ctx.hasChildren() ? 2 : -1);
i0.ɵɵadvance();
i0.ɵɵclassMap("status-" + ctx.getStatusClass());
i0.ɵɵadvance();
i0.ɵɵconditional((tmp_6_0 = ctx.getStatusClass()) === "pending" ? 4 : tmp_6_0 === "running" ? 5 : tmp_6_0 === "completed" ? 6 : tmp_6_0 === "failed" ? 7 : -1);
i0.ɵɵadvance(4);
i0.ɵɵproperty("title", ctx.getNodeTitle());
i0.ɵɵadvance();
i0.ɵɵconditional((tmp_8_0 = ctx.getStepTypeClass()) === "validation" ? 9 : tmp_8_0 === "prompt" ? 10 : tmp_8_0 === "action" ? 11 : tmp_8_0 === "sub-agent" ? 12 : tmp_8_0 === "decision" ? 13 : tmp_8_0 === "chat" ? 14 : -1);
i0.ɵɵadvance(7);
i0.ɵɵconditional(ctx.step.StepType === "Sub-Agent" && ctx.depth > 0 ? 16 : -1);
i0.ɵɵadvance();
i0.ɵɵtextInterpolate1(" ", ctx.getTruncatedName(), " ");
i0.ɵɵadvance();
i0.ɵɵconditional(ctx.getDuration() ? 18 : -1);
i0.ɵɵadvance();
i0.ɵɵconditional(ctx.getTokensUsed() || ctx.getCost() ? 19 : -1);
i0.ɵɵadvance();
i0.ɵɵconditional(ctx.hasNodeDetails() ? 20 : -1);
i0.ɵɵadvance();
i0.ɵɵconditional(ctx.detailsExpanded ? 21 : -1);
} }, styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n }\n \n \n\n .tree-node[_ngcontent-%COMP%] {\n margin: 4px 0;\n position: relative;\n }\n \n \n\n .depth-0[_ngcontent-%COMP%] { \n margin-left: 0;\n position: relative;\n z-index: 2;\n }\n .depth-0[_ngcontent-%COMP%]::after {\n display: none;\n }\n .depth-0[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] { \n background: #f8f9fa;\n border: 2px solid #e0e0e0;\n font-weight: 600;\n position: relative;\n z-index: 10;\n }\n \n \n\n .depth-1[_ngcontent-%COMP%] {\n margin-left: 24px;\n }\n .depth-1[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: #fafafa;\n border-color: #d0d0d0;\n }\n \n .depth-2[_ngcontent-%COMP%] {\n margin-left: 48px;\n }\n .depth-2[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: #fcfcfc;\n border-color: #c0c0c0;\n }\n \n .depth-3[_ngcontent-%COMP%] {\n margin-left: 72px;\n }\n .depth-3[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: #fdfdfd;\n border-color: #b0b0b0;\n }\n \n .depth-4[_ngcontent-%COMP%] {\n margin-left: 96px;\n }\n .depth-4[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: #fefefe;\n border-color: #a0a0a0;\n }\n \n .depth-5[_ngcontent-%COMP%] {\n margin-left: 120px;\n }\n .depth-5[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: #fefefe;\n border-color: #a0a0a0;\n }\n \n .depth-6[_ngcontent-%COMP%] {\n margin-left: 144px;\n }\n .depth-6[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] {\n background: #fefefe;\n border-color: #a0a0a0;\n }\n\n \n\n .depth-0[_ngcontent-%COMP%] { \n position: relative;\n overflow: hidden;\n z-index: 2;\n }\n .depth-0[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] { \n background: #f8f9fa;\n position: relative;\n z-index: 2;\n }\n \n \n\n .depth-0.has-children[_ngcontent-%COMP%]::before {\n content: '';\n position: absolute;\n left: 12px;\n top: 0;\n width: 2px;\n height: 100%;\n border-left: 2px dotted #c0c7d0;\n z-index: 0; \n\n }\n \n \n\n .depth-1[_ngcontent-%COMP%] { \n margin-left: 30px;\n padding-left: 12px;\n position: relative;\n z-index: 1;\n }\n .depth-1[_ngcontent-%COMP%] .node-header[_ngcontent-%COMP%] { \n background: #fafbfc;\n position: relative;\n z-index: 1;\n }\n \n \n\n .depth-1[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: -15px;\n top: 12px;\n width: 25px;\n height: 2px;\n border-bottom: 2px dotted #c0c7d0;\n z-index: 1;\n }\n \n \n\n .tree-node.details-expanded[_ngcontent-%COMP%] > .node-header[_ngcontent-%COMP%] {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n border-bottom: 1px solid #2196f3;\n }\n \n .node-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n transition: all 0.2s ease;\n user-select: none;\n position: relative;\n z-index: 5;\n background: white;\n }\n \n .node-header[_ngcontent-%COMP%]:hover {\n background: var(--gray-700);\n border-color: var(--mj-blue) !important;\n }\n \n \n\n .tree-node.type-sub-agent[_ngcontent-%COMP%] > .node-header[_ngcontent-%COMP%] {\n border-left: 4px solid var(--mj-blue);\n }\n \n \n\n .tree-node.type-action[_ngcontent-%COMP%] > .node-header[_ngcontent-%COMP%] {\n border-left: 4px solid #4caf50;\n }\n \n .expand-icon[_ngcontent-%COMP%] {\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: #666;\n font-size: 10px;\n cursor: pointer;\n border-radius: 3px;\n transition: all 0.2s ease;\n z-index: 10; \n\n position: relative;\n }\n \n .expand-icon[_ngcontent-%COMP%]:hover {\n background: #e0e0e0;\n color: #333;\n }\n \n .status-icon[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n font-size: 14px;\n }\n \n .status-pending[_ngcontent-%COMP%] { color: #999; }\n .status-running[_ngcontent-%COMP%] { color: #2196f3; }\n .status-completed[_ngcontent-%COMP%] { color: #4caf50; }\n .status-failed[_ngcontent-%COMP%] { color: #f44336; }\n \n .type-icon[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n color: #666;\n }\n \n .agent-logo-icon[_ngcontent-%COMP%] {\n width: 16px;\n height: 16px;\n object-fit: cover;\n border-radius: 3px;\n }\n \n .node-name[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 13px;\n font-weight: 500;\n color: #1a1a1a;\n }\n \n \n .node-duration[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666;\n font-weight: 500;\n }\n \n .node-metrics[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n font-size: 12px;\n }\n \n .tokens[_ngcontent-%COMP%] {\n color: #666;\n }\n \n .cost[_ngcontent-%COMP%] {\n color: #2196f3;\n font-weight: 500;\n }\n \n \n\n .details-toggle-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.2s ease;\n font-size: 12px;\n color: #666;\n margin-left: 4px;\n }\n \n .details-toggle-btn[_ngcontent-%COMP%]:hover {\n background: #f0f0f0;\n border-color: #2196f3;\n color: #2196f3;\n }\n \n .details-toggle-btn[_ngcontent-%COMP%]:active {\n background: #e3f2fd;\n }\n \n \n\n .tree-node.details-expanded[_ngcontent-%COMP%] .details-toggle-btn[_ngcontent-%COMP%] {\n background: #2196f3;\n border-color: #2196f3;\n color: white;\n }\n \n .tree-node.details-expanded[_ngcontent-%COMP%] .details-toggle-btn[_ngcontent-%COMP%]:hover {\n background: #1976d2;\n border-color: #1976d2;\n }\n \n .node-details[_ngcontent-%COMP%] {\n margin: 0 5px;\n padding: 16px;\n background: var(--gray-600);\n border: 1px solid var(--gray-700);\n border-top: none;\n border-radius: 0 0 6px 6px;\n font-size: 12px;\n position: relative;\n z-index: 4;\n }\n \n .detail-section[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n }\n \n .detail-section[_ngcontent-%COMP%]:last-child {\n margin-bottom: 0;\n }\n \n .detail-section.error[_ngcontent-%COMP%] {\n color: #d32f2f;\n }\n \n .detail-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-weight: 600;\n margin-bottom: 4px;\n color: #555;\n }\n \n .detail-content[_ngcontent-%COMP%] {\n white-space: pre-wrap;\n word-break: break-word;\n color: #333;\n line-height: 1.4;\n }\n \n .markdown-details[_ngcontent-%COMP%] {\n padding: 16px 16px 0 16px;\n background: var(--gray-600);\n border: 1px solid var(--gray-700);\n border-bottom: none;\n border-top: none;\n margin: 0 5px;\n }\n \n .markdown[_ngcontent-%COMP%] h3[_ngcontent-%COMP%], .markdown[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 8px 0 4px 0;\n color: #1a1a1a;\n }\n \n .markdown[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n font-size: 14px;\n }\n \n .markdown[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n font-size: 13px;\n }\n \n .markdown[_ngcontent-%COMP%] ul[_ngcontent-%COMP%] {\n margin: 4px 0;\n padding-left: 20px;\n }\n \n .markdown[_ngcontent-%COMP%] li[_ngcontent-%COMP%] {\n margin: 2px 0;\n }\n \n .markdown[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n background: #e8e8e8;\n padding: 1px 4px;\n border-radius: 3px;\n font-size: 12px;\n }\n \n .markdown[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] {\n background: #f5f5f5;\n padding: 8px;\n border-radius: 4px;\n overflow-x: auto;\n margin: 4px 0;\n }\n \n .markdown[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n background: none;\n padding: 0;\n }\n \n .markdown[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n font-weight: 600;\n color: #1a1a1a;\n }\n \n .markdown[_ngcontent-%COMP%] em[_ngcontent-%COMP%] {\n font-style: italic;\n color: #555;\n }\n \n .full-name[_ngcontent-%COMP%] {\n font-weight: 600;\n color: #1a1a1a;\n padding-bottom: 8px;\n border-bottom: 1px solid #e0e0e0;\n word-wrap: break-word;\n }"], changeDetection: 0 }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ExecutionNodeComponent, [{
type: Component,
args: [{ selector: 'mj-execution-node', changeDetection: ChangeDetectionStrategy.OnPush, template: `
<div class="tree-node"
[class.expanded]="expanded"
[class.has-children]="hasChildren()"
[class.details-expanded]="detailsExpanded"
[class]="'depth-' + depth + ' type-' + getStepTypeClass()">
<!-- Node Header -->
<div class="node-header"
(dblclick)="onDoubleClick()">
<!-- Expand/Collapse Icon - Only show if node has children -->
@if (hasChildren()) {
<i class="expand-icon fa-solid"
[class.fa-chevron-down]="expanded"
[class.fa-chevron-right]="!expanded"
(click)="onToggleChildren($event)"
title="Toggle children"></i>
}
<!-- Status Icon -->
<span class="status-icon" [class]="'status-' + getStatusClass()">
@switch (getStatusClass()) {
@case ('pending') {
<i class="fa-regular fa-circle"></i>
}
@case ('running') {
<i class="fa-solid fa-spinner fa-spin"></i>
}
@case ('completed') {
<i class="fa-solid fa-check-circle"></i>
}
@case ('failed') {
<i class="fa-solid fa-times-circle"></i>
}
}
</span>
<!-- Type Icon -->
<span class="type-icon" [title]="getNodeTitle()">
@switch (getStepTypeClass()) {
@case ('validation') {
<i class="fa-solid fa-shield-halved"></i>
}
@case ('prompt') {
<i class="fa-solid fa-brain"></i>
}
@case ('action') {
@if (getActionIconClass()) {
<i [class]="getActionIconClass()"></i>
} @else {
<i class="fa-solid fa-bolt"></i>
}
}
@case ('sub-agent') {
@if (getAgentLogoURL()) {
<img [src]="getAgentLogoURL()" [alt]="getAgentName() || 'Agent'" class="agent-logo-icon">
} @else if (getAgentIconClass()) {
<i [class]="getAgentIconClass()"></i>
} @else {
<i class="fa-solid fa-sitemap"></i>
}
}
@case ('decision') {
<i class="fa-solid fa-code-branch"></i>
}
@case ('chat') {
<i class="fa-solid fa-comments"></i>
}
}
</span>
<!-- Node Name with depth indicator for sub-agents -->
<span class="node-name">
@if (step.StepType === 'Sub-Agent' && depth > 0) {
<small style="color: #666; margin-right: 8px;">[Level {{ depth }}]</small>
}
{{ getTruncatedName() }}
</span>
<!-- Duration -->
@if (getDuration()) {
<span class="node-duration">{{ formatDuration(getDuration()) }}</span>
}
<!-- Tokens/Cost -->
@if (getTokensUsed() || getCost()) {
<span class="node-metrics">
@if (getTokensUsed()) {
<span class="tokens">{{ getTokensUsed() }} tokens</span>
}
@if (getCost()) {
<span class="cost">\${{ getCost()!.toFixed(4) }}</span>
}
</span>
}
<!-- Details Toggle Button - Only show if node has details -->
@if (hasNodeDetails()) {
<button class="details-toggle-btn"
(click)="onToggleDetails($event)"
[title]="detailsExpanded ? 'Hide details' : 'Show details'">
<i class="fa-solid"
[class.fa-info]="!detailsExpanded"
[class.fa-times]="detailsExpanded"></i>
</button>
}
</div>
<!-- Node Details (when details are expanded) -->
@if (detailsExpanded) {
<!-- Show markdown details first if available -->
@if (getDetailsMarkdown() || isNameTruncated()) {
<div class="markdown-details">
@if (isNameTruncated()) {
<div class="full-name">{{ step.StepName }}</div>
}
@if (getDetailsMarkdown()) {
<div class="detail-content markdown" [innerHTML]="formatMarkdown(getDetailsMarkdown()!)"></div>
}
</div>
}
<!-- Always show details section if node is expanded, even if some content is empty -->
<div class="node-details">
@if (step.ErrorMessage) {
<div class="detail-section error">
<div class="detail-label">
<i class="fa-solid fa-exclamation-triangle"></i> Error
</div>
<div class="detail-content">{{ step.ErrorMessage }}</div>
</div>
}
@if (getInputPreview()) {
<div class="detail-section">
<div class="detail-label">
<i class="fa-solid fa-sign-in-alt"></i> Input
</div>
<div class="detail-content">{{ getInputPreview() }}</div>
</div>
}
@if (getOutputPreview()) {
<div class="detail-section">
<div class="detail-label">
<i class="fa-solid fa-sign-out-alt"></i> Output
</div>
<div class="detail-content">{{ getOutputPreview() }}</div>
</div>
}
@if (!step.ErrorMessage && !getInputPreview() && !getOutputPreview() && !getDetailsMarkdown() && !isNameTruncated()) {
<div class="detail-section">
<div class="detail-content">No additional details available for this step.</div>
</div>
}
</div>
}
<!-- Note: Sub-agent children are rendered by the parent component to maintain proper depth tracking -->
</div>
`, styles: ["\n :host {\n display: block;\n width: 100%;\n }\n \n /* Depth-based indentation - each level indents by 24px */\n .tree-node {\n margin: 4px 0;\n position: relative;\n }\n \n /* Root level nodes (depth 0) */\n .depth-0 { \n margin-left: 0;\n position: relative;\n z-index: 2;\n }\n .depth-0::after {\n display: none;\n }\n .depth-0 .node-header { \n background: #f8f9fa;\n border: 2px solid #e0e0e0;\n font-weight: 600;\n position: relative;\n z-index: 10;\n }\n \n /* Sub-level nodes with increasing indentation */\n .depth-1 {\n margin-left: 24px;\n }\n .depth-1 .node-header {\n background: #fafafa;\n border-color: #d0d0d0;\n }\n \n .depth-2 {\n margin-left: 48px;\n }\n .depth-2 .node-header {\n background: #fcfcfc;\n border-color: #c0c0c0;\n }\n \n .depth-3 {\n margin-left: 72px;\n }\n .depth-3 .node-header {\n background: #fdfdfd;\n border-color: #b0b0b0;\n }\n \n .depth-4 {\n margin-left: 96px;\n }\n .depth-4 .node-header {\n background: #fefefe;\n border-color: #a0a0a0;\n }\n \n .depth-5 {\n margin-left: 120px;\n }\n .depth-5 .node-header {\n background: #fefefe;\n border-color: #a0a0a0;\n }\n \n .depth-6 {\n margin-left: 144px;\n }\n .depth-6 .node-header {\n background: #fefefe;\n border-color: #a0a0a0;\n }\n\n /* Root level - higher z-index to hide lines behind it */\n .depth-0 { \n position: relative;\n overflow: hidden;\n z-index: 2;\n }\n .depth-0 .node-header { \n background: #f8f9fa;\n position: relative;\n z-index: 2;\n }\n \n /* Only add left padding for nodes with children (that show chevrons) */\n .depth-0.has-children::before {\n content: '';\n position: absolute;\n left: 12px;\n top: 0;\n width: 2px;\n height: 100%;\n border-left: 2px dotted #c0c7d0;\n z-index: 0; /* Behind everything */\n }\n \n /* Child level - only for nodes that actually have parent chevrons */\n .depth-1 { \n margin-left: 30px;\n padding-left: 12px;\n position: relative;\n z-index: 1;\n }\n .depth-1 .node-header { \n background: #fafbfc;\n position: relative;\n z-index: 1;\n }\n \n /* Horizontal line connecting to each child node - only when parent has children */\n .depth-1::after {\n content: '';\n position: absolute;\n left: -15px;\n top: 12px;\n width: 25px;\n height: 2px;\n border-bottom: 2px dotted #c0c7d0;\n z-index: 1;\n }\n \n /* Visual indicator when details are expanded */\n .tree-node.details-expanded > .node-header {\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n border-bottom: 1px solid #2196f3;\n }\n \n .node-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n transition: all 0.2s ease;\n user-select: none;\n position: relative;\n z-index: 5;\n background: white;\n }\n \n .node-header:hover {\n background: var(--gray-700);\n border-color: var(--mj-blue) !important;\n }\n \n /* Sub-agent specific styling */\n .tree-node.type-sub-agent > .node-header {\n border-left: 4px solid var(--mj-blue);\n }\n \n /* Action specific styling */\n .tree-node.type-action > .node-header {\n border-left: 4px solid #4caf50;\n }\n \n .expand-icon {\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: #666;\n font-size: 10px;\n cursor: pointer;\n border-radius: 3px;\n transition: all 0.2s ease;\n z-index: 10; /* Ensure expand icon is clickable */\n position: relative;\n }\n \n .expand-icon:hover {\n background: #e0e0e0;\n color: #333;\n }\n \n .status-icon {\n width: 20px;\n text-align: center;\n font-size: 14px;\n }\n \n .status-pending { color: #999; }\n .status-running { color: #2196f3; }\n .status-completed { color: #4caf50; }\n .status-failed { color: #f44336; }\n \n .type-icon {\n width: 20px;\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n color: #666;\n }\n \n .agent-logo-icon {\n width: 16px;\n height: 16px;\n object-fit: cover;\n border-radius: 3px;\n }\n \n .node-name {\n flex: 1;\n font-size: 13px;\n font-weight: 500;\n color: #1a1a1a;\n }\n \n \n .node-duration {\n font-size: 12px;\n color: #666;\n font-weight: 500;\n }\n \n .node-metrics {\n display: flex;\n gap: 12px;\n font-size: 12px;\n }\n \n .tokens {\n color: #666;\n }\n \n .cost {\n color: #2196f3;\n font-weight: 500;\n }\n \n /* Details Toggle Button */\n .details-toggle-btn {\n width: 28px;\n height: 28px