flare 0.1.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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +148 -0
  5. data/app/controllers/flare/application_controller.rb +22 -0
  6. data/app/controllers/flare/jobs_controller.rb +55 -0
  7. data/app/controllers/flare/requests_controller.rb +73 -0
  8. data/app/controllers/flare/spans_controller.rb +101 -0
  9. data/app/helpers/flare/application_helper.rb +168 -0
  10. data/app/views/flare/jobs/index.html.erb +69 -0
  11. data/app/views/flare/jobs/show.html.erb +323 -0
  12. data/app/views/flare/requests/index.html.erb +120 -0
  13. data/app/views/flare/requests/show.html.erb +498 -0
  14. data/app/views/flare/spans/index.html.erb +112 -0
  15. data/app/views/flare/spans/show.html.erb +184 -0
  16. data/app/views/layouts/flare/application.html.erb +126 -0
  17. data/config/routes.rb +20 -0
  18. data/exe/flare +9 -0
  19. data/lib/flare/backoff_policy.rb +73 -0
  20. data/lib/flare/cli/doctor_command.rb +129 -0
  21. data/lib/flare/cli/output.rb +45 -0
  22. data/lib/flare/cli/setup_command.rb +404 -0
  23. data/lib/flare/cli/status_command.rb +47 -0
  24. data/lib/flare/cli.rb +50 -0
  25. data/lib/flare/configuration.rb +121 -0
  26. data/lib/flare/engine.rb +43 -0
  27. data/lib/flare/http_metrics_config.rb +101 -0
  28. data/lib/flare/metric_counter.rb +45 -0
  29. data/lib/flare/metric_flusher.rb +124 -0
  30. data/lib/flare/metric_key.rb +42 -0
  31. data/lib/flare/metric_span_processor.rb +470 -0
  32. data/lib/flare/metric_storage.rb +42 -0
  33. data/lib/flare/metric_submitter.rb +221 -0
  34. data/lib/flare/source_location.rb +113 -0
  35. data/lib/flare/sqlite_exporter.rb +279 -0
  36. data/lib/flare/storage/sqlite.rb +789 -0
  37. data/lib/flare/storage.rb +54 -0
  38. data/lib/flare/version.rb +5 -0
  39. data/lib/flare.rb +411 -0
  40. data/public/flare-assets/flare.css +1245 -0
  41. data/public/flare-assets/images/flipper.png +0 -0
  42. metadata +240 -0
@@ -0,0 +1,1245 @@
1
+ /* ==========================================================================
2
+ Flare Design System
3
+ Clean monochrome analytics dashboard for the Flare Rails engine.
4
+ System fonts, Bootstrap Icons, border-based design.
5
+ ========================================================================== */
6
+
7
+ /* --- CSS Variables --- */
8
+ :root {
9
+ --cb-bg: #ffffff;
10
+ --cb-bg-subtle: #fafafa;
11
+ --cb-bg-muted: #f0f0f0;
12
+ --cb-border: #e5e5e5;
13
+ --cb-border-hover: #ccc;
14
+ --cb-text-faint: #bbb;
15
+ --cb-text-muted: #999;
16
+ --cb-text-secondary: #666;
17
+ --cb-text: #1a1a1a;
18
+ --cb-text-strong: #000;
19
+ --cb-success: #2b7a3e;
20
+ --cb-info: #6B8A9A;
21
+ --cb-warning: #C4A35A;
22
+ --cb-danger: #c53030;
23
+ --cb-radius: 0.5rem;
24
+ --cb-radius-sm: 0.375rem;
25
+ --cb-radius-lg: 0.5rem;
26
+ --cb-shadow: none;
27
+ --cb-shadow-sm: none;
28
+ --cb-shadow-lg: 0 4px 24px rgba(0, 0, 0, 0.08);
29
+ --cb-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
30
+ --cb-font-mono: ui-monospace, 'SF Mono', Monaco, monospace;
31
+ --cb-sidebar-width: 220px;
32
+ --cb-sidebar-collapsed: 64px;
33
+ }
34
+
35
+ /* --- Reset & Base --- */
36
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
37
+
38
+ body {
39
+ font-family: var(--cb-font);
40
+ font-size: 0.875rem;
41
+ color: var(--cb-text);
42
+ background: var(--cb-bg);
43
+ line-height: 1.5;
44
+ min-height: 100vh;
45
+ -webkit-font-smoothing: antialiased;
46
+ -moz-osx-font-smoothing: grayscale;
47
+ }
48
+
49
+ a {
50
+ color: var(--cb-text-secondary);
51
+ text-decoration: none;
52
+ transition: color 0.15s ease;
53
+ }
54
+ a:hover { color: var(--cb-text); }
55
+
56
+ code, pre {
57
+ font-family: var(--cb-font-mono);
58
+ }
59
+
60
+ /* --- App Layout --- */
61
+ .app-layout {
62
+ display: flex;
63
+ min-height: 100vh;
64
+ }
65
+
66
+ /* --- Sidebar --- */
67
+ .sidebar {
68
+ width: var(--cb-sidebar-width);
69
+ background: var(--cb-bg);
70
+ border-right: 1px solid var(--cb-border);
71
+ display: flex;
72
+ flex-direction: column;
73
+ position: fixed;
74
+ top: 0;
75
+ left: 0;
76
+ bottom: 0;
77
+ z-index: 100;
78
+ }
79
+
80
+ .sidebar-header {
81
+ padding: 1.25rem 1rem;
82
+ border-bottom: 1px solid var(--cb-border);
83
+ min-height: 60px;
84
+ display: flex;
85
+ align-items: center;
86
+ }
87
+
88
+ .logo {
89
+ display: flex;
90
+ align-items: center;
91
+ gap: 0.625rem;
92
+ text-decoration: none;
93
+ color: var(--cb-text);
94
+ }
95
+
96
+ .logo-icon {
97
+ width: 28px;
98
+ height: 28px;
99
+ background: var(--cb-text);
100
+ border-radius: var(--cb-radius-sm);
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+ flex-shrink: 0;
105
+ }
106
+
107
+ .logo-icon svg {
108
+ width: 16px;
109
+ height: 16px;
110
+ color: white;
111
+ }
112
+
113
+ .logo-icon i {
114
+ font-size: 14px;
115
+ color: white;
116
+ }
117
+
118
+ .logo-text {
119
+ font-size: 1rem;
120
+ font-weight: 700;
121
+ letter-spacing: -0.02em;
122
+ color: var(--cb-text);
123
+ }
124
+
125
+ /* Sidebar Nav */
126
+ .sidebar-nav {
127
+ flex: 1;
128
+ padding: 0.75rem 0.625rem;
129
+ overflow-y: auto;
130
+ }
131
+
132
+ .nav-section {
133
+ margin-bottom: 1.25rem;
134
+ }
135
+
136
+ .nav-section-title {
137
+ font-size: 0.6875rem;
138
+ font-weight: 600;
139
+ text-transform: uppercase;
140
+ letter-spacing: 0.06em;
141
+ color: var(--cb-text-muted);
142
+ padding: 0 0.625rem;
143
+ margin-bottom: 0.375rem;
144
+ }
145
+
146
+ .nav-item {
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 0.625rem;
150
+ padding: 0.5rem 0.625rem;
151
+ border-radius: var(--cb-radius-sm);
152
+ color: var(--cb-text-secondary);
153
+ text-decoration: none;
154
+ font-size: 0.8125rem;
155
+ font-weight: 500;
156
+ transition: all 0.15s ease;
157
+ }
158
+
159
+ .nav-item:hover {
160
+ background: var(--cb-bg-subtle);
161
+ color: var(--cb-text);
162
+ text-decoration: none;
163
+ }
164
+
165
+ .nav-item.active {
166
+ background: var(--cb-bg-subtle);
167
+ color: var(--cb-text);
168
+ font-weight: 600;
169
+ }
170
+
171
+ .nav-item i {
172
+ font-size: 1rem;
173
+ width: 1.25rem;
174
+ text-align: center;
175
+ opacity: 0.6;
176
+ flex-shrink: 0;
177
+ }
178
+
179
+ .nav-item svg {
180
+ width: 1rem;
181
+ height: 1rem;
182
+ opacity: 0.6;
183
+ flex-shrink: 0;
184
+ }
185
+
186
+ .nav-item:hover i,
187
+ .nav-item:hover svg,
188
+ .nav-item.active i,
189
+ .nav-item.active svg {
190
+ opacity: 1;
191
+ }
192
+
193
+ .nav-item-count {
194
+ margin-left: auto;
195
+ font-size: 0.6875rem;
196
+ font-weight: 500;
197
+ color: var(--cb-text-muted);
198
+ background: var(--cb-bg-muted);
199
+ padding: 0.125rem 0.5rem;
200
+ border-radius: 10px;
201
+ }
202
+
203
+ .nav-item.active .nav-item-count {
204
+ background: var(--cb-text);
205
+ color: white;
206
+ }
207
+
208
+ /* Sidebar Footer */
209
+ .sidebar-footer {
210
+ padding: 0.75rem 0.625rem;
211
+ border-top: 1px solid var(--cb-border);
212
+ margin-top: auto;
213
+ }
214
+
215
+ .sidebar-ad {
216
+ display: block;
217
+ padding: 0.625rem;
218
+ margin-bottom: 0.5rem;
219
+ border-radius: var(--cb-radius-sm);
220
+ background: var(--cb-bg-subtle);
221
+ text-decoration: none;
222
+ transition: all 0.15s ease;
223
+ }
224
+
225
+ .sidebar-ad:hover {
226
+ background: var(--cb-bg-muted);
227
+ }
228
+
229
+ .sidebar-ad-inner {
230
+ display: flex;
231
+ align-items: center;
232
+ gap: 0.5rem;
233
+ }
234
+
235
+ .sidebar-ad-logo {
236
+ width: 36px;
237
+ height: 36px;
238
+ flex-shrink: 0;
239
+ }
240
+
241
+ .sidebar-ad-logo img {
242
+ width: 100%;
243
+ height: 100%;
244
+ object-fit: contain;
245
+ }
246
+
247
+ .sidebar-ad-text {
248
+ font-size: 0.6875rem;
249
+ line-height: 1.4;
250
+ color: var(--cb-text-secondary);
251
+ }
252
+
253
+ .sidebar-ad-title {
254
+ font-weight: 600;
255
+ color: var(--cb-text);
256
+ }
257
+
258
+ /* Clear button */
259
+ .btn-clear {
260
+ display: flex;
261
+ align-items: center;
262
+ gap: 0.625rem;
263
+ width: 100%;
264
+ padding: 0.5rem 0.625rem;
265
+ border-radius: var(--cb-radius-sm);
266
+ background: transparent;
267
+ border: none;
268
+ color: var(--cb-text-muted);
269
+ font-size: 0.8125rem;
270
+ font-weight: 500;
271
+ font-family: inherit;
272
+ cursor: pointer;
273
+ transition: all 0.15s ease;
274
+ }
275
+
276
+ .btn-clear:hover {
277
+ background: rgba(197,48,48,0.08);
278
+ color: var(--cb-danger);
279
+ }
280
+
281
+ .btn-clear svg,
282
+ .btn-clear i {
283
+ width: 1rem;
284
+ height: 1rem;
285
+ font-size: 1rem;
286
+ opacity: 0.6;
287
+ flex-shrink: 0;
288
+ }
289
+
290
+ .btn-clear:hover svg,
291
+ .btn-clear:hover i {
292
+ opacity: 1;
293
+ }
294
+
295
+ /* --- Main Content --- */
296
+ .main-content {
297
+ flex: 1;
298
+ margin-left: var(--cb-sidebar-width);
299
+ min-height: 100vh;
300
+ }
301
+
302
+ /* Page Header */
303
+ .page-header {
304
+ background: var(--cb-bg);
305
+ border-bottom: 1px solid var(--cb-border);
306
+ padding: 1rem 1.5rem;
307
+ min-height: 60px;
308
+ display: flex;
309
+ align-items: center;
310
+ justify-content: space-between;
311
+ gap: 1rem;
312
+ }
313
+
314
+ .page-title {
315
+ font-size: 1rem;
316
+ font-weight: 600;
317
+ color: var(--cb-text);
318
+ margin: 0;
319
+ }
320
+
321
+ /* Page Body */
322
+ .page-body {
323
+ padding: 1.25rem 1.5rem;
324
+ }
325
+
326
+ /* --- Cards --- */
327
+ .card {
328
+ background: var(--cb-bg);
329
+ border-radius: var(--cb-radius-lg);
330
+ border: 1px solid var(--cb-border);
331
+ overflow: hidden;
332
+ box-shadow: none;
333
+ }
334
+
335
+ .card-header {
336
+ padding: 0.75rem 1rem;
337
+ border-bottom: 1px solid var(--cb-border);
338
+ display: flex;
339
+ align-items: center;
340
+ justify-content: space-between;
341
+ background: transparent;
342
+ }
343
+
344
+ .card-title {
345
+ font-size: 0.8125rem;
346
+ font-weight: 600;
347
+ color: var(--cb-text);
348
+ }
349
+
350
+ .card-body {
351
+ padding: 0;
352
+ }
353
+
354
+ /* --- Data Table --- */
355
+ .data-table {
356
+ width: 100%;
357
+ border-collapse: collapse;
358
+ }
359
+
360
+ .data-table th {
361
+ text-align: left;
362
+ padding: 0.5rem 0.75rem;
363
+ font-size: 0.6875rem;
364
+ font-weight: 600;
365
+ text-transform: uppercase;
366
+ letter-spacing: 0.04em;
367
+ color: var(--cb-text-muted);
368
+ background: transparent;
369
+ border-bottom: 1px solid var(--cb-border);
370
+ }
371
+
372
+ .data-table td {
373
+ padding: 0.5rem 0.75rem;
374
+ border-bottom: 1px solid var(--cb-bg-muted);
375
+ vertical-align: middle;
376
+ font-size: 0.8125rem;
377
+ }
378
+
379
+ .data-table tbody tr {
380
+ transition: background 0.1s ease;
381
+ }
382
+
383
+ .data-table tbody tr:hover {
384
+ background: var(--cb-bg-subtle);
385
+ }
386
+
387
+ .data-table tbody tr:last-child td {
388
+ border-bottom: none;
389
+ }
390
+
391
+ /* --- Badges --- */
392
+ .badge {
393
+ display: inline-flex;
394
+ align-items: center;
395
+ padding: 0.2em 0.5em;
396
+ border-radius: 0.25rem;
397
+ font-size: 0.6875rem;
398
+ font-weight: 600;
399
+ font-family: var(--cb-font-mono);
400
+ letter-spacing: 0.02em;
401
+ }
402
+
403
+ .badge-verb {
404
+ background: var(--cb-bg-muted);
405
+ color: var(--cb-text-secondary);
406
+ }
407
+
408
+ .badge-get { background: rgba(107,138,154,0.15); color: #4a7a8a; }
409
+ .badge-post { background: rgba(43,122,62,0.15); color: #1d6b2f; }
410
+ .badge-put, .badge-patch { background: rgba(196,163,90,0.15); color: #9a7d2e; }
411
+ .badge-delete { background: rgba(197,48,48,0.15); color: #a53030; }
412
+
413
+ .badge-status {
414
+ min-width: 36px;
415
+ justify-content: center;
416
+ }
417
+
418
+ .badge-success { background: rgba(43,122,62,0.15); color: #1d6b2f; }
419
+ .badge-error { background: rgba(197,48,48,0.15); color: #a53030; }
420
+ .badge-warning { background: rgba(196,163,90,0.15); color: #9a7d2e; }
421
+
422
+ .badge-type {
423
+ font-family: var(--cb-font);
424
+ font-weight: 500;
425
+ }
426
+
427
+ .badge-request { background: rgba(107,138,154,0.15); color: #4a7a8a; }
428
+ .badge-job { background: rgba(138,154,107,0.15); color: #5a7a3d; }
429
+
430
+ .badge-clue { font-family: var(--cb-font-mono); font-size: 0.6875rem; }
431
+ .badge-sql { background: rgba(107,138,154,0.15); color: #4a7a8a; }
432
+ .badge-cache { background: rgba(43,122,62,0.15); color: #1d6b2f; }
433
+ .badge-view { background: rgba(196,163,90,0.15); color: #9a7d2e; }
434
+ .badge-mail { background: rgba(154,107,138,0.15); color: #8a4a7a; }
435
+ .badge-http { background: rgba(138,107,154,0.15); color: #7a4a8a; }
436
+ .badge-exception { background: rgba(197,48,48,0.15); color: #a53030; }
437
+ .badge-other { background: var(--cb-bg-muted); color: var(--cb-text-secondary); }
438
+ .badge-redis { background: rgba(197,48,48,0.12); color: #a53030; }
439
+ .badge-query { background: rgba(107,138,154,0.15); color: #4a7a8a; }
440
+
441
+ /* --- Search / Filters --- */
442
+ .filters {
443
+ display: flex;
444
+ gap: 0.5rem;
445
+ align-items: center;
446
+ flex-wrap: wrap;
447
+ }
448
+
449
+ .filter-select {
450
+ padding: 0.375rem 0.625rem;
451
+ border: 1px solid var(--cb-border);
452
+ border-radius: var(--cb-radius);
453
+ background: var(--cb-bg);
454
+ font-size: 0.8125rem;
455
+ font-family: var(--cb-font);
456
+ color: var(--cb-text);
457
+ cursor: pointer;
458
+ min-width: 100px;
459
+ transition: border-color 0.15s ease;
460
+ }
461
+
462
+ .filter-select:focus {
463
+ outline: none;
464
+ border-color: var(--cb-text);
465
+ box-shadow: 0 0 0 3px rgba(26,26,26,0.1);
466
+ }
467
+
468
+ .filter-select::placeholder {
469
+ color: var(--cb-text-muted);
470
+ }
471
+
472
+ /* --- Path / Name Cells --- */
473
+ .path-cell {
474
+ max-width: 400px;
475
+ }
476
+
477
+ .path-text {
478
+ display: block;
479
+ overflow: hidden;
480
+ text-overflow: ellipsis;
481
+ white-space: nowrap;
482
+ }
483
+
484
+ .path-subtext {
485
+ display: block;
486
+ overflow: hidden;
487
+ text-overflow: ellipsis;
488
+ white-space: nowrap;
489
+ color: var(--cb-text-muted);
490
+ font-size: 0.75rem;
491
+ margin-top: 1px;
492
+ }
493
+
494
+ .action-name,
495
+ .job-name,
496
+ .span-name {
497
+ font-weight: 600;
498
+ color: var(--cb-text);
499
+ }
500
+
501
+ /* --- Duration / Timestamp --- */
502
+ .duration {
503
+ font-variant-numeric: tabular-nums;
504
+ font-family: var(--cb-font-mono);
505
+ font-size: 0.8125rem;
506
+ font-weight: 500;
507
+ color: var(--cb-text-secondary);
508
+ text-align: right;
509
+ white-space: nowrap;
510
+ }
511
+
512
+ .timestamp {
513
+ color: var(--cb-text-muted);
514
+ font-size: 0.75rem;
515
+ text-align: right;
516
+ white-space: nowrap;
517
+ }
518
+
519
+ /* --- Pagination --- */
520
+ .pagination {
521
+ display: flex;
522
+ justify-content: space-between;
523
+ align-items: center;
524
+ padding: 0.75rem 1rem;
525
+ border-top: 1px solid var(--cb-border);
526
+ }
527
+
528
+ .pagination-link {
529
+ padding: 0.375rem 0.75rem;
530
+ border: 1px solid var(--cb-border);
531
+ border-radius: var(--cb-radius);
532
+ color: var(--cb-text-secondary);
533
+ text-decoration: none;
534
+ font-size: 0.8125rem;
535
+ font-weight: 500;
536
+ transition: all 0.15s ease;
537
+ }
538
+
539
+ .pagination-link:hover {
540
+ background: var(--cb-bg-subtle);
541
+ border-color: var(--cb-border-hover);
542
+ color: var(--cb-text);
543
+ }
544
+
545
+ .pagination-info {
546
+ font-size: 0.75rem;
547
+ color: var(--cb-text-muted);
548
+ }
549
+
550
+ .pagination-side {
551
+ min-width: 80px;
552
+ }
553
+
554
+ /* --- Empty State --- */
555
+ .empty-state {
556
+ text-align: center;
557
+ padding: 3rem 1.5rem;
558
+ color: var(--cb-text-secondary);
559
+ }
560
+
561
+ .empty-state-icon {
562
+ width: 3rem;
563
+ height: 3rem;
564
+ margin: 0 auto 1rem;
565
+ color: var(--cb-text-muted);
566
+ }
567
+
568
+ .empty-state h3 {
569
+ font-size: 0.9375rem;
570
+ font-weight: 600;
571
+ color: var(--cb-text);
572
+ margin-bottom: 0.375rem;
573
+ }
574
+
575
+ .empty-state p {
576
+ font-size: 0.8125rem;
577
+ color: var(--cb-text-secondary);
578
+ }
579
+
580
+ /* --- Detail Header --- */
581
+ .detail-header {
582
+ display: flex;
583
+ align-items: flex-start;
584
+ justify-content: space-between;
585
+ gap: 1.5rem;
586
+ }
587
+
588
+ .detail-header-left {
589
+ flex: 1;
590
+ min-width: 0;
591
+ }
592
+
593
+ .detail-title {
594
+ display: flex;
595
+ align-items: center;
596
+ gap: 0.625rem;
597
+ flex-wrap: wrap;
598
+ }
599
+
600
+ .detail-title h1 {
601
+ font-size: 1rem;
602
+ font-weight: 600;
603
+ word-break: break-all;
604
+ color: var(--cb-text);
605
+ margin: 0;
606
+ }
607
+
608
+ .detail-meta {
609
+ display: flex;
610
+ gap: 1.5rem;
611
+ margin-top: 0.75rem;
612
+ flex-wrap: wrap;
613
+ }
614
+
615
+ .meta-item {
616
+ display: flex;
617
+ flex-direction: column;
618
+ gap: 2px;
619
+ }
620
+
621
+ .meta-label {
622
+ font-size: 0.6875rem;
623
+ font-weight: 600;
624
+ text-transform: uppercase;
625
+ letter-spacing: 0.04em;
626
+ color: var(--cb-text-muted);
627
+ }
628
+
629
+ .meta-value {
630
+ font-size: 0.8125rem;
631
+ color: var(--cb-text);
632
+ font-weight: 500;
633
+ }
634
+
635
+ .meta-value.mono {
636
+ font-family: var(--cb-font-mono);
637
+ font-size: 0.8125rem;
638
+ }
639
+
640
+ .meta-value a {
641
+ color: var(--cb-text-secondary);
642
+ text-decoration: none;
643
+ }
644
+
645
+ .meta-value a:hover {
646
+ color: var(--cb-text);
647
+ text-decoration: underline;
648
+ }
649
+
650
+ /* --- Tabs --- */
651
+ .tabs {
652
+ display: flex;
653
+ gap: 0;
654
+ border-bottom: 1px solid var(--cb-border);
655
+ margin-bottom: 0;
656
+ }
657
+
658
+ .tab {
659
+ padding: 0.625rem 1rem;
660
+ font-size: 0.8125rem;
661
+ font-weight: 500;
662
+ color: var(--cb-text-secondary);
663
+ text-decoration: none;
664
+ border-bottom: 2px solid transparent;
665
+ margin-bottom: -1px;
666
+ transition: all 0.15s ease;
667
+ cursor: pointer;
668
+ }
669
+
670
+ .tab:hover {
671
+ color: var(--cb-text);
672
+ text-decoration: none;
673
+ }
674
+
675
+ .tab.active {
676
+ color: var(--cb-text);
677
+ border-bottom-color: var(--cb-text);
678
+ font-weight: 600;
679
+ }
680
+
681
+ .tab-content {
682
+ display: none;
683
+ padding: 1rem;
684
+ }
685
+
686
+ .tab-content.active {
687
+ display: block;
688
+ }
689
+
690
+ /* --- Details Sections --- */
691
+ .details-sections {
692
+ display: flex;
693
+ flex-direction: column;
694
+ gap: 0;
695
+ }
696
+
697
+ .details-section {
698
+ padding: 1rem 1.25rem;
699
+ border-bottom: 1px solid var(--cb-border);
700
+ }
701
+
702
+ .details-section:last-child {
703
+ border-bottom: none;
704
+ }
705
+
706
+ .section-header {
707
+ display: flex;
708
+ align-items: center;
709
+ gap: 0.5rem;
710
+ margin-bottom: 0.75rem;
711
+ }
712
+
713
+ .section-icon {
714
+ width: 1.75rem;
715
+ height: 1.75rem;
716
+ border-radius: var(--cb-radius-sm);
717
+ display: flex;
718
+ align-items: center;
719
+ justify-content: center;
720
+ flex-shrink: 0;
721
+ }
722
+
723
+ .section-icon svg {
724
+ width: 0.875rem;
725
+ height: 0.875rem;
726
+ }
727
+
728
+ .section-icon.request { background: rgba(107,138,154,0.15); color: #4a7a8a; }
729
+ .section-icon.route { background: rgba(138,107,154,0.15); color: #7a4a8a; }
730
+ .section-icon.client { background: rgba(196,163,90,0.15); color: #9a7d2e; }
731
+ .section-icon.other { background: var(--cb-bg-muted); color: var(--cb-text-secondary); }
732
+ .section-icon.job { background: rgba(138,154,107,0.15); color: #5a7a3d; }
733
+ .section-icon.queue { background: rgba(138,107,154,0.15); color: #7a4a8a; }
734
+ .section-icon.properties { background: rgba(107,138,154,0.15); color: #4a7a8a; }
735
+ .section-icon.statement { background: rgba(196,163,90,0.15); color: #9a7d2e; }
736
+ .section-icon.trace { background: rgba(138,107,154,0.15); color: #7a4a8a; }
737
+ .section-icon.events { background: rgba(197,48,48,0.15); color: #a53030; }
738
+
739
+ .section-title {
740
+ font-size: 0.6875rem;
741
+ font-weight: 700;
742
+ text-transform: uppercase;
743
+ letter-spacing: 0.06em;
744
+ color: var(--cb-text-muted);
745
+ }
746
+
747
+ .section-grid {
748
+ display: grid;
749
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
750
+ gap: 0.75rem 1.5rem;
751
+ }
752
+
753
+ .detail-item {
754
+ display: flex;
755
+ flex-direction: column;
756
+ gap: 2px;
757
+ }
758
+
759
+ .detail-item.full-width {
760
+ grid-column: 1 / -1;
761
+ }
762
+
763
+ .detail-label {
764
+ font-size: 0.6875rem;
765
+ font-weight: 500;
766
+ color: var(--cb-text-muted);
767
+ letter-spacing: 0.02em;
768
+ }
769
+
770
+ .detail-value {
771
+ font-size: 0.8125rem;
772
+ color: var(--cb-text);
773
+ word-break: break-word;
774
+ line-height: 1.4;
775
+ }
776
+
777
+ .detail-value.mono {
778
+ font-family: var(--cb-font-mono);
779
+ font-size: 0.8125rem;
780
+ }
781
+
782
+ .detail-value.prominent {
783
+ font-size: 1.125rem;
784
+ font-weight: 700;
785
+ color: var(--cb-text);
786
+ font-family: var(--cb-font-mono);
787
+ letter-spacing: -0.01em;
788
+ }
789
+
790
+ .detail-value.truncate {
791
+ white-space: nowrap;
792
+ overflow: hidden;
793
+ text-overflow: ellipsis;
794
+ }
795
+
796
+ .detail-value.secondary {
797
+ color: var(--cb-text-secondary);
798
+ font-size: 0.8125rem;
799
+ }
800
+
801
+ /* Status dot */
802
+ .status-dot {
803
+ display: inline-block;
804
+ width: 6px;
805
+ height: 6px;
806
+ border-radius: 50%;
807
+ margin-right: 0.375rem;
808
+ }
809
+
810
+ .status-dot.success { background: var(--cb-success); }
811
+ .status-dot.warning { background: var(--cb-warning); }
812
+ .status-dot.error { background: var(--cb-danger); }
813
+
814
+ /* --- Waterfall --- */
815
+ .waterfall {
816
+ display: flex;
817
+ flex-direction: column;
818
+ gap: 1px;
819
+ }
820
+
821
+ .waterfall-header {
822
+ display: flex;
823
+ justify-content: space-between;
824
+ padding: 0.5rem 0.75rem;
825
+ font-size: 0.6875rem;
826
+ font-weight: 600;
827
+ text-transform: uppercase;
828
+ letter-spacing: 0.04em;
829
+ color: var(--cb-text-muted);
830
+ border-bottom: 1px solid var(--cb-border);
831
+ margin-bottom: 0.25rem;
832
+ }
833
+
834
+ .waterfall-row {
835
+ padding: 0 0.75rem;
836
+ cursor: pointer;
837
+ border-radius: var(--cb-radius-sm);
838
+ transition: background 0.1s ease;
839
+ }
840
+
841
+ .waterfall-row:hover .waterfall-bar-container {
842
+ background: var(--cb-border-hover);
843
+ }
844
+
845
+ .waterfall-row.selected {
846
+ /* No background change */
847
+ }
848
+
849
+ .waterfall-bar-container {
850
+ height: 28px;
851
+ background: var(--cb-bg-muted);
852
+ border-radius: var(--cb-radius-sm);
853
+ position: relative;
854
+ overflow: hidden;
855
+ display: flex;
856
+ align-items: center;
857
+ justify-content: space-between;
858
+ }
859
+
860
+ .waterfall-label {
861
+ position: relative;
862
+ z-index: 1;
863
+ font-size: 0.75rem;
864
+ color: var(--cb-text);
865
+ font-family: var(--cb-font-mono);
866
+ padding: 2px 6px;
867
+ white-space: nowrap;
868
+ overflow: hidden;
869
+ text-overflow: ellipsis;
870
+ max-width: 100%;
871
+ background: rgba(255, 255, 255, 0.5);
872
+ border-radius: 3px;
873
+ margin-left: 3px;
874
+ }
875
+
876
+ .waterfall-bar {
877
+ position: absolute;
878
+ height: 100%;
879
+ border-radius: var(--cb-radius-sm);
880
+ min-width: 3px;
881
+ opacity: 0.85;
882
+ transition: opacity 0.15s ease;
883
+ }
884
+
885
+ .waterfall-bar:hover {
886
+ opacity: 1;
887
+ }
888
+
889
+ /* Waterfall bar colors */
890
+ .waterfall-bar.sql { background: var(--cb-info); }
891
+ .waterfall-bar.cache { background: var(--cb-success); }
892
+ .waterfall-bar.view { background: var(--cb-warning); }
893
+ .waterfall-bar.http { background: #9A6B8A; }
894
+ .waterfall-bar.mailer { background: #B07A8A; }
895
+ .waterfall-bar.job { background: #8A9A6B; }
896
+ .waterfall-bar.controller { background: #666; }
897
+ .waterfall-bar.other { background: var(--cb-border-hover); }
898
+
899
+ .waterfall-duration {
900
+ position: relative;
901
+ z-index: 1;
902
+ font-size: 0.75rem;
903
+ color: var(--cb-text);
904
+ font-variant-numeric: tabular-nums;
905
+ font-family: var(--cb-font-mono);
906
+ background: rgba(255, 255, 255, 0.5);
907
+ border-radius: 3px;
908
+ padding: 2px 6px;
909
+ margin-right: 3px;
910
+ flex-shrink: 0;
911
+ }
912
+
913
+ /* Span detail panel */
914
+ .span-detail {
915
+ display: none;
916
+ margin: 0 0.75rem 0.5rem 1.5rem;
917
+ padding: 0.75rem 1rem;
918
+ background: var(--cb-text);
919
+ border-radius: var(--cb-radius-sm) var(--cb-radius) var(--cb-radius) var(--cb-radius-sm);
920
+ font-size: 0.8125rem;
921
+ border-left: 3px solid var(--cb-border-hover);
922
+ }
923
+
924
+ .span-detail.visible {
925
+ display: block;
926
+ }
927
+
928
+ .span-detail.sql { border-left-color: var(--cb-info); }
929
+ .span-detail.cache { border-left-color: var(--cb-success); }
930
+ .span-detail.view { border-left-color: var(--cb-warning); }
931
+ .span-detail.http { border-left-color: #9A6B8A; }
932
+ .span-detail.mailer { border-left-color: #B07A8A; }
933
+ .span-detail.job { border-left-color: #8A9A6B; }
934
+ .span-detail.controller { border-left-color: #666; }
935
+ .span-detail.other { border-left-color: var(--cb-border-hover); }
936
+
937
+ .span-detail pre {
938
+ margin: 0;
939
+ padding: 0;
940
+ background: transparent;
941
+ white-space: pre-wrap;
942
+ word-break: break-all;
943
+ color: #e5e5e5;
944
+ font-size: 0.75rem;
945
+ line-height: 1.5;
946
+ }
947
+
948
+ /* Legend */
949
+ .legend {
950
+ display: flex;
951
+ gap: 0;
952
+ flex-wrap: wrap;
953
+ padding: 0.625rem 1rem;
954
+ background: var(--cb-bg-subtle);
955
+ border-bottom: 1px solid var(--cb-border);
956
+ }
957
+
958
+ .legend-item {
959
+ display: flex;
960
+ align-items: center;
961
+ gap: 0.375rem;
962
+ font-size: 0.75rem;
963
+ color: var(--cb-text-secondary);
964
+ }
965
+
966
+ .legend-color {
967
+ width: 12px;
968
+ height: 12px;
969
+ border-radius: 3px;
970
+ }
971
+
972
+ .legend-color.sql { background: var(--cb-info); }
973
+ .legend-color.cache { background: var(--cb-success); }
974
+ .legend-color.view { background: var(--cb-warning); }
975
+ .legend-color.http { background: #9A6B8A; }
976
+ .legend-color.mailer { background: #B07A8A; }
977
+ .legend-color.job { background: #8A9A6B; }
978
+ .legend-color.controller { background: #666; }
979
+ .legend-color.other { background: var(--cb-border-hover); }
980
+ .legend-color.all {
981
+ background: linear-gradient(135deg, var(--cb-info) 0%, var(--cb-success) 25%, var(--cb-warning) 50%, #9A6B8A 75%, var(--cb-text-secondary) 100%);
982
+ }
983
+
984
+ /* Clickable legend items */
985
+ .legend-item.clickable {
986
+ cursor: pointer;
987
+ padding: 0.3rem 0.625rem;
988
+ border-radius: var(--cb-radius-sm);
989
+ transition: all 0.15s ease;
990
+ }
991
+
992
+ .legend-item.clickable:hover {
993
+ background: var(--cb-bg-muted);
994
+ }
995
+
996
+ .legend-item.clickable.active {
997
+ background: var(--cb-text);
998
+ color: white;
999
+ }
1000
+
1001
+ .waterfall-row.hidden {
1002
+ display: none;
1003
+ }
1004
+
1005
+ .span-detail.hidden-by-filter {
1006
+ display: none !important;
1007
+ }
1008
+
1009
+ /* --- SQL Tooltip --- */
1010
+ #sql-tooltip {
1011
+ position: fixed;
1012
+ top: 50%;
1013
+ left: 50%;
1014
+ transform: translate(-50%, -50%);
1015
+ z-index: 1000;
1016
+ background: var(--cb-text);
1017
+ color: #e5e5e5;
1018
+ padding: 1rem 1.25rem;
1019
+ border-radius: var(--cb-radius);
1020
+ font-size: 0.8125rem;
1021
+ font-family: var(--cb-font-mono);
1022
+ white-space: pre-wrap;
1023
+ word-break: break-all;
1024
+ max-width: 600px;
1025
+ max-height: 200px;
1026
+ overflow: auto;
1027
+ box-shadow: var(--cb-shadow-lg);
1028
+ pointer-events: none;
1029
+ opacity: 0;
1030
+ visibility: hidden;
1031
+ transition: opacity 0.15s ease, visibility 0.15s ease;
1032
+ }
1033
+
1034
+ #sql-tooltip.visible {
1035
+ opacity: 1;
1036
+ visibility: visible;
1037
+ }
1038
+
1039
+ /* --- Code Blocks --- */
1040
+ .code-path {
1041
+ font-family: var(--cb-font-mono);
1042
+ font-size: 0.8125rem;
1043
+ color: var(--cb-text-secondary);
1044
+ background: var(--cb-bg-subtle);
1045
+ padding: 0.125rem 0.375rem;
1046
+ border-radius: var(--cb-radius-sm);
1047
+ display: inline-block;
1048
+ }
1049
+
1050
+ pre {
1051
+ background: var(--cb-text);
1052
+ color: #e5e5e5;
1053
+ padding: 1rem;
1054
+ border-radius: var(--cb-radius);
1055
+ overflow-x: auto;
1056
+ font-size: 0.8125rem;
1057
+ line-height: 1.5;
1058
+ }
1059
+
1060
+ /* Statement block */
1061
+ .statement-block {
1062
+ background: var(--cb-text);
1063
+ border-radius: var(--cb-radius);
1064
+ padding: 1rem;
1065
+ overflow-x: auto;
1066
+ }
1067
+
1068
+ .statement-block pre {
1069
+ margin: 0;
1070
+ padding: 0;
1071
+ white-space: pre-wrap;
1072
+ word-break: break-all;
1073
+ color: #e5e5e5;
1074
+ font-family: var(--cb-font-mono);
1075
+ font-size: 0.8125rem;
1076
+ line-height: 1.5;
1077
+ }
1078
+
1079
+ /* Stacktrace */
1080
+ .stacktrace-list {
1081
+ list-style: none;
1082
+ padding: 0;
1083
+ margin: 0;
1084
+ font-family: var(--cb-font-mono);
1085
+ font-size: 0.75rem;
1086
+ background: var(--cb-bg-subtle);
1087
+ border-radius: var(--cb-radius);
1088
+ overflow: hidden;
1089
+ }
1090
+
1091
+ .stacktrace-list li {
1092
+ padding: 0.375rem 0.75rem;
1093
+ border-bottom: 1px solid var(--cb-border);
1094
+ color: var(--cb-text-secondary);
1095
+ }
1096
+
1097
+ .stacktrace-list li:last-child {
1098
+ border-bottom: none;
1099
+ }
1100
+
1101
+ .stacktrace-list .file-path { color: var(--cb-text-secondary); }
1102
+ .stacktrace-list .line-number { color: var(--cb-text-muted); }
1103
+ .stacktrace-list .method-name { color: var(--cb-text); }
1104
+
1105
+ /* Events section */
1106
+ .event-item {
1107
+ padding: 0.75rem;
1108
+ background: var(--cb-bg-subtle);
1109
+ border-radius: var(--cb-radius-sm);
1110
+ margin-bottom: 0.5rem;
1111
+ }
1112
+
1113
+ .event-name {
1114
+ font-weight: 600;
1115
+ color: var(--cb-danger);
1116
+ margin-bottom: 0.5rem;
1117
+ font-size: 0.8125rem;
1118
+ }
1119
+
1120
+ .event-props {
1121
+ font-size: 0.8125rem;
1122
+ font-family: var(--cb-font-mono);
1123
+ color: var(--cb-text-secondary);
1124
+ }
1125
+
1126
+ /* --- Buttons --- */
1127
+ .copy-ai-btn,
1128
+ .collapse-btn {
1129
+ display: inline-flex;
1130
+ align-items: center;
1131
+ gap: 0.375rem;
1132
+ padding: 0.3rem 0.625rem;
1133
+ font-size: 0.75rem;
1134
+ font-weight: 500;
1135
+ font-family: var(--cb-font);
1136
+ color: var(--cb-text-secondary);
1137
+ background: var(--cb-bg);
1138
+ border: 1px solid var(--cb-border);
1139
+ border-radius: var(--cb-radius);
1140
+ cursor: pointer;
1141
+ transition: all 0.15s ease;
1142
+ }
1143
+
1144
+ .copy-ai-btn:hover,
1145
+ .collapse-btn:hover {
1146
+ background: var(--cb-bg-subtle);
1147
+ color: var(--cb-text);
1148
+ border-color: var(--cb-border-hover);
1149
+ }
1150
+
1151
+ .copy-ai-btn.copied {
1152
+ background: var(--cb-success);
1153
+ color: white;
1154
+ border-color: var(--cb-success);
1155
+ }
1156
+
1157
+ /* --- Alert --- */
1158
+ .alert {
1159
+ padding: 0.625rem 1rem;
1160
+ border-radius: var(--cb-radius);
1161
+ margin-bottom: 0.75rem;
1162
+ font-size: 0.8125rem;
1163
+ }
1164
+
1165
+ .alert-danger {
1166
+ background: rgba(197,48,48,0.1);
1167
+ color: var(--cb-danger);
1168
+ border: 1px solid rgba(197,48,48,0.2);
1169
+ }
1170
+
1171
+ .alert-success {
1172
+ background: rgba(43,122,62,0.1);
1173
+ color: var(--cb-success);
1174
+ border: 1px solid rgba(43,122,62,0.2);
1175
+ }
1176
+
1177
+ /* --- Muted --- */
1178
+ .muted { color: var(--cb-text-muted); }
1179
+
1180
+ /* --- Responsive --- */
1181
+ @media (max-width: 1024px) {
1182
+ .sidebar {
1183
+ width: var(--cb-sidebar-collapsed);
1184
+ }
1185
+ .sidebar-header {
1186
+ padding: 1rem 0.75rem;
1187
+ justify-content: center;
1188
+ }
1189
+ .logo-text, .nav-section-title, .nav-item span, .nav-item-count, .btn-clear span {
1190
+ display: none;
1191
+ }
1192
+ .nav-item {
1193
+ justify-content: center;
1194
+ padding: 0.625rem;
1195
+ }
1196
+ .nav-item i,
1197
+ .nav-item svg {
1198
+ margin: 0;
1199
+ }
1200
+ .btn-clear {
1201
+ justify-content: center;
1202
+ padding: 0.625rem;
1203
+ }
1204
+ .main-content {
1205
+ margin-left: var(--cb-sidebar-collapsed);
1206
+ }
1207
+ .sidebar-ad {
1208
+ display: none;
1209
+ }
1210
+ }
1211
+
1212
+ @media (max-width: 768px) {
1213
+ .page-header {
1214
+ flex-direction: column;
1215
+ align-items: flex-start;
1216
+ padding: 1rem;
1217
+ }
1218
+ .page-body {
1219
+ padding: 1rem;
1220
+ }
1221
+ .detail-meta {
1222
+ gap: 1rem;
1223
+ }
1224
+ .section-grid {
1225
+ grid-template-columns: 1fr;
1226
+ }
1227
+ }
1228
+
1229
+ /* Empty clues (for waterfall empty state) */
1230
+ .empty-clues {
1231
+ text-align: center;
1232
+ padding: 2.5rem 1rem;
1233
+ color: var(--cb-text-secondary);
1234
+ }
1235
+
1236
+ .empty-clues svg {
1237
+ width: 2.5rem;
1238
+ height: 2.5rem;
1239
+ margin-bottom: 0.75rem;
1240
+ color: var(--cb-text-muted);
1241
+ }
1242
+
1243
+ .empty-clues p {
1244
+ font-size: 0.8125rem;
1245
+ }