@pablozaiden/webapp 0.0.1

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 (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/docs/auth-validation.md +34 -0
  4. package/docs/auth.md +35 -0
  5. package/docs/deployment.md +49 -0
  6. package/docs/getting-started.md +62 -0
  7. package/docs/github-actions.md +421 -0
  8. package/docs/realtime.md +61 -0
  9. package/docs/release.md +64 -0
  10. package/docs/server.md +45 -0
  11. package/docs/settings.md +42 -0
  12. package/docs/sidebar.md +80 -0
  13. package/docs/ui-guidelines.md +55 -0
  14. package/package.json +60 -0
  15. package/src/build/build-binary.ts +42 -0
  16. package/src/build/index.ts +1 -0
  17. package/src/contracts/index.ts +86 -0
  18. package/src/server/auth/api-keys.ts +61 -0
  19. package/src/server/auth/crypto.ts +34 -0
  20. package/src/server/auth/device-auth.ts +324 -0
  21. package/src/server/auth/passkeys.ts +280 -0
  22. package/src/server/auth/request-origin.ts +33 -0
  23. package/src/server/auth/sqlite-store.ts +301 -0
  24. package/src/server/auth/store.ts +91 -0
  25. package/src/server/auth/types.ts +25 -0
  26. package/src/server/create-web-app-server.ts +447 -0
  27. package/src/server/index.ts +7 -0
  28. package/src/server/logger.ts +44 -0
  29. package/src/server/realtime/bus.ts +104 -0
  30. package/src/server/responses.ts +54 -0
  31. package/src/server/routes.ts +67 -0
  32. package/src/server/runtime-config.ts +101 -0
  33. package/src/server/same-origin.ts +35 -0
  34. package/src/types.d.ts +11 -0
  35. package/src/web/WebAppRoot.tsx +706 -0
  36. package/src/web/components/index.tsx +471 -0
  37. package/src/web/index.ts +5 -0
  38. package/src/web/realtime/useRealtime.ts +189 -0
  39. package/src/web/sidebar/types.ts +45 -0
  40. package/src/web/styles.css +1295 -0
@@ -0,0 +1,1295 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --wapp-bg: #f3f4f6;
4
+ --wapp-surface: #ffffff;
5
+ --wapp-surface-muted: #f9fafb;
6
+ --wapp-border: #d1d5db;
7
+ --wapp-border-soft: #e5e7eb;
8
+ --wapp-text: #111827;
9
+ --wapp-muted: #4b5563;
10
+ --wapp-muted-2: #64748b;
11
+ --wapp-danger: #991b1b;
12
+ --wapp-danger-bg: #991b1b;
13
+ --wapp-primary: #111827;
14
+ --wapp-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
15
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
16
+ }
17
+
18
+ :root.dark {
19
+ color-scheme: dark;
20
+ --wapp-bg: #171717;
21
+ --wapp-surface: #262626;
22
+ --wapp-surface-muted: #171717;
23
+ --wapp-border: #374151;
24
+ --wapp-border-soft: #27272a;
25
+ --wapp-text: #f3f4f6;
26
+ --wapp-muted: #a1a1aa;
27
+ --wapp-muted-2: #71717a;
28
+ --wapp-danger: #fca5a5;
29
+ --wapp-danger-bg: #7f1d1d;
30
+ --wapp-primary: #f9fafb;
31
+ --wapp-shadow: none;
32
+ }
33
+
34
+ * {
35
+ box-sizing: border-box;
36
+ }
37
+
38
+ html,
39
+ body,
40
+ #root {
41
+ height: 100%;
42
+ }
43
+
44
+ body {
45
+ margin: 0;
46
+ background: var(--wapp-bg);
47
+ color: var(--wapp-text);
48
+ overflow: hidden;
49
+ }
50
+
51
+ button,
52
+ input,
53
+ select,
54
+ textarea {
55
+ font: inherit;
56
+ }
57
+
58
+ input::placeholder,
59
+ textarea::placeholder {
60
+ color: color-mix(in srgb, var(--wapp-surface), var(--wapp-muted) 38%);
61
+ opacity: 1;
62
+ }
63
+
64
+ :root.dark input::placeholder,
65
+ :root.dark textarea::placeholder {
66
+ color: #525252;
67
+ }
68
+
69
+ .sr-only {
70
+ position: absolute;
71
+ width: 1px;
72
+ height: 1px;
73
+ padding: 0;
74
+ margin: -1px;
75
+ overflow: hidden;
76
+ clip: rect(0, 0, 0, 0);
77
+ white-space: nowrap;
78
+ border: 0;
79
+ }
80
+
81
+ .wapp-shell {
82
+ display: flex;
83
+ height: 100vh;
84
+ min-height: 100vh;
85
+ overflow: hidden;
86
+ background: var(--wapp-bg);
87
+ }
88
+
89
+ .wapp-sidebar {
90
+ width: 20rem;
91
+ min-width: 20rem;
92
+ height: 100vh;
93
+ display: flex;
94
+ flex-direction: column;
95
+ border-right: 1px solid var(--wapp-border-soft);
96
+ background: color-mix(in srgb, var(--wapp-bg), var(--wapp-surface) 25%);
97
+ backdrop-filter: blur(8px);
98
+ transition: width 180ms ease, min-width 180ms ease, transform 180ms ease, opacity 180ms ease;
99
+ }
100
+
101
+ .wapp-shell.sidebar-collapsed .wapp-sidebar {
102
+ width: 0;
103
+ min-width: 0;
104
+ overflow: hidden;
105
+ opacity: 0;
106
+ pointer-events: none;
107
+ }
108
+
109
+ .wapp-sidebar-header,
110
+ .wapp-main-header {
111
+ flex: 0 0 auto;
112
+ min-height: 4.5rem;
113
+ display: flex;
114
+ align-items: center;
115
+ gap: 0.75rem;
116
+ border-bottom: 1px solid var(--wapp-border-soft);
117
+ background: var(--wapp-surface);
118
+ }
119
+
120
+ .wapp-sidebar-header {
121
+ justify-content: space-between;
122
+ padding: 0.5rem 1rem;
123
+ }
124
+
125
+ .wapp-main-header {
126
+ justify-content: space-between;
127
+ padding: 0.5rem 2rem;
128
+ }
129
+
130
+ .wapp-main-header-title {
131
+ display: flex;
132
+ min-width: 0;
133
+ align-items: center;
134
+ gap: 0.75rem;
135
+ }
136
+
137
+ .wapp-main-header-actions {
138
+ display: flex;
139
+ flex: 0 0 auto;
140
+ align-items: center;
141
+ gap: 0.5rem;
142
+ }
143
+
144
+ .wapp-brand {
145
+ border: 0;
146
+ background: transparent;
147
+ color: var(--wapp-muted);
148
+ cursor: pointer;
149
+ font-size: 0.75rem;
150
+ font-weight: 600;
151
+ letter-spacing: 0.24em;
152
+ text-transform: uppercase;
153
+ padding: 0;
154
+ transition: color 120ms ease;
155
+ }
156
+
157
+ .wapp-brand:hover {
158
+ color: var(--wapp-text);
159
+ }
160
+
161
+ .wapp-sidebar-actions {
162
+ display: flex;
163
+ align-items: center;
164
+ gap: 0.5rem;
165
+ }
166
+
167
+ .wapp-icon-button {
168
+ display: inline-flex;
169
+ align-items: center;
170
+ justify-content: center;
171
+ width: 2.5rem;
172
+ height: 2.5rem;
173
+ border: 1px solid var(--wapp-border-soft);
174
+ border-radius: 0.75rem;
175
+ background: var(--wapp-surface);
176
+ color: var(--wapp-muted);
177
+ box-shadow: var(--wapp-shadow);
178
+ cursor: pointer;
179
+ transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
180
+ }
181
+
182
+ .wapp-icon-button:hover,
183
+ .wapp-icon-button.active {
184
+ border-color: var(--wapp-border);
185
+ color: var(--wapp-text);
186
+ }
187
+
188
+ .wapp-icon-button.active {
189
+ border-color: var(--wapp-primary);
190
+ }
191
+
192
+ .wapp-sidebar-top-button {
193
+ border-color: #262626;
194
+ background: #171717;
195
+ color: #e5e7eb;
196
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.24);
197
+ }
198
+
199
+ .wapp-sidebar-top-button:hover,
200
+ .wapp-sidebar-top-button.active {
201
+ border-color: #404040;
202
+ background: #171717;
203
+ color: #ffffff;
204
+ }
205
+
206
+ .wapp-svg {
207
+ width: 1.25rem;
208
+ height: 1.25rem;
209
+ fill: none;
210
+ stroke: currentColor;
211
+ stroke-width: 2;
212
+ stroke-linecap: round;
213
+ stroke-linejoin: round;
214
+ }
215
+
216
+ .wapp-sidebar-scroll {
217
+ flex: 1;
218
+ overflow-y: auto;
219
+ padding: 1rem 0.75rem;
220
+ }
221
+
222
+ .wapp-search input {
223
+ width: 100%;
224
+ height: 2.625rem;
225
+ margin-bottom: 1.5rem;
226
+ border: 1px solid var(--wapp-border);
227
+ border-radius: 0.75rem;
228
+ background: var(--wapp-surface);
229
+ color: var(--wapp-text);
230
+ box-shadow: var(--wapp-shadow);
231
+ outline: none;
232
+ padding: 0 0.75rem;
233
+ font-size: 0.875rem;
234
+ }
235
+
236
+ .wapp-search input:focus {
237
+ border-color: var(--wapp-muted);
238
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--wapp-muted), transparent 70%);
239
+ }
240
+
241
+ .wapp-sidebar-section {
242
+ margin-bottom: 1.5rem;
243
+ }
244
+
245
+ .wapp-sidebar-section-title {
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: space-between;
249
+ gap: 0.5rem;
250
+ margin-bottom: 0.5rem;
251
+ }
252
+
253
+ .wapp-sidebar-section-title button:first-child {
254
+ display: inline-flex;
255
+ align-items: center;
256
+ gap: 0.5rem;
257
+ min-width: 0;
258
+ border: 0;
259
+ background: transparent;
260
+ color: var(--wapp-muted);
261
+ cursor: pointer;
262
+ font-size: 0.75rem;
263
+ font-weight: 600;
264
+ letter-spacing: 0.18em;
265
+ text-transform: uppercase;
266
+ text-align: left;
267
+ }
268
+
269
+ .wapp-sidebar-action {
270
+ border: 0;
271
+ background: transparent;
272
+ color: var(--wapp-muted);
273
+ cursor: pointer;
274
+ min-width: 2.75rem;
275
+ font-size: 0.6875rem;
276
+ font-weight: 500;
277
+ padding: 0.25rem 0.5rem;
278
+ border-radius: 0.375rem;
279
+ }
280
+
281
+ .wapp-sidebar-action:hover {
282
+ background: color-mix(in srgb, var(--wapp-muted), transparent 88%);
283
+ color: var(--wapp-text);
284
+ }
285
+
286
+ .wapp-sidebar-empty {
287
+ border: 1px dashed var(--wapp-border);
288
+ border-radius: 0.75rem;
289
+ color: var(--wapp-muted);
290
+ font-size: 0.75rem;
291
+ padding: 0.75rem;
292
+ }
293
+
294
+ .wapp-sidebar-item-wrap {
295
+ margin: 0.25rem 0;
296
+ }
297
+
298
+ .wapp-sidebar-item-wrap.has-toggle {
299
+ display: flex;
300
+ align-items: stretch;
301
+ gap: 0.375rem;
302
+ flex-wrap: wrap;
303
+ }
304
+
305
+ .wapp-sidebar-item-wrap.has-toggle > .wapp-sidebar-item {
306
+ width: auto;
307
+ flex: 1;
308
+ }
309
+
310
+ .wapp-sidebar-children {
311
+ flex-basis: 100%;
312
+ padding-left: 0.5rem;
313
+ }
314
+
315
+ .wapp-sidebar-item {
316
+ width: 100%;
317
+ min-height: 2.375rem;
318
+ display: flex;
319
+ align-items: center;
320
+ justify-content: space-between;
321
+ gap: 0.75rem;
322
+ border: 1px solid transparent;
323
+ border-radius: 0.75rem;
324
+ background: transparent;
325
+ color: var(--wapp-text);
326
+ cursor: pointer;
327
+ padding: 0.4375rem 0.75rem 0.4375rem 0.25rem;
328
+ text-align: left;
329
+ }
330
+
331
+ .wapp-sidebar-item:hover {
332
+ border-color: var(--wapp-border-soft);
333
+ background: #f3f4f6;
334
+ }
335
+
336
+ :root.dark .wapp-sidebar-item:hover {
337
+ border-color: #1f2937;
338
+ background: rgb(38 38 38 / 0.8);
339
+ color: var(--wapp-text);
340
+ }
341
+
342
+ .wapp-sidebar-item.active {
343
+ border-color: var(--wapp-primary);
344
+ background: var(--wapp-primary);
345
+ color: #ffffff;
346
+ }
347
+
348
+ :root.dark .wapp-sidebar-item.active {
349
+ border-color: #f5f5f5;
350
+ background: #f5f5f5;
351
+ color: #030712;
352
+ }
353
+
354
+ .wapp-sidebar-item strong {
355
+ display: block;
356
+ font-size: 0.875rem;
357
+ font-weight: 500;
358
+ line-height: 1.2;
359
+ }
360
+
361
+ .wapp-sidebar-item small {
362
+ display: block;
363
+ color: var(--wapp-muted);
364
+ font-size: 0.75rem;
365
+ margin-top: 0.125rem;
366
+ }
367
+
368
+ :root.dark .wapp-sidebar-item.active small {
369
+ color: #374151;
370
+ }
371
+
372
+ .wapp-tree-toggle {
373
+ border: 0;
374
+ background: transparent;
375
+ color: var(--wapp-muted);
376
+ cursor: pointer;
377
+ flex: 0 0 auto;
378
+ height: 2.375rem;
379
+ width: 0.875rem;
380
+ margin: 0.1875rem 0 0 0;
381
+ border-radius: 0.5rem;
382
+ font-size: 0.6875rem;
383
+ line-height: 1;
384
+ }
385
+
386
+ .wapp-tree-toggle:hover {
387
+ background: #f3f4f6;
388
+ color: var(--wapp-text);
389
+ }
390
+
391
+ :root.dark .wapp-tree-toggle:hover {
392
+ background: rgb(38 38 38 / 0.8);
393
+ color: var(--wapp-text);
394
+ }
395
+
396
+ .wapp-sidebar-footer {
397
+ display: flex;
398
+ align-items: center;
399
+ justify-content: space-between;
400
+ min-height: 3rem;
401
+ padding: 0 1rem 1rem;
402
+ color: var(--wapp-muted-2);
403
+ font-size: 0.75rem;
404
+ }
405
+
406
+ .wapp-sidebar-footer button {
407
+ border: 0;
408
+ background: transparent;
409
+ color: inherit;
410
+ cursor: pointer;
411
+ display: inline-flex;
412
+ align-items: center;
413
+ justify-content: center;
414
+ width: 1.5rem;
415
+ height: 1.5rem;
416
+ }
417
+
418
+ .wapp-sidebar-footer .wapp-svg {
419
+ width: 1rem;
420
+ height: 1rem;
421
+ }
422
+
423
+ .wapp-main {
424
+ flex: 1;
425
+ min-width: 0;
426
+ min-height: 0;
427
+ height: 100vh;
428
+ display: flex;
429
+ flex-direction: column;
430
+ }
431
+
432
+ .wapp-main-header h1 {
433
+ margin: 0;
434
+ font-size: 1rem;
435
+ font-weight: 700;
436
+ text-transform: capitalize;
437
+ }
438
+
439
+ .wapp-main-content {
440
+ flex: 1;
441
+ min-height: 0;
442
+ overflow: auto;
443
+ padding: 1.5rem 2rem 2rem;
444
+ }
445
+
446
+ .wapp-panel {
447
+ border: 1px solid var(--wapp-border-soft);
448
+ border-radius: 1.5rem;
449
+ background: var(--wapp-surface);
450
+ padding: 1.25rem;
451
+ }
452
+
453
+ .wapp-panel + .wapp-panel {
454
+ margin-top: 1.5rem;
455
+ }
456
+
457
+ .wapp-panel-header {
458
+ display: flex;
459
+ align-items: flex-start;
460
+ justify-content: space-between;
461
+ gap: 1rem;
462
+ margin-bottom: 1rem;
463
+ }
464
+
465
+ .wapp-panel-header > div:first-child {
466
+ min-width: 0;
467
+ }
468
+
469
+ .wapp-panel-actions {
470
+ flex: 0 0 auto;
471
+ }
472
+
473
+ .wapp-panel-header h2 {
474
+ margin: 0;
475
+ font-size: 1.125rem;
476
+ font-weight: 600;
477
+ }
478
+
479
+ .wapp-panel-header p,
480
+ .wapp-empty-state span,
481
+ .wapp-form-section p {
482
+ color: var(--wapp-muted);
483
+ font-size: 0.875rem;
484
+ margin: 0.25rem 0 0;
485
+ }
486
+
487
+ .wapp-empty-state {
488
+ min-height: 2.625rem;
489
+ display: flex;
490
+ flex-direction: column;
491
+ align-items: flex-start;
492
+ justify-content: center;
493
+ border: 1px dashed var(--wapp-border);
494
+ border-radius: 0.75rem;
495
+ padding: 0.75rem;
496
+ color: var(--wapp-muted);
497
+ }
498
+
499
+ .wapp-loading-state,
500
+ .wapp-error-state {
501
+ min-height: 3rem;
502
+ display: flex;
503
+ flex-direction: column;
504
+ align-items: flex-start;
505
+ justify-content: center;
506
+ border: 1px dashed var(--wapp-border);
507
+ border-radius: 0.75rem;
508
+ padding: 0.875rem;
509
+ color: var(--wapp-muted);
510
+ }
511
+
512
+ .wapp-error-state {
513
+ border-color: color-mix(in srgb, var(--wapp-danger-bg), transparent 55%);
514
+ background: color-mix(in srgb, var(--wapp-danger-bg), transparent 92%);
515
+ }
516
+
517
+ .wapp-loading-state strong,
518
+ .wapp-error-state strong {
519
+ color: var(--wapp-text);
520
+ }
521
+
522
+ .wapp-loading-state span,
523
+ .wapp-error-state span {
524
+ font-size: 0.875rem;
525
+ }
526
+
527
+ .wapp-spinner {
528
+ width: 1rem;
529
+ height: 1rem;
530
+ margin-bottom: 0.5rem;
531
+ border: 2px solid var(--wapp-border);
532
+ border-top-color: var(--wapp-muted);
533
+ border-radius: 999px;
534
+ animation: wapp-spin 800ms linear infinite;
535
+ }
536
+
537
+ @keyframes wapp-spin {
538
+ to { transform: rotate(360deg); }
539
+ }
540
+
541
+ .wapp-state-actions {
542
+ margin-top: 0.75rem;
543
+ }
544
+
545
+ .wapp-entity-header {
546
+ display: flex;
547
+ align-items: flex-start;
548
+ justify-content: space-between;
549
+ gap: 1rem;
550
+ margin-bottom: 1rem;
551
+ }
552
+
553
+ .wapp-entity-header h2 {
554
+ margin: 0;
555
+ font-size: 1.125rem;
556
+ font-weight: 700;
557
+ }
558
+
559
+ .wapp-entity-header p {
560
+ margin: 0.25rem 0 0;
561
+ color: var(--wapp-muted);
562
+ font-size: 0.875rem;
563
+ }
564
+
565
+ .wapp-eyebrow {
566
+ display: block;
567
+ margin-bottom: 0.25rem;
568
+ color: var(--wapp-muted-2);
569
+ font-size: 0.6875rem;
570
+ font-weight: 700;
571
+ letter-spacing: 0.16em;
572
+ text-transform: uppercase;
573
+ }
574
+
575
+ .wapp-entity-header-actions {
576
+ display: flex;
577
+ flex: 0 0 auto;
578
+ align-items: center;
579
+ gap: 0.5rem;
580
+ }
581
+
582
+ .wapp-data-list {
583
+ overflow: hidden;
584
+ border: 1px solid var(--wapp-border-soft);
585
+ border-radius: 0.875rem;
586
+ }
587
+
588
+ .wapp-data-list-row {
589
+ width: 100%;
590
+ display: flex;
591
+ align-items: center;
592
+ gap: 1rem;
593
+ border: 0;
594
+ background: transparent;
595
+ color: var(--wapp-text);
596
+ padding: 0.875rem 1rem;
597
+ text-align: left;
598
+ }
599
+
600
+ .wapp-data-list-row + .wapp-data-list-row {
601
+ border-top: 1px solid var(--wapp-border-soft);
602
+ }
603
+
604
+ .wapp-data-list-row.interactive {
605
+ cursor: pointer;
606
+ }
607
+
608
+ .wapp-data-list-row.interactive:hover {
609
+ background: color-mix(in srgb, var(--wapp-muted), transparent 94%);
610
+ }
611
+
612
+ .wapp-data-list-row-main {
613
+ min-width: 0;
614
+ flex: 1;
615
+ }
616
+
617
+ .wapp-data-list-row-main strong,
618
+ .wapp-data-list-row-main small {
619
+ display: block;
620
+ }
621
+
622
+ .wapp-data-list-row-main strong {
623
+ overflow: hidden;
624
+ text-overflow: ellipsis;
625
+ white-space: nowrap;
626
+ font-size: 0.875rem;
627
+ }
628
+
629
+ .wapp-data-list-row-main small,
630
+ .wapp-data-list-row-meta {
631
+ color: var(--wapp-muted);
632
+ font-size: 0.8125rem;
633
+ }
634
+
635
+ .wapp-data-list-row-meta,
636
+ .wapp-data-list-row-badge,
637
+ .wapp-data-list-row-actions {
638
+ flex: 0 0 auto;
639
+ }
640
+
641
+ .wapp-auth-screen {
642
+ min-height: 100vh;
643
+ display: flex;
644
+ align-items: center;
645
+ justify-content: center;
646
+ padding: 1rem;
647
+ background: var(--wapp-bg);
648
+ }
649
+
650
+ .wapp-device-screen {
651
+ min-height: 100vh;
652
+ display: flex;
653
+ align-items: center;
654
+ justify-content: center;
655
+ padding: 1rem;
656
+ background: var(--wapp-bg);
657
+ }
658
+
659
+ .wapp-device-screen .wapp-panel {
660
+ width: min(34rem, 100%);
661
+ background: var(--wapp-surface);
662
+ }
663
+
664
+ .wapp-device-stack {
665
+ display: grid;
666
+ gap: 1rem;
667
+ }
668
+
669
+ .wapp-device-card {
670
+ display: grid;
671
+ gap: 0.75rem;
672
+ border: 1px solid var(--wapp-border-soft);
673
+ border-radius: 0.75rem;
674
+ background: var(--wapp-surface-muted);
675
+ padding: 0.875rem;
676
+ }
677
+
678
+ .wapp-device-card > div {
679
+ display: flex;
680
+ align-items: center;
681
+ justify-content: space-between;
682
+ gap: 1rem;
683
+ }
684
+
685
+ .wapp-device-card strong {
686
+ font-size: 0.8125rem;
687
+ }
688
+
689
+ .wapp-device-card span {
690
+ color: var(--wapp-muted);
691
+ font-size: 0.75rem;
692
+ text-align: right;
693
+ }
694
+
695
+ .wapp-auth-screen .wapp-panel {
696
+ width: min(28rem, 100%);
697
+ background: var(--wapp-surface);
698
+ }
699
+
700
+ .wapp-button {
701
+ min-height: 2.375rem;
702
+ border: 1px solid var(--wapp-border);
703
+ border-radius: 0.5rem;
704
+ background: var(--wapp-surface);
705
+ color: var(--wapp-text);
706
+ cursor: pointer;
707
+ font-size: 0.875rem;
708
+ font-weight: 600;
709
+ padding: 0.5rem 0.875rem;
710
+ transition: background 120ms ease, border-color 120ms ease, color 120ms ease, transform 120ms ease;
711
+ }
712
+
713
+ .wapp-button:hover:not(:disabled) {
714
+ border-color: var(--wapp-muted);
715
+ background: color-mix(in srgb, var(--wapp-surface), var(--wapp-muted) 12%);
716
+ }
717
+
718
+ .wapp-button:disabled {
719
+ cursor: not-allowed;
720
+ opacity: 0.55;
721
+ }
722
+
723
+ .wapp-button-primary {
724
+ border-color: var(--wapp-primary);
725
+ background: var(--wapp-primary);
726
+ color: var(--wapp-bg);
727
+ }
728
+
729
+ .wapp-button-primary:hover:not(:disabled) {
730
+ background: color-mix(in srgb, var(--wapp-primary), var(--wapp-bg) 12%);
731
+ }
732
+
733
+ .wapp-button-danger {
734
+ border-color: var(--wapp-danger-bg);
735
+ background: var(--wapp-danger-bg);
736
+ color: white;
737
+ }
738
+
739
+ .wapp-button-danger:hover:not(:disabled) {
740
+ background: color-mix(in srgb, var(--wapp-danger-bg), white 10%);
741
+ }
742
+
743
+ .wapp-button-ghost:hover:not(:disabled) {
744
+ background: color-mix(in srgb, var(--wapp-muted), transparent 88%);
745
+ }
746
+
747
+ .wapp-action-menu-trigger {
748
+ display: inline-flex;
749
+ align-items: center;
750
+ justify-content: center;
751
+ min-width: 2.75rem;
752
+ min-height: 2.75rem;
753
+ border: 1px solid var(--wapp-primary);
754
+ border-radius: 0.75rem;
755
+ background: var(--wapp-primary);
756
+ color: var(--wapp-bg);
757
+ box-shadow: var(--wapp-shadow);
758
+ cursor: pointer;
759
+ transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
760
+ }
761
+
762
+ .wapp-action-menu-trigger:hover:not(:disabled),
763
+ .wapp-action-menu-trigger[aria-expanded="true"] {
764
+ border-color: var(--wapp-primary);
765
+ background: color-mix(in srgb, var(--wapp-primary), var(--wapp-bg) 12%);
766
+ }
767
+
768
+ .wapp-action-menu-trigger:disabled {
769
+ cursor: not-allowed;
770
+ opacity: 0.55;
771
+ }
772
+
773
+ .wapp-action-menu {
774
+ z-index: 1000;
775
+ min-width: 11.25rem;
776
+ overflow: hidden;
777
+ border: 1px solid #cbd5e1;
778
+ border-radius: 0.375rem;
779
+ background: #ffffff;
780
+ box-shadow: 0 16px 36px rgb(15 23 42 / 0.18);
781
+ }
782
+
783
+ :root.dark .wapp-action-menu {
784
+ border-color: #334155;
785
+ background: #262626;
786
+ box-shadow: 0 18px 42px rgb(0 0 0 / 0.35);
787
+ }
788
+
789
+ .wapp-action-menu-items {
790
+ padding: 0.25rem 0;
791
+ }
792
+
793
+ .wapp-action-menu-item {
794
+ display: flex;
795
+ width: 100%;
796
+ align-items: center;
797
+ border: 0;
798
+ background: transparent;
799
+ color: #1f2937;
800
+ cursor: pointer;
801
+ padding: 0.75rem 1rem;
802
+ text-align: left;
803
+ font-size: 0.875rem;
804
+ transition: background 120ms ease, color 120ms ease;
805
+ }
806
+
807
+ .wapp-action-menu-item:hover:not(:disabled) {
808
+ background: #f3f4f6;
809
+ }
810
+
811
+ :root.dark .wapp-action-menu-item {
812
+ color: #f3f4f6;
813
+ }
814
+
815
+ :root.dark .wapp-action-menu-item:hover:not(:disabled) {
816
+ background: #3a3a3a;
817
+ }
818
+
819
+ .wapp-action-menu-item.danger {
820
+ color: var(--wapp-danger);
821
+ }
822
+
823
+ .wapp-action-menu-item.danger:hover:not(:disabled) {
824
+ background: color-mix(in srgb, var(--wapp-danger-bg), transparent 88%);
825
+ }
826
+
827
+ :root.dark .wapp-action-menu-item.danger:hover:not(:disabled) {
828
+ background: rgb(127 29 29 / 0.45);
829
+ }
830
+
831
+ .wapp-action-menu-item:disabled {
832
+ cursor: not-allowed;
833
+ opacity: 0.55;
834
+ }
835
+
836
+ .wapp-button-ghost {
837
+ background: transparent;
838
+ }
839
+
840
+ .wapp-badge {
841
+ border-radius: 999px;
842
+ background: color-mix(in srgb, var(--wapp-muted), transparent 86%);
843
+ color: var(--wapp-muted);
844
+ font-size: 0.6875rem;
845
+ font-weight: 700;
846
+ padding: 0.125rem 0.5rem;
847
+ }
848
+
849
+ .wapp-badge-success { color: #047857; }
850
+ .wapp-badge-warning { color: #b45309; }
851
+ .wapp-badge-error { color: var(--wapp-danger); }
852
+ .wapp-badge-info { color: #2563eb; }
853
+ .wapp-badge-disabled { color: var(--wapp-muted-2); }
854
+
855
+ .wapp-settings {
856
+ max-width: none;
857
+ font-size: 0.875rem;
858
+ }
859
+
860
+ .wapp-form-section {
861
+ display: block;
862
+ padding: 0;
863
+ margin-bottom: 2.5rem;
864
+ }
865
+
866
+ .wapp-form-section + .wapp-form-section {
867
+ border-top: 0;
868
+ }
869
+
870
+ .wapp-form-section h3 {
871
+ margin: 0 0 1rem;
872
+ font-size: 0.8125rem;
873
+ font-weight: 500;
874
+ }
875
+
876
+ .wapp-form-section-body {
877
+ min-width: 0;
878
+ border-radius: 0.5rem;
879
+ background: var(--wapp-surface-muted);
880
+ padding: 1rem;
881
+ }
882
+
883
+ .wapp-form-group {
884
+ border: 1px solid var(--wapp-border-soft);
885
+ border-radius: 0.75rem;
886
+ padding: 0.875rem;
887
+ }
888
+
889
+ .wapp-form-group + .wapp-form-group {
890
+ margin-top: 0.875rem;
891
+ }
892
+
893
+ .wapp-form-group-header {
894
+ display: flex;
895
+ align-items: flex-start;
896
+ justify-content: space-between;
897
+ gap: 1rem;
898
+ margin-bottom: 0.75rem;
899
+ }
900
+
901
+ .wapp-form-group-header strong {
902
+ font-size: 0.875rem;
903
+ }
904
+
905
+ .wapp-form-group-header p {
906
+ margin: 0.25rem 0 0;
907
+ color: var(--wapp-muted);
908
+ font-size: 0.8125rem;
909
+ }
910
+
911
+ .wapp-form-group-actions,
912
+ .wapp-form-actions {
913
+ display: flex;
914
+ align-items: center;
915
+ justify-content: flex-end;
916
+ gap: 0.5rem;
917
+ }
918
+
919
+ .wapp-form-group-body {
920
+ display: grid;
921
+ gap: 0.75rem;
922
+ }
923
+
924
+ .wapp-settings-row {
925
+ display: flex;
926
+ align-items: flex-start;
927
+ justify-content: space-between;
928
+ gap: 1rem;
929
+ }
930
+
931
+ .wapp-settings-row strong {
932
+ font-size: 0.875rem;
933
+ font-weight: 600;
934
+ }
935
+
936
+ .wapp-settings-row p {
937
+ color: var(--wapp-muted);
938
+ font-size: 0.8125rem;
939
+ line-height: 1.35;
940
+ margin: 0.25rem 0 0;
941
+ }
942
+
943
+ .wapp-settings-row + .wapp-settings-row {
944
+ margin-top: 0.75rem;
945
+ }
946
+
947
+ .wapp-settings-row.stacked {
948
+ display: block;
949
+ }
950
+
951
+ .wapp-settings-row-content {
952
+ margin-top: 0.75rem;
953
+ }
954
+
955
+ .wapp-row-actions {
956
+ display: flex;
957
+ flex-wrap: wrap;
958
+ align-items: center;
959
+ justify-content: flex-end;
960
+ gap: 0.5rem;
961
+ }
962
+
963
+ .wapp-danger-zone {
964
+ display: flex;
965
+ align-items: flex-start;
966
+ justify-content: space-between;
967
+ gap: 1rem;
968
+ border: 1px solid color-mix(in srgb, var(--wapp-danger-bg), transparent 45%);
969
+ border-radius: 0.75rem;
970
+ background: color-mix(in srgb, var(--wapp-danger-bg), transparent 92%);
971
+ padding: 1rem;
972
+ }
973
+
974
+ .wapp-danger-zone strong {
975
+ color: var(--wapp-danger);
976
+ font-size: 0.875rem;
977
+ }
978
+
979
+ .wapp-danger-zone p {
980
+ margin: 0.25rem 0 0;
981
+ color: color-mix(in srgb, var(--wapp-danger), var(--wapp-muted) 55%);
982
+ font-size: 0.8125rem;
983
+ }
984
+
985
+ .wapp-danger-zone-actions {
986
+ display: flex;
987
+ flex: 0 0 auto;
988
+ align-items: center;
989
+ gap: 0.5rem;
990
+ }
991
+
992
+ .wapp-field {
993
+ display: block;
994
+ }
995
+
996
+ .wapp-field span {
997
+ display: block;
998
+ font-size: 0.8125rem;
999
+ font-weight: 500;
1000
+ margin-bottom: 0.375rem;
1001
+ }
1002
+
1003
+ .wapp-field input,
1004
+ .wapp-field select,
1005
+ .wapp-field textarea {
1006
+ width: 100%;
1007
+ min-height: 2.375rem;
1008
+ border: 1px solid var(--wapp-border);
1009
+ border-radius: 0.375rem;
1010
+ background: var(--wapp-surface);
1011
+ color: var(--wapp-text);
1012
+ box-shadow: var(--wapp-shadow);
1013
+ padding: 0.5rem 0.75rem;
1014
+ font-size: 0.8125rem;
1015
+ }
1016
+
1017
+ .wapp-field textarea {
1018
+ min-height: 8rem;
1019
+ resize: vertical;
1020
+ }
1021
+
1022
+ .wapp-field small {
1023
+ display: block;
1024
+ color: var(--wapp-muted);
1025
+ font-size: 0.75rem;
1026
+ margin-top: 0.25rem;
1027
+ }
1028
+
1029
+ .wapp-error,
1030
+ .wapp-field .error {
1031
+ color: var(--wapp-danger);
1032
+ }
1033
+
1034
+ .wapp-notice {
1035
+ color: var(--wapp-muted);
1036
+ }
1037
+
1038
+ .wapp-segmented {
1039
+ display: flex;
1040
+ width: 100%;
1041
+ gap: 0;
1042
+ border: 0;
1043
+ border-radius: 0;
1044
+ background: transparent;
1045
+ padding: 0;
1046
+ }
1047
+
1048
+ .wapp-segmented button {
1049
+ flex: 1;
1050
+ border: 1px solid var(--wapp-border);
1051
+ border-right: 0;
1052
+ border-radius: 0;
1053
+ background: var(--wapp-surface);
1054
+ color: var(--wapp-text);
1055
+ cursor: pointer;
1056
+ font-size: 0.875rem;
1057
+ font-weight: 500;
1058
+ padding: 0.5rem 0.75rem;
1059
+ }
1060
+
1061
+ .wapp-segmented button:first-child {
1062
+ border-radius: 0.375rem 0 0 0.375rem;
1063
+ }
1064
+
1065
+ .wapp-segmented button:last-child {
1066
+ border-right: 1px solid var(--wapp-border);
1067
+ border-radius: 0 0.375rem 0.375rem 0;
1068
+ }
1069
+
1070
+ .wapp-segmented button.active {
1071
+ background: #f3f4f6;
1072
+ color: var(--wapp-text);
1073
+ box-shadow: none;
1074
+ }
1075
+
1076
+ .wapp-list {
1077
+ margin-top: 0.75rem;
1078
+ border: 1px solid var(--wapp-border-soft);
1079
+ border-radius: 0.75rem;
1080
+ overflow: hidden;
1081
+ }
1082
+
1083
+ .wapp-list-row {
1084
+ display: flex;
1085
+ align-items: center;
1086
+ justify-content: space-between;
1087
+ gap: 1rem;
1088
+ padding: 0.75rem;
1089
+ }
1090
+
1091
+ .wapp-list-row + .wapp-list-row {
1092
+ border-top: 1px solid var(--wapp-border-soft);
1093
+ }
1094
+
1095
+ .wapp-list-row small {
1096
+ display: block;
1097
+ color: var(--wapp-muted);
1098
+ font-size: 0.75rem;
1099
+ }
1100
+
1101
+ .wapp-settings .wapp-list-row strong {
1102
+ font-size: 0.875rem;
1103
+ font-weight: 600;
1104
+ }
1105
+
1106
+ .wapp-settings .wapp-empty-state strong {
1107
+ font-size: 0.875rem;
1108
+ }
1109
+
1110
+ .wapp-settings .wapp-empty-state span {
1111
+ font-size: 0.8125rem;
1112
+ }
1113
+
1114
+ .wapp-token {
1115
+ display: block;
1116
+ margin-top: 0.75rem;
1117
+ overflow-wrap: anywhere;
1118
+ border: 1px solid var(--wapp-border-soft);
1119
+ border-radius: 0.5rem;
1120
+ background: var(--wapp-surface-muted);
1121
+ padding: 0.75rem;
1122
+ }
1123
+
1124
+ .wapp-code-value {
1125
+ display: grid;
1126
+ grid-template-columns: minmax(0, 1fr) auto;
1127
+ align-items: center;
1128
+ gap: 0.5rem;
1129
+ border: 1px solid var(--wapp-border-soft);
1130
+ border-radius: 0.75rem;
1131
+ background: var(--wapp-surface-muted);
1132
+ padding: 0.75rem;
1133
+ }
1134
+
1135
+ .wapp-code-value > span {
1136
+ grid-column: 1 / -1;
1137
+ color: var(--wapp-muted);
1138
+ font-size: 0.75rem;
1139
+ font-weight: 600;
1140
+ }
1141
+
1142
+ .wapp-code-value code {
1143
+ min-width: 0;
1144
+ overflow: hidden;
1145
+ text-overflow: ellipsis;
1146
+ white-space: nowrap;
1147
+ color: var(--wapp-text);
1148
+ font-size: 0.8125rem;
1149
+ }
1150
+
1151
+ .wapp-mobile-backdrop {
1152
+ position: fixed;
1153
+ inset: 0;
1154
+ background: rgb(15 23 42 / 0.36);
1155
+ z-index: 50;
1156
+ }
1157
+
1158
+ .wapp-dialog-backdrop {
1159
+ position: fixed;
1160
+ inset: 0;
1161
+ z-index: 80;
1162
+ background: rgb(0 0 0 / 0.58);
1163
+ backdrop-filter: blur(10px);
1164
+ }
1165
+
1166
+ .wapp-dialog {
1167
+ position: fixed;
1168
+ top: 50%;
1169
+ left: 50%;
1170
+ transform: translate(-50%, -50%);
1171
+ width: min(28rem, calc(100vw - 2rem));
1172
+ z-index: 81;
1173
+ overflow: hidden;
1174
+ border: 1px solid var(--wapp-border);
1175
+ border-radius: 0.5rem;
1176
+ background: var(--wapp-surface);
1177
+ padding: 0;
1178
+ box-shadow: 0 24px 55px rgb(0 0 0 / 0.38);
1179
+ }
1180
+
1181
+ .wapp-dialog-title {
1182
+ display: flex;
1183
+ align-items: center;
1184
+ justify-content: space-between;
1185
+ gap: 1rem;
1186
+ border-bottom: 1px solid var(--wapp-border);
1187
+ padding: 1rem 1.5rem;
1188
+ }
1189
+
1190
+ .wapp-dialog h2 {
1191
+ margin: 0;
1192
+ font-size: 1.125rem;
1193
+ }
1194
+
1195
+ .wapp-dialog-close {
1196
+ display: inline-flex;
1197
+ align-items: center;
1198
+ justify-content: center;
1199
+ width: 1.75rem;
1200
+ height: 1.75rem;
1201
+ border: 0;
1202
+ border-radius: 0.375rem;
1203
+ background: transparent;
1204
+ color: var(--wapp-muted);
1205
+ cursor: pointer;
1206
+ font-size: 1.5rem;
1207
+ line-height: 1;
1208
+ }
1209
+
1210
+ .wapp-dialog-close:hover {
1211
+ background: color-mix(in srgb, var(--wapp-muted), transparent 88%);
1212
+ color: var(--wapp-text);
1213
+ }
1214
+
1215
+ .wapp-dialog-body {
1216
+ border-bottom: 1px solid var(--wapp-border);
1217
+ padding: 1.25rem 1.5rem;
1218
+ }
1219
+
1220
+ .wapp-dialog-body p {
1221
+ margin: 0;
1222
+ color: color-mix(in srgb, var(--wapp-text), var(--wapp-muted) 28%);
1223
+ line-height: 1.45;
1224
+ }
1225
+
1226
+ .wapp-dialog-actions {
1227
+ display: flex;
1228
+ justify-content: flex-end;
1229
+ gap: 0.5rem;
1230
+ padding: 1rem 1.5rem;
1231
+ }
1232
+
1233
+ .wapp-toolbar {
1234
+ display: flex;
1235
+ align-items: center;
1236
+ justify-content: space-between;
1237
+ gap: 0.75rem;
1238
+ margin-bottom: 1rem;
1239
+ }
1240
+
1241
+ .wapp-mobile-only,
1242
+ .wapp-mobile-backdrop {
1243
+ display: none;
1244
+ }
1245
+
1246
+ @media (max-width: 900px) {
1247
+ .wapp-shell {
1248
+ display: block;
1249
+ }
1250
+
1251
+ .wapp-sidebar,
1252
+ .wapp-shell.sidebar-collapsed .wapp-sidebar {
1253
+ position: fixed;
1254
+ inset: 0 auto 0 0;
1255
+ z-index: 60;
1256
+ width: min(20rem, 86vw);
1257
+ min-width: min(20rem, 86vw);
1258
+ transform: translateX(-100%);
1259
+ opacity: 1;
1260
+ pointer-events: auto;
1261
+ }
1262
+
1263
+ .wapp-shell.sidebar-open .wapp-sidebar {
1264
+ transform: translateX(0);
1265
+ }
1266
+
1267
+ .wapp-shell.sidebar-open .wapp-mobile-backdrop {
1268
+ display: block;
1269
+ }
1270
+
1271
+ .wapp-main-header {
1272
+ padding: 0.875rem 1rem;
1273
+ }
1274
+
1275
+ .wapp-main-content {
1276
+ padding: 1rem;
1277
+ }
1278
+
1279
+ .wapp-mobile-only {
1280
+ display: inline-flex;
1281
+ }
1282
+
1283
+ .wapp-form-section {
1284
+ grid-template-columns: 1fr;
1285
+ }
1286
+
1287
+ .wapp-settings-row {
1288
+ display: block;
1289
+ }
1290
+
1291
+ .wapp-row-actions {
1292
+ justify-content: flex-start;
1293
+ margin-top: 0.75rem;
1294
+ }
1295
+ }