banao-tech commited on
Commit
0853350
·
verified ·
1 Parent(s): af4673d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +306 -580
index.html CHANGED
@@ -1,622 +1,348 @@
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>Problem Solver — AI Intern Tool</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,wght@0,300;0,500;0,700;1,300&family=Geist+Mono:wght@400;500&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap" rel="stylesheet" />
9
  <style>
10
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
11
-
12
- :root {
13
- --bg: #f7f6f2;
14
- --white: #ffffff;
15
- --surface: #f0eeea;
16
- --border: #e2dfd8;
17
- --border2: #d4d0c8;
18
- --ink: #1a1916;
19
- --ink2: #3d3b35;
20
- --muted: #7a776e;
21
- --muted2: #a09d95;
22
- --accent: #d4621a;
23
- --accent2: #b8501a;
24
- --accent-bg: #fdf1ea;
25
- --green: #2d7a4f;
26
- --green-bg: #eaf4ee;
27
- --font-head: 'Fraunces', Georgia, serif;
28
- --font-body: 'Plus Jakarta Sans', sans-serif;
29
- --font-mono: 'Geist Mono', monospace;
30
- }
31
-
32
- html { scroll-behavior: smooth; }
33
- body {
34
- font-family: var(--font-body);
35
- background: var(--bg);
36
- color: var(--ink);
37
- min-height: 100vh;
38
- font-size: 14px;
39
- }
40
-
41
- /* ── App Shell ───────────────────────────────────────────── */
42
- .app {
43
- display: grid;
44
- grid-template-columns: 400px 1fr;
45
- grid-template-rows: 56px 1fr;
46
- height: 100vh;
47
- overflow: hidden;
48
- }
49
-
50
- /* ── Top Bar ─────────────────────────────────────────────── */
51
- .topbar {
52
- grid-column: 1 / -1;
53
- display: flex;
54
- align-items: center;
55
- justify-content: space-between;
56
- padding: 0 28px;
57
- border-bottom: 1px solid var(--border);
58
- background: var(--white);
59
- }
60
- .topbar-logo {
61
- font-family: var(--font-head);
62
- font-size: 17px;
63
- font-weight: 700;
64
- color: var(--ink);
65
- letter-spacing: -.02em;
66
- display: flex;
67
- align-items: center;
68
- gap: 8px;
69
- }
70
- .topbar-logo .dot { width: 8px; height: 8px; background: var(--accent); border-radius: 50%; }
71
- .topbar-meta { font-size: 12px; color: var(--muted); font-family: var(--font-mono); letter-spacing: .02em; }
72
-
73
- /* ── Left Panel ──────────────────────────────────────────── */
74
- .panel-left {
75
- border-right: 1px solid var(--border);
76
- background: var(--white);
77
- display: flex;
78
- flex-direction: column;
79
- overflow: hidden;
80
- }
81
- .panel-inner { flex: 1; overflow-y: auto; padding: 28px 24px; }
82
- .panel-inner::-webkit-scrollbar { width: 3px; }
83
- .panel-inner::-webkit-scrollbar-thumb { background: var(--border2); }
84
-
85
- .section-label {
86
- font-family: var(--font-mono);
87
- font-size: 10px;
88
- font-weight: 500;
89
- letter-spacing: .12em;
90
- text-transform: uppercase;
91
- color: var(--muted2);
92
- margin-bottom: 12px;
93
- }
94
-
95
- /* ── Fields ──────────────────────────────────────────────── */
96
- .fields-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 24px; }
97
- .field label { display: block; font-size: 11px; font-weight: 600; color: var(--muted); margin-bottom: 5px; letter-spacing: .03em; }
98
- input[type="text"] {
99
- width: 100%;
100
- background: var(--surface);
101
- border: 1px solid var(--border);
102
- border-radius: 8px;
103
- color: var(--ink);
104
- font-family: var(--font-body);
105
- font-size: 13px;
106
- padding: 9px 12px;
107
- transition: border-color .15s, box-shadow .15s;
108
- outline: none;
109
- }
110
- input[type="text"]:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(212,98,26,.1); background: var(--white); }
111
- input::placeholder { color: var(--muted2); }
112
-
113
- /* ── Tabs ────────────────────────────────────────────────── */
114
- .tabs {
115
- display: flex; gap: 2px;
116
- background: var(--surface);
117
- border-radius: 10px; padding: 3px;
118
- margin-bottom: 14px;
119
- }
120
- .tab {
121
- flex: 1; padding: 8px; text-align: center;
122
- font-size: 12px; font-weight: 600; color: var(--muted);
123
- border-radius: 8px; cursor: pointer;
124
- transition: background .15s, color .15s; user-select: none;
125
- }
126
- .tab.active { background: var(--white); color: var(--ink); box-shadow: 0 1px 4px rgba(0,0,0,.08); }
127
-
128
- #pastePane, #uploadPane { display: none; }
129
- #pastePane.shown, #uploadPane.shown { display: block; }
130
-
131
- textarea {
132
- width: 100%;
133
- background: var(--surface); border: 1px solid var(--border);
134
- border-radius: 10px; color: var(--ink);
135
- font-family: var(--font-body); font-size: 13px; line-height: 1.7;
136
- padding: 14px; min-height: 210px; resize: none; outline: none;
137
- transition: border-color .15s, box-shadow .15s;
138
- }
139
- textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(212,98,26,.1); background: var(--white); }
140
- textarea::placeholder { color: var(--muted2); }
141
-
142
- /* ── Upload ──────────────────────────────────────────────── */
143
- .upload-zone {
144
- border: 1.5px dashed var(--border2); border-radius: 10px;
145
- padding: 36px 20px; text-align: center; cursor: pointer;
146
- transition: border-color .2s, background .2s; background: var(--surface);
147
- }
148
- .upload-zone:hover, .upload-zone.drag { border-color: var(--accent); background: var(--accent-bg); }
149
- .upload-zone .icon { font-size: 26px; margin-bottom: 10px; }
150
- .upload-zone p { font-size: 13px; color: var(--muted); line-height: 1.6; }
151
- .upload-zone p strong { color: var(--ink2); }
152
- .upload-zone .formats { font-family: var(--font-mono); font-size: 10px; color: var(--muted2); margin-top: 8px; letter-spacing: .06em; }
153
- .file-chosen {
154
- display: none; align-items: center; gap: 10px;
155
- padding: 10px 14px; background: var(--green-bg);
156
- border: 1px solid #b6dfca; border-radius: 8px; margin-top: 10px;
157
- font-size: 12px; color: var(--green); font-weight: 500;
158
- }
159
- .file-chosen.show { display: flex; }
160
- .file-chosen .fname { flex: 1; font-family: var(--font-mono); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
161
- .file-clear { cursor: pointer; font-size: 15px; color: var(--muted); flex-shrink: 0; }
162
- input[type="file"] { display: none; }
163
-
164
- /* ── Buttons ─────────────────────────────────────────────── */
165
- .panel-footer { padding: 16px 24px; border-top: 1px solid var(--border); background: var(--white); }
166
- .btn-run {
167
- width: 100%; background: var(--accent); color: #fff; border: none;
168
- border-radius: 10px; padding: 13px; font-family: var(--font-body);
169
- font-size: 14px; font-weight: 600; cursor: pointer;
170
- display: flex; align-items: center; justify-content: center; gap: 8px;
171
- transition: background .15s, box-shadow .15s, transform .1s;
172
- }
173
- .btn-run:hover:not(:disabled) { background: var(--accent2); box-shadow: 0 4px 16px rgba(212,98,26,.28); }
174
- .btn-run:active:not(:disabled) { transform: scale(.98); }
175
- .btn-run:disabled { opacity: .45; cursor: not-allowed; }
176
-
177
- .btn-pdf {
178
- width: 100%; margin-top: 8px; background: transparent;
179
- color: var(--green); border: 1.5px solid #b6dfca;
180
- border-radius: 10px; padding: 10px; font-family: var(--font-body);
181
- font-size: 13px; font-weight: 600; cursor: pointer;
182
- display: flex; align-items: center; justify-content: center; gap: 7px;
183
- transition: background .15s;
184
- }
185
- .btn-pdf:hover:not(:disabled) { background: var(--green-bg); }
186
- .btn-pdf:disabled { opacity: .35; cursor: not-allowed; }
187
-
188
- /* ── Right Panel ─────────────────────────────────────────── */
189
- .panel-right { display: flex; flex-direction: column; overflow: hidden; background: var(--bg); }
190
-
191
- .status-strip {
192
- display: flex; align-items: center; gap: 10px;
193
- padding: 0 28px; height: 42px;
194
- border-bottom: 1px solid var(--border); background: var(--white); flex-shrink: 0;
195
- }
196
- .status-pill { display: flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: 11px; color: var(--muted); }
197
- .status-pill .s-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--border2); }
198
- .status-pill.running .s-dot { background: var(--accent); animation: pulse .8s infinite; }
199
- .status-pill.done .s-dot { background: var(--green); }
200
- .status-pill.error .s-dot { background: #c53030; }
201
- @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.3} }
202
-
203
- .pipe-track { margin-left: auto; display: flex; align-items: center; gap: 4px; }
204
- .p-node {
205
- font-family: var(--font-mono); font-size: 10px; color: var(--muted2);
206
- padding: 3px 8px; border-radius: 4px;
207
- background: var(--surface); border: 1px solid var(--border);
208
- transition: all .2s;
209
- }
210
- .p-node.active { color: var(--accent); border-color: var(--accent); background: var(--accent-bg); }
211
- .p-node.done { color: var(--green); border-color: #b6dfca; background: var(--green-bg); }
212
- .p-sep { color: var(--border2); font-size: 10px; }
213
-
214
- .output-scroll { flex: 1; overflow-y: auto; padding: 28px 32px; }
215
- .output-scroll::-webkit-scrollbar { width: 4px; }
216
- .output-scroll::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 4px; }
217
-
218
- /* ── Empty state ─────────────────────────────────────────── */
219
- .empty {
220
- display: flex; flex-direction: column; align-items: center;
221
- justify-content: center; height: 100%; text-align: center;
222
- color: var(--muted2); gap: 12px; padding-bottom: 60px;
223
- }
224
- .empty-icon {
225
- width: 52px; height: 52px; border-radius: 14px;
226
- background: var(--white); border: 1px solid var(--border);
227
- display: flex; align-items: center; justify-content: center; font-size: 22px;
228
- }
229
- .empty h3 { font-family: var(--font-head); font-size: 20px; font-weight: 500; color: var(--ink2); font-style: italic; }
230
- .empty p { font-size: 13px; max-width: 280px; line-height: 1.6; }
231
-
232
- /* ── Agent blocks ────────────────────────────────────────── */
233
- .agent-block {
234
- background: var(--white); border: 1px solid var(--border);
235
- border-radius: 12px; margin-bottom: 16px; overflow: hidden;
236
- opacity: 0; transform: translateY(10px);
237
- transition: opacity .35s, transform .35s;
238
- }
239
- .agent-block.in { opacity: 1; transform: translateY(0); }
240
-
241
- .agent-head {
242
- display: flex; align-items: center; gap: 10px;
243
- padding: 12px 18px; border-bottom: 1px solid var(--border);
244
- }
245
- .agent-num {
246
- width: 22px; height: 22px; border-radius: 6px;
247
- display: flex; align-items: center; justify-content: center;
248
- font-family: var(--font-mono); font-size: 11px; font-weight: 500; flex-shrink: 0;
249
- }
250
- .agent-name { font-size: 12px; font-weight: 600; letter-spacing: .02em; flex: 1; }
251
- .agent-spinner {
252
- width: 14px; height: 14px; border: 1.5px solid currentColor;
253
- border-top-color: transparent; border-radius: 50%;
254
- animation: spin .7s linear infinite; opacity: .6;
255
- }
256
- @keyframes spin { to { transform: rotate(360deg); } }
257
- .agent-check { font-size: 14px; }
258
-
259
- .ag-1 .agent-head { background: #fdf8f5; }
260
- .ag-1 .agent-num { background: #fde8d8; color: #c45518; }
261
- .ag-1 .agent-name { color: #c45518; }
262
- .ag-1 .agent-spinner, .ag-1 .agent-check { color: #c45518; }
263
-
264
- .ag-2 .agent-head { background: #f5fbf7; }
265
- .ag-2 .agent-num { background: #d6f0e2; color: #2d7a4f; }
266
- .ag-2 .agent-name { color: #2d7a4f; }
267
- .ag-2 .agent-spinner, .ag-2 .agent-check { color: #2d7a4f; }
268
-
269
- .ag-3 .agent-head { background: #f6f5fd; }
270
- .ag-3 .agent-num { background: #e0ddfb; color: #5b4fcf; }
271
- .ag-3 .agent-name { color: #5b4fcf; }
272
- .ag-3 .agent-spinner, .ag-3 .agent-check { color: #5b4fcf; }
273
-
274
- .ag-4 .agent-head { background: #fdf9f2; }
275
- .ag-4 .agent-num { background: #fdedc8; color: #b07c10; }
276
- .ag-4 .agent-name { color: #b07c10; }
277
- .ag-4 .agent-spinner, .ag-4 .agent-check { color: #b07c10; }
278
-
279
- .ag-5 .agent-head { background: #fdf5f5; }
280
- .ag-5 .agent-num { background: #fdd8d8; color: #c53030; }
281
- .ag-5 .agent-name { color: #c53030; }
282
- .ag-5 .agent-spinner, .ag-5 .agent-check { color: #c53030; }
283
-
284
- .agent-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.78; color: var(--ink2); }
285
- .agent-body h2 {
286
- font-family: var(--font-head); font-size: 16px; font-weight: 500;
287
- font-style: italic; color: var(--ink); margin: 14px 0 6px;
288
- padding-bottom: 6px; border-bottom: 1px solid var(--border);
289
- }
290
- .agent-body h2:first-child { margin-top: 0; }
291
- .agent-body h3 { font-size: 13px; font-weight: 600; color: var(--ink); margin: 10px 0 4px; }
292
- .agent-body strong { font-weight: 600; color: var(--ink); }
293
- .agent-body ul, .agent-body ol { padding-left: 18px; margin: 4px 0 8px; }
294
- .agent-body li { margin-bottom: 3px; }
295
-
296
- /* ── Toast ───────────────────────────────────────────────── */
297
- .toast {
298
- position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
299
- background: var(--ink); color: #fff; padding: 10px 20px;
300
- border-radius: 8px; font-size: 13px; z-index: 100;
301
- display: none; white-space: nowrap; box-shadow: 0 4px 16px rgba(0,0,0,.18);
302
- }
303
- .toast.err { background: #c53030; }
304
-
305
- @media (max-width: 800px) {
306
- .app { grid-template-columns: 1fr; grid-template-rows: 56px auto 1fr; height: auto; }
307
- .panel-left { border-right: none; border-bottom: 1px solid var(--border); }
308
- .panel-right { min-height: 60vh; }
309
- .pipe-track { display: none; }
310
- .output-scroll { padding: 20px; }
311
  }
312
  </style>
313
  </head>
314
  <body>
315
  <div class="app">
316
 
317
- <header class="topbar">
318
- <div class="topbar-logo"><span class="dot"></span>Problem Solver</div>
319
- <div class="topbar-meta">5-agent pipeline</div>
320
- </header>
 
321
 
322
- <!-- LEFT -->
323
- <aside class="panel-left">
324
- <div class="panel-inner">
325
 
326
- <div class="section-label">Your details</div>
327
- <div class="fields-row">
328
  <div class="field">
329
  <label>Name</label>
330
- <input type="text" id="internName" placeholder="Your name" />
331
  </div>
332
- <div class="field">
333
- <label>Role</label>
334
- <input type="text" id="internRole" value="AI Developer Intern" />
335
- </div>
336
- </div>
337
 
338
- <div class="section-label">Problem input</div>
339
- <div class="tabs">
340
- <div class="tab active" id="tabPaste" onclick="switchTab('paste')">Paste Text</div>
341
- <div class="tab" id="tabUpload" onclick="switchTab('upload')">Upload File</div>
342
- </div>
343
-
344
- <div id="pastePane" class="shown">
345
- <textarea id="pasteText" placeholder="Paste your meeting transcript, problem description, or blocker here…" rows="10"></textarea>
346
- </div>
347
-
348
- <div id="uploadPane">
349
- <div class="upload-zone" id="dropZone"
350
- onclick="document.getElementById('fileInput').click()"
351
- ondragover="onDrag(event,true)"
352
- ondragleave="onDrag(event,false)"
353
- ondrop="onDrop(event)">
354
- <div class="icon">📄</div>
355
- <p><strong>Click to upload</strong> or drag & drop</p>
356
- <div class="formats">PDF · TXT · MD</div>
357
  </div>
358
- <div class="file-chosen" id="fileChosen">
359
- <span>📎</span>
360
- <span class="fname" id="fileName">—</span>
361
- <span class="file-clear" onclick="clearFile()">✕</span>
362
- </div>
363
- <input type="file" id="fileInput" accept=".pdf,.txt,.md" onchange="onFileSelect(event)" />
364
- </div>
365
 
366
- </div>
 
 
367
 
368
- <div class="panel-footer">
369
- <button class="btn-run" id="btnRun" onclick="runAnalysis()">
370
- <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M5 3l14 9-14 9V3z"/></svg>
371
- Analyze Problem
372
- </button>
373
- <button class="btn-pdf" id="btnPdf" disabled onclick="downloadPDF()">
374
- <svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M12 15V3m0 12l-4-4m4 4l4-4M3 17v2a2 2 0 002 2h14a2 2 0 002-2v-2"/></svg>
375
- Download PDF Report
376
- </button>
377
- </div>
378
- </aside>
 
 
 
 
379
 
380
- <!-- RIGHT -->
381
- <main class="panel-right">
382
- <div class="status-strip">
383
- <div class="status-pill" id="statusPill">
384
- <span class="s-dot"></span>
385
- <span id="statusText">Ready</span>
386
  </div>
387
- <div class="pipe-track">
388
- <div class="p-node" id="pn-analyst">Analyst</div>
389
- <div class="p-sep"></div>
390
- <div class="p-node" id="pn-root_cause">Root Cause</div>
391
- <div class="p-sep">›</div>
392
- <div class="p-node" id="pn-solutions">Solutions</div>
393
- <div class="p-sep"></div>
394
- <div class="p-node" id="pn-action_plan">Action Plan</div>
395
- <div class="p-sep">›</div>
396
- <div class="p-node" id="pn-thinking">Coach</div>
397
  </div>
398
- </div>
399
-
400
- <div class="output-scroll" id="outputScroll">
401
- <div class="empty" id="emptyState">
402
- <div class="empty-icon"></div>
403
- <h3>Start with a problem.</h3>
404
- <p>Paste a transcript, meeting notes, or blocker — five agents will analyze it and hand you a plan.</p>
 
 
 
 
 
 
405
  </div>
406
- </div>
407
- </main>
 
 
 
 
 
 
408
  </div>
409
-
410
  <div class="toast" id="toast"></div>
411
 
412
  <script>
413
- // ── Config — update this to your HF Space URL ─────────────
414
- const API_URL = 'https://banao-tech-problem-decoder.hf.space';
415
-
416
- const AGENTS = [
417
- { key: 'analyst', label: 'Problem Analyst', cls: 'ag-1', num: '1' },
418
- { key: 'root_cause', label: 'Root Cause Analyst', cls: 'ag-2', num: '2' },
419
- { key: 'solutions', label: 'Solution Brainstorm', cls: 'ag-3', num: '3' },
420
- { key: 'action_plan', label: 'Action Planner', cls: 'ag-4', num: '4' },
421
- { key: 'thinking', label: 'Thinking Coach', cls: 'ag-5', num: '5' },
422
  ];
423
-
424
- let running = false, selectedFile = null, lastPayload = null, activeTab = 'paste';
425
-
426
- function switchTab(tab) {
427
- activeTab = tab;
428
- document.getElementById('tabPaste').classList.toggle('active', tab === 'paste');
429
- document.getElementById('tabUpload').classList.toggle('active', tab === 'upload');
430
- document.getElementById('pastePane').classList.toggle('shown', tab === 'paste');
431
- document.getElementById('uploadPane').classList.toggle('shown', tab === 'upload');
432
- }
433
-
434
- function onDrag(e, over) {
435
- e.preventDefault();
436
- document.getElementById('dropZone').classList.toggle('drag', over);
437
- }
438
- function onDrop(e) {
439
- e.preventDefault();
440
- document.getElementById('dropZone').classList.remove('drag');
441
- const f = e.dataTransfer.files[0];
442
- if (f) setFile(f);
443
- }
444
- function onFileSelect(e) { const f = e.target.files[0]; if (f) setFile(f); }
445
- function setFile(f) {
446
- selectedFile = f;
447
- document.getElementById('fileName').textContent = f.name;
448
- document.getElementById('fileChosen').classList.add('show');
449
- }
450
- function clearFile() {
451
- selectedFile = null;
452
- document.getElementById('fileName').textContent = '—';
453
- document.getElementById('fileChosen').classList.remove('show');
454
- document.getElementById('fileInput').value = '';
455
- }
456
-
457
- async function readFile(file) {
458
- if (file.name.endsWith('.pdf')) {
459
- return new Promise((res, rej) => {
460
- const r = new FileReader();
461
- r.onload = () => res('__PDF_BASE64__' + r.result.split(',')[1]);
462
- r.onerror = rej;
463
- r.readAsDataURL(file);
464
- });
465
- }
466
- return new Promise((res, rej) => {
467
- const r = new FileReader();
468
- r.onload = () => res(r.result);
469
- r.onerror = rej;
470
- r.readAsText(file);
471
  });
472
  }
473
-
474
- function toast(msg, isErr = false) {
475
- const t = document.getElementById('toast');
476
- t.textContent = msg;
477
- t.className = 'toast' + (isErr ? ' err' : '');
478
- t.style.display = 'block';
479
- setTimeout(() => t.style.display = 'none', 4000);
480
- }
481
- function setStatus(text, state = '') {
482
- document.getElementById('statusText').textContent = text;
483
- document.getElementById('statusPill').className = 'status-pill ' + state;
484
- }
485
- function setPipe(key, state) {
486
- const el = document.getElementById('pn-' + key);
487
- if (el) el.className = 'p-node ' + state;
488
- }
489
- function resetPipe() { AGENTS.forEach(a => setPipe(a.key, '')); }
490
-
491
- function renderMd(text) {
492
- return text
493
- .replace(/^## (.+)$/gm, '<h2>$1</h2>')
494
- .replace(/^### (.+)$/gm, '<h3>$1</h3>')
495
- .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
496
- .replace(/^[-*] (.+)$/gm, '<li>$1</li>')
497
- .replace(/(<li>.*<\/li>\n?)+/g, s => `<ul>${s}</ul>`)
498
- .replace(/\n/g, '<br>');
499
  }
500
 
501
- async function runAnalysis() {
502
- if (running) return;
503
-
504
- const name = document.getElementById('internName').value.trim() || 'Intern';
505
- const role = document.getElementById('internRole').value.trim() || 'AI Developer Intern';
506
- let content = '';
507
-
508
- if (activeTab === 'paste') {
509
- content = document.getElementById('pasteText').value.trim();
510
- if (!content) { toast('Paste a problem or transcript first.', true); return; }
511
  } else {
512
- if (!selectedFile) { toast('Select a file first.', true); return; }
513
- try { content = await readFile(selectedFile); }
514
- catch { toast('Could not read file.', true); return; }
515
  }
516
-
517
- running = true;
518
- lastPayload = { content, intern_name: name, intern_role: role };
519
- document.getElementById('btnRun').disabled = true;
520
- document.getElementById('btnPdf').disabled = true;
521
- resetPipe();
522
-
523
- const scroll = document.getElementById('outputScroll');
524
- scroll.innerHTML = '';
525
- setStatus('Starting pipeline…', 'running');
526
-
527
- AGENTS.forEach(a => {
528
- const div = document.createElement('div');
529
- div.className = `agent-block ${a.cls}`;
530
- div.id = `ab-${a.key}`;
531
- div.innerHTML = `
532
- <div class="agent-head">
533
- <div class="agent-num">${a.num}</div>
534
- <div class="agent-name">${a.label}</div>
535
- <div class="agent-spinner" id="spin-${a.key}"></div>
536
- </div>
537
- <div class="agent-body" id="body-${a.key}"></div>`;
538
- scroll.appendChild(div);
539
  });
540
 
541
- try {
542
- const res = await fetch(`${API_URL}/analyze/stream`, {
543
- method: 'POST',
544
- headers: { 'Content-Type': 'application/json' },
545
- body: JSON.stringify(lastPayload),
546
- });
547
- if (!res.ok) {
548
- const e = await res.json().catch(() => ({ detail: res.statusText }));
549
- throw new Error(e.detail || 'Server error');
550
- }
551
-
552
- const reader = res.body.getReader();
553
- const dec = new TextDecoder();
554
- let buf = '', current = null;
555
-
556
- while (true) {
557
- const { done, value } = await reader.read();
558
- if (done) break;
559
- buf += dec.decode(value, { stream: true });
560
- const lines = buf.split('\n');
561
- buf = lines.pop();
562
-
563
- for (const line of lines) {
564
- if (!line.startsWith('data: ')) continue;
565
- try {
566
- const msg = JSON.parse(line.slice(6));
567
- if (msg.event === 'agent_start') {
568
- current = msg.agent;
569
- setPipe(current, 'active');
570
- const block = document.getElementById(`ab-${current}`);
571
- block.classList.add('in');
572
- setStatus(`Running: ${msg.label}…`, 'running');
573
- block.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
574
- } else if (msg.event === 'token' && msg.agent === current) {
575
- const body = document.getElementById(`body-${msg.agent}`);
576
- if (body) {
577
- body.dataset.raw = (body.dataset.raw || '') + msg.text;
578
- body.innerHTML = renderMd(body.dataset.raw);
579
- }
580
- } else if (msg.event === 'agent_done') {
581
- const spin = document.getElementById(`spin-${msg.agent}`);
582
- if (spin) spin.outerHTML = `<span class="agent-check">✓</span>`;
583
- setPipe(msg.agent, 'done');
584
- } else if (msg.event === 'done') {
585
- setStatus('Analysis complete.', 'done');
586
- document.getElementById('btnPdf').disabled = false;
587
  }
588
- } catch {}
589
  }
590
  }
591
- } catch (err) {
592
- setStatus('Error — ' + err.message, 'error');
593
- toast(err.message, true);
594
- }
595
-
596
- running = false;
597
- document.getElementById('btnRun').disabled = false;
598
- }
599
-
600
- async function downloadPDF() {
601
- if (!lastPayload) return;
602
- const btn = document.getElementById('btnPdf');
603
- btn.textContent = 'Generating…';
604
- btn.disabled = true;
605
- try {
606
- const res = await fetch(`${API_URL}/analyze/pdf`, {
607
- method: 'POST',
608
- headers: { 'Content-Type': 'application/json' },
609
- body: JSON.stringify(lastPayload),
610
- });
611
- if (!res.ok) throw new Error('PDF generation failed');
612
- const blob = await res.blob();
613
- const a = document.createElement('a');
614
- a.href = URL.createObjectURL(blob);
615
- a.download = `analysis_${Date.now()}.pdf`;
616
- a.click();
617
- } catch (e) { toast(e.message, true); }
618
- btn.innerHTML = `<svg width="13" height="13" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M12 15V3m0 12l-4-4m4 4l4-4M3 17v2a2 2 0 002 2h14a2 2 0 002-2v-2"/></svg> Download PDF Report`;
619
- btn.disabled = false;
620
  }
621
  </script>
622
  </body>
 
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>Problem Solver</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet"/>
 
8
  <style>
9
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
10
+ :root{
11
+ --w:#fff;
12
+ --bg:#f9fafb;
13
+ --b:#e5e7eb;
14
+ --bf:#6366f1;
15
+ --t:#111827;
16
+ --t2:#374151;
17
+ --mu:#6b7280;
18
+ --mu2:#9ca3af;
19
+ --ac:#6366f1;
20
+ --ach:#4f46e5;
21
+ --acb:#eef2ff;
22
+ --gr:#059669;
23
+ --grb:#ecfdf5;
24
+ --grbd:#a7f3d0;
25
+ --red:#dc2626;
26
+ --sf:#f3f4f6;
27
+ }
28
+ html,body{height:100%;font-family:'Inter',sans-serif;font-size:14px;color:var(--t);background:var(--bg);}
29
+
30
+ .app{display:grid;grid-template-rows:52px 1fr;height:100vh;overflow:hidden;}
31
+ .nav{display:flex;align-items:center;gap:8px;padding:0 24px;background:var(--w);border-bottom:1px solid var(--b);}
32
+ .nav-dot{width:7px;height:7px;background:var(--ac);border-radius:50%;}
33
+ .nav-brand{font-size:14px;font-weight:600;}
34
+ .nav-tag{margin-left:auto;font-size:11px;color:var(--mu2);letter-spacing:.04em;}
35
+
36
+ .body{display:grid;grid-template-columns:340px 1fr;overflow:hidden;}
37
+ .side{background:var(--w);border-right:1px solid var(--b);display:flex;flex-direction:column;overflow:hidden;}
38
+ .side-inner{flex:1;overflow-y:auto;padding:20px;}
39
+ .side-inner::-webkit-scrollbar{width:3px;}
40
+ .side-inner::-webkit-scrollbar-thumb{background:var(--b);}
41
+ .side-foot{padding:14px 20px;border-top:1px solid var(--b);}
42
+
43
+ .lbl{font-size:11px;font-weight:600;color:var(--mu2);letter-spacing:.07em;text-transform:uppercase;margin-bottom:8px;}
44
+ .field{margin-bottom:18px;}
45
+ .field label{display:block;font-size:12px;font-weight:500;color:var(--mu);margin-bottom:5px;}
46
+ input[type=text]{width:100%;background:var(--bg);border:1px solid var(--b);border-radius:7px;color:var(--t);font:inherit;font-size:13px;padding:8px 11px;outline:none;transition:border-color .15s,box-shadow .15s;}
47
+ input[type=text]:focus{border-color:var(--bf);box-shadow:0 0 0 3px rgba(99,102,241,.1);}
48
+ input::placeholder{color:var(--mu2);}
49
+
50
+ .tabs{display:flex;gap:1px;background:var(--b);border-radius:8px;padding:2px;margin-bottom:12px;}
51
+ .tab{flex:1;padding:6px;text-align:center;font-size:12px;font-weight:500;color:var(--mu);border-radius:6px;cursor:pointer;transition:all .15s;user-select:none;}
52
+ .tab.on{background:var(--w);color:var(--t);font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,.07);}
53
+
54
+ #pp,#up{display:none;}
55
+ #pp.on,#up.on{display:block;}
56
+ textarea{width:100%;background:var(--bg);border:1px solid var(--b);border-radius:7px;color:var(--t);font:inherit;font-size:13px;line-height:1.65;padding:11px;min-height:195px;resize:none;outline:none;transition:border-color .15s,box-shadow .15s;}
57
+ textarea:focus{border-color:var(--bf);box-shadow:0 0 0 3px rgba(99,102,241,.1);}
58
+ textarea::placeholder{color:var(--mu2);}
59
+
60
+ .drop{border:1.5px dashed var(--b);border-radius:8px;padding:30px 16px;text-align:center;cursor:pointer;background:var(--bg);transition:all .2s;}
61
+ .drop:hover,.drop.over{border-color:var(--ac);background:var(--acb);}
62
+ .drop-ico{font-size:22px;color:var(--mu2);margin-bottom:8px;}
63
+ .drop p{font-size:13px;color:var(--mu);line-height:1.5;}
64
+ .drop p b{color:var(--t2);}
65
+ .drop-fmt{font-size:11px;color:var(--mu2);margin-top:5px;}
66
+ .fbadge{display:none;align-items:center;gap:8px;padding:9px 11px;background:var(--grb);border:1px solid var(--grbd);border-radius:7px;margin-top:9px;font-size:12px;color:var(--gr);font-weight:500;}
67
+ .fbadge.on{display:flex;}
68
+ .fname{flex:1;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
69
+ .fclear{cursor:pointer;color:var(--mu);font-size:14px;}
70
+ input[type=file]{display:none;}
71
+
72
+ .btn-p{width:100%;background:var(--ac);color:#fff;border:none;border-radius:7px;padding:10px;font:inherit;font-size:13px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;transition:background .15s,box-shadow .15s,transform .1s;}
73
+ .btn-p:hover:not(:disabled){background:var(--ach);box-shadow:0 3px 10px rgba(99,102,241,.28);}
74
+ .btn-p:active:not(:disabled){transform:scale(.98);}
75
+ .btn-p:disabled{opacity:.4;cursor:not-allowed;}
76
+ .btn-g{width:100%;margin-top:6px;background:var(--w);color:var(--mu);border:1px solid var(--b);border-radius:7px;padding:9px;font:inherit;font-size:13px;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;transition:background .15s,color .15s;}
77
+ .btn-g:hover:not(:disabled){background:var(--grb);color:var(--gr);border-color:var(--grbd);}
78
+ .btn-g:disabled{opacity:.35;cursor:not-allowed;}
79
+
80
+ .main{display:flex;flex-direction:column;overflow:hidden;background:var(--w);}
81
+ .bar{display:flex;align-items:center;gap:8px;padding:0 24px;height:42px;border-bottom:1px solid var(--b);flex-shrink:0;}
82
+ .sdot{width:6px;height:6px;border-radius:50%;background:var(--b);}
83
+ .sdot.run{background:var(--ac);animation:bl .8s infinite;}
84
+ .sdot.done{background:var(--gr);}
85
+ .sdot.err{background:var(--red);}
86
+ @keyframes bl{0%,100%{opacity:1}50%{opacity:.3}}
87
+ .stxt{font-size:12px;color:var(--mu);}
88
+ .pipe{margin-left:auto;display:flex;align-items:center;gap:3px;}
89
+ .pn{font-size:11px;color:var(--mu2);padding:2px 7px;border-radius:4px;border:1px solid var(--b);background:var(--bg);transition:all .2s;}
90
+ .pn.run{color:var(--ac);border-color:var(--ac);background:var(--acb);}
91
+ .pn.done{color:var(--gr);border-color:var(--grbd);background:var(--grb);}
92
+ .ps{font-size:10px;color:var(--b);}
93
+
94
+ .out{flex:1;overflow-y:auto;padding:0;}
95
+ .out::-webkit-scrollbar{width:4px;}
96
+ .out::-webkit-scrollbar-thumb{background:var(--b);border-radius:4px;}
97
+
98
+ .empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:10px;text-align:center;color:var(--mu2);}
99
+ .empty h3{font-size:15px;font-weight:600;color:var(--t2);}
100
+ .empty p{font-size:13px;max-width:240px;line-height:1.6;}
101
+
102
+ /* Output sections — flat, no cards, just dividers */
103
+ .section{border-bottom:1px solid var(--b);padding:20px 28px;opacity:0;transform:translateY(6px);transition:opacity .3s,transform .3s;}
104
+ .section.in{opacity:1;transform:translateY(0);}
105
+ .section-label{display:flex;align-items:center;gap:8px;margin-bottom:14px;}
106
+ .section-num{width:18px;height:18px;border-radius:4px;background:var(--sf);color:var(--mu);font-size:10px;font-weight:600;display:flex;align-items:center;justify-content:center;}
107
+ .section-title{font-size:12px;font-weight:600;color:var(--mu);letter-spacing:.03em;}
108
+ .section-spin{width:12px;height:12px;border:1.5px solid var(--mu2);border-top-color:transparent;border-radius:50%;animation:spin .7s linear infinite;margin-left:auto;}
109
+ .section-tick{font-size:12px;color:var(--gr);margin-left:auto;}
110
+ @keyframes spin{to{transform:rotate(360deg)}}
111
+
112
+ /* Output text plain readable prose */
113
+ .prose{font-size:13.5px;line-height:1.8;color:var(--t2);}
114
+ .prose h2{font-size:13px;font-weight:600;color:var(--t);margin:16px 0 6px;letter-spacing:.01em;}
115
+ .prose h2:first-child{margin-top:0;}
116
+ .prose h3{font-size:13px;font-weight:500;color:var(--t);margin:12px 0 4px;}
117
+ .prose strong{font-weight:600;color:var(--t);}
118
+ .prose ul,.prose ol{padding-left:18px;margin:4px 0 8px;}
119
+ .prose li{margin-bottom:3px;}
120
+ .prose p{margin-bottom:6px;}
121
+
122
+ .toast{position:fixed;bottom:16px;left:50%;transform:translateX(-50%);background:var(--t);color:#fff;padding:8px 16px;border-radius:7px;font-size:13px;display:none;z-index:99;}
123
+ .toast.err{background:var(--red);}
124
+
125
+ @media(max-width:720px){
126
+ .body{grid-template-columns:1fr;}
127
+ .side{border-right:none;border-bottom:1px solid var(--b);}
128
+ .main{min-height:50vh;}
129
+ .pipe{display:none;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  }
131
  </style>
132
  </head>
133
  <body>
134
  <div class="app">
135
 
136
+ <nav class="nav">
137
+ <div class="nav-dot"></div>
138
+ <span class="nav-brand">Problem Solver</span>
139
+ <span class="nav-tag">5 agents</span>
140
+ </nav>
141
 
142
+ <div class="body">
143
+ <aside class="side">
144
+ <div class="side-inner">
145
 
146
+ <div class="lbl">Your details</div>
 
147
  <div class="field">
148
  <label>Name</label>
149
+ <input type="text" id="iName" placeholder="e.g. Priya Sharma"/>
150
  </div>
 
 
 
 
 
151
 
152
+ <div class="lbl">Input</div>
153
+ <div class="tabs">
154
+ <div class="tab on" id="tPaste" onclick="setTab('paste')">Paste text</div>
155
+ <div class="tab" id="tUpload" onclick="setTab('upload')">Upload file</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  </div>
 
 
 
 
 
 
 
157
 
158
+ <div id="pp" class="on">
159
+ <textarea id="tx" placeholder="Paste your transcript, meeting notes, or problem description…"></textarea>
160
+ </div>
161
 
162
+ <div id="up">
163
+ <div class="drop" id="dz"
164
+ onclick="document.getElementById('fi').click()"
165
+ ondragover="dg(event,true)" ondragleave="dg(event,false)" ondrop="dp(event)">
166
+ <div class="drop-ico">↑</div>
167
+ <p><b>Click to upload</b> or drag & drop</p>
168
+ <div class="drop-fmt">PDF · TXT · MD</div>
169
+ </div>
170
+ <div class="fbadge" id="fb">
171
+ <span>📄</span>
172
+ <span class="fname" id="fn">—</span>
173
+ <span class="fclear" onclick="cf()">✕</span>
174
+ </div>
175
+ <input type="file" id="fi" accept=".pdf,.txt,.md" onchange="pf(event)"/>
176
+ </div>
177
 
 
 
 
 
 
 
178
  </div>
179
+ <div class="side-foot">
180
+ <button class="btn-p" id="bRun" onclick="run()">
181
+ <svg width="12" height="12" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path d="M5 3l14 9-14 9V3z"/></svg>
182
+ Analyze
183
+ </button>
184
+ <button class="btn-g" id="bPdf" disabled onclick="getPdf()">
185
+ <svg width="12" height="12" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M12 15V3m0 12l-4-4m4 4l4-4M3 17v2a2 2 0 002 2h14a2 2 0 002-2v-2"/></svg>
186
+ Download PDF
187
+ </button>
 
188
  </div>
189
+ </aside>
190
+
191
+ <main class="main">
192
+ <div class="bar">
193
+ <div class="sdot" id="sd"></div>
194
+ <span class="stxt" id="st">Ready</span>
195
+ <div class="pipe">
196
+ <div class="pn" id="pn0">Analyst</div><div class="ps">›</div>
197
+ <div class="pn" id="pn1">Root Cause</div><div class="ps">›</div>
198
+ <div class="pn" id="pn2">Solutions</div><div class="ps">›</div>
199
+ <div class="pn" id="pn3">Action Plan</div><div class="ps">›</div>
200
+ <div class="pn" id="pn4">Coach</div>
201
+ </div>
202
  </div>
203
+ <div class="out" id="out">
204
+ <div class="empty" id="es">
205
+ <h3>Start with your problem</h3>
206
+ <p>Paste a transcript or upload a file — five agents will analyze it and give you a plan.</p>
207
+ </div>
208
+ </div>
209
+ </main>
210
+ </div>
211
  </div>
 
212
  <div class="toast" id="toast"></div>
213
 
214
  <script>
215
+ const API='https://banao-tech-problem-decoder.hf.space';
216
+ const AG=[
217
+ {key:'analyst', title:'Problem Analyst', n:'1'},
218
+ {key:'root_cause', title:'Root Cause Analyst', n:'2'},
219
+ {key:'solutions', title:'Solutions', n:'3'},
220
+ {key:'action_plan', title:'Action Plan', n:'4'},
221
+ {key:'thinking', title:'Thinking Coach', n:'5'},
 
 
222
  ];
223
+ let running=false,selFile=null,lastP=null,tab='paste';
224
+
225
+ function setTab(t){
226
+ tab=t;
227
+ ['Paste','Upload'].forEach(x=>{
228
+ const k=x.toLowerCase();
229
+ document.getElementById('t'+x).classList.toggle('on',k===t);
230
+ document.getElementById(k==='paste'?'pp':'up').classList.toggle('on',k===t);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  });
232
  }
233
+ function dg(e,on){e.preventDefault();document.getElementById('dz').classList.toggle('over',on);}
234
+ function dp(e){e.preventDefault();document.getElementById('dz').classList.remove('over');const f=e.dataTransfer.files[0];if(f)sf(f);}
235
+ function pf(e){const f=e.target.files[0];if(f)sf(f);}
236
+ function sf(f){selFile=f;document.getElementById('fn').textContent=f.name;document.getElementById('fb').classList.add('on');}
237
+ function cf(){selFile=null;document.getElementById('fn').textContent='—';document.getElementById('fb').classList.remove('on');document.getElementById('fi').value='';}
238
+
239
+ function toast(m,e=false){const t=document.getElementById('toast');t.textContent=m;t.className='toast'+(e?' err':'');t.style.display='block';setTimeout(()=>t.style.display='none',3500);}
240
+ function ss(txt,s=''){document.getElementById('st').textContent=txt;document.getElementById('sd').className='sdot '+s;}
241
+ function sp(i,s){const e=document.getElementById('pn'+i);if(e)e.className='pn '+s;}
242
+ function rp(){for(let i=0;i<5;i++)sp(i,'');}
243
+
244
+ function md(s){
245
+ return s
246
+ .replace(/^## (.+)$/gm,'<h2>$1</h2>')
247
+ .replace(/^### (.+)$/gm,'<h3>$1</h3>')
248
+ .replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>')
249
+ .replace(/^[-*] (.+)$/gm,'<li>$1</li>')
250
+ .replace(/(<li>[^]*?<\/li>\n?)+/g,x=>`<ul>${x}</ul>`)
251
+ .replace(/\n/g,'<br>');
252
+ }
253
+
254
+ async function rf(f){
255
+ if(f.name.endsWith('.pdf')){
256
+ return new Promise((res,rej)=>{const r=new FileReader();r.onload=()=>res('__PDF_BASE64__'+r.result.split(',')[1]);r.onerror=rej;r.readAsDataURL(f);});
257
+ }
258
+ return new Promise((res,rej)=>{const r=new FileReader();r.onload=()=>res(r.result);r.onerror=rej;r.readAsText(f);});
259
  }
260
 
261
+ async function run(){
262
+ if(running)return;
263
+ const name=document.getElementById('iName').value.trim()||'Intern';
264
+ let content='';
265
+ if(tab==='paste'){
266
+ content=document.getElementById('tx').value.trim();
267
+ if(!content){toast('Paste something first.',true);return;}
 
 
 
268
  } else {
269
+ if(!selFile){toast('Select a file first.',true);return;}
270
+ try{content=await rf(selFile);}catch{toast('Could not read file.',true);return;}
 
271
  }
272
+ running=true;
273
+ lastP={content,intern_name:name,intern_role:'AI Developer Intern'};
274
+ document.getElementById('bRun').disabled=true;
275
+ document.getElementById('bPdf').disabled=true;
276
+ rp();
277
+ const out=document.getElementById('out');
278
+ out.innerHTML='';
279
+ ss('Running…','run');
280
+
281
+ AG.forEach(a=>{
282
+ const d=document.createElement('div');
283
+ d.className='section';
284
+ d.id='s-'+a.key;
285
+ d.innerHTML=`<div class="section-label">
286
+ <div class="section-num">${a.n}</div>
287
+ <div class="section-title">${a.title}</div>
288
+ <div class="section-spin" id="sp-${a.key}"></div>
289
+ </div><div class="prose" id="pr-${a.key}"></div>`;
290
+ out.appendChild(d);
 
 
 
 
291
  });
292
 
293
+ try{
294
+ const res=await fetch(`${API}/analyze/stream`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(lastP)});
295
+ if(!res.ok){const e=await res.json().catch(()=>({detail:res.statusText}));throw new Error(e.detail);}
296
+ const reader=res.body.getReader(),dec=new TextDecoder();
297
+ let buf='',cur=null;
298
+ while(true){
299
+ const{done,value}=await reader.read();
300
+ if(done)break;
301
+ buf+=dec.decode(value,{stream:true});
302
+ const lines=buf.split('\n');buf=lines.pop();
303
+ for(const line of lines){
304
+ if(!line.startsWith('data: '))continue;
305
+ try{
306
+ const m=JSON.parse(line.slice(6));
307
+ if(m.event==='agent_start'){
308
+ cur=m.agent;
309
+ const i=AG.findIndex(a=>a.key===cur);
310
+ sp(i,'run');
311
+ document.getElementById('s-'+cur).classList.add('in');
312
+ ss(m.label+'','run');
313
+ document.getElementById('s-'+cur).scrollIntoView({behavior:'smooth',block:'nearest'});
314
+ } else if(m.event==='token'&&m.agent===cur){
315
+ const p=document.getElementById('pr-'+m.agent);
316
+ if(p){p.dataset.r=(p.dataset.r||'')+m.text;p.innerHTML=md(p.dataset.r);}
317
+ } else if(m.event==='agent_done'){
318
+ const sp2=document.getElementById('sp-'+m.agent);
319
+ if(sp2)sp2.outerHTML='<span class="section-tick">✓</span>';
320
+ const i=AG.findIndex(a=>a.key===m.agent);
321
+ sp(i,'done');
322
+ } else if(m.event==='done'){
323
+ ss('Done','done');
324
+ document.getElementById('bPdf').disabled=false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  }
326
+ }catch{}
327
  }
328
  }
329
+ }catch(e){ss('Error','err');toast(e.message,true);}
330
+ running=false;
331
+ document.getElementById('bRun').disabled=false;
332
+ }
333
+
334
+ async function getPdf(){
335
+ if(!lastP)return;
336
+ const b=document.getElementById('bPdf');
337
+ b.textContent='Generating…';b.disabled=true;
338
+ try{
339
+ const res=await fetch(`${API}/analyze/pdf`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(lastP)});
340
+ if(!res.ok)throw new Error('PDF failed');
341
+ const blob=await res.blob(),a=document.createElement('a');
342
+ a.href=URL.createObjectURL(blob);a.download=`analysis_${Date.now()}.pdf`;a.click();
343
+ }catch(e){toast(e.message,true);}
344
+ b.innerHTML='<svg width="12" height="12" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path d="M12 15V3m0 12l-4-4m4 4l4-4M3 17v2a2 2 0 002 2h14a2 2 0 002-2v-2"/></svg> Download PDF';
345
+ b.disabled=false;
 
 
 
 
 
 
 
 
 
 
 
 
346
  }
347
  </script>
348
  </body>