AddisonJi/claude-code-training
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>Company Intel Agent</title>7 <style>8 @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=DM+Sans:wght@400;500;600;700&display=swap');9 10 :root {11 --bg: #0a0a0f;12 --surface: #12121a;13 --surface-2: #1a1a26;14 --border: #2a2a3a;15 --text: #e4e4ef;16 --text-dim: #7a7a8f;17 --accent: #6c63ff;18 --accent-glow: rgba(108, 99, 255, 0.15);19 --green: #4ade80;20 --yellow: #fbbf24;21 --blue: #60a5fa;22 --red: #f87171;23 --orange: #fb923c;24 }25 26 * { margin: 0; padding: 0; box-sizing: border-box; }27 28 body {29 font-family: 'DM Sans', sans-serif;30 background: var(--bg);31 color: var(--text);32 min-height: 100vh;33 }34 35 /* ===== HEADER ===== */36 .header {37 padding: 24px 40px;38 border-bottom: 1px solid var(--border);39 display: flex;40 align-items: center;41 gap: 12px;42 }43 .header h1 {44 font-size: 20px;45 font-weight: 600;46 letter-spacing: -0.02em;47 }48 .header .badge {49 font-size: 11px;50 font-family: 'JetBrains Mono', monospace;51 background: var(--accent-glow);52 color: var(--accent);53 padding: 3px 10px;54 border-radius: 20px;55 border: 1px solid rgba(108, 99, 255, 0.25);56 }57 58 /* ===== MAIN LAYOUT ===== */59 .main {60 display: grid;61 grid-template-columns: 380px 1fr;62 height: calc(100vh - 73px);63 }64 65 /* ===== LEFT PANEL - INPUT ===== */66 .input-panel {67 padding: 28px;68 border-right: 1px solid var(--border);69 display: flex;70 flex-direction: column;71 gap: 20px;72 overflow-y: auto;73 }74 75 .section-label {76 font-size: 11px;77 font-weight: 600;78 text-transform: uppercase;79 letter-spacing: 0.08em;80 color: var(--text-dim);81 margin-bottom: 4px;82 }83 84 .input-group {85 display: flex;86 flex-direction: column;87 gap: 6px;88 }89 90 .input-group label {91 font-size: 13px;92 font-weight: 500;93 color: var(--text-dim);94 }95 96 .input-group input,97 .input-group textarea {98 background: var(--surface);99 border: 1px solid var(--border);100 border-radius: 8px;101 padding: 10px 14px;102 color: var(--text);103 font-family: 'DM Sans', sans-serif;104 font-size: 14px;105 transition: border-color 0.2s;106 }107 .input-group input:focus,108 .input-group textarea:focus {109 outline: none;110 border-color: var(--accent);111 box-shadow: 0 0 0 3px var(--accent-glow);112 }113 .input-group textarea {114 resize: vertical;115 min-height: 80px;116 }117 118 .btn-primary {119 background: var(--accent);120 color: white;121 border: none;122 border-radius: 8px;123 padding: 12px 20px;124 font-size: 14px;125 font-weight: 600;126 font-family: 'DM Sans', sans-serif;127 cursor: pointer;128 transition: all 0.2s;129 display: flex;130 align-items: center;131 justify-content: center;132 gap: 8px;133 }134 .btn-primary:hover { background: #5b52e8; }135 .btn-primary:disabled {136 opacity: 0.5;137 cursor: not-allowed;138 }139 140 /* Quick action buttons */141 .quick-actions {142 display: flex;143 flex-direction: column;144 gap: 8px;145 }146 .btn-quick {147 background: var(--surface);148 color: var(--text-dim);149 border: 1px solid var(--border);150 border-radius: 8px;151 padding: 10px 14px;152 font-size: 13px;153 font-family: 'DM Sans', sans-serif;154 cursor: pointer;155 text-align: left;156 transition: all 0.2s;157 }158 .btn-quick:hover {159 border-color: var(--accent);160 color: var(--text);161 background: var(--accent-glow);162 }163 164 /* ===== RIGHT PANEL - AGENT LOG + RESULT ===== */165 .output-panel {166 display: flex;167 flex-direction: column;168 height: 100%;169 overflow: hidden;170 }171 172 /* Agent Log - terminal style */173 .log-header {174 padding: 16px 28px;175 border-bottom: 1px solid var(--border);176 display: flex;177 align-items: center;178 gap: 8px;179 }180 .log-header .dot {181 width: 8px;182 height: 8px;183 border-radius: 50%;184 background: var(--text-dim);185 }186 .log-header .dot.active { background: var(--green); }187 .log-header span {188 font-size: 12px;189 font-family: 'JetBrains Mono', monospace;190 color: var(--text-dim);191 }192 193 .log-container {194 flex: 1;195 overflow-y: auto;196 padding: 20px 28px;197 font-family: 'JetBrains Mono', monospace;198 font-size: 13px;199 line-height: 1.7;200 }201 202 .log-entry {203 margin-bottom: 4px;204 animation: fadeIn 0.3s ease;205 }206 207 @keyframes fadeIn {208 from { opacity: 0; transform: translateY(4px); }209 to { opacity: 1; transform: translateY(0); }210 }211 212 .log-agent {213 font-weight: 600;214 margin-right: 6px;215 }216 .log-agent.router { color: var(--accent); }217 .log-agent.competitor { color: var(--orange); }218 .log-agent.founder { color: var(--blue); }219 .log-agent.business { color: var(--green); }220 .log-agent.tavily { color: #00d4aa; }221 .log-agent.system { color: var(--text-dim); }222 223 .log-event-thinking { color: var(--yellow); }224 .log-event-decision { color: var(--green); }225 .log-event-tool_call { color: var(--blue); }226 .log-event-tool_result { color: var(--text-dim); }227 .log-event-error { color: var(--red); }228 229 /* Final answer section */230 .result-section {231 border-top: 1px solid var(--border);232 padding: 24px 28px;233 max-height: 45%;234 overflow-y: auto;235 }236 .result-section.hidden { display: none; }237 238 .result-header {239 font-size: 12px;240 font-weight: 600;241 text-transform: uppercase;242 letter-spacing: 0.08em;243 color: var(--accent);244 margin-bottom: 12px;245 }246 .result-content {247 font-size: 14px;248 line-height: 1.8;249 white-space: pre-wrap;250 }251 252 /* ===== EMPTY STATE ===== */253 .empty-state {254 display: flex;255 flex-direction: column;256 align-items: center;257 justify-content: center;258 height: 100%;259 color: var(--text-dim);260 gap: 12px;261 }262 .empty-state .icon { font-size: 40px; opacity: 0.4; }263 .empty-state p { font-size: 14px; }264 265 /* ===== SCROLLBAR ===== */266 ::-webkit-scrollbar { width: 6px; }267 ::-webkit-scrollbar-track { background: transparent; }268 ::-webkit-scrollbar-thumb {269 background: var(--border);270 border-radius: 3px;271 }272 273 /* ===== LOADING SPINNER ===== */274 .spinner {275 display: inline-block;276 width: 14px;277 height: 14px;278 border: 2px solid rgba(255,255,255,0.3);279 border-top-color: white;280 border-radius: 50%;281 animation: spin 0.6s linear infinite;282 }283 @keyframes spin { to { transform: rotate(360deg); } }284 </style>285</head>286<body>287 <div class="header">288 <h1>๐ Company Intel Agent</h1>289 <span class="badge">Claude + Tavily</span>290 </div>291 292 <div class="main">293 <!-- LEFT: Input Panel -->294 <div class="input-panel">295 <div class="section-label">Company Information</div>296 297 <div class="input-group">298 <label>Company Name</label>299 <input type="text" id="companyName" placeholder="e.g. Stripe" />300 </div>301 302 <div class="input-group">303 <label>Website (optional)</label>304 <input type="text" id="website" placeholder="e.g. stripe.com" />305 </div>306 307 <div class="input-group">308 <label>Your Question</label>309 <textarea id="query" placeholder="e.g. Who are their main competitors?"></textarea>310 </div>311 312 <button class="btn-primary" id="analyzeBtn" onclick="analyze()">313 ๐ Analyze314 </button>315 316 <div class="section-label" style="margin-top: 12px;">Quick Actions</div>317 <div class="quick-actions">318 <button class="btn-quick" onclick="quickAction('Who are the main competitors?')">319 ๐ข Find Competitors320 </button>321 <button class="btn-quick" onclick="quickAction('Who founded this company and who is the current CEO?')">322 ๐ค Find Founders323 </button>324 <button class="btn-quick" onclick="quickAction('What does this company do? Describe their business model and main products.')">325 ๐ Business Overview326 </button>327 </div>328 329 <!-- 330 ๐ฏ VIBE CODING CHALLENGE: 331 Add more quick action buttons here!332 For example: "Find funding history", "Find recent news"333 334 Hint: just copy a btn-quick button and change the text.335 For a REAL challenge: add a new agent in backend/agents/ 336 to handle the new intent type!337 -->338 </div>339 340 <!-- RIGHT: Agent Output -->341 <div class="output-panel">342 <div class="log-header">343 <div class="dot" id="statusDot"></div>344 <span id="statusText">Waiting for input...</span>345 </div>346 347 <div class="log-container" id="logContainer">348 <div class="empty-state" id="emptyState">349 <div class="icon">๐ค</div>350 <p>Enter a company name and question to see agents in action</p>351 </div>352 </div>353 354 <div class="result-section hidden" id="resultSection">355 <div class="result-header">๐ Final Answer</div>356 <div class="result-content" id="resultContent"></div>357 </div>358 </div>359 </div>360 361 <script>362 // ===== State =====363 let isRunning = false;364 365 // ===== Quick Actions =====366 function quickAction(queryText) {367 document.getElementById('query').value = queryText;368 }369 370 // ===== Main Analysis Function =====371 async function analyze() {372 if (isRunning) return;373 374 const companyName = document.getElementById('companyName').value.trim();375 const website = document.getElementById('website').value.trim();376 const query = document.getElementById('query').value.trim();377 378 if (!companyName || !query) {379 alert('Please enter a company name and your question.');380 return;381 }382 383 // Reset UI384 isRunning = true;385 const btn = document.getElementById('analyzeBtn');386 btn.disabled = true;387 btn.innerHTML = '<span class="spinner"></span> Analyzing...';388 389 const emptyState = document.getElementById('emptyState');390 if (emptyState) emptyState.style.display = 'none';391 document.getElementById('logContainer').innerHTML = '';392 document.getElementById('resultSection').classList.add('hidden');393 document.getElementById('resultContent').textContent = '';394 setStatus(true, 'Agents working...');395 396 try {397 const response = await fetch('/analyze', {398 method: 'POST',399 headers: { 'Content-Type': 'application/json' },400 body: JSON.stringify({401 company_name: companyName,402 website: website || null,403 query: query,404 }),405 });406 407 if (!response.ok) {408 throw new Error(`Server error: ${response.status}`);409 }410 411 const reader = response.body.getReader();412 const decoder = new TextDecoder();413 let buffer = '';414 415 while (true) {416 const { done, value } = await reader.read();417 if (done) break;418 419 const chunk = decoder.decode(value, { stream: true });420 buffer += chunk;421 422 // Process all complete SSE events in buffer423 // SSE events end with double newline (handle \r\n and \n)424 let eventEnd;425 while ((eventEnd = buffer.search(/\n\n|\r\n\r\n/)) !== -1) {426 const eventBlock = buffer.slice(0, eventEnd);427 buffer = buffer.slice(eventEnd + (buffer.slice(eventEnd, eventEnd + 4).includes('\r') ? 4 : 2));428 429 // Extract data line from SSE event block430 const lines = eventBlock.split(/\r?\n/);431 const dataLine = lines.find(line => line.startsWith('data:'));432 if (dataLine) {433 const dataStr = dataLine.slice(5).trim();434 try {435 const data = JSON.parse(dataStr);436 handleEvent(data);437 } catch (e) {438 // Skip malformed JSON439 }440 }441 }442 }443 } catch (error) {444 addLog('System', 'error', `โ Connection error: ${error.message}`);445 }446 447 // Done448 isRunning = false;449 btn.disabled = false;450 btn.innerHTML = '๐ Analyze';451 setStatus(false, 'Done');452 }453 454 // ===== Handle SSE Events =====455 function handleEvent(data) {456 const { agent, event, content } = data;457 458 if (event === 'final_answer') {459 // Show in the result section460 document.getElementById('resultSection').classList.remove('hidden');461 document.getElementById('resultContent').textContent = content;462 addLog(agent, 'decision', 'โ
Analysis complete - see results below');463 } else {464 addLog(agent, event, content);465 }466 }467 468 // ===== Add Log Entry =====469 function addLog(agent, event, content) {470 const container = document.getElementById('logContainer');471 const entry = document.createElement('div');472 entry.className = 'log-entry';473 474 const agentClass = agent.toLowerCase().includes('router') ? 'router'475 : agent.toLowerCase().includes('competitor') ? 'competitor'476 : agent.toLowerCase().includes('founder') ? 'founder'477 : agent.toLowerCase().includes('business') ? 'business'478 : agent.toLowerCase().includes('tavily') ? 'tavily'479 : 'system';480 481 entry.innerHTML = `<span class="log-agent ${agentClass}">[${agent}]</span><span class="log-event-${event}">${escapeHtml(content)}</span>`;482 container.appendChild(entry);483 484 // Auto-scroll to bottom485 container.scrollTop = container.scrollHeight;486 }487 488 // ===== Helpers =====489 function setStatus(active, text) {490 document.getElementById('statusDot').className = active ? 'dot active' : 'dot';491 document.getElementById('statusText').textContent = text;492 }493 494 function escapeHtml(text) {495 const div = document.createElement('div');496 div.textContent = text;497 return div.innerHTML;498 }499 500 // Allow Enter key in query field501 document.getElementById('query').addEventListener('keydown', (e) => {502 if (e.key === 'Enter' && !e.shiftKey) {503 e.preventDefault();504 analyze();505 }506 });507 </script>508</body>509</html>510 