4bnode 4.1.5 → 4.1.7

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 (68) hide show
  1. package/add-api.js +5 -3
  2. package/add-email.js +118 -28
  3. package/add-login.js +35 -16
  4. package/add-mongo-schema.js +13 -5
  5. package/lib/codegen.js +191 -57
  6. package/lib/codegen.test.js +87 -15
  7. package/lib/names.js +1 -1
  8. package/package.json +1 -1
  9. package/skeleton/.4bnode/assets/js/pages/ApiTesterPage.js +54 -9
  10. package/skeleton/.4bnode/assets/js/pages/LogsPage.js +49 -16
  11. package/skeleton/.4bnode/assets/js/pages/MailPage.js +110 -50
  12. package/skeleton/.4bnode/codegen.js +191 -57
  13. package/skeleton/.4bnode/dev-api.js +284 -56
  14. package/skeleton/.env.example +17 -3
  15. package/skeleton/_gitignore +1 -0
  16. package/skeleton/index.js +27 -4
  17. package/skeleton/src/middleware/apiKey.js +14 -1
  18. package/skeleton/src/middleware/errorHandler.js +7 -2
  19. package/app-1/.4bnode/.passkey +0 -1
  20. package/app-1/.4bnode/assets/fonts/NotoSans-Bold.woff2 +0 -0
  21. package/app-1/.4bnode/assets/fonts/NotoSans-Light.woff2 +0 -0
  22. package/app-1/.4bnode/assets/fonts/NotoSans-Medium.woff2 +0 -0
  23. package/app-1/.4bnode/assets/fonts/NotoSans-Regular.woff2 +0 -0
  24. package/app-1/.4bnode/assets/fonts/NotoSans-SemiBold.woff2 +0 -0
  25. package/app-1/.4bnode/assets/images/mongo.png +0 -0
  26. package/app-1/.4bnode/assets/js/App.js +0 -139
  27. package/app-1/.4bnode/assets/js/Root.js +0 -40
  28. package/app-1/.4bnode/assets/js/components/Modal.js +0 -52
  29. package/app-1/.4bnode/assets/js/components/Sidebar.js +0 -73
  30. package/app-1/.4bnode/assets/js/components/ToastContainer.js +0 -15
  31. package/app-1/.4bnode/assets/js/context.js +0 -7
  32. package/app-1/.4bnode/assets/js/lib.js +0 -113
  33. package/app-1/.4bnode/assets/js/main.js +0 -5
  34. package/app-1/.4bnode/assets/js/pages/AiBuilderPage.js +0 -268
  35. package/app-1/.4bnode/assets/js/pages/ApiTesterPage.js +0 -1879
  36. package/app-1/.4bnode/assets/js/pages/DashboardPage.js +0 -435
  37. package/app-1/.4bnode/assets/js/pages/DatabasePage.js +0 -300
  38. package/app-1/.4bnode/assets/js/pages/DocsPage.js +0 -150
  39. package/app-1/.4bnode/assets/js/pages/EnvironmentPage.js +0 -103
  40. package/app-1/.4bnode/assets/js/pages/LogsPage.js +0 -205
  41. package/app-1/.4bnode/assets/js/pages/MailPage.js +0 -224
  42. package/app-1/.4bnode/assets/js/pages/RealtimePage.js +0 -186
  43. package/app-1/.4bnode/assets/js/pages/RoutesPage.js +0 -1485
  44. package/app-1/.4bnode/assets/js/pages/SchemasPage.js +0 -838
  45. package/app-1/.4bnode/assets/js/pages/SecurityPage.js +0 -74
  46. package/app-1/.4bnode/assets/js/pages/SerialPortPage.js +0 -170
  47. package/app-1/.4bnode/assets/js/screens/PasskeyScreen.js +0 -105
  48. package/app-1/.4bnode/codegen.js +0 -1440
  49. package/app-1/.4bnode/dev-api.js +0 -3747
  50. package/app-1/.4bnode/ui.html +0 -1578
  51. package/app-1/.env.example +0 -11
  52. package/app-1/README.md +0 -47
  53. package/app-1/ecosystem.config.cjs +0 -25
  54. package/app-1/index.js +0 -188
  55. package/app-1/package.json +0 -37
  56. package/app-1/public/index.html +0 -292
  57. package/app-1/src/db.js +0 -13
  58. package/app-1/src/docs.js +0 -84
  59. package/app-1/src/middleware/apiKey.js +0 -36
  60. package/app-1/src/middleware/auth.js +0 -17
  61. package/app-1/src/middleware/errorHandler.js +0 -15
  62. package/app-1/src/middleware/roles.js +0 -14
  63. package/app-1/src/middleware/validate.js +0 -20
  64. package/app-1/src/models/user.js +0 -11
  65. package/app-1/src/openapi.json +0 -80
  66. package/app-1/src/routes/upload-resume.js +0 -21
  67. package/app-1/src/services/mailer.js +0 -52
  68. package/app-1/src/validators/upload-resume.js +0 -8
@@ -1,1578 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>4bnode Dashboard</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
11
- rel="stylesheet"
12
- />
13
- <style>
14
- @font-face {
15
- font-family: "Noto Sans";
16
- src: url("/_dev/assets/fonts/NotoSans-Light.woff2") format("woff2");
17
- font-weight: 300;
18
- font-style: normal;
19
- font-display: swap;
20
- }
21
- @font-face {
22
- font-family: "Noto Sans";
23
- src: url("/_dev/assets/fonts/NotoSans-Regular.woff2") format("woff2");
24
- font-weight: 400;
25
- font-style: normal;
26
- font-display: swap;
27
- }
28
- @font-face {
29
- font-family: "Noto Sans";
30
- src: url("/_dev/assets/fonts/NotoSans-Medium.woff2") format("woff2");
31
- font-weight: 500;
32
- font-style: normal;
33
- font-display: swap;
34
- }
35
- @font-face {
36
- font-family: "Noto Sans";
37
- src: url("/_dev/assets/fonts/NotoSans-SemiBold.woff2") format("woff2");
38
- font-weight: 600;
39
- font-style: normal;
40
- font-display: swap;
41
- }
42
- @font-face {
43
- font-family: "Noto Sans";
44
- src: url("/_dev/assets/fonts/NotoSans-Bold.woff2") format("woff2");
45
- font-weight: 700;
46
- font-style: normal;
47
- font-display: swap;
48
- }
49
-
50
- *,
51
- *::before,
52
- *::after {
53
- margin: 0;
54
- padding: 0;
55
- box-sizing: border-box;
56
- }
57
-
58
- :root {
59
- --bg: #141720;
60
- --bg-surface: #11141e;
61
- --bg-card: #1a1d2b;
62
- --bg-card-hover: #21253a;
63
- --bg-input: #13161f;
64
- --border: rgba(255, 255, 255, 0.08);
65
- --border-focus: rgba(0, 121, 255, 0.5);
66
- --text: #eceef4;
67
- --text-secondary: #8b8fa3;
68
- --text-muted: #626880;
69
- --accent: #0079ff;
70
- --accent-light: #3daefd;
71
- --accent-glow: rgba(0, 94, 255, 0.18);
72
- --success: #22c55e;
73
- --success-glow: rgba(34, 197, 94, 0.12);
74
- --warning: #f59e0b;
75
- --warning-glow: rgba(245, 158, 11, 0.12);
76
- --error: #ef4444;
77
- --error-glow: rgba(239, 68, 68, 0.12);
78
- --info: #3b82f6;
79
- --info-glow: rgba(59, 130, 246, 0.12);
80
- --radius: 8px;
81
- --radius-lg: 12px;
82
- --radius-xl: 16px;
83
- --sidebar-w: 220px;
84
- --transition: 150ms ease;
85
- }
86
-
87
- html,
88
- body {
89
- height: 100%;
90
- }
91
- body {
92
- font-family:
93
- "Noto Sans",
94
- -apple-system,
95
- BlinkMacSystemFont,
96
- sans-serif;
97
- background: var(--bg);
98
- color: var(--text);
99
- overflow: hidden;
100
- -webkit-font-smoothing: antialiased;
101
- }
102
-
103
- #root {
104
- display: flex;
105
- height: 100vh;
106
- }
107
-
108
- /* ── Scrollbar ── */
109
- ::-webkit-scrollbar {
110
- width: 6px;
111
- }
112
- ::-webkit-scrollbar-track {
113
- background: transparent;
114
- }
115
- ::-webkit-scrollbar-thumb {
116
- background: rgba(255, 255, 255, 0.08);
117
- border-radius: 3px;
118
- }
119
- ::-webkit-scrollbar-thumb:hover {
120
- background: rgba(255, 255, 255, 0.14);
121
- }
122
-
123
- /* ── Sidebar ── */
124
- .sidebar {
125
- width: var(--sidebar-w);
126
- background: var(--bg-surface);
127
- border-right: 1px solid var(--border);
128
- display: flex;
129
- flex-direction: column;
130
- flex-shrink: 0;
131
- padding: 0 0 16px;
132
- overflow-y: auto;
133
- }
134
- .sidebar-brand {
135
- height: 61px;
136
- display: flex;
137
- align-items: center;
138
- padding: 0 20px;
139
- border-bottom: 1px solid var(--border);
140
- margin-bottom: 8px;
141
- flex-shrink: 0;
142
- }
143
- .sidebar-brand pre {
144
- font-family: monospace;
145
- font-size: 4px;
146
- line-height: 1.1;
147
- letter-spacing: 0.5px;
148
- background: linear-gradient(135deg, #3daefd, #0079ff, #005eff);
149
- -webkit-background-clip: text;
150
- -webkit-text-fill-color: transparent;
151
- margin: 0 0 0 -2px;
152
- white-space: pre;
153
- user-select: none;
154
- }
155
- .sidebar-brand span {
156
- font-size: 11px;
157
- color: var(--text-muted);
158
- font-weight: 400;
159
- }
160
- .sidebar-section {
161
- padding: 12px 12px 4px;
162
- font-size: 10px;
163
- font-weight: 600;
164
- color: var(--text-muted);
165
- text-transform: uppercase;
166
- letter-spacing: 1px;
167
- }
168
- .sidebar-item {
169
- display: flex;
170
- align-items: center;
171
- gap: 10px;
172
- padding: 9px 16px;
173
- margin: 1px 8px;
174
- border-radius: var(--radius);
175
- font-size: 13px;
176
- font-weight: 500;
177
- color: var(--text-secondary);
178
- cursor: pointer;
179
- transition: all var(--transition);
180
- position: relative;
181
- }
182
- .sidebar-item:hover {
183
- background: rgba(255, 255, 255, 0.04);
184
- color: var(--text);
185
- }
186
- .sidebar-item.active {
187
- background: rgba(255, 255, 255, 0.06);
188
- color: #f0f1f5;
189
- }
190
- .sidebar-item.active::before {
191
- content: "";
192
- position: absolute;
193
- left: -8px;
194
- top: 50%;
195
- transform: translateY(-50%);
196
- width: 3px;
197
- height: 16px;
198
- border-radius: 0 3px 3px 0;
199
- background: var(--accent-light);
200
- }
201
- .sidebar-item.active .sidebar-icon {
202
- color: var(--accent-light);
203
- }
204
- .sidebar-item.locked {
205
- opacity: 0.4;
206
- cursor: not-allowed;
207
- }
208
- .sidebar-item .lock-icon {
209
- position: absolute;
210
- right: 12px;
211
- font-size: 10px;
212
- opacity: 0.6;
213
- }
214
- .sidebar-icon {
215
- width: 18px;
216
- height: 18px;
217
- display: flex;
218
- align-items: center;
219
- justify-content: center;
220
- flex-shrink: 0;
221
- }
222
- .sidebar-icon svg {
223
- width: 16px;
224
- height: 16px;
225
- stroke: currentColor;
226
- fill: none;
227
- }
228
-
229
- /* ── Main Area ── */
230
- .main {
231
- flex: 1;
232
- display: flex;
233
- flex-direction: column;
234
- overflow: hidden;
235
- }
236
- .topbar {
237
- height: 61px;
238
- display: flex;
239
- align-items: center;
240
- justify-content: space-between;
241
- padding: 0 28px;
242
- background: var(--bg-surface);
243
- border-bottom: 1px solid var(--border);
244
- flex-shrink: 0;
245
- }
246
- .topbar h2 {
247
- font-size: 16px;
248
- font-weight: 600;
249
- }
250
- .topbar-badge {
251
- font-size: 11px;
252
- padding: 3px 10px;
253
- border-radius: 20px;
254
- background: var(--accent-glow);
255
- color: var(--accent-light);
256
- font-weight: 500;
257
- }
258
- .content {
259
- flex: 1;
260
- overflow-y: auto;
261
- padding: 28px;
262
- }
263
-
264
- /* ── Cards ── */
265
- .card {
266
- background: var(--bg-card);
267
- border: 1px solid var(--border);
268
- border-radius: var(--radius-lg);
269
- padding: 20px;
270
- transition: all var(--transition);
271
- }
272
- .card:hover {
273
- border-color: rgba(255, 255, 255, 0.1);
274
- }
275
- .card-header {
276
- display: flex;
277
- align-items: center;
278
- justify-content: space-between;
279
- margin-bottom: 16px;
280
- }
281
- .card-title {
282
- font-size: 14px;
283
- font-weight: 600;
284
- }
285
- .card-subtitle {
286
- font-size: 12px;
287
- color: var(--text-muted);
288
- margin-top: 2px;
289
- }
290
-
291
- /* ── Stat Cards ── */
292
- .stats-grid {
293
- display: grid;
294
- grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
295
- gap: 16px;
296
- margin-bottom: 24px;
297
- }
298
- .stat-card {
299
- background: var(--bg-card);
300
- border: 1px solid var(--border);
301
- border-radius: var(--radius-lg);
302
- padding: 18px 20px;
303
- display: flex;
304
- flex-direction: column;
305
- gap: 8px;
306
- }
307
- .stat-card .stat-label {
308
- font-size: 12px;
309
- color: var(--text-muted);
310
- font-weight: 500;
311
- }
312
- .stat-card .stat-value {
313
- font-size: 28px;
314
- font-weight: 700;
315
- letter-spacing: -1px;
316
- }
317
- .stat-card .stat-badge {
318
- display: inline-flex;
319
- align-items: center;
320
- gap: 4px;
321
- font-size: 11px;
322
- font-weight: 500;
323
- padding: 2px 8px;
324
- border-radius: 20px;
325
- width: fit-content;
326
- }
327
- .stat-badge.success {
328
- background: var(--success-glow);
329
- color: var(--success);
330
- }
331
- .stat-badge.warning {
332
- background: var(--warning-glow);
333
- color: var(--warning);
334
- }
335
- .stat-badge.error {
336
- background: var(--error-glow);
337
- color: var(--error);
338
- }
339
- .stat-badge.info {
340
- background: var(--info-glow);
341
- color: var(--info);
342
- }
343
-
344
- /* ── Buttons ── */
345
- .btn {
346
- display: inline-flex;
347
- align-items: center;
348
- gap: 6px;
349
- padding: 8px 16px;
350
- border-radius: var(--radius);
351
- font-size: 13px;
352
- font-weight: 500;
353
- font-family: inherit;
354
- border: none;
355
- cursor: pointer;
356
- transition: all var(--transition);
357
- white-space: nowrap;
358
- }
359
- .btn:disabled {
360
- opacity: 0.5;
361
- cursor: not-allowed;
362
- }
363
- .btn-primary {
364
- background: var(--accent);
365
- color: white;
366
- box-shadow: 0 0 20px var(--accent-glow);
367
- }
368
- .btn-primary:hover:not(:disabled) {
369
- background: var(--accent-light);
370
- }
371
- .btn-secondary {
372
- background: rgba(255, 255, 255, 0.06);
373
- color: var(--text);
374
- border: 1px solid var(--border);
375
- }
376
- .btn-secondary:hover:not(:disabled) {
377
- background: rgba(255, 255, 255, 0.1);
378
- border-color: rgba(255, 255, 255, 0.12);
379
- }
380
- .btn-ghost {
381
- background: transparent;
382
- color: var(--text-secondary);
383
- }
384
- .btn-ghost:hover:not(:disabled) {
385
- background: rgba(255, 255, 255, 0.04);
386
- color: var(--text);
387
- }
388
- .btn-danger {
389
- background: var(--error-glow);
390
- color: var(--error);
391
- border: 1px solid rgba(239, 68, 68, 0.2);
392
- }
393
- .btn-danger:hover:not(:disabled) {
394
- background: rgba(239, 68, 68, 0.2);
395
- }
396
- .btn-sm {
397
- padding: 5px 10px;
398
- font-size: 12px;
399
- }
400
- .btn-icon {
401
- padding: 6px;
402
- }
403
-
404
- /* ── Form elements ── */
405
- .form-group {
406
- margin-bottom: 16px;
407
- }
408
- .form-label {
409
- display: block;
410
- font-size: 12px;
411
- font-weight: 500;
412
- color: var(--text-secondary);
413
- margin-bottom: 6px;
414
- }
415
- .form-input,
416
- .form-select {
417
- width: 100%;
418
- padding: 9px 12px;
419
- background: var(--bg-input);
420
- border: 1px solid var(--border);
421
- border-radius: var(--radius);
422
- color: var(--text);
423
- font-size: 13px;
424
- font-family: inherit;
425
- outline: none;
426
- transition: border-color var(--transition);
427
- }
428
- .form-input:focus,
429
- .form-select:focus {
430
- border-color: var(--border-focus);
431
- }
432
- .form-input::placeholder {
433
- color: var(--text-muted);
434
- }
435
- .form-select option {
436
- background: var(--bg-card);
437
- }
438
-
439
- .form-toggle {
440
- position: relative;
441
- width: 36px;
442
- height: 20px;
443
- display: inline-block;
444
- }
445
- .form-toggle input {
446
- opacity: 0;
447
- width: 0;
448
- height: 0;
449
- }
450
- .form-toggle .slider {
451
- position: absolute;
452
- cursor: pointer;
453
- inset: 0;
454
- background: rgba(255, 255, 255, 0.1);
455
- border-radius: 20px;
456
- transition: var(--transition);
457
- }
458
- .form-toggle .slider::before {
459
- content: "";
460
- position: absolute;
461
- width: 14px;
462
- height: 14px;
463
- left: 3px;
464
- bottom: 3px;
465
- background: white;
466
- border-radius: 50%;
467
- transition: var(--transition);
468
- }
469
- .form-toggle input:checked + .slider {
470
- background: var(--accent);
471
- }
472
- .form-toggle input:checked + .slider::before {
473
- transform: translateX(16px);
474
- }
475
-
476
- /* ── Grid layouts ── */
477
- .grid-2 {
478
- display: grid;
479
- grid-template-columns: 1fr 1fr;
480
- gap: 16px;
481
- }
482
- .grid-3 {
483
- display: grid;
484
- grid-template-columns: repeat(3, 1fr);
485
- gap: 16px;
486
- }
487
- .grid-4 {
488
- display: grid;
489
- grid-template-columns: repeat(4, 1fr);
490
- gap: 12px;
491
- }
492
-
493
- /* ── Badge ── */
494
- .badge {
495
- display: inline-flex;
496
- align-items: center;
497
- gap: 4px;
498
- font-size: 11px;
499
- font-weight: 500;
500
- padding: 3px 8px;
501
- border-radius: 20px;
502
- background: rgba(255, 255, 255, 0.07);
503
- color: var(--text-secondary);
504
- }
505
- .badge-success {
506
- background: var(--success-glow);
507
- color: var(--success);
508
- }
509
- .badge-warning {
510
- background: var(--warning-glow);
511
- color: var(--warning);
512
- }
513
- .badge-error {
514
- background: var(--error-glow);
515
- color: var(--error);
516
- }
517
- .badge-info {
518
- background: var(--info-glow);
519
- color: var(--info);
520
- }
521
- .badge-muted {
522
- background: rgba(255, 255, 255, 0.05);
523
- color: var(--text-muted);
524
- }
525
-
526
- /* ── DB Cards ── */
527
- .db-card {
528
- background: var(--bg-card);
529
- border: 1px solid var(--border);
530
- border-radius: var(--radius-lg);
531
- padding: 24px;
532
- text-align: center;
533
- cursor: pointer;
534
- transition: all var(--transition);
535
- position: relative;
536
- overflow: hidden;
537
- }
538
- .db-card:hover:not(.disabled) {
539
- border-color: var(--accent);
540
- box-shadow: 0 0 30px var(--accent-glow);
541
- transform: translateY(-2px);
542
- }
543
- .db-card.active {
544
- border-color: var(--accent);
545
- box-shadow: 0 0 30px var(--accent-glow);
546
- }
547
- .db-card.disabled {
548
- opacity: 0.5;
549
- cursor: not-allowed;
550
- }
551
- .db-card .db-icon {
552
- width: 36px;
553
- height: 36px;
554
- margin: 0 auto 10px;
555
- display: flex;
556
- align-items: center;
557
- justify-content: center;
558
- }
559
- .db-card .db-name {
560
- font-size: 14px;
561
- font-weight: 600;
562
- margin-bottom: 4px;
563
- }
564
- .db-card .db-desc {
565
- font-size: 11px;
566
- color: var(--text-muted);
567
- }
568
- .db-card .coming-soon {
569
- position: absolute;
570
- top: 8px;
571
- right: 8px;
572
- font-size: 9px;
573
- font-weight: 600;
574
- text-transform: uppercase;
575
- letter-spacing: 0.5px;
576
- padding: 2px 8px;
577
- border-radius: 20px;
578
- background: var(--warning-glow);
579
- color: var(--warning);
580
- }
581
-
582
- /* ── Empty State ── */
583
- .empty-state {
584
- text-align: center;
585
- padding: 48px 24px;
586
- color: var(--text-muted);
587
- }
588
- .empty-state .empty-icon {
589
- font-size: 36px;
590
- margin-bottom: 12px;
591
- opacity: 0.5;
592
- display: flex;
593
- align-items: center;
594
- justify-content: center;
595
- color: var(--text-muted);
596
- }
597
- .empty-state h3 {
598
- font-size: 15px;
599
- font-weight: 600;
600
- color: var(--text-secondary);
601
- margin-bottom: 6px;
602
- }
603
- .empty-state p {
604
- font-size: 13px;
605
- margin-bottom: 16px;
606
- }
607
-
608
- /* ── Locked State ── */
609
- .locked-state {
610
- text-align: center;
611
- padding: 60px 24px;
612
- }
613
- .locked-state .locked-icon {
614
- width: 56px;
615
- height: 56px;
616
- border-radius: 50%;
617
- background: rgba(255, 255, 255, 0.04);
618
- display: inline-flex;
619
- align-items: center;
620
- justify-content: center;
621
- font-size: 24px;
622
- margin-bottom: 16px;
623
- }
624
- .locked-state h3 {
625
- font-size: 16px;
626
- font-weight: 600;
627
- margin-bottom: 6px;
628
- }
629
- .locked-state p {
630
- font-size: 13px;
631
- color: var(--text-muted);
632
- margin-bottom: 20px;
633
- max-width: 360px;
634
- margin-left: auto;
635
- margin-right: auto;
636
- }
637
-
638
- /* ── Table ── */
639
- .table-wrap {
640
- overflow-x: auto;
641
- }
642
- table {
643
- width: 100%;
644
- border-collapse: collapse;
645
- }
646
- th {
647
- text-align: left;
648
- font-size: 11px;
649
- font-weight: 600;
650
- color: var(--text-muted);
651
- text-transform: uppercase;
652
- letter-spacing: 0.5px;
653
- padding: 10px 14px;
654
- border-bottom: 1px solid var(--border);
655
- }
656
- td {
657
- padding: 12px 14px;
658
- font-size: 13px;
659
- border-bottom: 1px solid var(--border);
660
- }
661
- tr:last-child td {
662
- border-bottom: none;
663
- }
664
- tr:hover td {
665
- background: rgba(255, 255, 255, 0.02);
666
- }
667
-
668
- /* ── Schema Builder (full page) ── */
669
- .schema-builder {
670
- display: grid;
671
- grid-template-columns: 1fr 340px;
672
- gap: 20px;
673
- min-height: calc(100vh - 160px);
674
- }
675
- .schema-builder-fields {
676
- display: flex;
677
- flex-direction: column;
678
- }
679
- .schema-builder-sidebar {
680
- position: sticky;
681
- top: 0;
682
- align-self: start;
683
- }
684
- .field-table {
685
- width: 100%;
686
- border-collapse: collapse;
687
- }
688
- .field-table th {
689
- text-align: left;
690
- font-size: 10px;
691
- font-weight: 600;
692
- color: var(--text-muted);
693
- text-transform: uppercase;
694
- letter-spacing: 0.8px;
695
- padding: 8px 12px;
696
- border-bottom: 1px solid var(--border);
697
- position: sticky;
698
- top: 0;
699
- background: var(--bg-card);
700
- z-index: 1;
701
- }
702
- .field-table td {
703
- padding: 6px 12px;
704
- font-size: 13px;
705
- border-bottom: 1px solid rgba(255, 255, 255, 0.03);
706
- vertical-align: middle;
707
- }
708
- .field-table tr:hover td {
709
- background: rgba(255, 255, 255, 0.02);
710
- }
711
- .field-table .ft-num {
712
- color: var(--text-muted);
713
- font-size: 11px;
714
- width: 32px;
715
- font-variant-numeric: tabular-nums;
716
- }
717
- .field-table .ft-name {
718
- font-weight: 500;
719
- }
720
- .field-table .ft-type {
721
- color: var(--accent-light);
722
- font-size: 12px;
723
- }
724
- .field-table .ft-toggle {
725
- width: 28px;
726
- height: 16px;
727
- border-radius: 8px;
728
- border: none;
729
- cursor: pointer;
730
- transition: all var(--transition);
731
- }
732
- .ft-toggle.on {
733
- background: var(--success);
734
- }
735
- .ft-toggle.off {
736
- background: rgba(255, 255, 255, 0.08);
737
- }
738
- .field-count {
739
- font-size: 11px;
740
- color: var(--text-muted);
741
- font-variant-numeric: tabular-nums;
742
- padding: 2px 8px;
743
- background: rgba(255, 255, 255, 0.04);
744
- border-radius: 20px;
745
- }
746
-
747
- /* Old field-row kept for schema cards in list view */
748
- .field-row {
749
- display: flex;
750
- align-items: center;
751
- gap: 10px;
752
- padding: 8px 12px;
753
- background: var(--bg-surface);
754
- border: 1px solid var(--border);
755
- border-radius: var(--radius);
756
- margin-bottom: 4px;
757
- font-size: 13px;
758
- }
759
- .field-row .field-name {
760
- font-weight: 500;
761
- min-width: 80px;
762
- }
763
- .field-row .field-type {
764
- font-size: 12px;
765
- color: var(--accent-light);
766
- min-width: 60px;
767
- }
768
- .field-row .field-badges {
769
- display: flex;
770
- gap: 4px;
771
- flex: 1;
772
- }
773
-
774
- /* ── Route Page Layout ── */
775
- .routes-layout {
776
- display: grid;
777
- grid-template-columns: 340px 1fr;
778
- gap: 20px;
779
- min-height: calc(100vh - 160px);
780
- }
781
- .routes-list {
782
- display: flex;
783
- flex-direction: column;
784
- gap: 0;
785
- }
786
- .routes-detail {
787
- position: sticky;
788
- top: 0;
789
- align-self: start;
790
- }
791
- .route-item {
792
- display: flex;
793
- align-items: center;
794
- gap: 10px;
795
- padding: 12px 16px;
796
- cursor: pointer;
797
- border-left: 2px solid transparent;
798
- transition: all var(--transition);
799
- }
800
- .route-item:hover {
801
- background: rgba(255, 255, 255, 0.02);
802
- }
803
- .route-item.active {
804
- background: var(--accent-glow);
805
- border-left-color: var(--accent);
806
- }
807
- .route-item .ri-name {
808
- font-weight: 500;
809
- font-size: 13px;
810
- flex: 1;
811
- }
812
- .route-item .ri-file {
813
- font-size: 10px;
814
- color: var(--text-muted);
815
- }
816
- .route-method {
817
- font-size: 9px;
818
- font-weight: 700;
819
- letter-spacing: 0.5px;
820
- padding: 2px 7px;
821
- border-radius: 3px;
822
- font-family: "SF Mono", "Fira Code", monospace;
823
- text-align: center;
824
- display: inline-block;
825
- }
826
- .route-method.get {
827
- background: rgba(34, 197, 94, 0.15);
828
- color: #22c55e;
829
- }
830
- .route-method.post {
831
- background: rgba(59, 130, 246, 0.15);
832
- color: #3b82f6;
833
- }
834
- .route-method.put {
835
- background: rgba(245, 158, 11, 0.15);
836
- color: #f59e0b;
837
- }
838
- .route-method.patch {
839
- background: rgba(168, 85, 247, 0.15);
840
- color: #a855f7;
841
- }
842
- .route-method.delete {
843
- background: rgba(239, 68, 68, 0.15);
844
- color: #ef4444;
845
- }
846
- .route-method.multi {
847
- background: rgba(0, 94, 255, 0.15);
848
- color: #3daefd;
849
- }
850
- .route-endpoint-row {
851
- display: flex;
852
- align-items: center;
853
- gap: 10px;
854
- padding: 10px 14px;
855
- border-bottom: 1px solid rgba(255, 255, 255, 0.03);
856
- font-size: 13px;
857
- }
858
- .route-endpoint-row:last-child {
859
- border-bottom: none;
860
- }
861
- .route-endpoint-path {
862
- font-family: "SF Mono", "Fira Code", monospace;
863
- font-size: 12px;
864
- color: var(--text-secondary);
865
- }
866
- .route-code-wrap {
867
- max-height: 400px;
868
- overflow-y: auto;
869
- border-radius: var(--radius);
870
- border: 1px solid var(--border);
871
- }
872
- .route-code-wrap .code-block {
873
- border: none;
874
- border-radius: 0;
875
- margin: 0;
876
- font-size: 11px;
877
- line-height: 1.6;
878
- }
879
-
880
- /* ── API Tester ── */
881
- .api-tester-bar {
882
- display: flex;
883
- gap: 8px;
884
- align-items: stretch;
885
- margin-bottom: 16px;
886
- }
887
- .api-tester-method {
888
- padding: 8px 12px;
889
- border: 1px solid var(--border);
890
- border-radius: var(--radius);
891
- background: var(--bg-input);
892
- color: var(--text-primary);
893
- font-weight: 600;
894
- font-size: 13px;
895
- font-family: inherit;
896
- cursor: pointer;
897
- min-width: 100px;
898
- }
899
- .api-tester-url {
900
- flex: 1;
901
- padding: 8px 14px;
902
- border: 1px solid var(--border);
903
- border-radius: var(--radius);
904
- background: var(--bg-input);
905
- color: var(--text-primary);
906
- font-family: 'SF Mono', 'Fira Code', monospace;
907
- font-size: 13px;
908
- outline: none;
909
- transition: border-color 0.15s;
910
- }
911
- .api-tester-url:focus {
912
- border-color: var(--accent);
913
- }
914
- .api-tester-url::placeholder {
915
- color: var(--text-muted);
916
- }
917
- .api-tester-send {
918
- padding: 8px 20px;
919
- border: none;
920
- border-radius: var(--radius);
921
- background: var(--accent);
922
- color: white;
923
- font-weight: 600;
924
- font-size: 13px;
925
- font-family: inherit;
926
- cursor: pointer;
927
- display: flex;
928
- align-items: center;
929
- gap: 6px;
930
- transition: opacity 0.15s;
931
- }
932
- .api-tester-send:hover {
933
- opacity: 0.9;
934
- }
935
- .api-tester-send:disabled {
936
- opacity: 0.5;
937
- cursor: not-allowed;
938
- }
939
- .api-tester-tabs {
940
- display: flex;
941
- gap: 0;
942
- border-bottom: 1px solid var(--border);
943
- margin-bottom: 0;
944
- }
945
- .api-tester-tab {
946
- padding: 8px 16px;
947
- font-size: 12px;
948
- font-weight: 500;
949
- color: var(--text-muted);
950
- border: none;
951
- background: none;
952
- cursor: pointer;
953
- border-bottom: 2px solid transparent;
954
- font-family: inherit;
955
- transition: all 0.15s;
956
- }
957
- .api-tester-tab:hover {
958
- color: var(--text-primary);
959
- }
960
- .api-tester-tab.active {
961
- color: var(--accent-light);
962
- border-bottom-color: var(--accent);
963
- }
964
- .api-tester-body-editor {
965
- width: 100%;
966
- min-height: 180px;
967
- padding: 14px;
968
- border: none;
969
- outline: none;
970
- font-family: 'SF Mono', 'Fira Code', monospace;
971
- font-size: 12px;
972
- line-height: 1.6;
973
- background: var(--bg-card);
974
- color: var(--text-primary);
975
- resize: vertical;
976
- tab-size: 2;
977
- }
978
- .api-tester-kv-row {
979
- display: flex;
980
- gap: 8px;
981
- align-items: center;
982
- padding: 6px 14px;
983
- border-bottom: 1px solid var(--border);
984
- }
985
- .api-tester-kv-row input {
986
- flex: 1;
987
- padding: 6px 10px;
988
- border: 1px solid var(--border);
989
- border-radius: 4px;
990
- background: var(--bg-input);
991
- color: var(--text-primary);
992
- font-size: 12px;
993
- font-family: 'SF Mono', monospace;
994
- outline: none;
995
- }
996
- .api-tester-kv-row input:focus {
997
- border-color: var(--accent);
998
- }
999
- .api-tester-status {
1000
- display: inline-flex;
1001
- align-items: center;
1002
- padding: 3px 10px;
1003
- border-radius: 4px;
1004
- font-size: 12px;
1005
- font-weight: 600;
1006
- font-family: 'SF Mono', monospace;
1007
- }
1008
- .api-tester-status.s2xx { background: rgba(52,211,153,0.12); color: var(--success); }
1009
- .api-tester-status.s3xx { background: rgba(96,165,250,0.12); color: #60a5fa; }
1010
- .api-tester-status.s4xx { background: rgba(251,191,36,0.12); color: var(--warning); }
1011
- .api-tester-status.s5xx { background: rgba(248,113,113,0.12); color: var(--danger); }
1012
- .api-tester-status.s0xx { background: rgba(248,113,113,0.12); color: var(--danger); }
1013
- .api-tester-response-body {
1014
- padding: 14px;
1015
- font-family: 'SF Mono', 'Fira Code', monospace;
1016
- font-size: 12px;
1017
- line-height: 1.6;
1018
- white-space: pre-wrap;
1019
- word-break: break-word;
1020
- color: var(--text-secondary);
1021
- max-height: 400px;
1022
- overflow-y: auto;
1023
- }
1024
- .api-tester-saved-item {
1025
- padding: 10px 14px;
1026
- cursor: pointer;
1027
- border-bottom: 1px solid var(--border);
1028
- transition: background 0.1s;
1029
- }
1030
- .api-tester-saved-item:hover {
1031
- background: var(--bg-input);
1032
- }
1033
- .api-tester-saved-item.active {
1034
- background: var(--accent-glow);
1035
- border-left: 2px solid var(--accent);
1036
- }
1037
-
1038
- .api-tester-mode-tabs {
1039
- display: inline-flex;
1040
- gap: 2px;
1041
- padding: 3px;
1042
- background: var(--bg-input);
1043
- border-radius: 8px;
1044
- margin-bottom: 16px;
1045
- }
1046
- .api-tester-mode-tab {
1047
- padding: 6px 16px;
1048
- border-radius: 6px;
1049
- border: none;
1050
- cursor: pointer;
1051
- display: flex;
1052
- align-items: center;
1053
- gap: 6px;
1054
- font-size: 13px;
1055
- font-weight: 400;
1056
- font-family: inherit;
1057
- background: transparent;
1058
- color: var(--text-muted);
1059
- transition: all 0.15s;
1060
- }
1061
- .api-tester-mode-tab.active {
1062
- background: var(--bg-surface);
1063
- color: var(--text-primary);
1064
- font-weight: 600;
1065
- box-shadow: 0 1px 3px rgba(0,0,0,0.2);
1066
- }
1067
- .ws-log {
1068
- max-height: 350px;
1069
- overflow-y: auto;
1070
- font-family: 'SF Mono', 'Fira Code', monospace;
1071
- font-size: 12px;
1072
- line-height: 1.6;
1073
- }
1074
- .ws-log-entry {
1075
- padding: 4px 14px;
1076
- border-bottom: 1px solid var(--border);
1077
- display: flex;
1078
- gap: 8px;
1079
- align-items: flex-start;
1080
- }
1081
- .ws-log-entry .dir {
1082
- font-size: 10px;
1083
- font-weight: 600;
1084
- padding: 1px 5px;
1085
- border-radius: 3px;
1086
- flex-shrink: 0;
1087
- margin-top: 2px;
1088
- }
1089
- .ws-log-entry .dir.sent {
1090
- background: rgba(59,130,246,0.12);
1091
- color: #60a5fa;
1092
- }
1093
- .ws-log-entry .dir.received {
1094
- background: rgba(52,211,153,0.12);
1095
- color: var(--success);
1096
- }
1097
- .ws-log-entry .dir.system {
1098
- background: rgba(251,191,36,0.12);
1099
- color: var(--warning);
1100
- }
1101
- .ws-log-entry .msg {
1102
- white-space: pre-wrap;
1103
- word-break: break-word;
1104
- color: var(--text-secondary);
1105
- }
1106
- .ws-log-entry .ts {
1107
- font-size: 10px;
1108
- color: var(--text-muted);
1109
- margin-left: auto;
1110
- flex-shrink: 0;
1111
- }
1112
- .ws-connected {
1113
- display: inline-flex;
1114
- align-items: center;
1115
- gap: 5px;
1116
- font-size: 12px;
1117
- font-weight: 500;
1118
- }
1119
- .ws-connected .dot {
1120
- width: 7px;
1121
- height: 7px;
1122
- border-radius: 50%;
1123
- }
1124
- .ws-connected .dot.on { background: var(--success); }
1125
- .ws-connected .dot.off { background: var(--text-muted); }
1126
-
1127
- /* ── Pipeline/Flow ── */
1128
- .pipeline {
1129
- display: flex;
1130
- align-items: center;
1131
- gap: 0;
1132
- margin-bottom: 32px;
1133
- overflow-x: auto;
1134
- padding: 8px 0;
1135
- }
1136
- .pipeline-step {
1137
- display: flex;
1138
- flex-direction: column;
1139
- align-items: center;
1140
- gap: 8px;
1141
- min-width: 100px;
1142
- padding: 16px;
1143
- }
1144
- .pipeline-dot {
1145
- width: 40px;
1146
- height: 40px;
1147
- border-radius: 50%;
1148
- display: flex;
1149
- align-items: center;
1150
- justify-content: center;
1151
- font-size: 16px;
1152
- border: 2px solid var(--border);
1153
- background: var(--bg-card);
1154
- transition: all 0.3s;
1155
- }
1156
- .pipeline-dot.done {
1157
- border-color: var(--success);
1158
- background: var(--success-glow);
1159
- color: var(--success);
1160
- }
1161
- .pipeline-dot.current {
1162
- border-color: var(--accent);
1163
- background: var(--accent-glow);
1164
- color: var(--accent-light);
1165
- box-shadow: 0 0 20px var(--accent-glow);
1166
- }
1167
- .pipeline-label {
1168
- font-size: 11px;
1169
- font-weight: 500;
1170
- color: var(--text-muted);
1171
- text-align: center;
1172
- }
1173
- .pipeline-label.done {
1174
- color: var(--success);
1175
- }
1176
- .pipeline-label.current {
1177
- color: var(--accent-light);
1178
- }
1179
- .pipeline-connector {
1180
- width: 40px;
1181
- height: 2px;
1182
- background: var(--border);
1183
- flex-shrink: 0;
1184
- margin-top: -24px;
1185
- }
1186
- .pipeline-connector.done {
1187
- background: var(--success);
1188
- }
1189
-
1190
- /* ── Toast ── */
1191
- .toast-container {
1192
- position: fixed;
1193
- top: 16px;
1194
- right: 16px;
1195
- z-index: 1000;
1196
- display: flex;
1197
- flex-direction: column;
1198
- gap: 8px;
1199
- }
1200
- .toast {
1201
- padding: 12px 16px;
1202
- border-radius: var(--radius);
1203
- font-size: 13px;
1204
- font-weight: 500;
1205
- background: var(--bg-card);
1206
- border: 1px solid var(--border);
1207
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
1208
- animation: slideIn 0.2s ease;
1209
- max-width: 400px;
1210
- }
1211
- .toast.success {
1212
- border-left: 3px solid var(--success);
1213
- }
1214
- .toast.error {
1215
- border-left: 3px solid var(--error);
1216
- }
1217
- .toast.info {
1218
- border-left: 3px solid var(--info);
1219
- }
1220
-
1221
- @keyframes slideIn {
1222
- from {
1223
- transform: translateX(100%);
1224
- opacity: 0;
1225
- }
1226
- to {
1227
- transform: translateX(0);
1228
- opacity: 1;
1229
- }
1230
- }
1231
-
1232
- /* ── Modal ── */
1233
- .modal-overlay {
1234
- position: fixed;
1235
- inset: 0;
1236
- background: rgba(0, 0, 0, 0.6);
1237
- backdrop-filter: blur(4px);
1238
- display: flex;
1239
- align-items: center;
1240
- justify-content: center;
1241
- z-index: 900;
1242
- animation: fadeIn 0.15s ease;
1243
- }
1244
- .modal {
1245
- background: var(--bg-card);
1246
- border: 1px solid var(--border);
1247
- border-radius: var(--radius-xl);
1248
- padding: 24px;
1249
- min-width: 420px;
1250
- max-width: 560px;
1251
- max-height: 80vh;
1252
- overflow-y: auto;
1253
- box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5);
1254
- animation: scaleIn 0.15s ease;
1255
- }
1256
- .modal-title {
1257
- font-size: 16px;
1258
- font-weight: 600;
1259
- margin-bottom: 16px;
1260
- }
1261
- .modal-actions {
1262
- display: flex;
1263
- gap: 8px;
1264
- justify-content: flex-end;
1265
- margin-top: 20px;
1266
- }
1267
-
1268
- @keyframes fadeIn {
1269
- from {
1270
- opacity: 0;
1271
- }
1272
- to {
1273
- opacity: 1;
1274
- }
1275
- }
1276
- @keyframes scaleIn {
1277
- from {
1278
- transform: scale(0.96);
1279
- opacity: 0;
1280
- }
1281
- to {
1282
- transform: scale(1);
1283
- opacity: 1;
1284
- }
1285
- }
1286
-
1287
- /* ── Code block ── */
1288
- .code-block {
1289
- background: #11141e;
1290
- border: 1px solid var(--border);
1291
- border-radius: var(--radius);
1292
- padding: 16px;
1293
- font-family: "SF Mono", "Fira Code", monospace;
1294
- font-size: 12px;
1295
- line-height: 1.6;
1296
- overflow-x: auto;
1297
- white-space: pre;
1298
- color: var(--text-secondary);
1299
- }
1300
-
1301
- /* ── Checkbox ── */
1302
- .checkbox-group {
1303
- display: flex;
1304
- flex-wrap: wrap;
1305
- gap: 8px;
1306
- }
1307
- .checkbox-item {
1308
- display: flex;
1309
- align-items: center;
1310
- gap: 8px;
1311
- padding: 8px 14px;
1312
- background: var(--bg-surface);
1313
- border: 1px solid var(--border);
1314
- border-radius: var(--radius);
1315
- cursor: pointer;
1316
- font-size: 13px;
1317
- transition: all var(--transition);
1318
- }
1319
- .checkbox-item:hover {
1320
- border-color: rgba(255, 255, 255, 0.12);
1321
- }
1322
- .checkbox-item.checked {
1323
- border-color: var(--accent);
1324
- background: var(--accent-glow);
1325
- color: var(--accent-light);
1326
- }
1327
- .checkbox-item input {
1328
- display: none;
1329
- }
1330
-
1331
- /* ── Utilities ── */
1332
- .flex {
1333
- display: flex;
1334
- }
1335
- .flex-col {
1336
- flex-direction: column;
1337
- }
1338
- .items-center {
1339
- align-items: center;
1340
- }
1341
- .justify-between {
1342
- justify-content: space-between;
1343
- }
1344
- .gap-2 {
1345
- gap: 8px;
1346
- }
1347
- .gap-3 {
1348
- gap: 12px;
1349
- }
1350
- .gap-4 {
1351
- gap: 16px;
1352
- }
1353
- .mb-2 {
1354
- margin-bottom: 8px;
1355
- }
1356
- .mb-3 {
1357
- margin-bottom: 12px;
1358
- }
1359
- .mb-4 {
1360
- margin-bottom: 16px;
1361
- }
1362
- .mb-6 {
1363
- margin-bottom: 24px;
1364
- }
1365
- .mt-4 {
1366
- margin-top: 16px;
1367
- }
1368
- .text-sm {
1369
- font-size: 12px;
1370
- }
1371
- .text-muted {
1372
- color: var(--text-muted);
1373
- }
1374
- .text-secondary {
1375
- color: var(--text-secondary);
1376
- }
1377
- .w-full {
1378
- width: 100%;
1379
- }
1380
-
1381
- /* ── Spinner ── */
1382
- .spinner {
1383
- width: 16px;
1384
- height: 16px;
1385
- border: 2px solid rgba(255, 255, 255, 0.15);
1386
- border-top-color: var(--accent-light);
1387
- border-radius: 50%;
1388
- animation: spin 0.6s linear infinite;
1389
- }
1390
- @keyframes spin {
1391
- to {
1392
- transform: rotate(360deg);
1393
- }
1394
- }
1395
- @keyframes pulse {
1396
- 0%, 100% { opacity: 1; }
1397
- 50% { opacity: 0.7; }
1398
- }
1399
- .spin {
1400
- animation: spin 0.6s linear infinite;
1401
- }
1402
-
1403
- /* ── Env editor ── */
1404
- .env-editor {
1405
- width: 100%;
1406
- min-height: 280px;
1407
- background: var(--bg-input);
1408
- border: 1px solid var(--border);
1409
- border-radius: var(--radius);
1410
- color: var(--text-primary);
1411
- font-family: "SF Mono", "Fira Code", "Cascadia Code", monospace;
1412
- font-size: 13px;
1413
- line-height: 1.6;
1414
- padding: 14px 16px;
1415
- resize: vertical;
1416
- outline: none;
1417
- transition: border-color 0.15s;
1418
- }
1419
- .env-editor:focus {
1420
- border-color: var(--accent);
1421
- }
1422
-
1423
- /* ── Splash Screen ── */
1424
- .splash {
1425
- position: fixed;
1426
- inset: 0;
1427
- z-index: 9999;
1428
- display: flex;
1429
- flex-direction: column;
1430
- align-items: center;
1431
- justify-content: center;
1432
- background: var(--bg);
1433
- }
1434
- .splash-logo {
1435
- font-family: monospace;
1436
- font-size: 10px;
1437
- line-height: 1.15;
1438
- letter-spacing: 1px;
1439
- white-space: pre;
1440
- background: linear-gradient(135deg, #3daefd, #0079ff, #005eff);
1441
- -webkit-background-clip: text;
1442
- -webkit-text-fill-color: transparent;
1443
- user-select: none;
1444
- text-align: center;
1445
- }
1446
- .splash-tagline {
1447
- font-family: "Noto Sans", sans-serif;
1448
- font-size: 16px;
1449
- font-weight: 400;
1450
- color: var(--text-secondary);
1451
- letter-spacing: 0.5px;
1452
- margin-top: 20px;
1453
- }
1454
- .splash-company {
1455
- position: absolute;
1456
- bottom: 36px;
1457
- font-family: "Noto Sans", sans-serif;
1458
- font-size: 12px;
1459
- color: var(--text-muted);
1460
- letter-spacing: 0.3px;
1461
- }
1462
- .splash-company span {
1463
- font-weight: 600;
1464
- background: linear-gradient(135deg, #3daefd, #005eff);
1465
- -webkit-background-clip: text;
1466
- -webkit-text-fill-color: transparent;
1467
- }
1468
- .splash-glow {
1469
- position: absolute;
1470
- width: 300px;
1471
- height: 300px;
1472
- border-radius: 50%;
1473
- background: radial-gradient(circle, rgba(0,94,255,0.12) 0%, transparent 70%);
1474
- pointer-events: none;
1475
- }
1476
-
1477
- /* ── Passkey Login Screen ── */
1478
- .passkey-screen {
1479
- position: fixed;
1480
- inset: 0;
1481
- z-index: 9999;
1482
- display: flex;
1483
- flex-direction: column;
1484
- align-items: center;
1485
- justify-content: center;
1486
- background: var(--bg);
1487
- }
1488
- .passkey-card {
1489
- display: flex;
1490
- flex-direction: column;
1491
- align-items: center;
1492
- gap: 24px;
1493
- padding: 48px 40px;
1494
- background: var(--card);
1495
- border: 1px solid var(--border);
1496
- border-radius: 16px;
1497
- min-width: 340px;
1498
- }
1499
- .passkey-card h2 {
1500
- font-family: "Noto Sans", sans-serif;
1501
- font-size: 20px;
1502
- font-weight: 600;
1503
- color: var(--text);
1504
- margin: 0;
1505
- }
1506
- .passkey-card p {
1507
- font-family: "Noto Sans", sans-serif;
1508
- font-size: 13px;
1509
- color: var(--text-secondary);
1510
- margin: 0;
1511
- }
1512
- .passkey-field {
1513
- width: 100%;
1514
- }
1515
- .passkey-field input {
1516
- width: 100%;
1517
- height: 48px;
1518
- padding: 0 14px;
1519
- font-size: 16px;
1520
- font-weight: 500;
1521
- font-family: "Noto Sans", sans-serif;
1522
- background: var(--bg);
1523
- border: 1px solid var(--border);
1524
- border-radius: 10px;
1525
- color: var(--text);
1526
- outline: none;
1527
- transition: border-color 0.2s;
1528
- box-sizing: border-box;
1529
- }
1530
- .passkey-field input:focus {
1531
- border-color: #0079ff;
1532
- box-shadow: 0 0 0 2px rgba(0, 94, 255, 0.15);
1533
- }
1534
- .passkey-field input.error {
1535
- border-color: #ef4444;
1536
- box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.15);
1537
- }
1538
- .passkey-error {
1539
- font-family: "Noto Sans", sans-serif;
1540
- font-size: 13px;
1541
- color: #ef4444;
1542
- min-height: 18px;
1543
- }
1544
- .passkey-btn {
1545
- width: 100%;
1546
- padding: 12px;
1547
- font-family: "Noto Sans", sans-serif;
1548
- font-size: 14px;
1549
- font-weight: 600;
1550
- color: #fff;
1551
- background: linear-gradient(135deg, #0079ff, #005eff);
1552
- border: none;
1553
- border-radius: 10px;
1554
- cursor: pointer;
1555
- transition: opacity 0.2s;
1556
- }
1557
- .passkey-btn:hover { opacity: 0.9; }
1558
- .passkey-btn:disabled { opacity: 0.5; cursor: not-allowed; }
1559
- </style>
1560
- </head>
1561
- <body>
1562
- <div id="root"></div>
1563
- <script type="importmap">
1564
- {
1565
- "imports": {
1566
- "react": "https://esm.sh/react@18.3.1",
1567
- "react/jsx-runtime": "https://esm.sh/react@18.3.1/jsx-runtime",
1568
- "react-dom/client": "https://esm.sh/react-dom@18.3.1/client",
1569
- "htm": "https://esm.sh/htm@3.1.1",
1570
- "react-icons/lu": "https://esm.sh/react-icons@4.12.0/lu?external=react",
1571
- "framer-motion": "https://esm.sh/framer-motion@11.0.3?external=react,react-dom",
1572
- "socket.io-client": "https://esm.sh/socket.io-client@4.7.4"
1573
- }
1574
- }
1575
- </script>
1576
- <script type="module" src="/_dev/assets/js/main.js"></script>
1577
- </body>
1578
- </html>