adeem turky
Upload 4 files
e4e2691 verified
<!DOCTYPE html>
<html lang="ar" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AISA Workflow Engine</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* Custom Animations & Glows */
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
body { font-family: 'JetBrains Mono', monospace; background-color: #0B1120; }
.node {
transition: all 0.4s ease;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
}
.node.active {
border-color: #38bdf8;
box-shadow: 0 0 20px #38bdf8, inset 0 0 10px rgba(56, 189, 248, 0.2);
transform: scale(1.05);
color: #fff;
}
.connector {
width: 2px;
background-color: #334155;
transition: background-color 0.3s;
}
.connector.active {
background-color: #38bdf8;
box-shadow: 0 0 8px #38bdf8;
}
/* Scrollbar Styling */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #1e293b; }
::-webkit-scrollbar-thumb { background: #475569; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #64748b; }
</style>
</head>
<body class="text-slate-200 h-screen flex flex-col overflow-hidden">
<header class="bg-slate-900 border-b border-slate-800 p-4 flex justify-between items-center shadow-lg z-10">
<div class="flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-blue-500 animate-pulse"></div>
<h1 class="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-cyan-300">
AISA Orchestrator
</h1>
</div>
<div id="status-badge" class="px-3 py-1 text-xs rounded-full bg-slate-800 text-slate-400 border border-slate-700">
System Idle
</div>
</header>
<main class="flex flex-1 relative">
<div class="w-1/2 flex flex-col items-center justify-center bg-slate-900/50 p-10 relative">
<div class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/carbon-fibre.png')] opacity-10 pointer-events-none"></div>
<div id="node-start" class="node w-32 h-12 rounded-full border-2 border-slate-600 bg-slate-800 flex items-center justify-center text-sm mb-2 z-10">
Start
</div>
<div class="connector h-8 mb-2"></div>
<div id="node-planner" class="node w-48 h-16 rounded-lg border-2 border-slate-600 bg-slate-800 flex items-center justify-center gap-2 mb-2 z-10">
<span>🧠</span> Planner
</div>
<div class="connector h-8 mb-2"></div>
<div class="p-6 rounded-xl border border-dashed border-slate-700 bg-slate-800/30 flex flex-col items-center w-full max-w-sm relative">
<span class="absolute -top-3 left-4 bg-slate-900 px-2 text-xs text-slate-500">Execution Cycle</span>
<div id="node-executor" class="node w-48 h-16 rounded-lg border-2 border-slate-600 bg-slate-800 flex items-center justify-center gap-2 mb-4 z-10">
<span>🔨</span> Executor
</div>
<div class="connector h-6 mb-4"></div>
<div id="node-validator" class="node w-48 h-16 rounded-lg border-2 border-slate-600 bg-slate-800 flex items-center justify-center gap-2 mb-4 z-10">
<span>🛡️</span> Validator
</div>
<div class="connector h-6 mb-4"></div>
<div id="node-decision" class="node w-12 h-12 rotate-45 border-2 border-slate-600 bg-slate-800 flex items-center justify-center z-10">
<span class="-rotate-45 text-xs">⚖️</span>
</div>
</div>
<div class="connector h-8 mt-2 mb-2"></div>
<div id="node-end" class="node w-32 h-12 rounded-full border-2 border-slate-600 bg-slate-800 flex items-center justify-center text-sm z-10">
🚀 Output
</div>
</div>
<div class="w-1/2 flex flex-col border-l border-slate-800 bg-slate-900 overflow-y-auto custom-scrollbar">
<div class="p-6 space-y-6">
<div>
<label class="block text-xs text-blue-400 mb-2 font-bold uppercase tracking-wider">Mission Objective</label>
<div class="flex gap-2">
<input type="text" id="taskInput" value="Search about KSA Vision 2030"
class="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 focus:outline-none focus:border-blue-500 text-sm transition">
<button onclick="startWorkflow()" id="startBtn"
class="bg-blue-600 hover:bg-blue-500 text-white px-6 py-3 rounded-lg font-bold transition shadow-lg shadow-blue-900/50 flex items-center gap-2">
<span>RUN</span>
</button>
</div>
</div>
<div class="flex flex-col bg-slate-950 rounded-lg border border-slate-800 overflow-hidden shadow-inner h-80 shrink-0">
<div class="bg-slate-900 px-4 py-2 border-b border-slate-800 flex justify-between items-center">
<span class="text-xs text-slate-400">System Logs</span>
<div class="flex gap-1">
<span class="w-2 h-2 rounded-full bg-red-500"></span>
<span class="w-2 h-2 rounded-full bg-yellow-500"></span>
<span class="w-2 h-2 rounded-full bg-green-500"></span>
</div>
</div>
<div id="logs" class="flex-1 p-4 overflow-y-auto font-mono text-sm space-y-2 scroll-smooth">
<div class="text-slate-600 italic">> System ready. Waiting for input...</div>
</div>
</div>
<div id="resultBox" class="hidden bg-slate-800/80 border border-green-500/30 rounded-lg p-6 shadow-lg shadow-green-900/20 backdrop-blur-sm flex flex-col max-h-[60vh]">
<h3 class="text-green-400 font-bold text-lg mb-4 flex items-center gap-2 border-b border-slate-700 pb-2 shrink-0">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
Final Report Generated
</h3>
<div class="overflow-y-auto custom-scrollbar pr-2">
<div id="finalReport" class="text-sm text-slate-300 whitespace-pre-wrap font-sans leading-relaxed"></div>
</div>
</div>
<div class="h-10"></div>
</div>
</div>
</main>
<script>
function startWorkflow() {
const task = document.getElementById('taskInput').value;
const btn = document.getElementById('startBtn');
const logs = document.getElementById('logs');
const resultBox = document.getElementById('resultBox');
// 1. Reset UI
logs.innerHTML = '';
resultBox.classList.add('hidden');
document.getElementById('finalReport').innerHTML = '';
// Disable Button
btn.disabled = true;
btn.classList.add('opacity-50', 'cursor-not-allowed');
btn.innerHTML = '<span>⏳ Processing...</span>';
resetNodes();
// 2. Connect to Stream
const eventSource = new EventSource(`/stream_workflow?task=${encodeURIComponent(task)}`);
// Helper to clean up state
function stopExecution(success = true) {
eventSource.close();
btn.disabled = false;
btn.classList.remove('opacity-50', 'cursor-not-allowed');
btn.innerHTML = '<span>RUN</span>';
if (success) {
updateStatus("Mission Completed");
document.getElementById('node-end').classList.add('active');
} else {
updateStatus("Stopped / Error");
}
}
eventSource.onmessage = function(e) {
let data;
try {
data = JSON.parse(e.data);
} catch (err) {
console.error("Parse Error", err);
return;
}
if (data.type === 'activate') {
highlightNode(data.node);
addLog(data.msg, 'info');
updateStatus(data.msg);
}
else if (data.type === 'log') {
addLog(data.msg, data.role);
}
else if (data.type === 'retry_animation') {
flashDecisionNode();
}
else if (data.type === 'finish') {
// Format Markdown
let formattedReport = data.report
.replace(/### (.*?)\n/g, '<h3 class="text-blue-400 font-bold text-lg mt-6 mb-2 border-b border-slate-700 pb-1">$1</h3>')
.replace(/\*\*(.*?)\*\*/g, '<strong class="text-white font-semibold">$1</strong>')
.replace(/- (.*?)\n/g, '<li class="ml-4 text-slate-300 list-disc marker:text-blue-500 mb-1">$1</li>')
.replace(/\n/g, '<br>');
document.getElementById('finalReport').innerHTML = formattedReport;
resultBox.classList.remove('hidden');
addLog("Workflow Finished Successfully.", "success");
stopExecution(true); // Close safely
}
};
eventSource.onerror = function() {
addLog("Stream connection closed or interrupted.", "warning");
stopExecution(false); // Force button enable
};
}
// --- Helper Functions ---
function highlightNode(nodeId) {
document.querySelectorAll('.node').forEach(n => n.classList.remove('active'));
document.querySelectorAll('.connector').forEach(c => c.classList.remove('active'));
const el = document.getElementById(`node-${nodeId}`);
if (el) {
el.classList.add('active');
if (el.previousElementSibling && el.previousElementSibling.classList.contains('connector')) {
el.previousElementSibling.classList.add('active');
}
}
}
function resetNodes() {
document.querySelectorAll('.node').forEach(n => n.classList.remove('active'));
document.querySelectorAll('.connector').forEach(c => c.classList.remove('active'));
}
function flashDecisionNode() {
const decision = document.getElementById('node-decision');
decision.style.borderColor = '#ef4444';
decision.style.boxShadow = '0 0 15px #ef4444';
setTimeout(() => {
decision.style.borderColor = '';
decision.style.boxShadow = '';
}, 500);
}
function updateStatus(text) {
const badge = document.getElementById('status-badge');
badge.textContent = text;
badge.classList.remove('text-slate-400');
badge.classList.add('text-blue-400', 'border-blue-500');
}
function addLog(msg, type) {
const logs = document.getElementById('logs');
const div = document.createElement('div');
div.className = 'border-l-2 pl-2 text-xs py-1 animate-fade-in';
if (type === 'error') { div.classList.add('border-red-500', 'text-red-400'); }
else if (type === 'success') { div.classList.add('border-green-500', 'text-green-400'); }
else if (type === 'warning') { div.classList.add('border-yellow-500', 'text-yellow-400'); }
else { div.classList.add('border-blue-500', 'text-slate-300'); }
div.innerHTML = `> ${msg}`;
logs.appendChild(div);
logs.scrollTop = logs.scrollHeight;
}
</script>
</body>