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