// UI Components for G3 Console
const components = {
// Render status badge
statusBadge(status) {
const colors = {
running: 'badge-success',
completed: 'badge-success',
failed: 'badge-error',
idle: 'badge-warning',
terminated: 'badge-neutral'
};
return `${status}`;
},
// Render progress bar
progressBar(instance, stats) {
const duration = stats.duration_secs;
const estimated = duration * 1.5; // Simple estimation
const progress = Math.min((duration / estimated) * 100, 100);
// Check if this is ensemble mode with turn data
if (instance.instance_type === 'ensemble' && stats.turns && stats.turns.length > 0) {
return this.ensembleProgressBar(stats.turns, duration);
}
return `
${Math.round(duration / 60)}m elapsed
`;
},
// Render multi-segment progress bar for ensemble mode
ensembleProgressBar(turns, totalDuration) {
const colors = {
coach: '#3b82f6',
player: '#6b7280',
completed: '#10b981',
error: '#ef4444'
};
let segments = '';
for (const turn of turns) {
const percentage = (turn.duration_secs / totalDuration) * 100;
const color = colors[turn.agent] || colors.player;
const statusColor = turn.status === 'error' ? colors.error : color;
segments += `
`;
}
return `
${segments}
${Math.round(totalDuration / 60)}m elapsed
`;
},
// Render instance panel
instancePanel(instance, stats, latestMessage) {
return `
${this.progressBar(instance, stats)}
Tokens
${stats.total_tokens.toLocaleString()}
Tool Calls
${stats.tool_calls}
Errors
${stats.errors}
Duration
${Math.round(stats.duration_secs / 60)}m
${latestMessage ? `
Latest: ${this.truncate(latestMessage, 100)}
` : ''}
${instance.status === 'running' ? `
` : ''}
${instance.status === 'terminated' ? `
` : ''}
`;
},
// Render loading spinner
spinner(message = 'Loading...') {
return `
`;
},
// Render error message
error(message) {
return `
Error: ${message}
`;
},
// Render empty state
emptyState(message) {
return `
`;
},
// Truncate text
truncate(text, length) {
if (text.length <= length) return text;
return text.substring(0, length) + '...';
},
// Render chat message
chatMessage(message, agent = null) {
const agentClass = agent === 'coach' ? 'message-coach' : agent === 'player' ? 'message-player' : '';
return `
${agent ? `
${agent}
` : ''}
${marked.parse(message)}
`;
},
// Render tool call
toolCall(toolCall) {
const statusIcon = toolCall.success ? '✓' : '✗';
const statusClass = toolCall.success ? 'success' : 'error';
return `
`;
},
// Render git status section
gitStatus(gitStatus) {
if (!gitStatus) {
return 'No git repository detected
';
}
return `
${gitStatus.uncommitted_changes > 0 ? `
${gitStatus.modified_files.length > 0 ? `
Modified:
${gitStatus.modified_files.map(f => `- ${f}
`).join('')}
` : ''}
${gitStatus.added_files.length > 0 ? `
Added:
${gitStatus.added_files.map(f => `- ${f}
`).join('')}
` : ''}
${gitStatus.deleted_files.length > 0 ? `
Deleted:
${gitStatus.deleted_files.map(f => `- ${f}
`).join('')}
` : ''}
` : ''}
`;
},
// Render project files section
projectFiles(projectFiles) {
if (!projectFiles || (!projectFiles.requirements && !projectFiles.readme && !projectFiles.agents)) {
return 'No project files found
';
}
let html = '';
if (projectFiles.requirements) {
html += `
${this.escapeHtml(projectFiles.requirements)}
`;
}
if (projectFiles.readme) {
html += `
${this.escapeHtml(projectFiles.readme)}
`;
}
if (projectFiles.agents) {
html += `
${this.escapeHtml(projectFiles.agents)}
`;
}
html += '
';
return html;
},
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
};