web_plsql 0.18.0 → 1.2.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 (74) hide show
  1. package/README.md +29 -9
  2. package/package.json +17 -7
  3. package/src/admin/client/charts.ts +598 -0
  4. package/src/admin/client/main.js +3 -0
  5. package/src/admin/client/tailwind.css +41 -0
  6. package/src/admin/favicon.svg +3 -0
  7. package/src/admin/index.html +529 -0
  8. package/src/admin/js/api.ts +170 -0
  9. package/src/admin/js/app.ts +614 -0
  10. package/src/admin/js/eslint.config.js +74 -0
  11. package/src/admin/js/schemas.ts +215 -0
  12. package/src/admin/js/templates/config.ts +147 -0
  13. package/src/admin/js/templates/errorRow.ts +21 -0
  14. package/src/admin/js/templates/index.ts +4 -0
  15. package/src/admin/js/templates/poolCard.ts +61 -0
  16. package/src/admin/js/templates/traceRow.ts +24 -0
  17. package/src/admin/js/tsconfig.json +24 -0
  18. package/src/admin/js/types.ts +336 -0
  19. package/src/admin/js/ui/components.ts +44 -0
  20. package/src/admin/js/ui/table.ts +173 -0
  21. package/src/admin/js/ui/theme.ts +93 -0
  22. package/src/admin/js/ui/views.ts +427 -0
  23. package/src/admin/js/util/format.ts +46 -0
  24. package/src/admin/js/util/metrics.ts +24 -0
  25. package/src/admin/lib/chart.bundle.css +1 -0
  26. package/src/admin/lib/chart.bundle.js +146 -0
  27. package/src/admin/style.css +1437 -0
  28. package/src/bin/load-test.js +202 -0
  29. package/src/handler/handlerAdmin.js +293 -0
  30. package/src/handler/plsql/cgi.js +1 -1
  31. package/src/handler/plsql/errorPage.js +31 -6
  32. package/src/handler/plsql/handlerPlSql.js +32 -6
  33. package/src/handler/plsql/owaPageStream.js +93 -0
  34. package/src/handler/plsql/parsePage.js +7 -3
  35. package/src/handler/plsql/procedure.js +221 -105
  36. package/src/handler/plsql/procedureNamed.js +34 -49
  37. package/src/handler/plsql/procedureSanitize.js +120 -45
  38. package/src/handler/plsql/procedureVariable.js +4 -2
  39. package/src/handler/plsql/request.js +8 -3
  40. package/src/handler/plsql/sendResponse.js +43 -5
  41. package/src/index.js +0 -1
  42. package/src/server/adminContext.js +23 -0
  43. package/src/server/config.js +1 -0
  44. package/src/server/server.js +130 -9
  45. package/src/types.js +7 -1
  46. package/src/util/cache.js +123 -0
  47. package/src/util/jsonLogger.js +47 -0
  48. package/src/util/shutdown.js +6 -2
  49. package/src/util/statsManager.js +368 -0
  50. package/src/util/trace.js +7 -6
  51. package/src/util/traceManager.js +68 -0
  52. package/src/version.js +1 -1
  53. package/types/admin/js/schemas.d.ts +384 -0
  54. package/types/admin/js/types.d.ts +312 -0
  55. package/types/handler/handlerAdmin.d.ts +79 -0
  56. package/types/handler/plsql/errorPage.d.ts +1 -0
  57. package/types/handler/plsql/handlerPlSql.d.ts +6 -1
  58. package/types/handler/plsql/owaPageStream.d.ts +28 -0
  59. package/types/handler/plsql/procedure.d.ts +4 -1
  60. package/types/handler/plsql/procedureNamed.d.ts +2 -5
  61. package/types/handler/plsql/procedureSanitize.d.ts +2 -5
  62. package/types/handler/plsql/procedureVariable.d.ts +1 -1
  63. package/types/handler/plsql/request.d.ts +3 -1
  64. package/types/handler/plsql/sendResponse.d.ts +1 -1
  65. package/types/index.d.ts +0 -1
  66. package/types/server/adminContext.d.ts +17 -0
  67. package/types/server/server.d.ts +5 -0
  68. package/types/types.d.ts +19 -1
  69. package/types/util/cache.d.ts +69 -0
  70. package/types/util/jsonLogger.d.ts +45 -0
  71. package/types/util/statsManager.d.ts +395 -0
  72. package/types/util/traceManager.d.ts +35 -0
  73. package/src/handler/handlerMetrics.js +0 -68
  74. package/types/handler/handlerMetrics.d.ts +0 -25
@@ -0,0 +1,1437 @@
1
+ :root {
2
+ /* Material Design 3 inspired tokens - Light Mode */
3
+ --accent-rgb: 59, 130, 246;
4
+ --bg-main: #f8fafc;
5
+ --bg-side: #fff;
6
+ --bg-card: #fff;
7
+ --bg-hover: #f1f5f9;
8
+ --text-main: #475569;
9
+ --text-bright: #0f172a;
10
+ --accent: #3b82f6;
11
+ --border: #e2e8f0;
12
+ --success: #10b981;
13
+ --warning: #f59e0b;
14
+ --danger: #ef4444;
15
+ --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
16
+ --terminal-bg: #1e293b;
17
+ --config-key: #3b82f6;
18
+ }
19
+
20
+ body.dark {
21
+ /* Material Design 3 inspired tokens - Dark Mode */
22
+ --bg-main: #0f172a;
23
+ --bg-side: #1e293b;
24
+ --bg-card: #1e293b;
25
+ --bg-hover: #334155;
26
+ --text-main: #94a3b8;
27
+ --text-bright: #f8fafc;
28
+ --accent: #3b82f6;
29
+ --border: #334155;
30
+ --success: #10b981;
31
+ --warning: #f59e0b;
32
+ --danger: #ef4444;
33
+ --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
34
+ --terminal-bg: #000;
35
+ --config-key: #60a5fa;
36
+ }
37
+
38
+ body {
39
+ margin: 0;
40
+ font-family:
41
+ 'Inter',
42
+ -apple-system,
43
+ BlinkMacSystemFont,
44
+ 'Segoe UI',
45
+ Roboto,
46
+ Helvetica,
47
+ Arial,
48
+ sans-serif;
49
+ background-color: var(--bg-main);
50
+ color: var(--text-main);
51
+ -webkit-font-smoothing: antialiased;
52
+ transition:
53
+ background-color 0.3s,
54
+ color 0.3s;
55
+ }
56
+
57
+ .app-container {
58
+ display: flex;
59
+ height: 100vh;
60
+ }
61
+
62
+ /* Sidebar */
63
+ .sidebar {
64
+ width: 240px;
65
+ background-color: var(--bg-side);
66
+ border-right: 1px solid var(--border);
67
+ display: flex;
68
+ flex-direction: column;
69
+ z-index: 10;
70
+ }
71
+
72
+ .logo {
73
+ padding: 24px;
74
+ font-weight: 700;
75
+ font-size: 1.25rem;
76
+ color: var(--text-bright);
77
+ display: flex;
78
+ align-items: center;
79
+ gap: 12px;
80
+ }
81
+
82
+ .logo .material-symbols-rounded {
83
+ color: var(--accent);
84
+ font-size: 28px;
85
+ }
86
+
87
+ nav {
88
+ flex: 1;
89
+ padding: 12px;
90
+ }
91
+
92
+ nav button {
93
+ width: 100%;
94
+ padding: 12px 16px;
95
+ text-align: left;
96
+ background: none;
97
+ border: none;
98
+ color: var(--text-main);
99
+ cursor: pointer;
100
+ font-size: 0.95rem;
101
+ display: flex;
102
+ align-items: center;
103
+ gap: 12px;
104
+ border-radius: 8px;
105
+ transition: all 0.2s;
106
+ margin-bottom: 4px;
107
+ }
108
+
109
+ nav button:hover {
110
+ background-color: var(--bg-hover);
111
+ color: var(--text-bright);
112
+ }
113
+
114
+ nav button.active {
115
+ background-color: var(--accent);
116
+ color: white;
117
+ }
118
+
119
+ nav button .material-symbols-rounded {
120
+ font-size: 20px;
121
+ }
122
+
123
+ .status-indicator {
124
+ padding: 20px;
125
+ border-top: 1px solid var(--border);
126
+ display: flex;
127
+ align-items: center;
128
+ font-size: 0.85rem;
129
+ font-weight: 500;
130
+ }
131
+
132
+ .dot {
133
+ height: 8px;
134
+ width: 8px;
135
+ background-color: #64748b;
136
+ border-radius: 50%;
137
+ display: inline-block;
138
+ margin-right: 12px;
139
+ box-shadow: 0 0 0 4px rgba(100, 116, 139, 0.1);
140
+ }
141
+
142
+ .dot.running {
143
+ background-color: var(--success);
144
+ box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.2);
145
+ }
146
+ .dot.paused {
147
+ background-color: var(--warning);
148
+ box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.2);
149
+ }
150
+
151
+ /* Content */
152
+ .content {
153
+ flex: 1;
154
+ display: flex;
155
+ flex-direction: column;
156
+ overflow: hidden;
157
+ }
158
+
159
+ .main-header {
160
+ padding: 20px 32px;
161
+ background-color: var(--bg-main);
162
+ border-bottom: 1px solid var(--border);
163
+ display: flex;
164
+ justify-content: space-between;
165
+ align-items: center;
166
+ }
167
+
168
+ .main-header h1 {
169
+ margin: 0;
170
+ font-size: 1.5rem;
171
+ color: var(--text-bright);
172
+ font-weight: 600;
173
+ display: flex;
174
+ align-items: center;
175
+ gap: 12px;
176
+ }
177
+
178
+ .main-header h1 .material-symbols-rounded {
179
+ color: var(--accent);
180
+ font-size: 28px;
181
+ }
182
+
183
+ .header-toolbar {
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 24px;
187
+ }
188
+
189
+ .auto-refresh-group {
190
+ display: flex;
191
+ align-items: center;
192
+ gap: 12px;
193
+ font-size: 0.875rem;
194
+ background: var(--bg-side);
195
+ padding: 6px 12px;
196
+ border-radius: 6px;
197
+ border: 1px solid var(--border);
198
+ }
199
+
200
+ .auto-refresh-group select {
201
+ background: none;
202
+ border: none;
203
+ color: var(--text-bright);
204
+ cursor: pointer;
205
+ font-weight: 500;
206
+ }
207
+
208
+ .view {
209
+ flex: 1;
210
+ padding: 32px;
211
+ overflow-y: auto;
212
+ }
213
+
214
+ /* Cards & Grid */
215
+ .metrics-grid,
216
+ .pools-grid,
217
+ .cache-grid {
218
+ display: grid;
219
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
220
+ gap: 24px;
221
+ margin-bottom: 32px;
222
+ }
223
+
224
+ .card {
225
+ background-color: var(--bg-card);
226
+ padding: 24px;
227
+ border-radius: 12px;
228
+ border: 1px solid var(--border);
229
+ box-shadow: var(--shadow);
230
+ }
231
+
232
+ .card-header {
233
+ display: flex;
234
+ align-items: center;
235
+ gap: 12px;
236
+ margin-bottom: 16px;
237
+ }
238
+
239
+ .card-header .material-symbols-rounded {
240
+ color: var(--accent);
241
+ }
242
+
243
+ .card h3 {
244
+ margin: 0;
245
+ font-size: 0.875rem;
246
+ text-transform: uppercase;
247
+ letter-spacing: 0.05em;
248
+ color: var(--text-main);
249
+ }
250
+
251
+ .card p {
252
+ font-size: 2rem;
253
+ font-weight: 700;
254
+ margin: 0;
255
+ color: var(--text-bright);
256
+ }
257
+
258
+ .card small {
259
+ display: block;
260
+ margin-top: 8px;
261
+ font-size: 0.875rem;
262
+ color: var(--text-main);
263
+ }
264
+
265
+ .card.warning {
266
+ border-left: 4px solid var(--warning);
267
+ }
268
+
269
+ /* Table */
270
+ .table-container {
271
+ background: var(--bg-card);
272
+ border-radius: 12px;
273
+ border: 1px solid var(--border);
274
+ overflow: hidden;
275
+ box-shadow: var(--shadow);
276
+ }
277
+
278
+ table {
279
+ width: 100%;
280
+ border-collapse: collapse;
281
+ }
282
+
283
+ th,
284
+ td {
285
+ padding: 16px 24px;
286
+ text-align: left;
287
+ border-bottom: 1px solid var(--border);
288
+ }
289
+
290
+ th {
291
+ background-color: var(--bg-hover);
292
+ color: var(--text-main);
293
+ font-size: 0.75rem;
294
+ text-transform: uppercase;
295
+ letter-spacing: 0.05em;
296
+ font-weight: 600;
297
+ }
298
+
299
+ td {
300
+ color: var(--text-bright);
301
+ font-size: 0.9375rem;
302
+ }
303
+
304
+ tr:last-child td {
305
+ border-bottom: none;
306
+ }
307
+
308
+ /* Access Log & Config */
309
+ .terminal-container,
310
+ .config-container {
311
+ background-color: var(--bg-side);
312
+ border-radius: 12px;
313
+ border: 1px solid var(--border);
314
+ overflow: hidden;
315
+ box-shadow: var(--shadow);
316
+ }
317
+
318
+ .terminal {
319
+ background-color: var(--terminal-bg);
320
+ color: #10b981;
321
+ padding: 24px;
322
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
323
+ font-size: 0.875rem;
324
+ line-height: 1.6;
325
+ margin: 0;
326
+ overflow-x: auto;
327
+ white-space: pre; /* No wrapping */
328
+ }
329
+
330
+ .config-view-content {
331
+ padding: 0;
332
+ max-width: 900px;
333
+ margin: 0 auto;
334
+ }
335
+
336
+ .config-table {
337
+ width: 100%;
338
+ border-collapse: collapse;
339
+ }
340
+
341
+ .config-table tr {
342
+ border-bottom: 1px solid var(--border);
343
+ }
344
+
345
+ .config-table tr:last-child {
346
+ border-bottom: none;
347
+ }
348
+
349
+ .config-table td {
350
+ padding: 16px 24px;
351
+ vertical-align: top;
352
+ }
353
+
354
+ .config-label {
355
+ font-weight: 600;
356
+ color: var(--text-main);
357
+ width: 240px;
358
+ font-size: 0.875rem;
359
+ background-color: var(--bg-main);
360
+ }
361
+
362
+ .config-value {
363
+ font-family: 'JetBrains Mono', monospace;
364
+ color: var(--text-bright);
365
+ word-break: break-all;
366
+ font-size: 0.875rem;
367
+ }
368
+
369
+ .config-section-header {
370
+ background-color: var(--bg-hover);
371
+ padding: 12px 24px;
372
+ font-weight: 700;
373
+ font-size: 0.75rem;
374
+ text-transform: uppercase;
375
+ color: var(--accent);
376
+ letter-spacing: 0.05em;
377
+ border-bottom: 1px solid var(--border);
378
+ }
379
+
380
+ .inner-config-table {
381
+ width: 100%;
382
+ border-collapse: collapse;
383
+ font-size: 0.8125rem;
384
+ }
385
+
386
+ .inner-config-table td {
387
+ padding: 4px 8px;
388
+ border: none;
389
+ }
390
+
391
+ /* Cache card specific enhancements */
392
+ .cache-card .badge {
393
+ align-self: center;
394
+ }
395
+
396
+ /* PL/SQL Route card enhancements */
397
+ .card-header h4 {
398
+ color: var(--accent);
399
+ }
400
+
401
+ .stat-row-label {
402
+ flex: 0 0 180px;
403
+ white-space: nowrap;
404
+ overflow: hidden;
405
+ text-overflow: ellipsis;
406
+ }
407
+
408
+ .stat-row-value {
409
+ flex: 1;
410
+ text-align: right;
411
+ }
412
+
413
+ @media (max-width: 640px) {
414
+ .stat-row {
415
+ flex-direction: column;
416
+ align-items: flex-start;
417
+ gap: 4px;
418
+ }
419
+ .stat-row-label {
420
+ flex: none;
421
+ opacity: 0.7;
422
+ font-size: 0.75rem;
423
+ text-align: left;
424
+ }
425
+ .stat-row-value {
426
+ text-align: left;
427
+ width: 100%;
428
+ }
429
+ }
430
+
431
+ /* System View Compact */
432
+ .view-system-compact {
433
+ padding: 20px 32px;
434
+ }
435
+
436
+ .view-system-compact .metrics-grid {
437
+ display: grid;
438
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
439
+ gap: 16px;
440
+ align-items: start;
441
+ margin-bottom: 24px;
442
+ }
443
+
444
+ .view-system-compact .card {
445
+ padding: 16px;
446
+ }
447
+
448
+ .view-system-compact .stat-row {
449
+ padding: 4px 0;
450
+ }
451
+
452
+ .view-system-compact .card-header {
453
+ margin-bottom: 12px;
454
+ }
455
+
456
+ .resource-card {
457
+ display: flex;
458
+ flex-direction: column;
459
+ min-height: 350px;
460
+ }
461
+
462
+ .resource-card .chart-container-mini {
463
+ margin-top: auto;
464
+ }
465
+
466
+ .chart-container-mini {
467
+ height: 100px;
468
+ width: 100%;
469
+ position: relative;
470
+ overflow: hidden;
471
+ border: none;
472
+ border-radius: 4px;
473
+ }
474
+
475
+ /* Theme Toggle */
476
+
477
+ /* Chart Styles */
478
+ .chart-container {
479
+ margin-bottom: 32px;
480
+ height: 300px;
481
+ position: relative;
482
+ }
483
+
484
+ /* Buttons - Unified base styles */
485
+ .btn,
486
+ .icon-btn {
487
+ display: inline-flex;
488
+ align-items: center;
489
+ justify-content: center;
490
+ gap: 8px;
491
+ padding: 10px 20px;
492
+ border-radius: 8px;
493
+ font-weight: 600;
494
+ font-size: 0.875rem;
495
+ cursor: pointer;
496
+ border: none;
497
+ transition: all 0.2s;
498
+ background-color: var(--bg-hover);
499
+ color: var(--text-bright);
500
+ }
501
+
502
+ .btn .material-symbols-rounded,
503
+ .icon-btn .material-symbols-rounded {
504
+ font-size: 18px;
505
+ }
506
+
507
+ .btn:hover,
508
+ .icon-btn:hover {
509
+ background-color: var(--border);
510
+ }
511
+
512
+ .btn-primary {
513
+ background-color: var(--accent);
514
+ color: white;
515
+ }
516
+
517
+ .btn-primary:hover {
518
+ opacity: 0.9;
519
+ background-color: var(--accent);
520
+ }
521
+
522
+ .btn-success {
523
+ background-color: var(--success);
524
+ color: white;
525
+ }
526
+
527
+ .btn-success:hover {
528
+ opacity: 0.9;
529
+ }
530
+
531
+ .btn-warning {
532
+ background-color: var(--warning);
533
+ color: #000;
534
+ }
535
+
536
+ .btn-warning:hover {
537
+ opacity: 0.9;
538
+ }
539
+
540
+ .btn-danger {
541
+ background-color: var(--danger);
542
+ color: white;
543
+ }
544
+
545
+ .btn-danger:hover {
546
+ opacity: 0.9;
547
+ }
548
+
549
+ .btn-outline {
550
+ background: transparent;
551
+ border: 1px solid var(--border);
552
+ color: var(--accent);
553
+ }
554
+
555
+ .btn-outline:hover {
556
+ background-color: var(--bg-hover);
557
+ }
558
+
559
+ .btn-full {
560
+ width: 100%;
561
+ }
562
+
563
+ .btn-sm {
564
+ padding: 6px 12px;
565
+ font-size: 0.8125rem;
566
+ }
567
+
568
+ .btn-sm .material-symbols-rounded {
569
+ font-size: 16px;
570
+ }
571
+
572
+ .btn-lg {
573
+ padding: 14px 28px;
574
+ font-size: 1rem;
575
+ }
576
+
577
+ .btn-lg .material-symbols-rounded {
578
+ font-size: 22px;
579
+ }
580
+
581
+ /* Navigation buttons */
582
+ nav button {
583
+ width: 100%;
584
+ padding: 12px 16px;
585
+ text-align: left;
586
+ background: none;
587
+ border: none;
588
+ color: var(--text-main);
589
+ cursor: pointer;
590
+ font-size: 0.95rem;
591
+ display: flex;
592
+ align-items: center;
593
+ gap: 12px;
594
+ border-radius: 8px;
595
+ transition: all 0.2s;
596
+ margin-bottom: 4px;
597
+ }
598
+
599
+ nav button:hover {
600
+ background-color: var(--bg-hover);
601
+ color: var(--text-bright);
602
+ }
603
+
604
+ nav button.active {
605
+ background-color: var(--accent);
606
+ color: white;
607
+ }
608
+
609
+ nav button .material-symbols-rounded {
610
+ font-size: 20px;
611
+ }
612
+
613
+ /* Icon-only buttons */
614
+ .icon-btn {
615
+ padding: 10px;
616
+ min-width: 40px;
617
+ min-height: 40px;
618
+ }
619
+
620
+ .icon-btn.icon-sm {
621
+ padding: 6px;
622
+ min-width: 32px;
623
+ min-height: 32px;
624
+ }
625
+
626
+ .icon-btn.icon-sm .material-symbols-rounded {
627
+ font-size: 20px;
628
+ }
629
+
630
+ .control-group {
631
+ display: flex;
632
+ gap: 16px;
633
+ margin-top: 24px;
634
+ flex-wrap: wrap;
635
+ }
636
+
637
+ .control-group .btn {
638
+ flex: 1;
639
+ min-width: 140px;
640
+ }
641
+
642
+ /* Utility Classes for Visual Polish */
643
+
644
+ /* Spacing utilities */
645
+ .gap-1 {
646
+ gap: 4px;
647
+ }
648
+ .gap-2 {
649
+ gap: 8px;
650
+ }
651
+ .gap-3 {
652
+ gap: 12px;
653
+ }
654
+ .gap-4 {
655
+ gap: 16px;
656
+ }
657
+ .gap-6 {
658
+ gap: 24px;
659
+ }
660
+
661
+ .mt-1 {
662
+ margin-top: 4px;
663
+ }
664
+ .mt-2 {
665
+ margin-top: 8px;
666
+ }
667
+ .mt-3 {
668
+ margin-top: 12px;
669
+ }
670
+ .mt-4 {
671
+ margin-top: 16px;
672
+ }
673
+ .mt-6 {
674
+ margin-top: 24px;
675
+ }
676
+ .mb-1 {
677
+ margin-bottom: 4px;
678
+ }
679
+ .mb-2 {
680
+ margin-bottom: 8px;
681
+ }
682
+ .mb-3 {
683
+ margin-bottom: 12px;
684
+ }
685
+ .mb-4 {
686
+ margin-bottom: 16px;
687
+ }
688
+ .mb-6 {
689
+ margin-bottom: 24px;
690
+ }
691
+
692
+ .p-2 {
693
+ padding: 8px;
694
+ }
695
+ .p-3 {
696
+ padding: 12px;
697
+ }
698
+ .p-4 {
699
+ padding: 16px;
700
+ }
701
+ .p-6 {
702
+ padding: 24px;
703
+ }
704
+
705
+ .mx-auto {
706
+ margin-left: auto;
707
+ margin-right: auto;
708
+ }
709
+
710
+ /* Flex utilities */
711
+ .flex {
712
+ display: flex;
713
+ }
714
+ .flex-col {
715
+ flex-direction: column;
716
+ }
717
+ .flex-wrap {
718
+ flex-wrap: wrap;
719
+ }
720
+ .items-center {
721
+ align-items: center;
722
+ }
723
+ .items-baseline {
724
+ align-items: baseline;
725
+ }
726
+ .justify-between {
727
+ justify-content: space-between;
728
+ }
729
+ .justify-center {
730
+ justify-content: center;
731
+ }
732
+ .justify-end {
733
+ justify-content: flex-end;
734
+ }
735
+ .self-center {
736
+ align-self: center;
737
+ }
738
+ .self-end {
739
+ align-self: flex-end;
740
+ }
741
+
742
+ /* Text utilities */
743
+ .text-xs {
744
+ font-size: 0.75rem;
745
+ }
746
+ .text-sm {
747
+ font-size: 0.875rem;
748
+ }
749
+ .text-lg {
750
+ font-size: 1.125rem;
751
+ }
752
+ .text-xl {
753
+ font-size: 1.25rem;
754
+ }
755
+ .text-2xl {
756
+ font-size: 1.5rem;
757
+ }
758
+ .text-3xl {
759
+ font-size: 1.875rem;
760
+ }
761
+ .font-medium {
762
+ font-weight: 500;
763
+ }
764
+ .font-semibold {
765
+ font-weight: 600;
766
+ }
767
+ .font-bold {
768
+ font-weight: 700;
769
+ }
770
+ .text-center {
771
+ text-align: center;
772
+ }
773
+ .text-right {
774
+ text-align: right;
775
+ }
776
+
777
+ .text-main {
778
+ color: var(--text-main);
779
+ }
780
+ .text-bright {
781
+ color: var(--text-bright);
782
+ }
783
+ .text-accent {
784
+ color: var(--accent);
785
+ }
786
+ .text-success {
787
+ color: var(--success);
788
+ }
789
+ .text-warning {
790
+ color: var(--warning);
791
+ }
792
+ .text-danger {
793
+ color: var(--danger);
794
+ }
795
+
796
+ /* Background utilities */
797
+ .bg-main {
798
+ background-color: var(--bg-main);
799
+ }
800
+ .bg-card {
801
+ background-color: var(--bg-card);
802
+ }
803
+ .bg-hover {
804
+ background-color: var(--bg-hover);
805
+ }
806
+ .bg-success {
807
+ background-color: var(--success);
808
+ }
809
+ .bg-warning {
810
+ background-color: var(--warning);
811
+ }
812
+ .bg-danger {
813
+ background-color: var(--danger);
814
+ }
815
+
816
+ /* Border utilities */
817
+ .border {
818
+ border: 1px solid var(--border);
819
+ }
820
+ .border-b {
821
+ border-bottom: 1px solid var(--border);
822
+ }
823
+ .border-none {
824
+ border: none;
825
+ }
826
+ .border-t {
827
+ border-top: 1px solid var(--border);
828
+ }
829
+ .border-l {
830
+ border-left: 1px solid var(--border);
831
+ }
832
+ .border-r {
833
+ border-right: 1px solid var(--border);
834
+ }
835
+ .rounded {
836
+ border-radius: 4px;
837
+ }
838
+ .rounded-md {
839
+ border-radius: 6px;
840
+ }
841
+ .rounded-lg {
842
+ border-radius: 8px;
843
+ }
844
+ .rounded-xl {
845
+ border-radius: 12px;
846
+ }
847
+ .rounded-full {
848
+ border-radius: 9999px;
849
+ }
850
+
851
+ /* Shadow */
852
+ .shadow-sm {
853
+ box-shadow: var(--shadow);
854
+ }
855
+ .shadow-md {
856
+ box-shadow:
857
+ 0 4px 6px -1px rgb(0 0 0 / 0.1),
858
+ 0 2px 4px -2px rgb(0 0 0 / 0.1);
859
+ }
860
+
861
+ /* Width utilities */
862
+ .w-full {
863
+ width: 100%;
864
+ }
865
+ .w-auto {
866
+ width: auto;
867
+ }
868
+ .max-w-xs {
869
+ max-width: 120px;
870
+ }
871
+ .max-w-sm {
872
+ max-width: 200px;
873
+ }
874
+ .max-w-md {
875
+ max-width: 280px;
876
+ }
877
+
878
+ /* Overflow */
879
+ .overflow-hidden {
880
+ overflow: hidden;
881
+ }
882
+ .overflow-auto {
883
+ overflow: auto;
884
+ }
885
+ .overflow-x-auto {
886
+ overflow-x: auto;
887
+ }
888
+
889
+ /* Cursor */
890
+ .cursor-pointer {
891
+ cursor: pointer;
892
+ }
893
+
894
+ /* Transitions */
895
+ .transition {
896
+ transition: all 0.2s ease;
897
+ }
898
+ .transition-colors {
899
+ transition:
900
+ background-color 0.2s,
901
+ color 0.2s,
902
+ border-color 0.2s;
903
+ }
904
+ .transition-all {
905
+ transition: all 0.2s ease;
906
+ }
907
+ .duration-200 {
908
+ transition-duration: 0.2s;
909
+ }
910
+ .duration-300 {
911
+ transition-duration: 0.3s;
912
+ }
913
+
914
+ /* Hover states */
915
+ .hover-bg:hover {
916
+ background-color: var(--bg-hover);
917
+ }
918
+ .hover-border:hover {
919
+ border-color: var(--accent);
920
+ }
921
+
922
+ /* Opacity */
923
+ .opacity-50 {
924
+ opacity: 0.5;
925
+ }
926
+ .opacity-75 {
927
+ opacity: 0.75;
928
+ }
929
+ .opacity-90 {
930
+ opacity: 0.9;
931
+ }
932
+
933
+ /* Line height */
934
+ .leading-tight {
935
+ line-height: 1.25;
936
+ }
937
+ .leading-normal {
938
+ line-height: 1.5;
939
+ }
940
+ .leading-relaxed {
941
+ line-height: 1.625;
942
+ }
943
+
944
+ /* Whitespace */
945
+ .whitespace-normal {
946
+ white-space: normal;
947
+ }
948
+ .whitespace-nowrap {
949
+ white-space: nowrap;
950
+ }
951
+ .break-all {
952
+ word-break: break-all;
953
+ }
954
+
955
+ /* Grid utilities */
956
+ .grid {
957
+ display: grid;
958
+ }
959
+ .grid-cols-2 {
960
+ grid-template-columns: repeat(2, 1fr);
961
+ }
962
+ .grid-cols-3 {
963
+ grid-template-columns: repeat(3, 1fr);
964
+ }
965
+ .grid-cols-auto {
966
+ grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
967
+ }
968
+ .gap-2 {
969
+ gap: 8px;
970
+ }
971
+ .gap-3 {
972
+ gap: 12px;
973
+ }
974
+ .gap-4 {
975
+ gap: 16px;
976
+ }
977
+ .gap-6 {
978
+ gap: 24px;
979
+ }
980
+
981
+ /* Stat card specific */
982
+ .stat-card {
983
+ background-color: var(--bg-card);
984
+ padding: 16px;
985
+ border-radius: 8px;
986
+ border: 1px solid var(--border);
987
+ text-align: center;
988
+ }
989
+
990
+ .stat-card-value {
991
+ font-size: 1.75rem;
992
+ font-weight: 700;
993
+ color: var(--text-bright);
994
+ line-height: 1.2;
995
+ }
996
+
997
+ .stat-card-label {
998
+ font-size: 0.75rem;
999
+ text-transform: uppercase;
1000
+ letter-spacing: 0.05em;
1001
+ color: var(--text-main);
1002
+ margin-top: 4px;
1003
+ }
1004
+
1005
+ /* Progress bar */
1006
+ .progress-bar {
1007
+ height: 6px;
1008
+ background: var(--bg-main);
1009
+ border-radius: 3px;
1010
+ overflow: hidden;
1011
+ }
1012
+
1013
+ .progress-bar-fill {
1014
+ height: 100%;
1015
+ border-radius: 3px;
1016
+ transition: width 0.3s ease;
1017
+ }
1018
+
1019
+ .progress-bar-sm {
1020
+ height: 4px;
1021
+ }
1022
+
1023
+ .progress-bar-lg {
1024
+ height: 8px;
1025
+ }
1026
+
1027
+ /* Badge */
1028
+ .badge {
1029
+ display: inline-flex;
1030
+ align-items: center;
1031
+ padding: 4px 8px;
1032
+ border-radius: 9999px;
1033
+ font-size: 0.75rem;
1034
+ font-weight: 600;
1035
+ gap: 4px;
1036
+ }
1037
+
1038
+ .badge-success {
1039
+ background-color: rgba(16, 185, 129, 0.15);
1040
+ color: var(--success);
1041
+ }
1042
+
1043
+ .badge-warning {
1044
+ background-color: rgba(245, 158, 11, 0.15);
1045
+ color: var(--warning);
1046
+ }
1047
+
1048
+ .badge-danger {
1049
+ background-color: rgba(239, 68, 68, 0.15);
1050
+ color: var(--danger);
1051
+ }
1052
+
1053
+ .badge-info {
1054
+ background-color: rgba(59, 130, 246, 0.15);
1055
+ color: var(--accent);
1056
+ }
1057
+
1058
+ /* Status dot with badge */
1059
+ .status-dot {
1060
+ display: inline-flex;
1061
+ align-items: center;
1062
+ gap: 6px;
1063
+ font-size: 0.875rem;
1064
+ font-weight: 500;
1065
+ }
1066
+
1067
+ .status-dot .dot {
1068
+ margin: 0;
1069
+ flex-shrink: 0;
1070
+ }
1071
+
1072
+ /* Table improvements */
1073
+ .table-row-hover tbody tr {
1074
+ transition: background-color 0.15s ease;
1075
+ }
1076
+
1077
+ .table-row-hover tbody tr:hover {
1078
+ background-color: var(--bg-hover);
1079
+ }
1080
+
1081
+ .table-cell {
1082
+ padding: 12px 16px;
1083
+ text-align: left;
1084
+ border-bottom: 1px solid var(--border);
1085
+ }
1086
+
1087
+ .table-cell-sm {
1088
+ padding: 8px 12px;
1089
+ font-size: 0.8125rem;
1090
+ }
1091
+
1092
+ .table-header {
1093
+ background-color: var(--bg-hover);
1094
+ color: var(--text-main);
1095
+ font-size: 0.75rem;
1096
+ text-transform: uppercase;
1097
+ letter-spacing: 0.05em;
1098
+ font-weight: 600;
1099
+ padding: 12px 16px;
1100
+ text-align: left;
1101
+ }
1102
+
1103
+ /* Dense table variant */
1104
+ .dense-table th,
1105
+ .dense-table td {
1106
+ padding: 6px 12px;
1107
+ font-size: 0.8125rem;
1108
+ }
1109
+
1110
+ /* Code style */
1111
+ .code {
1112
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
1113
+ font-size: 0.875rem;
1114
+ background-color: var(--bg-main);
1115
+ padding: 2px 6px;
1116
+ border-radius: 4px;
1117
+ color: var(--accent);
1118
+ }
1119
+
1120
+ .code-sm {
1121
+ font-size: 0.75rem;
1122
+ padding: 1px 4px;
1123
+ }
1124
+
1125
+ /* Description text */
1126
+ .description {
1127
+ font-size: 0.75rem;
1128
+ color: var(--text-main);
1129
+ font-weight: 400;
1130
+ }
1131
+
1132
+ /* Inline stat row */
1133
+ .stat-row {
1134
+ display: flex;
1135
+ justify-content: space-between;
1136
+ align-items: center;
1137
+ padding: 8px 0;
1138
+ border-bottom: 1px solid var(--border);
1139
+ }
1140
+
1141
+ .stat-row:last-child {
1142
+ border-bottom: none;
1143
+ }
1144
+
1145
+ .stat-row-label {
1146
+ color: var(--text-main);
1147
+ font-size: 0.875rem;
1148
+ }
1149
+
1150
+ .stat-row-value {
1151
+ color: var(--text-bright);
1152
+ font-weight: 600;
1153
+ font-family: monospace;
1154
+ }
1155
+
1156
+ /* Card section */
1157
+ .card-section {
1158
+ margin-bottom: 24px;
1159
+ }
1160
+
1161
+ .card-section:last-child {
1162
+ margin-bottom: 0;
1163
+ }
1164
+
1165
+ /* Clickable card */
1166
+ .card-clickable {
1167
+ cursor: pointer;
1168
+ transition: all 0.2s ease;
1169
+ }
1170
+
1171
+ .card-clickable:hover {
1172
+ border-color: var(--accent);
1173
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
1174
+ }
1175
+
1176
+ /* Icon size utilities */
1177
+ .icon-xs {
1178
+ font-size: 14px;
1179
+ }
1180
+ .icon-sm {
1181
+ font-size: 18px;
1182
+ }
1183
+ .icon-md {
1184
+ font-size: 20px;
1185
+ }
1186
+ .icon-lg {
1187
+ font-size: 24px;
1188
+ }
1189
+ .icon-xl {
1190
+ font-size: 28px;
1191
+ }
1192
+
1193
+ /* Modal */
1194
+ .modal {
1195
+ position: fixed;
1196
+ inset: 0;
1197
+ background-color: rgba(0, 0, 0, 0.7);
1198
+ display: flex;
1199
+ align-items: center;
1200
+ justify-content: center;
1201
+ z-index: 1000;
1202
+ padding: 20px;
1203
+ }
1204
+
1205
+ .modal-content {
1206
+ background-color: var(--bg-card);
1207
+ border-radius: 12px;
1208
+ width: 100%;
1209
+ max-width: 900px;
1210
+ box-shadow: var(--shadow);
1211
+ display: flex;
1212
+ flex-direction: column;
1213
+ max-height: 90vh;
1214
+ }
1215
+
1216
+ .modal-header {
1217
+ padding: 16px 24px;
1218
+ border-bottom: 1px solid var(--border);
1219
+ display: flex;
1220
+ justify-content: space-between;
1221
+ align-items: center;
1222
+ }
1223
+
1224
+ .modal-header h3 {
1225
+ margin: 0;
1226
+ font-size: 1.125rem;
1227
+ color: var(--text-bright);
1228
+ }
1229
+
1230
+ .modal-body {
1231
+ padding: 24px;
1232
+ overflow-y: auto;
1233
+ }
1234
+
1235
+ .stat-values-inline {
1236
+ display: flex;
1237
+ align-items: center;
1238
+ gap: 8px;
1239
+ }
1240
+
1241
+ /* Stat values for min/max */
1242
+ .stat-values {
1243
+ display: flex;
1244
+ flex-direction: column;
1245
+ align-items: flex-end;
1246
+ }
1247
+
1248
+ .min-max {
1249
+ font-size: 0.7rem;
1250
+ opacity: 0.7;
1251
+ margin-top: 2px;
1252
+ }
1253
+
1254
+ /* Search Group */
1255
+ .search-group {
1256
+ position: relative;
1257
+ display: flex;
1258
+ align-items: center;
1259
+ }
1260
+
1261
+ .input-sm {
1262
+ background-color: var(--bg-side);
1263
+ border: 1px solid var(--border);
1264
+ border-radius: 6px;
1265
+ color: var(--text-bright);
1266
+ padding: 6px 12px;
1267
+ font-size: 0.875rem;
1268
+ }
1269
+
1270
+ .input-sm:focus {
1271
+ border-color: var(--accent);
1272
+ outline: none;
1273
+ }
1274
+
1275
+ .errors-table-row {
1276
+ cursor: pointer;
1277
+ transition: background-color 0.2s;
1278
+ }
1279
+
1280
+ .errors-table-row:hover {
1281
+ background-color: rgba(var(--accent-rgb), 0.08);
1282
+ }
1283
+
1284
+ /* Server status indicator enhanced */
1285
+ .server-status {
1286
+ display: flex;
1287
+ align-items: center;
1288
+ gap: 8px;
1289
+ padding: 8px 12px;
1290
+ background-color: var(--bg-card);
1291
+ border-radius: 8px;
1292
+ border: 1px solid var(--border);
1293
+ }
1294
+
1295
+ .server-status-indicator {
1296
+ display: flex;
1297
+ align-items: center;
1298
+ gap: 8px;
1299
+ }
1300
+
1301
+ .server-status-dot {
1302
+ width: 10px;
1303
+ height: 10px;
1304
+ border-radius: 50%;
1305
+ flex-shrink: 0;
1306
+ }
1307
+
1308
+ .server-status-dot.running {
1309
+ background-color: var(--success);
1310
+ box-shadow: 0 0 8px rgba(16, 185, 129, 0.4);
1311
+ }
1312
+
1313
+ .server-status-dot.paused {
1314
+ background-color: var(--warning);
1315
+ box-shadow: 0 0 8px rgba(245, 158, 11, 0.4);
1316
+ }
1317
+
1318
+ .server-status-dot.stopped {
1319
+ background-color: var(--danger);
1320
+ box-shadow: 0 0 8px rgba(239, 68, 68, 0.4);
1321
+ }
1322
+
1323
+ .server-status-text {
1324
+ font-size: 0.875rem;
1325
+ font-weight: 500;
1326
+ text-transform: uppercase;
1327
+ letter-spacing: 0.025em;
1328
+ }
1329
+
1330
+ /* Danger zone card */
1331
+ .danger-zone {
1332
+ border: 1px solid var(--warning);
1333
+ border-left-width: 4px;
1334
+ background-color: rgba(245, 158, 11, 0.03);
1335
+ }
1336
+
1337
+ .danger-zone-title {
1338
+ color: var(--warning);
1339
+ font-weight: 600;
1340
+ margin-bottom: 8px;
1341
+ }
1342
+
1343
+ /* Empty state */
1344
+ .empty-state {
1345
+ text-align: center;
1346
+ padding: 48px 24px;
1347
+ color: var(--text-main);
1348
+ }
1349
+
1350
+ .empty-state-icon {
1351
+ font-size: 48px;
1352
+ color: var(--text-main);
1353
+ opacity: 0.5;
1354
+ margin-bottom: 16px;
1355
+ }
1356
+
1357
+ .empty-state-text {
1358
+ font-size: 0.875rem;
1359
+ }
1360
+
1361
+ /* Loading skeleton */
1362
+ .skeleton {
1363
+ background: linear-gradient(90deg, var(--bg-main) 25%, var(--bg-hover) 50%, var(--bg-main) 75%);
1364
+ background-size: 200% 100%;
1365
+ animation: skeleton-loading 1.5s infinite;
1366
+ border-radius: 4px;
1367
+ }
1368
+
1369
+ @keyframes skeleton-loading {
1370
+ 0% {
1371
+ background-position: 200% 0;
1372
+ }
1373
+ 100% {
1374
+ background-position: -200% 0;
1375
+ }
1376
+ }
1377
+
1378
+ /* Scrollbar styling */
1379
+ ::-webkit-scrollbar {
1380
+ width: 8px;
1381
+ height: 8px;
1382
+ }
1383
+
1384
+ ::-webkit-scrollbar-track {
1385
+ background: var(--bg-main);
1386
+ }
1387
+
1388
+ ::-webkit-scrollbar-thumb {
1389
+ background: var(--border);
1390
+ border-radius: 4px;
1391
+ }
1392
+
1393
+ ::-webkit-scrollbar-thumb:hover {
1394
+ background: var(--text-main);
1395
+ opacity: 0.5;
1396
+ }
1397
+
1398
+ /* Focus styles for accessibility */
1399
+ button:focus-visible,
1400
+ input:focus-visible,
1401
+ select:focus-visible {
1402
+ outline: 2px solid var(--accent);
1403
+ outline-offset: 2px;
1404
+ }
1405
+
1406
+ /* Input styling */
1407
+ input[type='checkbox'] {
1408
+ width: 18px;
1409
+ height: 18px;
1410
+ accent-color: var(--accent);
1411
+ cursor: pointer;
1412
+ }
1413
+
1414
+ select {
1415
+ accent-color: var(--accent);
1416
+ cursor: pointer;
1417
+ }
1418
+
1419
+ /* Animations */
1420
+ @keyframes spin {
1421
+ from {
1422
+ transform: rotate(0deg);
1423
+ }
1424
+ to {
1425
+ transform: rotate(360deg);
1426
+ }
1427
+ }
1428
+
1429
+ .animate-spin {
1430
+ animation: spin 1s linear infinite;
1431
+ display: inline-block;
1432
+ }
1433
+
1434
+ .btn.loading {
1435
+ pointer-events: none;
1436
+ opacity: 0.8;
1437
+ }