/**
* NetCop AI Agents - Shared Utilities
* Contains common functions used across all agent templates
*/
window.AgentUtils = {
/**
* Enhanced Markdown Parser
* Converts markdown syntax to styled HTML with proper formatting
*/
parseMarkdown(text) {
// Handle null, undefined, or non-string inputs
if (!text || typeof text !== 'string') {
return '';
}
let html = text;
// Convert headers with proper styling
html = html.replace(/^### (.*$)/gm, '
$1
');
html = html.replace(/^## (.*$)/gm, '$1
');
html = html.replace(/^# (.*$)/gm, '$1
');
// Convert bold text
html = html.replace(/\*\*(.*?)\*\*/g, '$1');
// Convert bullet points to proper lists
const lines = html.split('\n');
let inList = false;
let processedLines = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('- ')) {
if (!inList) {
processedLines.push('');
inList = true;
}
processedLines.push(`- ${line.substring(2).trim()}
`);
} else {
if (inList) {
processedLines.push('
');
inList = false;
}
processedLines.push(line);
}
}
if (inList) {
processedLines.push('');
}
html = processedLines.join('\n');
// Reduce excessive line breaks and convert to paragraphs
html = html.replace(/\n{3,}/g, '\n\n');
// Convert double line breaks to paragraph breaks
const paragraphs = html.split('\n\n');
html = paragraphs
.filter(p => p.trim() !== '')
.map(p => {
const trimmed = p.trim();
// Don't wrap headers or lists in paragraphs
if (trimmed.startsWith('') || trimmed.includes('${trimmed}`;
})
.join('');
// Convert remaining single line breaks to
tags
html = html.replace(/\n/g, '
');
return html.trim();
},
/**
* Update wallet balance display across all agents
*/
updateWalletBalance(newBalance) {
const balanceElement = document.querySelector('[data-wallet-balance]') ||
document.getElementById('walletBalance');
if (balanceElement) {
balanceElement.textContent = `${newBalance.toFixed(2)} AED`;
}
window.currentWalletBalance = newBalance;
},
/**
* Reset UI to initial state
*/
resetUI(config) {
const elements = {
processingStatus: document.getElementById(config.processingStatusId || 'processingStatus'),
processButton: document.getElementById(config.processButtonId || 'processButton'),
results: document.getElementById(config.resultsId)
};
if (elements.processingStatus) {
elements.processingStatus.style.display = 'none';
}
if (elements.processButton) {
elements.processButton.disabled = false;
elements.processButton.innerHTML = config.buttonText || 'Process';
elements.processButton.classList.remove('loading');
}
if (elements.results) {
elements.results.style.display = 'none';
}
},
/**
* Show processing status
*/
showProcessing(config) {
const elements = {
processingStatus: document.getElementById(config.processingStatusId || 'processingStatus'),
processButton: document.getElementById(config.processButtonId || 'processButton'),
results: document.getElementById(config.resultsId)
};
if (elements.processingStatus) {
elements.processingStatus.style.display = 'block';
}
if (elements.processButton) {
elements.processButton.disabled = true;
elements.processButton.classList.add('loading');
elements.processButton.innerHTML = config.processingText || '⏳ Processing...';
}
if (elements.results) {
elements.results.style.display = 'none';
}
},
/**
* Show toast notification with duplicate prevention
*/
showToast(message, type = 'info') {
// Prevent duplicate toasts
const existingToast = document.querySelector('.agent-toast');
if (existingToast) {
existingToast.remove();
}
const toast = document.createElement('div');
toast.className = 'agent-toast';
toast.style.cssText = `
position: fixed;
top: 16px;
right: 16px;
padding: 8px 12px;
border-radius: 4px;
color: white;
font-size: 13px;
z-index: 1000;
max-width: 300px;
font-weight: 500;
${type === 'success' ? 'background: #10b981;' : 'background: #ef4444;'}
`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
if (toast.parentNode) {
toast.remove();
}
}, 2000);
},
/**
* Display results with markdown parsing
* Standardized across all agents
*/
displayResults(config) {
const resultsContainer = document.getElementById(config.resultsId);
const contentContainer = document.getElementById(config.contentId);
if (config.result.success && config.result.status === 'completed') {
// Get content from various possible fields
const content = config.result.content ||
config.result.job_posting_content ||
config.result.ad_copy_content ||
config.result.analysis_results ||
config.result.insights_summary ||
config.result.report_text ||
config.result.weather_data ||
config.result.formatted_report ||
config.result.output_text ||
config.defaultMessage ||
'Content generated successfully!';
// Parse markdown and display as HTML
const formattedContent = this.parseMarkdown(content);
contentContainer.innerHTML = formattedContent;
resultsContainer.style.display = 'block';
// Update wallet balance if provided
if (config.result.wallet_balance !== undefined) {
this.updateWalletBalance(config.result.wallet_balance);
}
this.showToast(config.successMessage || '✅ Content generated and payment processed!', 'success');
} else {
this.showToast(config.errorMessage || '❌ Failed to generate content - no charge applied', 'error');
}
},
/**
* Generate text for copy/download functionality
*/
generateTextForExport(contentElementId) {
const content = document.getElementById(contentElementId);
if (content) {
return content.innerText || content.textContent || '';
}
return 'No content available';
},
/**
* Copy content to clipboard
*/
copyToClipboard(text, successMessage = 'Content copied to clipboard!') {
navigator.clipboard.writeText(text).then(() => {
this.showToast(`📋 ${successMessage}`, 'success');
}).catch(() => {
this.showToast('Failed to copy content', 'error');
});
},
/**
* Download content as text file
*/
downloadAsFile(text, filename, successMessage = 'File downloaded!') {
const blob = new Blob([text], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename || `content-${Date.now()}.txt`;
a.click();
URL.revokeObjectURL(url);
this.showToast(`💾 ${successMessage}`, 'success');
}
};
/**
* Progressive status steps for better UX
*/
window.StatusStepper = class {
constructor(steps, statusTextElementId, interval = 800) {
this.steps = steps;
this.statusTextElement = document.getElementById(statusTextElementId);
this.interval = interval;
this.currentStep = 0;
this.stepInterval = null;
}
start() {
this.currentStep = 0;
this.stepInterval = setInterval(() => {
if (this.currentStep < this.steps.length && this.statusTextElement) {
this.statusTextElement.textContent = this.steps[this.currentStep];
this.currentStep++;
} else {
this.stop();
}
}, this.interval);
}
stop() {
if (this.stepInterval) {
clearInterval(this.stepInterval);
this.stepInterval = null;
}
}
};