@memberjunction/ng-ai-test-harness
Version:
MemberJunction AI Test Harness - A reusable component for testing AI agents and prompts with beautiful UX
163 lines • 6.86 kB
JavaScript
import { Injectable, createComponent } from '@angular/core';
import { TestHarnessCustomWindowComponent } from './test-harness-custom-window.component';
import { Subject } from 'rxjs';
import * as i0 from "@angular/core";
import * as i1 from "./window-dock.service";
/**
* Service for managing test harness windows for AI Agents and AI Prompts.
* Creates Kendo Window components with custom titlebar including minimize button.
*/
export class TestHarnessWindowManagerService {
constructor(appRef, injector, dockService) {
this.appRef = appRef;
this.injector = injector;
this.dockService = dockService;
this.openWindows = new Map();
this.windowCounter = 0;
}
/**
* Opens the AI Agent Test Harness in a window
*/
openAgentTestHarness(options) {
const data = {
agentId: options.agentId,
agent: options.agent,
title: options.title,
width: options.width || '90vw',
height: options.height || '90vh',
initialDataContext: options.initialDataContext,
initialTemplateData: options.initialTemplateData,
mode: 'agent'
};
return this.createWindow(data, options.viewContainerRef);
}
/**
* Opens the AI Prompt Test Harness in a window
*/
openPromptTestHarness(options) {
const data = {
promptId: options.promptId,
prompt: options.prompt,
title: options.title || 'Test AI Prompt',
width: options.width || '90vw',
height: options.height || '90vh',
initialTemplateVariables: options.initialTemplateVariables,
selectedModelId: options.selectedModelId,
mode: 'prompt'
};
return this.createWindow(data, options.viewContainerRef);
}
/**
* Creates a test harness window
*/
createWindow(data, viewContainerRef) {
const resultSubject = new Subject();
const windowId = `test-harness-${++this.windowCounter}`;
// Create component
const componentRef = createComponent(TestHarnessCustomWindowComponent, {
environmentInjector: this.appRef.injector,
elementInjector: this.injector,
hostElement: document.createElement('div')
});
// Set component data
componentRef.instance.data = data;
// Handle window events
componentRef.instance.closeWindow.subscribe(() => {
this.closeWindow(windowId);
resultSubject.next({
success: true
});
resultSubject.complete();
});
// Handle minimize event
componentRef.instance.minimizeWindow.subscribe(() => {
const title = componentRef.instance.windowTitle;
let icon = 'fa-solid fa-flask'; // default
let iconUrl;
// Get the appropriate icon based on mode and entity
if (componentRef.instance.mode === 'agent' && componentRef.instance.agent) {
// For agents, use LogoURL since AIAgentEntityExtended doesn't have IconClass
if (componentRef.instance.agent.LogoURL) {
iconUrl = componentRef.instance.agent.LogoURL;
icon = undefined; // Clear icon when using URL
}
else {
icon = 'fa-solid fa-robot'; // Default agent icon
}
}
else if (componentRef.instance.mode === 'prompt') {
icon = 'fa-solid fa-comment-dots'; // Default prompt icon
}
// Add to dock with icon or iconUrl
this.dockService.addWindow(windowId, title, icon, () => {
// Restore callback
componentRef.instance.restoreFromMinimized();
}, iconUrl);
});
// Handle restore event
componentRef.instance.restoreWindow.subscribe(() => {
// Remove from dock
this.dockService.removeWindow(windowId);
});
// Handle execution state changes
componentRef.instance.executionStateChange.subscribe((state) => {
if (state.isExecuting) {
// Show indeterminate progress (spinning/pulsing)
this.dockService.updateWindowProgress(windowId, 50); // 50% for indeterminate animation
}
else {
// Clear progress
this.dockService.updateWindowProgress(windowId, undefined);
}
});
// Attach to DOM
if (viewContainerRef) {
// When using viewContainerRef, the view is automatically attached to Angular
viewContainerRef.insert(componentRef.hostView);
}
else {
// When appending to document.body, we need to manually attach to Angular
document.body.appendChild(componentRef.location.nativeElement);
this.appRef.attachView(componentRef.hostView);
}
// Store reference with info about how it was created
this.openWindows.set(windowId, {
componentRef,
usedViewContainerRef: !!viewContainerRef
});
return resultSubject.asObservable();
}
/**
* Closes a specific window
*/
closeWindow(windowId) {
const windowInfo = this.openWindows.get(windowId);
if (windowInfo) {
// Remove from dock if minimized
this.dockService.removeWindow(windowId);
// Only detach from appRef if we attached it manually (not using viewContainerRef)
if (!windowInfo.usedViewContainerRef) {
this.appRef.detachView(windowInfo.componentRef.hostView);
}
windowInfo.componentRef.destroy();
this.openWindows.delete(windowId);
}
}
/**
* Closes all open windows
*/
closeAllWindows() {
this.openWindows.forEach((windowInfo, id) => {
this.closeWindow(id);
});
}
static { this.ɵfac = function TestHarnessWindowManagerService_Factory(t) { return new (t || TestHarnessWindowManagerService)(i0.ɵɵinject(i0.ApplicationRef), i0.ɵɵinject(i0.Injector), i0.ɵɵinject(i1.WindowDockService)); }; }
static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: TestHarnessWindowManagerService, factory: TestHarnessWindowManagerService.ɵfac, providedIn: 'root' }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestHarnessWindowManagerService, [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], () => [{ type: i0.ApplicationRef }, { type: i0.Injector }, { type: i1.WindowDockService }], null); })();
//# sourceMappingURL=test-harness-window-manager.service.js.map