composition-manager.ts•6.67 kB
/**
* EuConquisto Composer Management
*
* High-level composition operations using the HTTP API client.
* Handles natural language processing and widget creation.
*/
import { EuConquistoAPIClient, CompositionData, CompositionWidget, CompositionMetadata } from './api-client.js';
export interface CompositionRequest {
title: string;
description?: string;
content: string;
widgets?: Array<{
type: 'text' | 'header' | 'list' | 'image';
content: string;
properties?: Record<string, any>;
}>;
}
export interface CompositionResult {
success: boolean;
compositionUID?: string;
message: string;
data?: any;
error?: string;
}
export class CompositionManager {
private apiClient: EuConquistoAPIClient;
constructor() {
this.apiClient = new EuConquistoAPIClient();
}
/**
* Create a new composition from natural language description
*/
async createFromDescription(request: CompositionRequest): Promise<CompositionResult> {
try {
// Create basic composition structure
const composition = this.apiClient.createBasicComposition(
request.title,
request.description
);
// Process content and add widgets
if (request.widgets && request.widgets.length > 0) {
// Use explicitly provided widgets
for (const widget of request.widgets) {
const compositionWidget = this.createWidgetFromSpec(widget);
composition.structure.push(compositionWidget);
}
} else {
// Parse natural language content into widgets
const widgets = this.parseContentIntoWidgets(request.content);
composition.structure.push(...widgets);
}
// Create via API
const result = await this.apiClient.createComposition(composition);
if (result.success) {
return {
success: true,
compositionUID: result.data?.uid || 'unknown',
message: `Composition "${request.title}" created successfully`,
data: result.data
};
} else {
return {
success: false,
message: 'Failed to create composition',
error: result.error
};
}
} catch (error) {
return {
success: false,
message: 'Error creating composition',
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Update composition metadata
*/
async updateMetadata(uid: string, metadata: Partial<CompositionMetadata>): Promise<CompositionResult> {
try {
// For now, we'll need to get the existing composition and update it
// This is a simplified version - in production, you'd want to fetch existing data first
const basicComposition = this.apiClient.createBasicComposition(
metadata.title || "Updated Composition",
metadata.description
);
if (metadata.tags) {
basicComposition.metadata.tags = metadata.tags;
}
const result = await this.apiClient.updateComposition(uid, basicComposition);
if (result.success) {
return {
success: true,
compositionUID: uid,
message: 'Composition metadata updated successfully',
data: result.data
};
} else {
return {
success: false,
message: 'Failed to update composition metadata',
error: result.error
};
}
} catch (error) {
return {
success: false,
message: 'Error updating composition metadata',
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Test API connection
*/
async testConnection(): Promise<CompositionResult> {
try {
const result = await this.apiClient.testConnection();
return {
success: result.success,
message: result.success ? 'API connection successful' : 'API connection failed',
data: result.data,
error: result.error
};
} catch (error) {
return {
success: false,
message: 'Connection test failed',
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Parse natural language content into widgets
*/
private parseContentIntoWidgets(content: string): CompositionWidget[] {
const widgets: CompositionWidget[] = [];
// Simple parsing logic - can be enhanced with more sophisticated NLP
const lines = content.split('\n').filter(line => line.trim());
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('#')) {
// Header widget
const headerText = trimmed.replace(/^#+\s*/, '');
widgets.push(this.apiClient.addHeaderWidget(headerText));
} else if (trimmed.length > 0) {
// Text widget
widgets.push(this.apiClient.addTextWidget(trimmed));
}
}
// If no widgets created, add the entire content as a single text widget
if (widgets.length === 0) {
widgets.push(this.apiClient.addTextWidget(content));
}
return widgets;
}
/**
* Create widget from specification
*/
private createWidgetFromSpec(spec: {
type: 'text' | 'header' | 'list' | 'image';
content: string;
properties?: Record<string, any>;
}): CompositionWidget {
switch (spec.type) {
case 'header':
return this.apiClient.addHeaderWidget(spec.content);
case 'text':
return this.apiClient.addTextWidget(spec.content);
case 'list':
// Simple list implementation
const listContent = `<ul>${spec.content.split('\n')
.filter(item => item.trim())
.map(item => `<li>${item.trim()}</li>`)
.join('')}</ul>`;
return {
id: this.generateUID(),
type: "list-1",
content: listContent,
background_color: "#FFFFFF",
padding_top: 0,
padding_bottom: 0
};
case 'image':
return {
id: this.generateUID(),
type: "image-1",
content: spec.content, // Image URL or description
background_color: "#FFFFFF",
padding_top: 0,
padding_bottom: 0,
...spec.properties
};
default:
return this.apiClient.addTextWidget(spec.content);
}
}
/**
* Generate a simple UID
*/
private generateUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
}