flow-debugger 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/PORTFOLIO_README_SECTION.md +177 -0
  2. package/README.md +251 -0
  3. package/dashboard/app.js +339 -0
  4. package/dashboard/index.html +168 -0
  5. package/dashboard/style.css +846 -0
  6. package/dist/cjs/core/Analytics.js +174 -0
  7. package/dist/cjs/core/Analytics.js.map +1 -0
  8. package/dist/cjs/core/Classifier.js +66 -0
  9. package/dist/cjs/core/Classifier.js.map +1 -0
  10. package/dist/cjs/core/HealthMonitor.js +79 -0
  11. package/dist/cjs/core/HealthMonitor.js.map +1 -0
  12. package/dist/cjs/core/RootCause.js +89 -0
  13. package/dist/cjs/core/RootCause.js.map +1 -0
  14. package/dist/cjs/core/Sampler.js +34 -0
  15. package/dist/cjs/core/Sampler.js.map +1 -0
  16. package/dist/cjs/core/Timeline.js +90 -0
  17. package/dist/cjs/core/Timeline.js.map +1 -0
  18. package/dist/cjs/core/TraceEngine.js +222 -0
  19. package/dist/cjs/core/TraceEngine.js.map +1 -0
  20. package/dist/cjs/core/types.js +21 -0
  21. package/dist/cjs/core/types.js.map +1 -0
  22. package/dist/cjs/index.js +46 -0
  23. package/dist/cjs/index.js.map +1 -0
  24. package/dist/cjs/integrations/axios.js +136 -0
  25. package/dist/cjs/integrations/axios.js.map +1 -0
  26. package/dist/cjs/integrations/fetch.js +153 -0
  27. package/dist/cjs/integrations/fetch.js.map +1 -0
  28. package/dist/cjs/integrations/mongo.js +111 -0
  29. package/dist/cjs/integrations/mongo.js.map +1 -0
  30. package/dist/cjs/integrations/mysql.js +212 -0
  31. package/dist/cjs/integrations/mysql.js.map +1 -0
  32. package/dist/cjs/integrations/postgres.js +182 -0
  33. package/dist/cjs/integrations/postgres.js.map +1 -0
  34. package/dist/cjs/integrations/redis.js +105 -0
  35. package/dist/cjs/integrations/redis.js.map +1 -0
  36. package/dist/cjs/middleware/express.js +255 -0
  37. package/dist/cjs/middleware/express.js.map +1 -0
  38. package/dist/esm/core/Analytics.js +170 -0
  39. package/dist/esm/core/Analytics.js.map +1 -0
  40. package/dist/esm/core/Classifier.js +61 -0
  41. package/dist/esm/core/Classifier.js.map +1 -0
  42. package/dist/esm/core/HealthMonitor.js +75 -0
  43. package/dist/esm/core/HealthMonitor.js.map +1 -0
  44. package/dist/esm/core/RootCause.js +86 -0
  45. package/dist/esm/core/RootCause.js.map +1 -0
  46. package/dist/esm/core/Sampler.js +30 -0
  47. package/dist/esm/core/Sampler.js.map +1 -0
  48. package/dist/esm/core/Timeline.js +86 -0
  49. package/dist/esm/core/Timeline.js.map +1 -0
  50. package/dist/esm/core/TraceEngine.js +217 -0
  51. package/dist/esm/core/TraceEngine.js.map +1 -0
  52. package/dist/esm/core/types.js +18 -0
  53. package/dist/esm/core/types.js.map +1 -0
  54. package/dist/esm/index.js +22 -0
  55. package/dist/esm/index.js.map +1 -0
  56. package/dist/esm/integrations/axios.js +133 -0
  57. package/dist/esm/integrations/axios.js.map +1 -0
  58. package/dist/esm/integrations/fetch.js +149 -0
  59. package/dist/esm/integrations/fetch.js.map +1 -0
  60. package/dist/esm/integrations/mongo.js +107 -0
  61. package/dist/esm/integrations/mongo.js.map +1 -0
  62. package/dist/esm/integrations/mysql.js +209 -0
  63. package/dist/esm/integrations/mysql.js.map +1 -0
  64. package/dist/esm/integrations/postgres.js +179 -0
  65. package/dist/esm/integrations/postgres.js.map +1 -0
  66. package/dist/esm/integrations/redis.js +102 -0
  67. package/dist/esm/integrations/redis.js.map +1 -0
  68. package/dist/esm/middleware/express.js +219 -0
  69. package/dist/esm/middleware/express.js.map +1 -0
  70. package/dist/types/core/Analytics.d.ts +35 -0
  71. package/dist/types/core/Analytics.d.ts.map +1 -0
  72. package/dist/types/core/Classifier.d.ts +21 -0
  73. package/dist/types/core/Classifier.d.ts.map +1 -0
  74. package/dist/types/core/HealthMonitor.d.ts +14 -0
  75. package/dist/types/core/HealthMonitor.d.ts.map +1 -0
  76. package/dist/types/core/RootCause.d.ts +12 -0
  77. package/dist/types/core/RootCause.d.ts.map +1 -0
  78. package/dist/types/core/Sampler.d.ts +13 -0
  79. package/dist/types/core/Sampler.d.ts.map +1 -0
  80. package/dist/types/core/Timeline.d.ts +22 -0
  81. package/dist/types/core/Timeline.d.ts.map +1 -0
  82. package/dist/types/core/TraceEngine.d.ts +47 -0
  83. package/dist/types/core/TraceEngine.d.ts.map +1 -0
  84. package/dist/types/core/types.d.ts +118 -0
  85. package/dist/types/core/types.d.ts.map +1 -0
  86. package/dist/types/index.d.ts +18 -0
  87. package/dist/types/index.d.ts.map +1 -0
  88. package/dist/types/integrations/axios.d.ts +22 -0
  89. package/dist/types/integrations/axios.d.ts.map +1 -0
  90. package/dist/types/integrations/fetch.d.ts +25 -0
  91. package/dist/types/integrations/fetch.d.ts.map +1 -0
  92. package/dist/types/integrations/mongo.d.ts +26 -0
  93. package/dist/types/integrations/mongo.d.ts.map +1 -0
  94. package/dist/types/integrations/mysql.d.ts +20 -0
  95. package/dist/types/integrations/mysql.d.ts.map +1 -0
  96. package/dist/types/integrations/postgres.d.ts +20 -0
  97. package/dist/types/integrations/postgres.d.ts.map +1 -0
  98. package/dist/types/integrations/redis.d.ts +20 -0
  99. package/dist/types/integrations/redis.d.ts.map +1 -0
  100. package/dist/types/middleware/express.d.ts +39 -0
  101. package/dist/types/middleware/express.d.ts.map +1 -0
  102. package/example/server.ts +234 -0
  103. package/jest.config.js +8 -0
  104. package/package.json +110 -0
  105. package/portfolio-repo/APIRESPONSE DASH.png +0 -0
  106. package/portfolio-repo/PAYLOAD.png +0 -0
  107. package/portfolio-repo/README.md +182 -0
  108. package/src/core/Analytics.ts +209 -0
  109. package/src/core/Classifier.ts +82 -0
  110. package/src/core/HealthMonitor.ts +92 -0
  111. package/src/core/RootCause.ts +105 -0
  112. package/src/core/Sampler.ts +35 -0
  113. package/src/core/Timeline.ts +108 -0
  114. package/src/core/TraceEngine.ts +266 -0
  115. package/src/core/types.ts +170 -0
  116. package/src/index.ts +42 -0
  117. package/src/integrations/axios.ts +164 -0
  118. package/src/integrations/fetch.ts +172 -0
  119. package/src/integrations/mongo.ts +130 -0
  120. package/src/integrations/mysql.ts +239 -0
  121. package/src/integrations/postgres.ts +217 -0
  122. package/src/integrations/redis.ts +122 -0
  123. package/src/middleware/express.ts +264 -0
  124. package/tests/Analytics.test.ts +136 -0
  125. package/tests/Classifier.test.ts +57 -0
  126. package/tests/RootCause.test.ts +69 -0
  127. package/tests/TraceEngine.test.ts +110 -0
  128. package/tsconfig.cjs.json +9 -0
  129. package/tsconfig.esm.json +9 -0
  130. package/tsconfig.json +31 -0
  131. package/tsconfig.types.json +8 -0
@@ -0,0 +1,846 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ flow-debugger — Dashboard Styles
3
+ Premium dark theme with glassmorphism & micro-animations
4
+ ───────────────────────────────────────────────────────────── */
5
+
6
+ :root {
7
+ --bg-primary: #06060f;
8
+ --bg-secondary: #0d0d1a;
9
+ --bg-card: rgba(16, 16, 32, 0.85);
10
+ --bg-card-hover: rgba(22, 22, 44, 0.95);
11
+ --border: rgba(124, 58, 237, 0.15);
12
+ --border-hover: rgba(124, 58, 237, 0.35);
13
+ --text-primary: #f0f0f5;
14
+ --text-secondary: #9090b0;
15
+ --text-dim: #606080;
16
+ --accent: #7c3aed;
17
+ --accent-light: #a78bfa;
18
+ --accent-glow: rgba(124, 58, 237, 0.2);
19
+ --green: #22c55e;
20
+ --green-bg: rgba(34, 197, 94, 0.1);
21
+ --red: #ef4444;
22
+ --red-bg: rgba(239, 68, 68, 0.1);
23
+ --yellow: #f59e0b;
24
+ --yellow-bg: rgba(245, 158, 11, 0.1);
25
+ --blue: #3b82f6;
26
+ --blue-bg: rgba(59, 130, 246, 0.1);
27
+ --cyan: #06b6d4;
28
+ --magenta: #d946ef;
29
+ --font: 'Inter', system-ui, -apple-system, sans-serif;
30
+ --mono: 'JetBrains Mono', 'Fira Code', monospace;
31
+ --radius: 14px;
32
+ --radius-sm: 8px;
33
+ --shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
34
+ --shadow-glow: 0 0 40px rgba(124, 58, 237, 0.06);
35
+ }
36
+
37
+ * {
38
+ margin: 0;
39
+ padding: 0;
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ body {
44
+ font-family: var(--font);
45
+ background: var(--bg-primary);
46
+ color: var(--text-primary);
47
+ min-height: 100vh;
48
+ padding: 0 24px 40px;
49
+ overflow-x: hidden;
50
+ }
51
+
52
+ body::before {
53
+ content: '';
54
+ position: fixed;
55
+ top: 0;
56
+ left: 0;
57
+ right: 0;
58
+ height: 400px;
59
+ background: radial-gradient(ellipse at 50% 0%, rgba(124, 58, 237, 0.08), transparent 70%);
60
+ pointer-events: none;
61
+ z-index: 0;
62
+ }
63
+
64
+ /* ─── Header ───── */
65
+ .header {
66
+ position: sticky;
67
+ top: 0;
68
+ z-index: 100;
69
+ display: flex;
70
+ justify-content: space-between;
71
+ align-items: center;
72
+ padding: 16px 0;
73
+ margin-bottom: 24px;
74
+ border-bottom: 1px solid var(--border);
75
+ backdrop-filter: blur(20px);
76
+ -webkit-backdrop-filter: blur(20px);
77
+ background: rgba(6, 6, 15, 0.8);
78
+ }
79
+
80
+ .header-left {
81
+ display: flex;
82
+ align-items: center;
83
+ gap: 16px;
84
+ }
85
+
86
+ .header-right {
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 12px;
90
+ }
91
+
92
+ .logo {
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 10px;
96
+ }
97
+
98
+ .logo-icon {
99
+ font-size: 28px;
100
+ filter: drop-shadow(0 0 8px var(--accent-glow));
101
+ }
102
+
103
+ .logo h1 {
104
+ font-size: 22px;
105
+ font-weight: 700;
106
+ background: linear-gradient(135deg, var(--accent-light), var(--cyan));
107
+ -webkit-background-clip: text;
108
+ -webkit-text-fill-color: transparent;
109
+ background-clip: text;
110
+ letter-spacing: -0.5px;
111
+ }
112
+
113
+ .badge {
114
+ padding: 4px 12px;
115
+ border-radius: 20px;
116
+ font-size: 11px;
117
+ font-weight: 600;
118
+ letter-spacing: 0.5px;
119
+ text-transform: uppercase;
120
+ }
121
+
122
+ .badge-live {
123
+ background: var(--green-bg);
124
+ color: var(--green);
125
+ border: 1px solid rgba(34, 197, 94, 0.25);
126
+ animation: pulse-live 2s ease-in-out infinite;
127
+ }
128
+
129
+ @keyframes pulse-live {
130
+
131
+ 0%,
132
+ 100% {
133
+ opacity: 1;
134
+ }
135
+
136
+ 50% {
137
+ opacity: 0.6;
138
+ }
139
+ }
140
+
141
+ .uptime {
142
+ font-family: var(--mono);
143
+ font-size: 12px;
144
+ color: var(--text-dim);
145
+ }
146
+
147
+ .btn {
148
+ padding: 8px 16px;
149
+ border-radius: var(--radius-sm);
150
+ border: none;
151
+ font-family: var(--font);
152
+ font-size: 13px;
153
+ font-weight: 500;
154
+ cursor: pointer;
155
+ transition: all 0.2s ease;
156
+ }
157
+
158
+ .btn-ghost {
159
+ background: rgba(124, 58, 237, 0.08);
160
+ color: var(--accent-light);
161
+ border: 1px solid var(--border);
162
+ }
163
+
164
+ .btn-ghost:hover {
165
+ background: rgba(255, 255, 255, 0.1);
166
+ border-color: var(--border-hover);
167
+ transform: translateY(-1px);
168
+ }
169
+
170
+ .btn-primary {
171
+ padding: 8px 16px;
172
+ background: linear-gradient(135deg, var(--purple), var(--blue));
173
+ border: none;
174
+ border-radius: 8px;
175
+ color: white;
176
+ font-size: 13px;
177
+ font-weight: 500;
178
+ cursor: pointer;
179
+ transition: all 0.2s;
180
+ }
181
+
182
+ .btn-primary:hover {
183
+ transform: translateY(-1px);
184
+ box-shadow: 0 4px 12px rgba(124, 58, 237, 0.4);
185
+ }
186
+
187
+ /* ─── Stats Row ───── */
188
+ .stats-row {
189
+ display: grid;
190
+ grid-template-columns: repeat(4, 1fr);
191
+ gap: 16px;
192
+ margin-bottom: 24px;
193
+ }
194
+
195
+ .stat-card {
196
+ background: var(--bg-card);
197
+ border: 1px solid var(--border);
198
+ border-radius: var(--radius);
199
+ padding: 20px;
200
+ display: flex;
201
+ align-items: center;
202
+ gap: 16px;
203
+ transition: all 0.3s ease;
204
+ backdrop-filter: blur(10px);
205
+ box-shadow: var(--shadow-glow);
206
+ }
207
+
208
+ .stat-card:hover {
209
+ background: var(--bg-card-hover);
210
+ border-color: var(--border-hover);
211
+ transform: translateY(-2px);
212
+ box-shadow: var(--shadow);
213
+ }
214
+
215
+ .stat-icon {
216
+ width: 48px;
217
+ height: 48px;
218
+ border-radius: 12px;
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ font-size: 22px;
223
+ }
224
+
225
+ .stat-icon-blue {
226
+ background: var(--blue-bg);
227
+ }
228
+
229
+ .stat-icon-red {
230
+ background: var(--red-bg);
231
+ }
232
+
233
+ .stat-icon-yellow {
234
+ background: var(--yellow-bg);
235
+ }
236
+
237
+ .stat-icon-green {
238
+ background: var(--green-bg);
239
+ }
240
+
241
+ .stat-content {
242
+ display: flex;
243
+ flex-direction: column;
244
+ gap: 2px;
245
+ }
246
+
247
+ .stat-label {
248
+ font-size: 12px;
249
+ color: var(--text-secondary);
250
+ font-weight: 500;
251
+ text-transform: uppercase;
252
+ letter-spacing: 0.5px;
253
+ }
254
+
255
+ .stat-value {
256
+ font-size: 28px;
257
+ font-weight: 800;
258
+ font-family: var(--mono);
259
+ letter-spacing: -1px;
260
+ }
261
+
262
+ .stat-error {
263
+ color: var(--red);
264
+ }
265
+
266
+ .stat-warn {
267
+ color: var(--yellow);
268
+ }
269
+
270
+ .stat-success {
271
+ color: var(--green);
272
+ }
273
+
274
+ /* ─── Cards ───── */
275
+ .main-grid {
276
+ display: grid;
277
+ grid-template-columns: 1fr 1fr;
278
+ gap: 16px;
279
+ margin-bottom: 16px;
280
+ }
281
+
282
+ .card {
283
+ background: var(--bg-card);
284
+ border: 1px solid var(--border);
285
+ border-radius: var(--radius);
286
+ overflow: hidden;
287
+ backdrop-filter: blur(10px);
288
+ box-shadow: var(--shadow-glow);
289
+ transition: border-color 0.3s ease;
290
+ }
291
+
292
+ .card:hover {
293
+ border-color: var(--border-hover);
294
+ }
295
+
296
+ .card.full-width {
297
+ margin-bottom: 16px;
298
+ }
299
+
300
+ .card-header {
301
+ padding: 16px 20px;
302
+ border-bottom: 1px solid var(--border);
303
+ background: rgba(124, 58, 237, 0.03);
304
+ }
305
+
306
+ .card-header h2 {
307
+ font-size: 14px;
308
+ font-weight: 600;
309
+ color: var(--text-primary);
310
+ letter-spacing: 0.2px;
311
+ }
312
+
313
+ .card-body {
314
+ padding: 20px;
315
+ }
316
+
317
+ .empty-state {
318
+ text-align: center;
319
+ color: var(--text-dim);
320
+ font-size: 13px;
321
+ padding: 30px 0;
322
+ }
323
+
324
+ /* ─── Health Pills ───── */
325
+ .health-grid {
326
+ display: flex;
327
+ flex-wrap: wrap;
328
+ gap: 10px;
329
+ }
330
+
331
+ .health-pill {
332
+ display: flex;
333
+ align-items: center;
334
+ gap: 8px;
335
+ padding: 10px 16px;
336
+ border-radius: 10px;
337
+ background: rgba(255, 255, 255, 0.03);
338
+ border: 1px solid var(--border);
339
+ transition: all 0.2s ease;
340
+ }
341
+
342
+ .health-pill:hover {
343
+ transform: translateY(-1px);
344
+ }
345
+
346
+ .health-dot {
347
+ width: 10px;
348
+ height: 10px;
349
+ border-radius: 50%;
350
+ flex-shrink: 0;
351
+ }
352
+
353
+ .health-dot.healthy {
354
+ background: var(--green);
355
+ box-shadow: 0 0 8px rgba(34, 197, 94, 0.4);
356
+ }
357
+
358
+ .health-dot.degraded {
359
+ background: var(--yellow);
360
+ box-shadow: 0 0 8px rgba(245, 158, 11, 0.4);
361
+ }
362
+
363
+ .health-dot.down {
364
+ background: var(--red);
365
+ box-shadow: 0 0 8px rgba(239, 68, 68, 0.4);
366
+ animation: pulse-down 1s ease-in-out infinite;
367
+ }
368
+
369
+ @keyframes pulse-down {
370
+
371
+ 0%,
372
+ 100% {
373
+ opacity: 1;
374
+ box-shadow: 0 0 8px rgba(239, 68, 68, 0.4);
375
+ }
376
+
377
+ 50% {
378
+ opacity: 0.5;
379
+ box-shadow: 0 0 16px rgba(239, 68, 68, 0.6);
380
+ }
381
+ }
382
+
383
+ .health-info {
384
+ display: flex;
385
+ flex-direction: column;
386
+ gap: 1px;
387
+ }
388
+
389
+ .health-name {
390
+ font-size: 13px;
391
+ font-weight: 600;
392
+ text-transform: capitalize;
393
+ }
394
+
395
+ .health-rate {
396
+ font-size: 11px;
397
+ font-family: var(--mono);
398
+ color: var(--text-dim);
399
+ }
400
+
401
+ /* ─── Failure Bars ───── */
402
+ .failure-bar-group {
403
+ display: flex;
404
+ flex-direction: column;
405
+ gap: 12px;
406
+ }
407
+
408
+ .failure-item {
409
+ display: flex;
410
+ align-items: center;
411
+ gap: 12px;
412
+ }
413
+
414
+ .failure-label {
415
+ width: 80px;
416
+ font-size: 12px;
417
+ font-weight: 600;
418
+ text-transform: capitalize;
419
+ color: var(--text-secondary);
420
+ }
421
+
422
+ .failure-bar-bg {
423
+ flex: 1;
424
+ height: 24px;
425
+ background: rgba(255, 255, 255, 0.03);
426
+ border-radius: 6px;
427
+ overflow: hidden;
428
+ }
429
+
430
+ .failure-bar {
431
+ height: 100%;
432
+ border-radius: 6px;
433
+ background: linear-gradient(90deg, var(--accent), var(--magenta));
434
+ transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
435
+ min-width: 2px;
436
+ }
437
+
438
+ .failure-pct {
439
+ width: 45px;
440
+ font-size: 13px;
441
+ font-family: var(--mono);
442
+ font-weight: 600;
443
+ color: var(--text-primary);
444
+ text-align: right;
445
+ }
446
+
447
+ /* ─── Table ───── */
448
+ .table-container {
449
+ overflow-x: auto;
450
+ }
451
+
452
+ .data-table {
453
+ width: 100%;
454
+ border-collapse: collapse;
455
+ font-size: 13px;
456
+ }
457
+
458
+ .data-table th {
459
+ text-align: left;
460
+ padding: 10px 14px;
461
+ font-size: 11px;
462
+ font-weight: 600;
463
+ text-transform: uppercase;
464
+ letter-spacing: 0.5px;
465
+ color: var(--text-dim);
466
+ border-bottom: 1px solid var(--border);
467
+ white-space: nowrap;
468
+ }
469
+
470
+ .data-table td {
471
+ padding: 12px 14px;
472
+ border-bottom: 1px solid rgba(255, 255, 255, 0.03);
473
+ vertical-align: top;
474
+ }
475
+
476
+ .data-table tbody tr {
477
+ transition: background 0.15s ease;
478
+ }
479
+
480
+ .data-table tbody tr:hover {
481
+ background: rgba(124, 58, 237, 0.04);
482
+ }
483
+
484
+ .method-badge {
485
+ display: inline-block;
486
+ padding: 2px 8px;
487
+ border-radius: 4px;
488
+ font-size: 11px;
489
+ font-weight: 700;
490
+ font-family: var(--mono);
491
+ letter-spacing: 0.3px;
492
+ }
493
+
494
+ .method-GET {
495
+ background: var(--green-bg);
496
+ color: var(--green);
497
+ }
498
+
499
+ .method-POST {
500
+ background: var(--blue-bg);
501
+ color: var(--blue);
502
+ }
503
+
504
+ .method-PUT {
505
+ background: var(--yellow-bg);
506
+ color: var(--yellow);
507
+ }
508
+
509
+ .method-DELETE {
510
+ background: var(--red-bg);
511
+ color: var(--red);
512
+ }
513
+
514
+ .method-PATCH {
515
+ background: rgba(139, 92, 246, 0.1);
516
+ color: var(--accent-light);
517
+ }
518
+
519
+ .endpoint-path {
520
+ font-family: var(--mono);
521
+ font-size: 12px;
522
+ color: var(--text-primary);
523
+ }
524
+
525
+ .issue-tag {
526
+ display: inline-block;
527
+ padding: 2px 8px;
528
+ border-radius: 4px;
529
+ font-size: 10px;
530
+ font-weight: 500;
531
+ margin: 2px 2px;
532
+ background: var(--red-bg);
533
+ color: var(--red);
534
+ border: 1px solid rgba(239, 68, 68, 0.15);
535
+ }
536
+
537
+ .td-mono {
538
+ font-family: var(--mono);
539
+ font-size: 12px;
540
+ }
541
+
542
+ /* ─── Slow Queries ───── */
543
+ .slow-query-list {
544
+ display: flex;
545
+ flex-direction: column;
546
+ gap: 8px;
547
+ }
548
+
549
+ .slow-query-item {
550
+ display: flex;
551
+ align-items: center;
552
+ gap: 12px;
553
+ padding: 12px 16px;
554
+ background: var(--yellow-bg);
555
+ border: 1px solid rgba(245, 158, 11, 0.15);
556
+ border-radius: var(--radius-sm);
557
+ font-family: var(--mono);
558
+ font-size: 12px;
559
+ }
560
+
561
+ .slow-query-icon {
562
+ color: var(--yellow);
563
+ font-size: 16px;
564
+ flex-shrink: 0;
565
+ }
566
+
567
+ .slow-query-sql {
568
+ flex: 1;
569
+ color: var(--text-primary);
570
+ word-break: break-all;
571
+ }
572
+
573
+ .slow-query-dur {
574
+ font-weight: 700;
575
+ color: var(--yellow);
576
+ white-space: nowrap;
577
+ }
578
+
579
+ /* ─── Traces ───── */
580
+ .trace-list {
581
+ display: flex;
582
+ flex-direction: column;
583
+ gap: 12px;
584
+ }
585
+
586
+ .trace-item {
587
+ background: rgba(255, 255, 255, 0.02);
588
+ border: 1px solid var(--border);
589
+ border-radius: var(--radius-sm);
590
+ overflow: hidden;
591
+ transition: border-color 0.2s ease;
592
+ }
593
+
594
+ .trace-item:hover {
595
+ border-color: var(--border-hover);
596
+ }
597
+
598
+ .trace-header {
599
+ display: flex;
600
+ justify-content: space-between;
601
+ align-items: center;
602
+ padding: 12px 16px;
603
+ cursor: pointer;
604
+ user-select: none;
605
+ }
606
+
607
+ .trace-header:hover {
608
+ background: rgba(124, 58, 237, 0.04);
609
+ }
610
+
611
+ .trace-header-left {
612
+ display: flex;
613
+ align-items: center;
614
+ gap: 10px;
615
+ }
616
+
617
+ .trace-header-right {
618
+ display: flex;
619
+ align-items: center;
620
+ gap: 12px;
621
+ }
622
+
623
+ .trace-id {
624
+ font-family: var(--mono);
625
+ font-size: 11px;
626
+ color: var(--text-dim);
627
+ }
628
+
629
+ .trace-endpoint {
630
+ font-family: var(--mono);
631
+ font-size: 13px;
632
+ font-weight: 500;
633
+ }
634
+
635
+ .trace-dur {
636
+ font-family: var(--mono);
637
+ font-size: 12px;
638
+ font-weight: 600;
639
+ }
640
+
641
+ .classification-badge {
642
+ padding: 2px 8px;
643
+ border-radius: 4px;
644
+ font-size: 10px;
645
+ font-weight: 700;
646
+ letter-spacing: 0.5px;
647
+ }
648
+
649
+ .classification-INFO {
650
+ background: var(--green-bg);
651
+ color: var(--green);
652
+ }
653
+
654
+ .classification-WARN {
655
+ background: var(--yellow-bg);
656
+ color: var(--yellow);
657
+ }
658
+
659
+ .classification-ERROR {
660
+ background: var(--red-bg);
661
+ color: var(--red);
662
+ }
663
+
664
+ .classification-CRITICAL {
665
+ background: rgba(217, 70, 239, 0.1);
666
+ color: var(--magenta);
667
+ }
668
+
669
+ .trace-steps {
670
+ display: none;
671
+ padding: 0 16px 16px;
672
+ }
673
+
674
+ .trace-steps.open {
675
+ display: block;
676
+ }
677
+
678
+ .step-timeline {
679
+ position: relative;
680
+ padding-left: 20px;
681
+ }
682
+
683
+ .step-timeline::before {
684
+ content: '';
685
+ position: absolute;
686
+ left: 6px;
687
+ top: 0;
688
+ bottom: 0;
689
+ width: 2px;
690
+ background: var(--border);
691
+ }
692
+
693
+ .step-item {
694
+ position: relative;
695
+ padding: 6px 0 6px 16px;
696
+ font-family: var(--mono);
697
+ font-size: 12px;
698
+ display: flex;
699
+ align-items: center;
700
+ gap: 8px;
701
+ }
702
+
703
+ .step-item::before {
704
+ content: '';
705
+ position: absolute;
706
+ left: -17px;
707
+ top: 50%;
708
+ transform: translateY(-50%);
709
+ width: 8px;
710
+ height: 8px;
711
+ border-radius: 50%;
712
+ background: var(--accent);
713
+ border: 2px solid var(--bg-primary);
714
+ }
715
+
716
+ .step-item.step-error::before {
717
+ background: var(--red);
718
+ }
719
+
720
+ .step-item.step-timeout::before {
721
+ background: var(--magenta);
722
+ }
723
+
724
+ .step-item.step-slow::before {
725
+ background: var(--yellow);
726
+ }
727
+
728
+ .step-offset {
729
+ color: var(--text-dim);
730
+ min-width: 60px;
731
+ }
732
+
733
+ .step-name {
734
+ color: var(--text-primary);
735
+ flex: 1;
736
+ }
737
+
738
+ .step-status-icon {
739
+ flex-shrink: 0;
740
+ }
741
+
742
+ .step-dur {
743
+ color: var(--text-secondary);
744
+ min-width: 60px;
745
+ text-align: right;
746
+ }
747
+
748
+ .step-service-tag {
749
+ padding: 1px 6px;
750
+ border-radius: 3px;
751
+ font-size: 10px;
752
+ background: rgba(124, 58, 237, 0.08);
753
+ color: var(--accent-light);
754
+ }
755
+
756
+ .root-cause-box {
757
+ margin-top: 10px;
758
+ padding: 10px 14px;
759
+ background: var(--red-bg);
760
+ border: 1px solid rgba(239, 68, 68, 0.2);
761
+ border-radius: var(--radius-sm);
762
+ font-size: 12px;
763
+ }
764
+
765
+ .root-cause-box .rc-label {
766
+ font-weight: 600;
767
+ color: var(--red);
768
+ margin-bottom: 4px;
769
+ }
770
+
771
+ .root-cause-box .rc-detail {
772
+ font-family: var(--mono);
773
+ color: var(--text-secondary);
774
+ font-size: 11px;
775
+ }
776
+
777
+ /* ─── Footer ───── */
778
+ .footer {
779
+ text-align: center;
780
+ padding: 24px 0;
781
+ color: var(--text-dim);
782
+ font-size: 12px;
783
+ border-top: 1px solid var(--border);
784
+ margin-top: 24px;
785
+ }
786
+
787
+ /* ─── Responsive ───── */
788
+ @media (max-width: 900px) {
789
+ .stats-row {
790
+ grid-template-columns: repeat(2, 1fr);
791
+ }
792
+
793
+ .main-grid {
794
+ grid-template-columns: 1fr;
795
+ }
796
+ }
797
+
798
+ @media (max-width: 600px) {
799
+ body {
800
+ padding: 0 12px 24px;
801
+ }
802
+
803
+ .stats-row {
804
+ grid-template-columns: 1fr;
805
+ }
806
+
807
+ .header {
808
+ flex-direction: column;
809
+ gap: 12px;
810
+ }
811
+
812
+ .stat-value {
813
+ font-size: 22px;
814
+ }
815
+ }
816
+
817
+ /* ─── Animations ───── */
818
+ @keyframes fadeIn {
819
+ from {
820
+ opacity: 0;
821
+ transform: translateY(8px);
822
+ }
823
+
824
+ to {
825
+ opacity: 1;
826
+ transform: translateY(0);
827
+ }
828
+ }
829
+
830
+ .stat-card,
831
+ .card,
832
+ .trace-item {
833
+ animation: fadeIn 0.4s ease forwards;
834
+ }
835
+
836
+ .stat-card:nth-child(2) {
837
+ animation-delay: 0.05s;
838
+ }
839
+
840
+ .stat-card:nth-child(3) {
841
+ animation-delay: 0.1s;
842
+ }
843
+
844
+ .stat-card:nth-child(4) {
845
+ animation-delay: 0.15s;
846
+ }