@embarkai/ui-kit 0.4.12 → 0.4.16

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.
@@ -1,881 +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
-
7
- <!-- Security Headers -->
8
- <!-- NOTE: These are fallback headers for development. Production uses _headers file from Cloudflare -->
9
- <!-- WARNING: frame-ancestors MUST be set via HTTP headers, not meta tags (it's ignored in meta) -->
10
- <!-- WARNING: localhost is allowed ONLY for local development, production CSP in _headers is stricter -->
11
- <meta
12
- http-equiv="Content-Security-Policy"
13
- content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-eval' https://telegram.org; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: blob:; font-src 'self'; connect-src 'self' http://localhost:* https://*.lumiapassport.com; frame-src https://oauth.telegram.org; base-uri 'self'; form-action 'self';"
14
- />
15
- <meta http-equiv="X-Content-Type-Options" content="nosniff" />
16
- <meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin" />
17
-
18
- <title>EmbarkAI Secure Wallet - iframe version 0.4.12</title>
19
-
20
- <!-- Styles will be injected by build process -->
21
- <style>
22
- :root {
23
- /* scales */
24
- --iframe-gap: 10px;
25
- --iframe-pd: 20px;
26
- --iframe-bdrs: 20px;
27
- --iframe-el-bdrs: 10px;
28
-
29
- /* heading */
30
- --iframe-heading-fw: 600;
31
- --iframe-h1-fz: 20px;
32
- --iframe-h2-fz: 14px;
33
-
34
- /* Default theme colors (can be overridden via URL params) */
35
- --iframe-bg: rgba(0, 0, 0, 0.8);
36
- --iframe-text: #212121;
37
- --iframe-text-secondary: #606060;
38
- --iframe-border: #e0e0e0;
39
- --iframe-modal-bg: white;
40
- --iframe-button-bg: #000000;
41
- --iframe-button-text: white;
42
- --iframe-section-bg: #f9fafb;
43
- --iframe-section-border: var(--iframe-border);
44
- --iframe-section-text: var(--iframe-text);
45
- --iframe-field-label: var(--iframe-text-secondary);
46
-
47
- --iframe-info: #e4e4e4;
48
- --iframe-success: #21ff51;
49
- --iframe-warning: #e9fa00;
50
- --iframe-error: #d6204e;
51
- }
52
-
53
- :root::-webkit-scrollbar {
54
- display: none;
55
- }
56
-
57
- * {
58
- margin: 0;
59
- padding: 0;
60
- box-sizing: border-box;
61
- }
62
-
63
- body {
64
- /* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif; */
65
- font-family: -apple-system, BlinkMacSystemFont, 'Inter', system-ui, sans-serif;
66
-
67
- background: #000000;
68
- /* backdrop-filter: blur(10px); */
69
-
70
- color: var(--iframe-text);
71
-
72
- min-height: 100dvh;
73
-
74
- display: flex;
75
- align-items: center;
76
- justify-content: center;
77
- }
78
-
79
- #app {
80
- width: 100%;
81
- height: 100dvh;
82
- display: flex;
83
- align-items: center;
84
- justify-content: center;
85
- }
86
-
87
- /* Ready Indicator START */
88
- /* OK */
89
- .ready-indicator {
90
- text-align: center;
91
- background: var(--iframe-modal-bg);
92
- padding: var(--iframe-pd);
93
- border-radius: var(--iframe-bdrs);
94
- max-width: 320px;
95
- width: calc(100% - 40px);
96
- display: flex;
97
- flex-direction: column;
98
- align-items: center;
99
- gap: var(--iframe-gap);
100
- padding: var(--iframe-pd);
101
- font-size: 10px;
102
- }
103
-
104
- /* OK */
105
- .status-icon {
106
- display: flex;
107
- align-items: center;
108
- justify-content: center;
109
- font-size: 20px;
110
- font-weight: 600;
111
- width: 48px;
112
- height: 48px;
113
- border-radius: 24px;
114
- background: var(--iframe-success);
115
- color: #000000;
116
- }
117
-
118
- /* OK */
119
- .ready-indicator h2 {
120
- font-size: var(--iframe-h1-fz);
121
- color: var(--iframe-text);
122
- }
123
-
124
- /* OK */
125
- .ready-indicator p {
126
- font-size: 16px;
127
- color: var(--iframe-text-secondary);
128
- }
129
-
130
- /* OK */
131
- .ready-indicator .info {
132
- width: 100%;
133
- background: var(--iframe-info);
134
- color: var(--iframe-text);
135
- border-radius: var(--iframe-el-bdrs);
136
- padding: var(--iframe-gap);
137
- }
138
-
139
- /* OK */
140
- .ready-indicator .info-row {
141
- display: flex;
142
- align-items: center;
143
- justify-content: space-between;
144
- min-height: 24px;
145
- }
146
-
147
- /* OK */
148
- .status-active {
149
- color: var(--iframe-text);
150
- background-color: var(--iframe-success);
151
- font-weight: 600;
152
- line-height: 14px;
153
- padding: 0 4px;
154
- border-radius: 7px;
155
- }
156
- /* Ready Indicator END */
157
-
158
- /* Loading Indicator */
159
- .loading-indicator {
160
- text-align: center;
161
- color: white;
162
- }
163
-
164
- .spinner {
165
- width: 50px;
166
- height: 50px;
167
- margin: 0 auto 1rem;
168
- /* border: 4px solid var(--iframe-text-secondary);
169
- border-top-color: white; */
170
- border-radius: 50%;
171
- animation: spin 1s linear infinite;
172
- }
173
-
174
- @keyframes spin {
175
- to {
176
- transform: rotate(360deg);
177
- }
178
- }
179
-
180
- .loading-indicator p {
181
- font-size: 1.1rem;
182
- }
183
-
184
- /* Error Indicator */
185
- .error-indicator {
186
- text-align: center;
187
- background: white;
188
- padding: 3rem;
189
- border-radius: 10px;
190
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
191
- max-width: 400px;
192
- }
193
-
194
- .error-icon {
195
- font-size: 4rem;
196
- margin-bottom: 1rem;
197
- }
198
-
199
- .error-indicator h2 {
200
- font-size: 1.5rem;
201
- margin-bottom: 0.5rem;
202
- color: #ef4444;
203
- }
204
-
205
- .error-indicator p {
206
- color: #666;
207
- }
208
-
209
- /* Modal Overlays */
210
- /* Authorization modal container */
211
- .authorization-modal {
212
- position: fixed;
213
- top: 0;
214
- left: 0;
215
- right: 0;
216
- bottom: 0;
217
- z-index: 10000;
218
- }
219
-
220
- /* Transaction confirmation modal container */
221
- .transaction-confirmation-modal {
222
- position: fixed;
223
- top: 0;
224
- left: 0;
225
- right: 0;
226
- bottom: 0;
227
- z-index: 10000;
228
- }
229
-
230
- .modal-overlay {
231
- position: fixed;
232
- top: 0;
233
- left: 0;
234
- right: 0;
235
- bottom: 0;
236
- display: flex;
237
- align-items: center;
238
- justify-content: center;
239
- z-index: 10000;
240
- backdrop-filter: blur(10px);
241
- }
242
-
243
- .modal-content {
244
- background: var(--iframe-modal-bg);
245
- border-radius: 20px;
246
- padding: 20px;
247
- max-width: 320px;
248
- width: calc(100% - 40px);
249
- max-height: 90vh;
250
- overflow-y: auto;
251
- }
252
-
253
- /* Authorization Header with Logos */
254
- .auth-header {
255
- padding: 1rem 0;
256
- text-align: center;
257
- border-bottom: 1px solid var(--iframe-border);
258
- }
259
-
260
- .logo-container {
261
- display: flex;
262
- align-items: center;
263
- justify-content: center;
264
- gap: 1rem;
265
- margin-bottom: 0.75rem;
266
- }
267
-
268
- .app-logo {
269
- width: 64px;
270
- /* height: 64px; */
271
- border-radius: 12px;
272
- border: 1px solid var(--iframe-border);
273
- object-fit: cover;
274
- }
275
-
276
- .app-logo-placeholder {
277
- width: 64px;
278
- height: 64px;
279
- border-radius: 12px;
280
- /* background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); */
281
- }
282
-
283
- .logo-connector {
284
- color: #6b7280;
285
- display: flex;
286
- align-items: center;
287
- }
288
-
289
- .lumia-logo {
290
- width: 48px;
291
- height: 48px;
292
- object-fit: contain;
293
- }
294
-
295
- .lumia-logo.user-avatar {
296
- border-radius: 50%;
297
- border: 2px solid var(--iframe-border);
298
- object-fit: cover;
299
- }
300
-
301
- /* Authorization Title */
302
- .auth-title {
303
- font-size: 1.5rem;
304
- font-weight: 600;
305
- color: var(--iframe-text);
306
- margin: 0;
307
- padding: 1rem 0;
308
- text-align: center;
309
- }
310
-
311
- /* Description Section */
312
- .auth-description {
313
- padding: 1rem 0;
314
- text-align: center;
315
- }
316
-
317
- .project-description {
318
- font-size: 0.875rem;
319
- color: var(--iframe-text-secondary);
320
- margin: 0 0 0.75rem 0;
321
- line-height: 1.5;
322
- }
323
-
324
- /* OK */
325
- .domain-info {
326
- display: inline-flex;
327
- align-items: center;
328
- gap: var(--iframe-gap);
329
- min-height: 48px;
330
- width: 100%;
331
- font-size: 12px;
332
- padding: 0 var(--iframe-pd);
333
- border-radius: 10px;
334
- background: var(--iframe-info);
335
- }
336
-
337
- /* OK */
338
- .domain-info.verified {
339
- background: var(--iframe-success);
340
- color: #000000;
341
- }
342
-
343
- /* OK */
344
- .domain-info.unverified {
345
- background: var(--iframe-error);
346
- color: #000000;
347
- }
348
-
349
- /* DEPRECATE */
350
- .domain-label {
351
- font-weight: 500;
352
- }
353
-
354
- /* DEPRECATE */
355
- .domain-value {
356
- font-family: monospace;
357
- font-size: 0.8125rem;
358
- }
359
-
360
- /* DEPRECATE */
361
- /* Permissions Box */
362
- .permissions-box {
363
- margin: 1.5rem 2rem;
364
- padding: 1rem;
365
- border: 1px solid var(--iframe-border);
366
- border-radius: 8px;
367
- background: transparent;
368
- }
369
-
370
- /* DEPRECATE */
371
- .permissions-header {
372
- font-size: 0.875rem;
373
- color: var(--iframe-text);
374
- margin-bottom: 1rem;
375
- line-height: 1.5;
376
- }
377
-
378
- .project-owner {
379
- color: var(--iframe-text-secondary);
380
- }
381
-
382
- /* DEPRECATE */
383
- .permission-group {
384
- margin-bottom: 1rem;
385
- }
386
-
387
- /* DEPRECATE */
388
- .permission-group:last-child {
389
- margin-bottom: 0;
390
- }
391
-
392
- /* DEPRECATE */
393
- .permission-group-title {
394
- font-size: 0.8125rem;
395
- font-weight: 600;
396
- color: var(--iframe-text);
397
- margin-bottom: 0.5rem;
398
- }
399
-
400
- /* DEPRECATE */
401
- .permissions-list ul {
402
- list-style: none;
403
- padding: 0;
404
- margin: 0;
405
- }
406
-
407
- /* DEPRECATE */
408
- .permissions-list li {
409
- font-size: 0.8125rem;
410
- color: var(--iframe-text);
411
- padding: 0.25rem 0;
412
- padding-left: 1.25rem;
413
- position: relative;
414
- }
415
-
416
- /* DEPRECATE */
417
- .permissions-list li:before {
418
- content: '•';
419
- position: absolute;
420
- left: 0.5rem;
421
- color: var(--iframe-text-secondary);
422
- }
423
-
424
- /* DEPRECATE */
425
- /* Actions Section */
426
- .actions {
427
- display: flex;
428
- gap: 0.5rem;
429
- padding: 1.5rem 2rem;
430
- border-top: 1px solid var(--iframe-border);
431
- }
432
-
433
- /* OK */
434
- button {
435
- cursor: pointer;
436
- flex: 1;
437
- padding: 0 var(--iframe-pd);
438
- border: 1px solid;
439
- border-radius: var(--iframe-el-bdrs);
440
- font-size: 16px;
441
- font-weight: 600;
442
- transition: all 0.15s;
443
- min-height: 48px;
444
- }
445
-
446
- /* OK */
447
- .cancel-btn {
448
- background: transparent;
449
- color: var(--iframe-text-secondary);
450
- border-color: rgba(27, 31, 36, 0.15);
451
- }
452
-
453
- /* OK */
454
- .confirm-btn,
455
- .authorize-btn {
456
- background: #000000;
457
- color: #ffffff;
458
- border-color: #000000;
459
- }
460
-
461
- /* OK */
462
- .cancel-btn:hover,
463
- .confirm-btn:hover,
464
- .authorize-btn:hover {
465
- opacity: 0.6;
466
- }
467
- /* OK */
468
- .cancel-btn:active,
469
- .confirm-btn:active,
470
- .authorize-btn:active {
471
- opacity: 0.4;
472
- }
473
-
474
- /* OK */
475
- button:disabled {
476
- opacity: 0.2;
477
- cursor: not-allowed;
478
- }
479
- /* OK */
480
- button:disabled:hover {
481
- opacity: 0.2;
482
- }
483
- /* OK */
484
- button:disabled:hover {
485
- opacity: 0.2;
486
- }
487
-
488
- /* Footer */
489
- .auth-footer {
490
- padding: 0 2rem 2rem;
491
- text-align: center;
492
- }
493
-
494
- .auth-footer p {
495
- font-size: 0.75rem;
496
- color: var(--iframe-text-secondary);
497
- margin: 0.5rem 0;
498
- line-height: 1.5;
499
- }
500
-
501
- .auth-footer strong {
502
- font-weight: 600;
503
- color: var(--iframe-text);
504
- }
505
-
506
- .footer-note {
507
- color: #6b7280;
508
- }
509
-
510
- .tx-details {
511
- background: #f9fafb;
512
- border-radius: 8px;
513
- padding: 1rem;
514
- margin: 1rem 0;
515
- }
516
-
517
- .detail-row {
518
- display: flex;
519
- justify-content: space-between;
520
- padding: 0.5rem 0;
521
- font-size: 0.9rem;
522
- }
523
-
524
- .detail-row code {
525
- font-family: 'Courier New', monospace;
526
- }
527
-
528
- /* Security Warning */
529
- .security-warning {
530
- display: flex;
531
- flex-direction: column;
532
- gap: var(--iframe-gap);
533
- padding: var(--iframe-gap);
534
- background: var(--iframe-warning);
535
- border-radius: var(--iframe-el-bdrs);
536
- color: var(--iframe-text);
537
- font-size: 10px;
538
- }
539
-
540
- /* .security-warning strong {
541
- display: block;
542
- margin-bottom: 0.5rem;
543
- } */
544
-
545
- /* .warning-details {
546
- margin-top: 0.5rem;
547
- font-family: monospace;
548
- font-size: 0.75rem;
549
- color: #7d5d00;
550
- } */
551
-
552
- .warning-details div {
553
- margin: 0.25rem 0;
554
- }
555
-
556
- .risk-warning {
557
- padding: 1rem;
558
- border-radius: 8px;
559
- margin: 1rem 0;
560
- }
561
-
562
- .risk-warning.medium-risk {
563
- background: #fef3c7;
564
- border-left: 4px solid #f59e0b;
565
- }
566
-
567
- .risk-warning.high-risk {
568
- background: #fee2e2;
569
- border-left: 4px solid #ef4444;
570
- }
571
-
572
- .risk-warning.critical-risk {
573
- background: #fce7f3;
574
- border-left: 4px solid #ec4899;
575
- }
576
-
577
- /* Additional modal styles for transaction confirmation */
578
- .app-identity .app-info h3 {
579
- font-size: 1.2rem;
580
- margin-bottom: 0.25rem;
581
- }
582
-
583
- .app-identity .app-info .origin {
584
- font-size: 0.9rem;
585
- color: #6b7280;
586
- }
587
-
588
- .app-identity.low-risk {
589
- border-color: #10b981;
590
- }
591
-
592
- .app-identity.medium-risk {
593
- border-color: #f59e0b;
594
- }
595
-
596
- .app-identity.high-risk {
597
- border-color: #ef4444;
598
- }
599
-
600
- .app-identity.critical-risk {
601
- border-color: #ec4899;
602
- }
603
-
604
- .risk-warning strong {
605
- display: block;
606
- margin-bottom: 0.5rem;
607
- font-size: 1rem;
608
- }
609
-
610
- /* Trust app checkbox section */
611
- /* OK */
612
- .trust-app-section {
613
- width: 100%;
614
- padding: var(--iframe-gap);
615
- background: var(--iframe-info);
616
- border-radius: var(--iframe-el-bdrs);
617
- border: 1px solid var(--iframe-bd);
618
- transition: opacity 0.2s ease;
619
- }
620
- .trust-app-section:hover {
621
- opacity: 0.6;
622
- }
623
- .trust-app-section:active {
624
- opacity: 0.4;
625
- }
626
-
627
- /* OK */
628
- .trust-app-label {
629
- display: flex;
630
- align-items: flex-start;
631
- gap: var(--iframe-gap);
632
- cursor: pointer;
633
- font-size: 0.9rem;
634
- }
635
-
636
- /* OK */
637
- .trust-app-checkbox {
638
- width: 24px;
639
- height: 24px;
640
- cursor: pointer;
641
- flex-shrink: 0;
642
- }
643
-
644
- .trust-app-label span {
645
- font-weight: 600;
646
- font-size: 14px;
647
- line-height: 1.2;
648
- user-select: none;
649
- }
650
-
651
- /* EIP712 Signature Request Modal Styles */
652
- .eip712-confirmation-modal {
653
- position: fixed;
654
- top: 0;
655
- left: 0;
656
- right: 0;
657
- bottom: 0;
658
- z-index: 10000;
659
- }
660
-
661
- .eip712-modal {
662
- max-width: 480px;
663
- }
664
-
665
- .eip712-content {
666
- padding: 1.5rem 2rem;
667
- }
668
-
669
- .section-title {
670
- font-size: 0.875rem;
671
- font-weight: 600;
672
- color: var(--iframe-section-text);
673
- margin-bottom: 0.75rem;
674
- text-transform: uppercase;
675
- letter-spacing: 0.05em;
676
- }
677
-
678
- .eip712-section {
679
- background: var(--iframe-section-bg);
680
- border: 1px solid var(--iframe-section-border);
681
- border-radius: 8px;
682
- padding: 1rem;
683
- margin-bottom: 1rem;
684
- }
685
-
686
- .section-subtitle {
687
- font-size: 1rem;
688
- font-weight: 700;
689
- color: var(--iframe-section-text);
690
- margin-bottom: 0.75rem;
691
- padding-bottom: 0.5rem;
692
- border-bottom: 1px solid var(--iframe-section-border);
693
- }
694
-
695
- .eip712-field {
696
- display: grid;
697
- grid-template-columns: 120px 1fr;
698
- gap: 0.75rem;
699
- padding: 0.5rem 0;
700
- align-items: start;
701
- }
702
-
703
- .eip712-field:not(:last-child) {
704
- border-bottom: 1px solid var(--iframe-section-border);
705
- }
706
-
707
- .field-name {
708
- font-size: 0.8125rem;
709
- font-weight: 500;
710
- color: var(--iframe-field-label);
711
- }
712
-
713
- .field-value {
714
- font-size: 0.8125rem;
715
- color: var(--iframe-section-text);
716
- word-break: break-word;
717
- font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
718
- }
719
-
720
- .eip712-details {
721
- margin-bottom: 1rem;
722
- border: 1px solid var(--iframe-section-border);
723
- border-radius: 6px;
724
- overflow: hidden;
725
- }
726
-
727
- .eip712-details summary {
728
- padding: 0.75rem 1rem;
729
- cursor: pointer;
730
- background: var(--iframe-section-bg);
731
- font-size: 0.875rem;
732
- font-weight: 600;
733
- color: var(--iframe-section-text);
734
- user-select: none;
735
- list-style: none;
736
- display: flex;
737
- align-items: center;
738
- justify-content: space-between;
739
- }
740
-
741
- .eip712-details summary::-webkit-details-marker {
742
- display: none;
743
- }
744
-
745
- .eip712-details summary:after {
746
- content: '▼';
747
- font-size: 0.7rem;
748
- transition: transform 0.2s;
749
- color: var(--iframe-section-text);
750
- }
751
-
752
- .eip712-details[open] summary:after {
753
- transform: rotate(180deg);
754
- }
755
-
756
- .eip712-details summary:hover {
757
- background: var(--iframe-modal-bg);
758
- opacity: 0.9;
759
- }
760
-
761
- .eip712-raw {
762
- margin: 0;
763
- padding: 1rem;
764
- background: var(--iframe-modal-bg);
765
- border-top: 1px solid var(--iframe-section-border);
766
- max-height: 200px;
767
- overflow-y: auto;
768
- }
769
-
770
- .eip712-raw code {
771
- font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
772
- font-size: 0.75rem;
773
- color: var(--iframe-section-text);
774
- line-height: 1.5;
775
- white-space: pre-wrap;
776
- word-break: break-all;
777
- }
778
-
779
- .security-info {
780
- background: var(--iframe-section-bg);
781
- border-radius: 6px;
782
- padding: 0.75rem 1rem;
783
- margin-bottom: 1rem;
784
- }
785
-
786
- .info-item {
787
- display: flex;
788
- justify-content: space-between;
789
- align-items: center;
790
- font-size: 0.8125rem;
791
- }
792
-
793
- .info-label {
794
- font-weight: 500;
795
- color: var(--iframe-field-label);
796
- }
797
-
798
- .info-value {
799
- font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
800
- color: var(--iframe-section-text);
801
- }
802
-
803
- .footer-notice {
804
- padding: 0 2rem 2rem;
805
- text-align: center;
806
- }
807
-
808
- .modal-title {
809
- font-size: 1.25rem;
810
- font-weight: 600;
811
- color: var(--iframe-text);
812
- margin: 0.5rem 0;
813
- }
814
-
815
- .origin-text {
816
- font-size: 0.875rem;
817
- color: var(--iframe-text-secondary);
818
- margin: 0;
819
- display: flex;
820
- align-items: center;
821
- justify-content: center;
822
- gap: 0.5rem;
823
- }
824
-
825
- .verified-badge {
826
- color: #10b981;
827
- font-weight: 600;
828
- }
829
-
830
- .project-logo {
831
- width: 48px;
832
- height: 48px;
833
- border-radius: 8px;
834
- object-fit: cover;
835
- }
836
-
837
- .project-logo-placeholder {
838
- width: 48px;
839
- height: 48px;
840
- border-radius: 8px;
841
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
842
- display: flex;
843
- align-items: center;
844
- justify-content: center;
845
- font-size: 1.5rem;
846
- }
847
-
848
- .arrow-icon {
849
- font-size: 1.25rem;
850
- color: #9ca3af;
851
- }
852
-
853
- .lumia-logo {
854
- width: 40px;
855
- height: 40px;
856
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
857
- border-radius: 8px;
858
- display: flex;
859
- align-items: center;
860
- justify-content: center;
861
- color: white;
862
- font-weight: 700;
863
- font-size: 1.25rem;
864
- }
865
- </style>
866
- </head>
867
- <body>
868
- <div id="app">
869
- <!-- App content will be injected here by main.ts -->
870
- <div class="loading-indicator">
871
- <div class="spinner"></div>
872
- <p>Initializing secure wallet...</p>
873
- </div>
874
- </div>
875
-
876
- <!-- Main script -->
877
- <!-- In dev mode (Vite): loads TypeScript from src/ -->
878
- <!-- In production: loads compiled JavaScript from dist/ -->
879
- <script type="module" src="./main.js"></script>
880
- </body>
881
- </html>