umairali64488 commited on
Commit
773ccfb
·
verified ·
1 Parent(s): ef2ff97

Update frontend/index.html

Browse files
Files changed (1) hide show
  1. frontend/index.html +628 -672
frontend/index.html CHANGED
@@ -3,769 +3,725 @@
3
  <head>
4
  <meta charset="UTF-8"/>
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
- <title>⚡ CodeDebug — Multi-LLM Debugger</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Syne:wght@400;600;700;800&display=swap" rel="stylesheet">
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css">
10
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
11
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
12
  <style>
13
- /* ══════════════════════════════════════════
14
- RESET & CSS VARIABLES
15
- ══════════════════════════════════════════ */
16
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
17
-
18
- :root {
19
- --bg: #07090e;
20
- --bg2: #0c1018;
21
- --bg3: #10151f;
22
- --bg4: #161d2a;
23
- --bg5: #1c2535;
24
- --border: #1e2d40;
25
- --border2: #253650;
26
- --text: #c8d6e5;
27
- --text-dim: #5a7590;
28
- --text-mute: #384f63;
29
-
30
- --cyan: #00d4ff;
31
- --cyan-glow: rgba(0, 212, 255, 0.15);
32
- --green: #00e87a;
33
- --green-glow: rgba(0, 232, 122, 0.12);
34
- --amber: #ffb020;
35
- --amber-glow: rgba(255, 176, 32, 0.12);
36
- --purple: #a855f7;
37
- --purple-glow:rgba(168, 85, 247, 0.15);
38
- --red: #ff4455;
39
-
40
- --m1: #00d4ff;
41
- --m2: #00e87a;
42
- --m3: #ffb020;
43
-
44
- --mono: 'JetBrains Mono', monospace;
45
- --sans: 'Syne', sans-serif;
46
- --radius: 8px;
47
- }
48
-
49
- html, body { height: 100%; }
50
-
51
- body {
52
- background: var(--bg);
53
- color: var(--text);
54
- font-family: var(--mono);
55
- font-size: 13px;
56
- display: flex;
57
- flex-direction: column;
58
- overflow: hidden;
59
- height: 100vh;
60
- }
61
-
62
- body::after {
63
- content: '';
64
- position: fixed; inset: 0; pointer-events: none; z-index: 9999;
65
- background: repeating-linear-gradient(
66
- 0deg, transparent, transparent 2px,
67
- rgba(0,0,0,0.025) 2px, rgba(0,0,0,0.025) 4px
68
- );
69
- }
70
-
71
- /* ══════════════════════════════════════════
72
- TOP BAR
73
- ══════════════════════════════════════════ */
74
- .topbar {
75
- flex-shrink: 0;
76
- height: 52px;
77
- background: var(--bg2);
78
- border-bottom: 1px solid var(--border);
79
- display: flex;
80
- align-items: center;
81
- padding: 0 20px;
82
- gap: 16px;
83
- }
84
-
85
- .logo { display: flex; align-items: center; gap: 10px; }
86
- .logo-mark {
87
- width: 30px; height: 30px;
88
- background: linear-gradient(135deg, var(--cyan) 0%, var(--purple) 100%);
89
- border-radius: 7px;
90
- display: flex; align-items: center; justify-content: center;
91
- font-size: 15px;
92
- box-shadow: 0 0 16px rgba(0,212,255,0.35);
93
- flex-shrink: 0;
94
- }
95
- .logo-name { font-family: var(--sans); font-size: 17px; font-weight: 800; letter-spacing: -0.3px; }
96
- .logo-name span { color: var(--cyan); }
97
-
98
- .topbar-divider { width: 1px; height: 22px; background: var(--border2); flex-shrink: 0; }
99
-
100
- .model-pills { display: flex; gap: 6px; }
101
- .pill {
102
- padding: 3px 10px; border-radius: 20px;
103
- font-size: 10px; font-weight: 500; letter-spacing: 0.3px;
104
- border: 1px solid; white-space: nowrap;
105
- }
106
- .pill-1 { color: var(--m1); border-color: var(--m1); background: var(--cyan-glow); }
107
- .pill-2 { color: var(--m2); border-color: var(--m2); background: var(--green-glow); }
108
- .pill-3 { color: var(--m3); border-color: var(--m3); background: var(--amber-glow); }
109
- .pill-j { color: var(--purple); border-color: var(--purple); background: var(--purple-glow); }
110
-
111
- .topbar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
112
- .topbar-stat { font-size: 10px; color: var(--text-dim); display: flex; align-items: center; gap: 5px; }
113
- .topbar-stat span { color: var(--cyan); font-weight: 500; }
114
-
115
- /* ══════════════════════════════════════════
116
- QUERY BAR
117
- ══════════════════════════════════════════ */
118
- .querybar {
119
- flex-shrink: 0;
120
- background: var(--bg2);
121
- border-bottom: 1px solid var(--border);
122
- padding: 12px 20px;
123
- }
124
- .query-row { display: flex; gap: 10px; align-items: flex-end; }
125
- .query-input-wrap { flex: 1; position: relative; }
126
- .query-prompt {
127
- position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
128
- color: var(--cyan); font-size: 14px; font-weight: 700;
129
- pointer-events: none; z-index: 1; line-height: 1;
130
- }
131
- #queryInput {
132
- width: 100%; min-height: 44px; max-height: 140px;
133
- padding: 11px 12px 11px 30px;
134
- background: var(--bg3); border: 1px solid var(--border2); border-radius: var(--radius);
135
- color: var(--text); font-family: var(--mono); font-size: 13px;
136
- resize: none; outline: none; line-height: 1.5; overflow-y: hidden;
137
- transition: border-color 0.2s, box-shadow 0.2s;
138
- }
139
- #queryInput:focus { border-color: var(--cyan); box-shadow: 0 0 0 2px rgba(0,212,255,0.1); }
140
- #queryInput::placeholder { color: var(--text-mute); }
141
-
142
- .query-actions { display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }
143
- .temp-row { display: flex; align-items: center; gap: 8px; font-size: 10px; color: var(--text-dim); }
144
- .temp-row input[type=range] { width: 80px; accent-color: var(--cyan); cursor: pointer; }
145
- #tempVal { color: var(--cyan); min-width: 24px; text-align: right; }
146
-
147
- .btn-run {
148
- padding: 10px 24px;
149
- background: linear-gradient(135deg, var(--cyan), #0099cc);
150
- color: #000; border: none; border-radius: var(--radius);
151
- font-family: var(--sans); font-size: 12px; font-weight: 700;
152
- letter-spacing: 0.5px; text-transform: uppercase; cursor: pointer;
153
- white-space: nowrap; transition: all 0.2s;
154
- box-shadow: 0 0 18px rgba(0,212,255,0.2);
155
- }
156
- .btn-run:hover { transform: translateY(-1px); box-shadow: 0 0 26px rgba(0,212,255,0.4); }
157
- .btn-run:active { transform: none; }
158
- .btn-run:disabled { background: var(--bg5); color: var(--text-mute); cursor: not-allowed; box-shadow: none; transform: none; }
159
-
160
- .hint { font-size: 10px; color: var(--text-mute); margin-top: 6px; }
161
- kbd { background: var(--bg4); border: 1px solid var(--border2); border-radius: 3px; padding: 1px 5px; font-size: 9px; color: var(--text-dim); }
162
-
163
- /* ══════════════════════════════════════════
164
- STATUS BAR
165
- ══════════════════════════════════════════ */
166
- .statusbar {
167
- flex-shrink: 0; height: 28px;
168
- background: var(--bg3); border-bottom: 1px solid var(--border);
169
- display: flex; align-items: center; padding: 0 20px;
170
- gap: 14px; font-size: 11px; color: var(--text-dim);
171
- }
172
- .status-dot {
173
- width: 6px; height: 6px; border-radius: 50%;
174
- background: var(--text-mute); flex-shrink: 0; transition: background 0.3s;
175
- }
176
- .status-dot.idle { background: var(--text-mute); }
177
- .status-dot.running { background: var(--cyan); animation: pulse 0.8s ease-in-out infinite; }
178
- .status-dot.done { background: var(--green); }
179
- .status-dot.error { background: var(--red); }
180
- @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
181
- #statusMsg { flex: 1; }
182
- #statusTime { color: var(--cyan); }
183
-
184
- /* ══════════════════════════════════════════
185
- MAIN CONTENT — 2 cols
186
- ══════════════════════════════════════════ */
187
- .main {
188
- flex: 1;
189
- display: grid;
190
- grid-template-columns: 1fr 1.25fr;
191
- overflow: hidden;
192
- min-height: 0;
193
- }
194
-
195
- .col { display: flex; flex-direction: column; overflow: hidden; min-height: 0; }
196
- .col-left { border-right: 1px solid var(--border); }
197
-
198
- .col-head {
199
- flex-shrink: 0;
200
- padding: 9px 16px;
201
- background: var(--bg2); border-bottom: 1px solid var(--border);
202
- font-size: 10px; text-transform: uppercase; letter-spacing: 1.5px;
203
- color: var(--text-dim); display: flex; align-items: center; gap: 8px;
204
- }
205
- .col-head .hdot { width: 5px; height: 5px; border-radius: 50%; flex-shrink: 0; }
206
-
207
- .col-body {
208
- flex: 1; overflow-y: auto; padding: 12px;
209
- display: flex; flex-direction: column; gap: 10px;
210
- }
211
-
212
- /* ══════════════════════════════════════════
213
- PANEL MODEL CARDS
214
- ══════════════════════════════════════════ */
215
- .model-card {
216
- background: var(--bg2); border: 1px solid var(--border);
217
- border-radius: var(--radius); overflow: hidden; transition: border-color 0.3s;
218
- }
219
- .model-card[data-state="done-1"] { border-color: rgba(0,212,255,0.3); }
220
- .model-card[data-state="done-2"] { border-color: rgba(0,232,122,0.3); }
221
- .model-card[data-state="done-3"] { border-color: rgba(255,176,32,0.3); }
222
- .model-card[data-state="error"] { border-color: rgba(255,68,85,0.3); }
223
-
224
- .card-head {
225
- display: flex; align-items: center; gap: 10px;
226
- padding: 9px 13px; background: var(--bg3);
227
- border-bottom: 1px solid var(--border);
228
- cursor: pointer; user-select: none;
229
- }
230
- .card-head:hover { background: var(--bg4); }
231
-
232
- .card-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; transition: box-shadow 0.3s; }
233
- .dot-1 { background: var(--m1); }
234
- .dot-2 { background: var(--m2); }
235
- .dot-3 { background: var(--m3); }
236
- .model-card[data-state="done-1"] .dot-1 { box-shadow: 0 0 8px var(--m1); }
237
- .model-card[data-state="done-2"] .dot-2 { box-shadow: 0 0 8px var(--m2); }
238
- .model-card[data-state="done-3"] .dot-3 { box-shadow: 0 0 8px var(--m3); }
239
-
240
- .card-info { flex: 1; }
241
- .card-name { font-family: var(--sans); font-size: 12px; font-weight: 700; }
242
- .card-name-1 { color: var(--m1); }
243
- .card-name-2 { color: var(--m2); }
244
- .card-name-3 { color: var(--m3); }
245
- .card-id { font-size: 10px; color: var(--text-mute); }
246
-
247
- .card-latency {
248
- font-size: 10px; padding: 2px 8px;
249
- border-radius: 10px; background: var(--bg);
250
- min-width: 38px; text-align: center;
251
- }
252
- .model-card[data-state="done-1"] .card-latency { color: var(--m1); }
253
- .model-card[data-state="done-2"] .card-latency { color: var(--m2); }
254
- .model-card[data-state="done-3"] .card-latency { color: var(--m3); }
255
-
256
- .card-chevron { color: var(--text-mute); font-size: 10px; transition: transform 0.2s; flex-shrink: 0; }
257
- .model-card.collapsed .card-chevron { transform: rotate(-90deg); }
258
-
259
- .card-body { overflow: hidden; transition: max-height 0.3s ease; max-height: 600px; }
260
- .model-card.collapsed .card-body { max-height: 0 !important; }
261
- .card-inner { padding: 13px; max-height: 460px; overflow-y: auto; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
 
263
  /* skeleton */
264
- .skeleton { display: flex; flex-direction: column; gap: 8px; padding: 13px; }
265
- .skel {
266
- height: 11px; border-radius: 4px;
267
- background: linear-gradient(90deg, var(--bg3) 25%, var(--bg4) 50%, var(--bg3) 75%);
268
- background-size: 200% 100%; animation: shimmer 1.6s infinite;
269
- }
270
- .skel:nth-child(2){ width:80%; animation-delay:.15s; }
271
- .skel:nth-child(3){ width:65%; animation-delay:.3s; }
272
- .skel:nth-child(4){ width:88%; animation-delay:.45s; }
273
- @keyframes shimmer { 0%{background-position:200% 0} 100%{background-position:-200% 0} }
274
-
275
- .empty-card { padding: 26px 13px; text-align: center; color: var(--text-mute); font-size: 12px; }
276
- .empty-icon { font-size: 22px; margin-bottom: 8px; opacity: 0.3; }
277
-
278
- .err-text { color: #ff7080; font-size: 12px; padding: 13px; font-style: italic; }
279
-
280
- /* ══════════════════════════════════════════
281
- JUDGE PANEL
282
- ══════════════════════════════════════════ */
283
- .judge-card {
284
- background: var(--bg2); border: 1px solid var(--border);
285
- border-radius: var(--radius); display: flex; flex-direction: column;
286
- flex: 1; min-height: 0; overflow: hidden; transition: border-color 0.3s;
287
- }
288
- .judge-card.done { border-color: rgba(168,85,247,0.4); }
289
-
290
- .judge-head {
291
- flex-shrink: 0; display: flex; align-items: center; gap: 11px;
292
- padding: 11px 15px;
293
- background: linear-gradient(135deg, rgba(168,85,247,0.09), rgba(168,85,247,0.02));
294
- border-bottom: 1px solid var(--border);
295
- }
296
- .judge-icon {
297
- width: 30px; height: 30px;
298
- background: linear-gradient(135deg, var(--purple), #6020cc);
299
- border-radius: 7px; display: flex; align-items: center; justify-content: center;
300
- font-size: 15px; box-shadow: 0 0 14px rgba(168,85,247,0.45); flex-shrink: 0;
301
- }
302
- .judge-title { font-family: var(--sans); font-size: 14px; font-weight: 800; color: var(--purple); line-height: 1.1; }
303
- .judge-sub { font-size: 10px; color: var(--text-mute); }
304
- .judge-lat {
305
- margin-left: auto; font-size: 11px; color: var(--purple);
306
- padding: 3px 10px; background: rgba(168,85,247,0.1);
307
- border: 1px solid rgba(168,85,247,0.25); border-radius: 12px;
308
- }
309
-
310
- /* reasoning toggle */
311
- .reasoning-sect { flex-shrink: 0; border-bottom: 1px solid var(--border); }
312
- .reasoning-toggle {
313
- display: flex; align-items: center; gap: 8px; padding: 8px 15px;
314
- cursor: pointer; user-select: none; background: var(--bg3);
315
- font-size: 10px; text-transform: uppercase; letter-spacing: 1px;
316
- color: var(--text-dim); transition: background 0.2s;
317
- }
318
- .reasoning-toggle:hover { background: var(--bg4); }
319
- .toggle-arr { transition: transform 0.2s; font-size: 9px; }
320
- .reasoning-sect.open .toggle-arr { transform: rotate(90deg); }
321
- .reasoning-badge {
322
- margin-left: auto; padding: 2px 8px;
323
- background: rgba(255,176,32,0.1); border: 1px solid rgba(255,176,32,0.25);
324
- color: var(--amber); border-radius: 10px; font-size: 9px;
325
- }
326
- .reasoning-body { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; }
327
- .reasoning-sect.open .reasoning-body { max-height: 260px; }
328
- .reasoning-inner {
329
- margin: 0 15px 12px; padding: 11px 14px;
330
- max-height: 260px; overflow-y: auto;
331
- border-left: 2px solid var(--amber);
332
- background: rgba(255,176,32,0.03);
333
- font-size: 12px; line-height: 1.7; color: var(--text-dim);
334
- }
335
-
336
- /* final answer */
337
- .final-sect { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-height: 0; }
338
- .final-label {
339
- flex-shrink: 0; padding: 8px 15px;
340
- font-size: 10px; text-transform: uppercase; letter-spacing: 1px;
341
- color: var(--purple); background: var(--bg3); border-bottom: 1px solid var(--border);
342
- }
343
- .final-body { flex: 1; overflow-y: auto; padding: 14px 15px; font-size: 13px; line-height: 1.7; }
344
-
345
- /* judge placeholder */
346
- .judge-placeholder {
347
- flex: 1; display: flex; flex-direction: column;
348
- align-items: center; justify-content: center;
349
- gap: 12px; color: var(--text-mute); text-align: center; padding: 40px 20px;
350
- }
351
- .j-ph-icon { font-size: 38px; opacity: 0.25; }
352
- .j-ph-text { font-size: 12px; line-height: 1.7; }
353
-
354
- /* judge skeleton */
355
- .judge-skel-wrap { padding: 18px 15px; display: flex; flex-direction: column; gap: 10px; }
356
- .jskel {
357
- height: 12px; border-radius: 4px;
358
- background: linear-gradient(90deg, rgba(168,85,247,0.07) 25%, rgba(168,85,247,0.14) 50%, rgba(168,85,247,0.07) 75%);
359
- background-size: 200% 100%; animation: shimmer 2s infinite;
360
- }
361
-
362
- /* ══════════════════════════════════════════
363
- MARKDOWN
364
- ══════════════════════════════════════════ */
365
- .md h1,.md h2,.md h3 { font-family: var(--sans); font-weight: 700; margin: 14px 0 7px; }
366
- .md h1 { font-size: 16px; }
367
- .md h2 { font-size: 14px; color: var(--cyan); }
368
- .md h3 { font-size: 13px; color: var(--green); }
369
- .md p { margin-bottom: 9px; }
370
- .md ul,.md ol { padding-left: 18px; margin-bottom: 9px; }
371
- .md li { margin-bottom: 3px; }
372
- .md code {
373
- font-family: var(--mono); font-size: 11.5px;
374
- background: var(--bg3); border: 1px solid var(--border2);
375
- padding: 1px 5px; border-radius: 3px; color: var(--cyan);
376
- }
377
- .md pre {
378
- background: #070b12 !important;
379
- border: 1px solid var(--border2); border-radius: var(--radius);
380
- padding: 13px; overflow-x: auto; margin: 9px 0; position: relative;
381
- }
382
- .md pre code { background: none !important; border: none !important; padding: 0 !important; color: inherit !important; }
383
- .md blockquote { border-left: 3px solid var(--purple); padding-left: 12px; color: var(--text-dim); font-style: italic; margin: 9px 0; }
384
- .md strong { color: var(--cyan); }
385
- .md em { color: var(--green); }
386
- .md hr { border: none; border-top: 1px solid var(--border2); margin: 14px 0; }
387
- .md a { color: var(--cyan); text-decoration: none; }
388
- .md table { width: 100%; border-collapse: collapse; margin: 9px 0; font-size: 12px; }
389
- .md th { background: var(--bg4); color: var(--cyan); padding: 7px 11px; text-align: left; border: 1px solid var(--border2); }
390
- .md td { padding: 5px 11px; border: 1px solid var(--border); }
391
- .md tr:nth-child(even) { background: rgba(255,255,255,0.015); }
392
-
393
- /* copy button */
394
- .code-wrap { position: relative; }
395
- .copy-btn {
396
- position: absolute; top: 5px; right: 5px;
397
- padding: 2px 8px; background: var(--bg5);
398
- border: 1px solid var(--border2); border-radius: 4px;
399
- color: var(--text-dim); font-family: var(--mono);
400
- font-size: 9px; cursor: pointer; opacity: 0; transition: opacity 0.15s;
401
- }
402
- .code-wrap:hover .copy-btn { opacity: 1; }
403
- .copy-btn:hover { color: var(--cyan); border-color: var(--cyan); }
404
-
405
- /* ══════════════════════════════════════════
406
  SCROLLBARS
407
- ══════════════════════════════════════════ */
408
- ::-webkit-scrollbar { width: 4px; height: 4px; }
409
- ::-webkit-scrollbar-track { background: transparent; }
410
- ::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 4px; }
411
- ::-webkit-scrollbar-thumb:hover { background: var(--text-mute); }
412
-
413
- /* animations */
414
- @keyframes fadeUp { from{opacity:0;transform:translateY(6px)} to{opacity:1;transform:translateY(0)} }
415
- .fade-up { animation: fadeUp 0.3s ease forwards; }
416
-
417
- /* ══════════════════════════════════════════
418
- RESPONSIVE
419
- ══════════════════════════════════════════ */
420
- @media (max-width: 860px) {
421
- .main { grid-template-columns: 1fr; grid-template-rows: auto; overflow-y: auto; }
422
- body { overflow-y: auto; height: auto; }
423
- .col { overflow: visible; min-height: auto; }
424
- .col-body { overflow: visible; }
425
- .judge-card { min-height: 400px; }
426
- .model-pills .pill-1,.model-pills .pill-2 { display: none; }
427
  }
428
  </style>
429
  </head>
430
  <body>
431
 
432
- <!-- TOP BAR -->
433
- <div class="topbar">
434
  <div class="logo">
435
- <div class="logo-mark">⚡</div>
436
- <div class="logo-name">Code<span>Debug</span></div>
437
  </div>
438
- <div class="topbar-divider"></div>
439
- <div class="model-pills">
440
- <span class="pill pill-1">Trinity Large</span>
441
- <span class="pill pill-2">StepFun Flash</span>
442
- <span class="pill pill-3">Nemotron Nano</span>
443
- <span class="pill pill-j">⚖ Qwen3 Judge</span>
444
  </div>
445
- <div class="topbar-right">
446
- <div class="topbar-stat">Queries: <span id="qCount">0</span></div>
447
- </div>
448
- </div>
449
-
450
- <!-- QUERY BAR -->
451
- <div class="querybar">
452
- <div class="query-row">
453
- <div class="query-input-wrap">
454
- <div class="query-prompt">&gt;</div>
455
- <textarea id="queryInput" rows="1"
456
- placeholder="Paste your code or describe the bug… e.g. Why does this Python async function silently return None?"></textarea>
457
- </div>
458
- <div class="query-actions">
459
- <div class="temp-row">
460
- <span>temp</span>
461
- <input type="range" id="tempSlider" min="0" max="1" step="0.05" value="0.3"
462
- oninput="document.getElementById('tempVal').textContent=this.value">
463
- <span id="tempVal">0.3</span>
464
- </div>
465
- <button class="btn-run" id="runBtn" onclick="runDebug()">▶ Debug</button>
466
- </div>
467
- </div>
468
- <div class="hint">Press <kbd>Ctrl</kbd>+<kbd>Enter</kbd> to submit &nbsp;·&nbsp; 3 models run in parallel &nbsp;·&nbsp; Qwen3 judges</div>
469
  </div>
470
 
471
- <!-- STATUS BAR -->
472
- <div class="statusbar">
473
- <div class="status-dot idle" id="statusDot"></div>
474
- <span id="statusMsg">Ready — enter a debugging query above</span>
475
- <span id="statusTime"></span>
476
  </div>
477
 
478
- <!-- MAIN -->
479
- <div class="main">
480
 
481
- <!-- LEFT: 3 panel models -->
482
- <div class="col col-left">
483
- <div class="col-head">
484
- <div class="hdot" style="background:var(--cyan)"></div>
485
- Panel Models — parallel analysis
486
- </div>
487
  <div class="col-body">
488
 
489
- <div class="model-card" id="card-0" data-state="idle">
490
- <div class="card-head" onclick="toggleCard(0)">
491
- <div class="card-dot dot-1"></div>
492
- <div class="card-info">
493
- <div class="card-name card-name-1">Trinity Large</div>
494
- <div class="card-id">arcee-ai/trinity-large-preview:free</div>
495
- </div>
496
- <div class="card-latency" id="lat-0">—</div>
497
- <div class="card-chevron">▾</div>
498
- </div>
499
- <div class="card-body">
500
- <div class="empty-card"><div class="empty-icon">🔵</div>Awaiting query…</div>
501
  </div>
502
  </div>
503
 
504
- <div class="model-card" id="card-1" data-state="idle">
505
- <div class="card-head" onclick="toggleCard(1)">
506
- <div class="card-dot dot-2"></div>
507
- <div class="card-info">
508
- <div class="card-name card-name-2">StepFun Flash</div>
509
- <div class="card-id">stepfun/step-3.5-flash:free</div>
510
- </div>
511
- <div class="card-latency" id="lat-1">—</div>
512
- <div class="card-chevron">▾</div>
513
- </div>
514
- <div class="card-body">
515
- <div class="empty-card"><div class="empty-icon">🟢</div>Awaiting query…</div>
516
  </div>
517
  </div>
518
 
519
- <div class="model-card" id="card-2" data-state="idle">
520
- <div class="card-head" onclick="toggleCard(2)">
521
- <div class="card-dot dot-3"></div>
522
- <div class="card-info">
523
- <div class="card-name card-name-3">Nemotron Nano</div>
524
- <div class="card-id">nvidia/nemotron-3-nano-30b-a3b:free</div>
525
- </div>
526
- <div class="card-latency" id="lat-2">—</div>
527
- <div class="card-chevron">▾</div>
528
- </div>
529
- <div class="card-body">
530
- <div class="empty-card"><div class="empty-icon">🟡</div>Awaiting query…</div>
531
- </div>
532
  </div>
533
 
534
  </div>
535
  </div>
536
 
537
- <!-- RIGHT: Judge -->
538
  <div class="col">
539
- <div class="col-head">
540
- <div class="hdot" style="background:var(--purple)"></div>
541
- Qwen3 Judge — synthesized verdict
542
  </div>
543
  <div class="col-body">
544
- <div class="judge-card" id="judgeCard">
545
- <div class="judge-head">
546
- <div class="judge-icon">⚖</div>
 
547
  <div>
548
- <div class="judge-title">Qwen3 Judge</div>
549
- <div class="judge-sub">qwen/qwq-32b:free — Master Arbitrator</div>
550
  </div>
551
- <div class="judge-lat" id="judgeLat">—</div>
552
  </div>
553
- <div id="judgeBody" style="display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden;">
554
- <div class="judge-placeholder">
555
- <div class="j-ph-icon">⚖️</div>
556
- <div class="j-ph-text">
557
- The judge will analyze all three responses<br>
558
- and produce the definitive final answer.
 
559
  </div>
560
  </div>
561
  </div>
562
  </div>
 
563
  </div>
564
  </div>
565
 
566
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
 
568
  <script>
569
  /* ── Config ── */
570
  const API = '';
 
571
 
572
- /* ── Marked setup ── */
573
- marked.setOptions({ breaks: true, gfm: true });
 
 
 
 
 
 
574
 
575
- function renderMD(text) {
576
- if (!text) return '<em style="color:var(--text-mute)">No content</em>';
577
  return `<div class="md">${marked.parse(text)}</div>`;
578
  }
579
 
580
- function attachCodeCopy(el) {
581
- el.querySelectorAll('pre code').forEach(block => {
582
- hljs.highlightElement(block);
583
- const pre = block.parentNode;
584
- const wrap = document.createElement('div');
585
- wrap.className = 'code-wrap';
586
- pre.parentNode.insertBefore(wrap, pre);
587
- wrap.appendChild(pre);
588
- const btn = document.createElement('button');
589
- btn.className = 'copy-btn';
590
- btn.textContent = 'copy';
591
- btn.onclick = () => {
592
- navigator.clipboard.writeText(block.textContent);
593
- btn.textContent = 'copied ✓';
594
- setTimeout(() => btn.textContent = 'copy', 1800);
595
- };
596
- wrap.appendChild(btn);
597
- });
598
  }
599
 
600
- /* ── State ── */
601
- let running = false;
602
- let queryCount = 0;
603
- const collapsed = [false, false, false];
604
-
605
- /* ── Helpers ── */
606
- function esc(s){ return String(s??'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
607
-
608
- function setStatus(state, msg, time = '') {
609
- document.getElementById('statusDot').className = `status-dot ${state}`;
610
- document.getElementById('statusMsg').textContent = msg;
611
- document.getElementById('statusTime').textContent = time;
612
  }
613
 
614
- function toggleCard(i) {
615
- collapsed[i] = !collapsed[i];
616
- document.getElementById(`card-${i}`).classList.toggle('collapsed', collapsed[i]);
617
  }
618
 
619
- /* ── Panel rendering ── */
620
- function showPanelSkeleton(i) {
621
- document.getElementById(`card-${i}`).dataset.state = 'loading';
622
- document.getElementById(`lat-${i}`).textContent = '…';
623
- document.querySelector(`#card-${i} .card-body`).innerHTML =
624
- `<div class="skeleton">
625
- <div class="skel"></div><div class="skel"></div>
626
- <div class="skel"></div><div class="skel"></div>
627
- </div>`;
628
  }
629
 
630
- function showPanelResult(i, r) {
631
- const card = document.getElementById(`card-${i}`);
632
- const body = document.querySelector(`#card-${i} .card-body`);
633
- document.getElementById(`lat-${i}`).textContent = `${r.latency_ms}ms`;
634
- if (r.error) {
635
- card.dataset.state = 'error';
636
- body.innerHTML = `<div class="err-text">⚠ ${esc(r.error)}</div>`;
637
  return;
638
  }
639
- card.dataset.state = `done-${i+1}`;
640
- body.innerHTML = `<div class="card-inner fade-up">${renderMD(r.response)}</div>`;
641
- attachCodeCopy(body);
642
  }
643
 
644
  /* ── Judge rendering ── */
645
- function showJudgeSkeleton() {
646
- document.getElementById('judgeBody').innerHTML =
647
- `<div class="judge-skel-wrap">
648
- <div class="jskel" style="width:55%"></div>
649
- <div class="jskel"></div>
650
- <div class="jskel" style="width:80%"></div>
651
- <div class="jskel" style="width:42%"></div>
652
- <div class="jskel"></div>
653
- <div class="jskel" style="width:72%"></div>
654
- </div>`;
655
- document.getElementById('judgeLat').textContent = '…';
656
- document.getElementById('judgeCard').className = 'judge-card';
657
- }
658
-
659
- function showJudgeResult(r) {
660
- const jb = document.getElementById('judgeBody');
661
- const jc = document.getElementById('judgeCard');
662
- document.getElementById('judgeLat').textContent = `${r.latency_ms}ms`;
663
- jc.className = 'judge-card done';
664
-
665
- if (r.error) {
666
- jb.innerHTML = `<div class="err-text" style="padding:18px 15px">⚠ Judge error: ${esc(r.error)}</div>`;
667
  return;
668
  }
669
 
670
- const hasReasoning = r.reasoning && r.reasoning.trim().length > 10;
671
-
672
- jb.innerHTML = `
673
- ${hasReasoning ? `
674
- <div class="reasoning-sect open" id="reasoningSect">
675
- <div class="reasoning-toggle" onclick="toggleReasoning()">
676
- <span class="toggle-arr">▶</span>
677
- 🔍 Reasoning
678
- <div class="reasoning-badge">ANALYSIS</div>
679
- </div>
680
- <div class="reasoning-body">
681
- <div class="reasoning-inner" id="reasoningInner"></div>
682
- </div>
683
- </div>` : ''}
684
- <div class="final-sect">
685
- <div class="final-label">✅ Final Answer</div>
686
- <div class="final-body fade-up" id="finalBody"></div>
687
- </div>
688
- `;
689
-
690
- if (hasReasoning) {
691
- const ri = document.getElementById('reasoningInner');
692
- ri.innerHTML = renderMD(r.reasoning);
693
- attachCodeCopy(ri);
694
- }
695
- const fb = document.getElementById('finalBody');
696
- fb.innerHTML = renderMD(r.final_answer);
697
- attachCodeCopy(fb);
698
- }
699
-
700
- function toggleReasoning() {
701
- document.getElementById('reasoningSect')?.classList.toggle('open');
702
- }
703
-
704
- /* ── Main debug run ── */
705
- async function runDebug() {
706
- if (running) return;
707
- const q = document.getElementById('queryInput').value.trim();
708
- const temp = parseFloat(document.getElementById('tempSlider').value);
709
- if (!q) { document.getElementById('queryInput').focus(); return; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
710
 
711
  running = true;
712
  document.getElementById('runBtn').disabled = true;
713
  const t0 = Date.now();
714
 
715
- setStatus('running', 'Dispatching to 3 panel models in parallel…');
716
- [0,1,2].forEach(showPanelSkeleton);
717
- showJudgeSkeleton();
718
 
719
- try {
720
- const res = await fetch(`${API}/api/v1/debug`, {
721
- method: 'POST',
722
- headers: { 'Content-Type': 'application/json' },
723
- body: JSON.stringify({ question: q, temperature: temp }),
724
  });
725
 
726
- if (!res.ok) {
727
- const e = await res.json().catch(() => ({}));
728
  throw new Error(e.detail || e.error || `HTTP ${res.status}`);
729
  }
730
 
731
  const data = await res.json();
732
- const ms = Date.now() - t0;
733
-
734
- data.panel.forEach((r, i) => showPanelResult(i, r));
735
- await new Promise(r => setTimeout(r, 180));
736
- showJudgeResult(data.judge);
737
-
738
- const ok = data.panel.filter(p => !p.error).length;
739
- setStatus('done', `Done — ${ok}/3 models responded`, `${ms}ms total`);
740
-
741
- queryCount++;
742
- document.getElementById('qCount').textContent = queryCount;
743
-
744
- } catch (err) {
745
- setStatus('error', `Error: ${err.message}`);
746
- document.getElementById('judgeBody').innerHTML =
747
- `<div class="err-text" style="padding:18px 15px">⚠ ${esc(err.message)}</div>`;
748
- [0,1,2].forEach(i => {
749
- document.getElementById(`card-${i}`).dataset.state = 'error';
750
- document.querySelector(`#card-${i} .card-body`).innerHTML =
751
- '<div class="err-text">Request failed</div>';
752
  });
753
  }
754
 
755
- running = false;
756
- document.getElementById('runBtn').disabled = false;
757
  }
758
 
759
- /* ── Auto-resize textarea ── */
760
- const ta = document.getElementById('queryInput');
761
- ta.addEventListener('input', () => {
762
- ta.style.height = 'auto';
763
- ta.style.height = Math.min(ta.scrollHeight, 140) + 'px';
764
  });
765
 
766
- /* ── Ctrl+Enter shortcut ── */
767
- ta.addEventListener('keydown', e => {
768
- if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); runDebug(); }
769
  });
770
  </script>
771
  </body>
 
3
  <head>
4
  <meta charset="UTF-8"/>
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>⚡ CodeDebug</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Syne:wght@600;700;800&display=swap" rel="stylesheet">
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css">
10
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
11
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
12
  <style>
13
+ /* ════════════════════════════════════════
14
+ BASE
15
+ ════════════════════════════════════════ */
16
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
17
+
18
+ :root{
19
+ --bg: #06090f;
20
+ --bg2: #0b1018;
21
+ --bg3: #0f1520;
22
+ --bg4: #141c2a;
23
+ --bg5: #192233;
24
+ --line: #1b2b3d;
25
+ --line2: #223348;
26
+
27
+ --ink: #bdd0e3;
28
+ --ink2: #567090;
29
+ --ink3: #344e66;
30
+
31
+ --cyan: #00ccf5;
32
+ --cglow: rgba(0,204,245,.14);
33
+ --green: #00e57a;
34
+ --gglow: rgba(0,229,122,.11);
35
+ --amber: #ffa820;
36
+ --aglow: rgba(255,168,32,.11);
37
+ --purp: #b060ff;
38
+ --pglow: rgba(176,96,255,.13);
39
+ --red: #ff3f52;
40
+
41
+ --m1: #00ccf5; /* Trinity */
42
+ --m2: #ffa820; /* Nemotron */
43
+ --judge: #00e57a;/* StepFun */
44
+
45
+ --mono:'JetBrains Mono',monospace;
46
+ --sans:'Syne',sans-serif;
47
+ --r:6px;
48
+ }
49
+
50
+ html,body{height:100%;overflow:hidden}
51
+ body{
52
+ background:var(--bg);color:var(--ink);
53
+ font-family:var(--mono);font-size:13px;
54
+ display:flex;flex-direction:column;
55
+ }
56
+
57
+ /* scanlines */
58
+ body::after{
59
+ content:'';position:fixed;inset:0;pointer-events:none;z-index:9999;
60
+ background:repeating-linear-gradient(0deg,transparent,transparent 2px,
61
+ rgba(0,0,0,.022) 2px,rgba(0,0,0,.022) 4px);
62
+ }
63
+
64
+ /* ════════════════════════════════════════
65
+ HEADER BAR
66
+ ════════════════════════════════════════ */
67
+ .hdr{
68
+ flex-shrink:0;height:46px;
69
+ background:var(--bg2);border-bottom:1px solid var(--line);
70
+ display:flex;align-items:center;padding:0 18px;gap:14px;
71
+ }
72
+ .logo{display:flex;align-items:center;gap:9px}
73
+ .logo-gem{
74
+ width:28px;height:28px;border-radius:7px;
75
+ background:linear-gradient(135deg,var(--cyan),var(--purp));
76
+ display:flex;align-items:center;justify-content:center;font-size:14px;
77
+ box-shadow:0 0 14px rgba(0,204,245,.35);
78
+ }
79
+ .logo-txt{font-family:var(--sans);font-size:16px;font-weight:800;letter-spacing:-.3px}
80
+ .logo-txt b{color:var(--cyan)}
81
+ .hdr-sep{width:1px;height:20px;background:var(--line2)}
82
+ .hdr-pills{display:flex;gap:5px}
83
+ .hp{padding:2px 9px;border-radius:20px;font-size:10px;border:1px solid;white-space:nowrap}
84
+ .hp1{color:var(--m1);border-color:var(--m1);background:var(--cglow)}
85
+ .hp2{color:var(--m2);border-color:var(--m2);background:var(--aglow)}
86
+ .hpj{color:var(--judge);border-color:var(--judge);background:var(--gglow)}
87
+ .hdr-right{margin-left:auto;font-size:10px;color:var(--ink2)}
88
+ .hdr-right b{color:var(--cyan)}
89
+
90
+ /* ════════════════════════════════════════
91
+ STATUS STRIP
92
+ ════════════════════════════════════════ */
93
+ .sbar{
94
+ flex-shrink:0;height:26px;
95
+ background:var(--bg3);border-bottom:1px solid var(--line);
96
+ display:flex;align-items:center;padding:0 18px;gap:12px;
97
+ font-size:11px;color:var(--ink2);
98
+ }
99
+ .sdot{width:6px;height:6px;border-radius:50%;background:var(--ink3);flex-shrink:0;transition:background .3s}
100
+ .sdot.idle {background:var(--ink3)}
101
+ .sdot.run {background:var(--cyan);animation:blink .8s ease-in-out infinite}
102
+ .sdot.done {background:var(--green)}
103
+ .sdot.err {background:var(--red)}
104
+ @keyframes blink{0%,100%{opacity:1}50%{opacity:.3}}
105
+ #sMsg{flex:1}
106
+ #sTime{color:var(--cyan)}
107
+
108
+ /* ════════════════════════════════════════
109
+ 3-COLUMN BODY 25 | 50 | 25
110
+ ════════════════════════════════════════ */
111
+ .body{
112
+ flex:1;display:grid;
113
+ grid-template-columns:25% 50% 25%;
114
+ overflow:hidden;min-height:0;
115
+ }
116
+
117
+ /* shared column chrome */
118
+ .col{display:flex;flex-direction:column;overflow:hidden;min-height:0}
119
+ .col+.col{border-left:1px solid var(--line)}
120
+
121
+ .col-hdr{
122
+ flex-shrink:0;padding:8px 14px;
123
+ background:var(--bg2);border-bottom:1px solid var(--line);
124
+ font-size:10px;text-transform:uppercase;letter-spacing:1.5px;
125
+ color:var(--ink2);display:flex;align-items:center;gap:7px;
126
+ }
127
+ .chd{width:5px;height:5px;border-radius:50%;flex-shrink:0}
128
+ .col-body{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:10px}
129
+
130
+ /* ════════════════════════════════════════
131
+ LEFT 25% — INPUT
132
+ ════════════════════════════════════════ */
133
+ .query-wrap{position:relative}
134
+ .q-prompt{
135
+ position:absolute;left:11px;top:12px;
136
+ color:var(--cyan);font-size:13px;font-weight:700;pointer-events:none;z-index:1
137
+ }
138
+ #qInput{
139
+ width:100%;min-height:120px;max-height:340px;
140
+ padding:11px 11px 11px 26px;
141
+ background:var(--bg3);border:1px solid var(--line2);border-radius:var(--r);
142
+ color:var(--ink);font-family:var(--mono);font-size:13px;
143
+ resize:vertical;outline:none;line-height:1.55;
144
+ transition:border-color .2s,box-shadow .2s;
145
+ }
146
+ #qInput:focus{border-color:var(--cyan);box-shadow:0 0 0 2px rgba(0,204,245,.09)}
147
+ #qInput::placeholder{color:var(--ink3)}
148
+
149
+ .field-lbl{font-size:10px;color:var(--ink2);margin-bottom:5px;text-transform:uppercase;letter-spacing:.8px}
150
+ .temp-row{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--ink2)}
151
+ .temp-row input[type=range]{flex:1;accent-color:var(--cyan);cursor:pointer}
152
+ #tVal{color:var(--cyan);min-width:26px;text-align:right}
153
+
154
+ .btn-run{
155
+ width:100%;padding:11px;
156
+ background:linear-gradient(135deg,var(--cyan),#0088bb);
157
+ color:#000;border:none;border-radius:var(--r);
158
+ font-family:var(--sans);font-size:13px;font-weight:700;
159
+ letter-spacing:.5px;text-transform:uppercase;cursor:pointer;
160
+ transition:all .2s;box-shadow:0 0 18px rgba(0,204,245,.18);
161
+ }
162
+ .btn-run:hover{transform:translateY(-1px);box-shadow:0 0 26px rgba(0,204,245,.38)}
163
+ .btn-run:active{transform:none}
164
+ .btn-run:disabled{background:var(--bg5);color:var(--ink3);cursor:not-allowed;box-shadow:none;transform:none}
165
+
166
+ .hint-txt{font-size:10px;color:var(--ink3);text-align:center}
167
+ kbd{background:var(--bg4);border:1px solid var(--line2);border-radius:3px;padding:1px 5px;font-size:9px;color:var(--ink2)}
168
+
169
+ /* divider line in left col */
170
+ .field-sep{height:1px;background:var(--line);margin:4px 0}
171
+
172
+ /* previous queries list */
173
+ .q-history{display:flex;flex-direction:column;gap:6px}
174
+ .q-hist-item{
175
+ padding:7px 10px;background:var(--bg3);border:1px solid var(--line);
176
+ border-radius:var(--r);font-size:11px;color:var(--ink2);
177
+ cursor:pointer;line-height:1.4;transition:border-color .15s,color .15s;
178
+ overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
179
+ }
180
+ .q-hist-item:hover{border-color:var(--cyan);color:var(--ink)}
181
+
182
+ /* ════════════════════════════════════════
183
+ MIDDLE 50% — JUDGE OUTPUT
184
+ ════════════════════════════════════════ */
185
+ .judge-panel{
186
+ background:var(--bg2);border:1px solid var(--line);
187
+ border-radius:var(--r);display:flex;flex-direction:column;
188
+ flex:1;min-height:0;overflow:hidden;transition:border-color .3s;
189
+ }
190
+ .judge-panel.done{border-color:rgba(0,229,122,.35)}
191
+
192
+ .jp-head{
193
+ flex-shrink:0;display:flex;align-items:center;gap:10px;
194
+ padding:10px 14px;
195
+ background:linear-gradient(135deg,rgba(0,229,122,.08),rgba(0,229,122,.02));
196
+ border-bottom:1px solid var(--line);
197
+ }
198
+ .jp-icon{
199
+ width:28px;height:28px;border-radius:6px;flex-shrink:0;
200
+ background:linear-gradient(135deg,var(--judge),#008844);
201
+ display:flex;align-items:center;justify-content:center;font-size:14px;
202
+ box-shadow:0 0 12px rgba(0,229,122,.4);
203
+ }
204
+ .jp-title{font-family:var(--sans);font-size:13px;font-weight:800;color:var(--judge)}
205
+ .jp-sub {font-size:10px;color:var(--ink3)}
206
+ .jp-lat {
207
+ margin-left:auto;font-size:10px;color:var(--judge);
208
+ padding:2px 9px;background:rgba(0,229,122,.09);
209
+ border:1px solid rgba(0,229,122,.22);border-radius:10px;
210
+ }
211
+
212
+ /* error analysis box */
213
+ .err-box{
214
+ flex-shrink:0;margin:12px 12px 0;
215
+ background:rgba(255,63,82,.05);border:1px solid rgba(255,63,82,.22);
216
+ border-radius:var(--r);overflow:hidden;
217
+ }
218
+ .err-box-hdr{
219
+ padding:7px 12px;background:rgba(255,63,82,.08);
220
+ font-size:10px;text-transform:uppercase;letter-spacing:1px;
221
+ color:#ff6070;border-bottom:1px solid rgba(255,63,82,.15);
222
+ display:flex;align-items:center;gap:7px;
223
+ }
224
+ .err-box-body{padding:11px 12px;font-size:12px;line-height:1.7;color:var(--ink)}
225
+
226
+ /* corrected code box */
227
+ .code-box{
228
+ flex:1;display:flex;flex-direction:column;
229
+ margin:10px 12px 12px;min-height:0;
230
+ background:var(--bg3);border:1px solid rgba(0,229,122,.2);border-radius:var(--r);
231
+ overflow:hidden;
232
+ }
233
+ .code-box-hdr{
234
+ flex-shrink:0;padding:7px 12px;background:rgba(0,229,122,.07);
235
+ font-size:10px;text-transform:uppercase;letter-spacing:1px;
236
+ color:var(--judge);border-bottom:1px solid rgba(0,229,122,.15);
237
+ display:flex;align-items:center;gap:7px;
238
+ }
239
+ .copy-code-btn{
240
+ margin-left:auto;padding:2px 9px;background:var(--bg4);
241
+ border:1px solid var(--line2);border-radius:3px;
242
+ color:var(--ink2);font-size:9px;font-family:var(--mono);cursor:pointer;transition:color .15s,border-color .15s;
243
+ }
244
+ .copy-code-btn:hover{color:var(--judge);border-color:var(--judge)}
245
+ .code-box-body{flex:1;overflow-y:auto;padding:0}
246
+ .code-box-body pre{margin:0;border-radius:0;border:none !important;height:100%}
247
+ .code-box-body pre code{font-size:12.5px;line-height:1.6}
248
+
249
+ /* judge placeholder / skeleton */
250
+ .jp-placeholder{
251
+ flex:1;display:flex;flex-direction:column;
252
+ align-items:center;justify-content:center;
253
+ gap:11px;color:var(--ink3);text-align:center;padding:40px 20px;
254
+ }
255
+ .jp-ph-icon{font-size:36px;opacity:.22}
256
+ .jp-ph-txt{font-size:12px;line-height:1.7}
257
+
258
+ .jp-skel{padding:16px 14px;display:flex;flex-direction:column;gap:9px}
259
+ .jsk{
260
+ height:12px;border-radius:4px;
261
+ background:linear-gradient(90deg,rgba(0,229,122,.05) 25%,rgba(0,229,122,.12) 50%,rgba(0,229,122,.05) 75%);
262
+ background-size:200% 100%;animation:shim 2s infinite;
263
+ }
264
+ @keyframes shim{0%{background-position:200% 0}100%{background-position:-200% 0}}
265
+
266
+ /* ════════════════════════════════════════
267
+ RIGHT 25% — PANEL MODELS
268
+ ════════════════════════════════════════ */
269
+ .model-card{
270
+ background:var(--bg2);border:1px solid var(--line);
271
+ border-radius:var(--r);overflow:hidden;transition:border-color .3s;
272
+ }
273
+ .model-card[data-s="d1"]{border-color:rgba(0,204,245,.28)}
274
+ .model-card[data-s="d2"]{border-color:rgba(255,168,32,.28)}
275
+ .model-card[data-s="er"]{border-color:rgba(255,63,82,.28)}
276
+
277
+ .mc-hdr{
278
+ display:flex;align-items:center;gap:9px;padding:8px 12px;
279
+ background:var(--bg3);border-bottom:1px solid var(--line);
280
+ cursor:pointer;user-select:none;
281
+ }
282
+ .mc-hdr:hover{background:var(--bg4)}
283
+
284
+ .mc-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;transition:box-shadow .3s}
285
+ .mcd1{background:var(--m1)} .mcd2{background:var(--m2)}
286
+ .model-card[data-s="d1"] .mcd1{box-shadow:0 0 7px var(--m1)}
287
+ .model-card[data-s="d2"] .mcd2{box-shadow:0 0 7px var(--m2)}
288
+
289
+ .mc-info{flex:1}
290
+ .mc-name{font-family:var(--sans);font-size:11px;font-weight:700}
291
+ .mcn1{color:var(--m1)} .mcn2{color:var(--m2)}
292
+ .mc-id{font-size:9px;color:var(--ink3)}
293
+
294
+ .mc-lat{font-size:10px;padding:2px 7px;border-radius:10px;background:var(--bg);min-width:34px;text-align:center}
295
+ .model-card[data-s="d1"] .mc-lat{color:var(--m1)}
296
+ .model-card[data-s="d2"] .mc-lat{color:var(--m2)}
297
+
298
+ .mc-chev{color:var(--ink3);font-size:9px;transition:transform .2s;flex-shrink:0}
299
+ .model-card.coll .mc-chev{transform:rotate(-90deg)}
300
+
301
+ .mc-body{overflow:hidden;transition:max-height .3s ease;max-height:480px}
302
+ .model-card.coll .mc-body{max-height:0!important}
303
+ .mc-inner{padding:11px;max-height:430px;overflow-y:auto}
304
 
305
  /* skeleton */
306
+ .skel{display:flex;flex-direction:column;gap:7px;padding:11px}
307
+ .sk{
308
+ height:10px;border-radius:3px;
309
+ background:linear-gradient(90deg,var(--bg3) 25%,var(--bg4) 50%,var(--bg3) 75%);
310
+ background-size:200% 100%;animation:shim 1.6s infinite;
311
+ }
312
+ .sk:nth-child(2){width:78%;animation-delay:.15s}
313
+ .sk:nth-child(3){width:62%;animation-delay:.3s}
314
+ .sk:nth-child(4){width:85%;animation-delay:.45s}
315
+
316
+ .mc-empty{padding:22px 11px;text-align:center;color:var(--ink3);font-size:11px}
317
+ .mc-eicon{font-size:20px;margin-bottom:6px;opacity:.28}
318
+ .mc-err{color:#ff6070;font-size:11px;padding:11px;font-style:italic}
319
+
320
+ /* ════════════════════════════════════════
321
+ MARKDOWN RENDERER
322
+ ════════════════════════════════════════ */
323
+ .md p{margin-bottom:8px}
324
+ .md ul,.md ol{padding-left:16px;margin-bottom:8px}
325
+ .md li{margin-bottom:3px}
326
+ .md h1,.md h2,.md h3{font-family:var(--sans);font-weight:700;margin:12px 0 6px}
327
+ .md h1{font-size:15px}
328
+ .md h2{font-size:13px;color:var(--cyan)}
329
+ .md h3{font-size:12px;color:var(--green)}
330
+ .md code{
331
+ font-family:var(--mono);font-size:11px;
332
+ background:var(--bg3);border:1px solid var(--line2);
333
+ padding:1px 5px;border-radius:3px;color:var(--cyan);
334
+ }
335
+ .md pre{
336
+ background:#070b12!important;border:1px solid var(--line2);
337
+ border-radius:var(--r);padding:11px;overflow-x:auto;margin:8px 0;
338
+ }
339
+ .md pre code{background:none!important;border:none!important;padding:0!important;color:inherit!important}
340
+ .md blockquote{border-left:2px solid var(--purp);padding-left:10px;color:var(--ink2);font-style:italic;margin:8px 0}
341
+ .md strong{color:var(--cyan)}
342
+ .md em{color:var(--green)}
343
+ .md hr{border:none;border-top:1px solid var(--line2);margin:12px 0}
344
+ .md a{color:var(--cyan);text-decoration:none}
345
+ .md table{width:100%;border-collapse:collapse;margin:8px 0;font-size:11px}
346
+ .md th{background:var(--bg4);color:var(--cyan);padding:6px 10px;text-align:left;border:1px solid var(--line2)}
347
+ .md td{padding:4px 10px;border:1px solid var(--line)}
348
+
349
+ /* ════════════════════════════════════════
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  SCROLLBARS
351
+ ════════════════════════════════════════ */
352
+ ::-webkit-scrollbar{width:4px;height:4px}
353
+ ::-webkit-scrollbar-track{background:transparent}
354
+ ::-webkit-scrollbar-thumb{background:var(--line2);border-radius:4px}
355
+ ::-webkit-scrollbar-thumb:hover{background:var(--ink3)}
356
+
357
+ /* ════════════════════════════════════════
358
+ UTILS
359
+ ════════════════════════════════════════ */
360
+ @keyframes fadeUp{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}
361
+ .fu{animation:fadeUp .3s ease forwards}
362
+
363
+ @media(max-width:800px){
364
+ .body{grid-template-columns:1fr;grid-template-rows:auto auto auto;overflow-y:auto}
365
+ html,body{overflow-y:auto;height:auto}
366
+ .col{overflow:visible;min-height:auto}
367
+ .col-body{overflow:visible}
368
+ .judge-panel{min-height:300px}
 
 
369
  }
370
  </style>
371
  </head>
372
  <body>
373
 
374
+ <!-- HEADER -->
375
+ <div class="hdr">
376
  <div class="logo">
377
+ <div class="logo-gem">⚡</div>
378
+ <div class="logo-txt"><b>Code</b>Debug</div>
379
  </div>
380
+ <div class="hdr-sep"></div>
381
+ <div class="hdr-pills">
382
+ <span class="hp hp1">Trinity Large</span>
383
+ <span class="hp hp2">Nemotron Nano</span>
384
+ <span class="hp hpj">⚖ StepFun Judge</span>
 
385
  </div>
386
+ <div class="hdr-right">Queries: <b id="qCnt">0</b></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  </div>
388
 
389
+ <!-- STATUS -->
390
+ <div class="sbar">
391
+ <div class="sdot idle" id="sDot"></div>
392
+ <span id="sMsg">Ready — enter your code or describe the bug</span>
393
+ <span id="sTime"></span>
394
  </div>
395
 
396
+ <!-- 3-COLUMN BODY -->
397
+ <div class="body">
398
 
399
+ <!-- ══ LEFT 25%: INPUT ══ -->
400
+ <div class="col">
401
+ <div class="col-hdr"><div class="chd" style="background:var(--cyan)"></div>Input</div>
 
 
 
402
  <div class="col-body">
403
 
404
+ <div>
405
+ <div class="field-lbl">Your code / question</div>
406
+ <div class="query-wrap">
407
+ <div class="q-prompt">&gt;</div>
408
+ <textarea id="qInput" rows="5"
409
+ placeholder="Paste buggy code or describe the issue…&#10;&#10;e.g.&#10;def divide(a,b):&#10; return a/b&#10;divide(10,0)"></textarea>
 
 
 
 
 
 
410
  </div>
411
  </div>
412
 
413
+ <div>
414
+ <div class="field-lbl">Temperature</div>
415
+ <div class="temp-row">
416
+ <input type="range" id="tSlider" min="0" max="1" step="0.05" value="0.3"
417
+ oninput="document.getElementById('tVal').textContent=this.value">
418
+ <span id="tVal">0.3</span>
 
 
 
 
 
 
419
  </div>
420
  </div>
421
 
422
+ <button class="btn-run" id="runBtn" onclick="runDebug()">▶ Run Debug</button>
423
+ <div class="hint-txt"><kbd>Ctrl</kbd>+<kbd>Enter</kbd> to submit</div>
424
+
425
+ <div class="field-sep"></div>
426
+
427
+ <!-- history -->
428
+ <div id="histWrap" style="display:none">
429
+ <div class="field-lbl" style="margin-bottom:7px">Recent</div>
430
+ <div class="q-history" id="histList"></div>
 
 
 
 
431
  </div>
432
 
433
  </div>
434
  </div>
435
 
436
+ <!-- ══ MIDDLE 50%: JUDGE OUTPUT ══ -->
437
  <div class="col">
438
+ <div class="col-hdr">
439
+ <div class="chd" style="background:var(--judge)"></div>
440
+ Judge Output — StepFun Flash
441
  </div>
442
  <div class="col-body">
443
+
444
+ <div class="judge-panel" id="jPanel">
445
+ <div class="jp-head">
446
+ <div class="jp-icon">⚖</div>
447
  <div>
448
+ <div class="jp-title">StepFun Flash Judge</div>
449
+ <div class="jp-sub">stepfun/step-3.5-flash:free · synthesized verdict</div>
450
  </div>
451
+ <div class="jp-lat" id="jLat">—</div>
452
  </div>
453
+
454
+ <div id="jBody" style="display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden">
455
+ <div class="jp-placeholder" id="jPlaceholder">
456
+ <div class="jp-ph-icon">⚖️</div>
457
+ <div class="jp-ph-txt">
458
+ The judge analyzes both model responses<br>
459
+ and returns the exact bug + corrected code.
460
  </div>
461
  </div>
462
  </div>
463
  </div>
464
+
465
  </div>
466
  </div>
467
 
468
+ <!-- ══ RIGHT 25%: PANEL MODELS ══ -->
469
+ <div class="col">
470
+ <div class="col-hdr"><div class="chd" style="background:var(--ink2)"></div>Debugger Models</div>
471
+ <div class="col-body">
472
+
473
+ <!-- Card 1: Trinity -->
474
+ <div class="model-card" id="c0" data-s="idle">
475
+ <div class="mc-hdr" onclick="toggleCard(0)">
476
+ <div class="mc-dot mcd1"></div>
477
+ <div class="mc-info">
478
+ <div class="mc-name mcn1">Trinity Large</div>
479
+ <div class="mc-id">arcee-ai/trinity-large-preview</div>
480
+ </div>
481
+ <div class="mc-lat" id="l0">—</div>
482
+ <div class="mc-chev">▾</div>
483
+ </div>
484
+ <div class="mc-body">
485
+ <div class="mc-empty"><div class="mc-eicon">🔵</div>Awaiting query…</div>
486
+ </div>
487
+ </div>
488
+
489
+ <!-- Card 2: Nemotron -->
490
+ <div class="model-card" id="c1" data-s="idle">
491
+ <div class="mc-hdr" onclick="toggleCard(1)">
492
+ <div class="mc-dot mcd2"></div>
493
+ <div class="mc-info">
494
+ <div class="mc-name mcn2">Nemotron Nano</div>
495
+ <div class="mc-id">nvidia/nemotron-3-nano-30b-a3b</div>
496
+ </div>
497
+ <div class="mc-lat" id="l1">—</div>
498
+ <div class="mc-chev">▾</div>
499
+ </div>
500
+ <div class="mc-body">
501
+ <div class="mc-empty"><div class="mc-eicon">🟡</div>Awaiting query…</div>
502
+ </div>
503
+ </div>
504
+
505
+ </div>
506
+ </div>
507
+
508
+ </div><!-- /.body -->
509
 
510
  <script>
511
  /* ── Config ── */
512
  const API = '';
513
+ marked.setOptions({breaks:true,gfm:true});
514
 
515
+ /* ── State ── */
516
+ let running = false;
517
+ let qCount = 0;
518
+ const coll = [false, false];
519
+ const hist = [];
520
+
521
+ /* ── Utils ── */
522
+ function esc(s){ return String(s??'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') }
523
 
524
+ function md(text){
525
+ if(!text) return '<em style="color:var(--ink3)">—</em>';
526
  return `<div class="md">${marked.parse(text)}</div>`;
527
  }
528
 
529
+ function hlCode(el){
530
+ el.querySelectorAll('pre code').forEach(b => hljs.highlightElement(b));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
531
  }
532
 
533
+ function setStatus(state, msg, time=''){
534
+ document.getElementById('sDot').className = `sdot ${state}`;
535
+ document.getElementById('sMsg').textContent = msg;
536
+ document.getElementById('sTime').textContent = time;
 
 
 
 
 
 
 
 
537
  }
538
 
539
+ function toggleCard(i){
540
+ coll[i] = !coll[i];
541
+ document.getElementById(`c${i}`).classList.toggle('coll', coll[i]);
542
  }
543
 
544
+ /* ── Panel card rendering ── */
545
+ function panelSkel(i){
546
+ document.getElementById(`c${i}`).dataset.s = 'loading';
547
+ document.getElementById(`l${i}`).textContent = '…';
548
+ document.querySelector(`#c${i} .mc-body`).innerHTML =
549
+ '<div class="skel"><div class="sk"></div><div class="sk"></div><div class="sk"></div><div class="sk"></div></div>';
 
 
 
550
  }
551
 
552
+ function panelResult(i, r){
553
+ const card = document.getElementById(`c${i}`);
554
+ const body = document.querySelector(`#c${i} .mc-body`);
555
+ document.getElementById(`l${i}`).textContent = `${r.latency_ms}ms`;
556
+ if(r.error){
557
+ card.dataset.s = 'er';
558
+ body.innerHTML = `<div class="mc-err">⚠ ${esc(r.error)}</div>`;
559
  return;
560
  }
561
+ card.dataset.s = `d${i+1}`;
562
+ body.innerHTML = `<div class="mc-inner fu">${md(r.response)}</div>`;
563
+ hlCode(body);
564
  }
565
 
566
  /* ── Judge rendering ── */
567
+ function judgeSkel(){
568
+ document.getElementById('jLat').textContent = '…';
569
+ document.getElementById('jPanel').className = 'judge-panel';
570
+ document.getElementById('jBody').innerHTML = `
571
+ <div class="jp-skel">
572
+ <div class="jsk" style="width:55%"></div><div class="jsk"></div>
573
+ <div class="jsk" style="width:78%"></div><div class="jsk" style="width:40%"></div>
574
+ <div class="jsk"></div><div class="jsk" style="width:68%"></div>
575
+ </div>`;
576
+ }
577
+
578
+ function judgeResult(r){
579
+ const jb = document.getElementById('jBody');
580
+ const jp = document.getElementById('jPanel');
581
+ document.getElementById('jLat').textContent = `${r.latency_ms}ms`;
582
+
583
+ if(r.error){
584
+ jp.className = 'judge-panel';
585
+ jb.innerHTML = `<div style="padding:16px 14px"><div class="mc-err">⚠ ${esc(r.error)}</div></div>`;
 
 
 
586
  return;
587
  }
588
 
589
+ jp.className = 'judge-panel done';
590
+
591
+ /* Extract raw code from corrected_code field (strip fences for display) */
592
+ let rawCode = r.corrected_code || '';
593
+ let lang = 'python';
594
+ const fenceMatch = rawCode.match(/^```(\w+)?\n?([\s\S]*?)```$/s);
595
+ if(fenceMatch){ lang = fenceMatch[1] || 'python'; rawCode = fenceMatch[2].trim(); }
596
+
597
+ const errHtml = r.error_analysis
598
+ ? `<div class="err-box">
599
+ <div class="err-box-hdr">🐛 Error Analysis</div>
600
+ <div class="err-box-body fu">${md(r.error_analysis)}</div>
601
+ </div>`
602
+ : '';
603
+
604
+ const codeHtml = rawCode
605
+ ? `<div class="code-box">
606
+ <div class="code-box-hdr">
607
+ ✅ Corrected Code
608
+ <button class="copy-code-btn" id="copyCodeBtn" onclick="copyCode()">copy</button>
609
+ </div>
610
+ <div class="code-box-body">
611
+ <pre><code id="correctedCode" class="language-${esc(lang)}">${esc(rawCode)}</code></pre>
612
+ </div>
613
+ </div>`
614
+ : `<div style="padding:14px;color:var(--ink2)">No corrected code returned.</div>`;
615
+
616
+ jb.innerHTML = errHtml + codeHtml;
617
+
618
+ /* highlight */
619
+ const codeEl = document.getElementById('correctedCode');
620
+ if(codeEl) hljs.highlightElement(codeEl);
621
+
622
+ /* highlight err-box md code blocks */
623
+ if(r.error_analysis) hlCode(jb);
624
+ }
625
+
626
+ function copyCode(){
627
+ const el = document.getElementById('correctedCode');
628
+ if(!el) return;
629
+ navigator.clipboard.writeText(el.textContent);
630
+ const btn = document.getElementById('copyCodeBtn');
631
+ if(btn){ btn.textContent = 'copied ✓'; setTimeout(()=>btn.textContent='copy', 1800); }
632
+ }
633
+
634
+ /* ── History ── */
635
+ function addHistory(q){
636
+ if(hist.includes(q)) return;
637
+ hist.unshift(q);
638
+ if(hist.length > 6) hist.pop();
639
+ renderHistory();
640
+ }
641
+
642
+ function renderHistory(){
643
+ const list = document.getElementById('histList');
644
+ const wrap = document.getElementById('histWrap');
645
+ if(!hist.length){ wrap.style.display='none'; return; }
646
+ wrap.style.display = '';
647
+ list.innerHTML = hist.map(q =>
648
+ `<div class="q-hist-item" onclick="fillQuery(this)" title="${esc(q)}">${esc(q)}</div>`
649
+ ).join('');
650
+ }
651
+
652
+ function fillQuery(el){
653
+ const ta = document.getElementById('qInput');
654
+ ta.value = el.title;
655
+ ta.style.height='auto';
656
+ ta.style.height = Math.min(ta.scrollHeight,340)+'px';
657
+ ta.focus();
658
+ }
659
+
660
+ /* ── Main ── */
661
+ async function runDebug(){
662
+ if(running) return;
663
+ const q = document.getElementById('qInput').value.trim();
664
+ const temp = parseFloat(document.getElementById('tSlider').value);
665
+ if(!q){ document.getElementById('qInput').focus(); return; }
666
 
667
  running = true;
668
  document.getElementById('runBtn').disabled = true;
669
  const t0 = Date.now();
670
 
671
+ setStatus('run','Querying Trinity Large + Nemotron Nano in parallel…');
672
+ panelSkel(0); panelSkel(1);
673
+ judgeSkel();
674
 
675
+ try{
676
+ const res = await fetch(`${API}/api/v1/debug`,{
677
+ method:'POST',
678
+ headers:{'Content-Type':'application/json'},
679
+ body:JSON.stringify({question:q, temperature:temp})
680
  });
681
 
682
+ if(!res.ok){
683
+ const e = await res.json().catch(()=>({}));
684
  throw new Error(e.detail || e.error || `HTTP ${res.status}`);
685
  }
686
 
687
  const data = await res.json();
688
+ const ms = Date.now()-t0;
689
+
690
+ data.panel.forEach((r,i)=>panelResult(i,r));
691
+ await new Promise(r=>setTimeout(r,150));
692
+ judgeResult(data.judge);
693
+
694
+ const ok = data.panel.filter(p=>!p.error).length;
695
+ setStatus('done',`Done — ${ok}/2 models + judge completed`, `${ms}ms`);
696
+
697
+ qCount++;
698
+ document.getElementById('qCnt').textContent = qCount;
699
+ addHistory(q);
700
+
701
+ }catch(err){
702
+ setStatus('err',`Error: ${err.message}`);
703
+ document.getElementById('jBody').innerHTML =
704
+ `<div style="padding:16px 14px"><div class="mc-err">⚠ ${esc(err.message)}</div></div>`;
705
+ [0,1].forEach(i=>{
706
+ document.getElementById(`c${i}`).dataset.s='er';
707
+ document.querySelector(`#c${i} .mc-body`).innerHTML='<div class="mc-err">Request failed</div>';
708
  });
709
  }
710
 
711
+ running=false;
712
+ document.getElementById('runBtn').disabled=false;
713
  }
714
 
715
+ /* ── textarea auto-resize ── */
716
+ const ta = document.getElementById('qInput');
717
+ ta.addEventListener('input',()=>{
718
+ ta.style.height='auto';
719
+ ta.style.height=Math.min(ta.scrollHeight,340)+'px';
720
  });
721
 
722
+ /* ── Ctrl+Enter ── */
723
+ ta.addEventListener('keydown',e=>{
724
+ if((e.ctrlKey||e.metaKey)&&e.key==='Enter'){e.preventDefault();runDebug();}
725
  });
726
  </script>
727
  </body>