+455
-24
package-lock.json
+455
-24
package-lock.json
···
34
34
"postcss": "^8.5.6",
35
35
"tailwindcss": "^3.4.0",
36
36
"typescript": "^5.3.3",
37
-
"vite": "^5.4.0"
37
+
"vite": "^5.4.0",
38
+
"vite-plugin-svgr": "^4.5.0"
38
39
}
39
40
},
40
41
"node_modules/@alloc/quick-lru": {
···
55
56
"resolved": "https://registry.npmjs.org/@atcute/identity/-/identity-1.1.0.tgz",
56
57
"integrity": "sha512-6vRvRqJatDB+JUQsb+UswYmtBGQnSZcqC3a2y6H5DB/v5KcIh+6nFFtc17G0+3W9rxdk7k9M4KkgkdKf/YDNoQ==",
57
58
"license": "0BSD",
59
+
"peer": true,
58
60
"dependencies": {
59
61
"@atcute/lexicons": "^1.1.1",
60
62
"@badrap/valita": "^0.4.5"
···
407
409
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
408
410
"dev": true,
409
411
"license": "MIT",
412
+
"peer": true,
410
413
"dependencies": {
411
414
"@babel/code-frame": "^7.27.1",
412
415
"@babel/generator": "^7.28.3",
···
2103
2106
"cpu": [
2104
2107
"arm"
2105
2108
],
2106
-
"dev": true,
2107
2109
"license": "MIT",
2108
2110
"optional": true,
2109
2111
"os": [
···
2117
2119
"cpu": [
2118
2120
"arm64"
2119
2121
],
2120
-
"dev": true,
2121
2122
"license": "MIT",
2122
2123
"optional": true,
2123
2124
"os": [
···
2131
2132
"cpu": [
2132
2133
"arm64"
2133
2134
],
2134
-
"dev": true,
2135
2135
"license": "MIT",
2136
2136
"optional": true,
2137
2137
"os": [
···
2145
2145
"cpu": [
2146
2146
"x64"
2147
2147
],
2148
-
"dev": true,
2149
2148
"license": "MIT",
2150
2149
"optional": true,
2151
2150
"os": [
···
2159
2158
"cpu": [
2160
2159
"arm64"
2161
2160
],
2162
-
"dev": true,
2163
2161
"license": "MIT",
2164
2162
"optional": true,
2165
2163
"os": [
···
2173
2171
"cpu": [
2174
2172
"x64"
2175
2173
],
2176
-
"dev": true,
2177
2174
"license": "MIT",
2178
2175
"optional": true,
2179
2176
"os": [
···
2187
2184
"cpu": [
2188
2185
"arm"
2189
2186
],
2190
-
"dev": true,
2191
2187
"license": "MIT",
2192
2188
"optional": true,
2193
2189
"os": [
···
2201
2197
"cpu": [
2202
2198
"arm"
2203
2199
],
2204
-
"dev": true,
2205
2200
"license": "MIT",
2206
2201
"optional": true,
2207
2202
"os": [
···
2215
2210
"cpu": [
2216
2211
"arm64"
2217
2212
],
2218
-
"dev": true,
2219
2213
"license": "MIT",
2220
2214
"optional": true,
2221
2215
"os": [
···
2229
2223
"cpu": [
2230
2224
"arm64"
2231
2225
],
2232
-
"dev": true,
2233
2226
"license": "MIT",
2234
2227
"optional": true,
2235
2228
"os": [
···
2243
2236
"cpu": [
2244
2237
"loong64"
2245
2238
],
2246
-
"dev": true,
2247
2239
"license": "MIT",
2248
2240
"optional": true,
2249
2241
"os": [
···
2257
2249
"cpu": [
2258
2250
"ppc64"
2259
2251
],
2260
-
"dev": true,
2261
2252
"license": "MIT",
2262
2253
"optional": true,
2263
2254
"os": [
···
2271
2262
"cpu": [
2272
2263
"riscv64"
2273
2264
],
2274
-
"dev": true,
2275
2265
"license": "MIT",
2276
2266
"optional": true,
2277
2267
"os": [
···
2285
2275
"cpu": [
2286
2276
"riscv64"
2287
2277
],
2288
-
"dev": true,
2289
2278
"license": "MIT",
2290
2279
"optional": true,
2291
2280
"os": [
···
2299
2288
"cpu": [
2300
2289
"s390x"
2301
2290
],
2302
-
"dev": true,
2303
2291
"license": "MIT",
2304
2292
"optional": true,
2305
2293
"os": [
···
2313
2301
"cpu": [
2314
2302
"x64"
2315
2303
],
2316
-
"dev": true,
2317
2304
"license": "MIT",
2318
2305
"optional": true,
2319
2306
"os": [
···
2327
2314
"cpu": [
2328
2315
"x64"
2329
2316
],
2330
-
"dev": true,
2331
2317
"license": "MIT",
2332
2318
"optional": true,
2333
2319
"os": [
···
2341
2327
"cpu": [
2342
2328
"arm64"
2343
2329
],
2344
-
"dev": true,
2345
2330
"license": "MIT",
2346
2331
"optional": true,
2347
2332
"os": [
···
2355
2340
"cpu": [
2356
2341
"arm64"
2357
2342
],
2358
-
"dev": true,
2359
2343
"license": "MIT",
2360
2344
"optional": true,
2361
2345
"os": [
···
2369
2353
"cpu": [
2370
2354
"ia32"
2371
2355
],
2372
-
"dev": true,
2373
2356
"license": "MIT",
2374
2357
"optional": true,
2375
2358
"os": [
···
2383
2366
"cpu": [
2384
2367
"x64"
2385
2368
],
2386
-
"dev": true,
2387
2369
"license": "MIT",
2388
2370
"optional": true,
2389
2371
"os": [
···
2397
2379
"cpu": [
2398
2380
"x64"
2399
2381
],
2400
-
"dev": true,
2401
2382
"license": "MIT",
2402
2383
"optional": true,
2403
2384
"os": [
···
2414
2395
"text-hex": "1.0.x"
2415
2396
}
2416
2397
},
2398
+
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
2399
+
"version": "8.0.0",
2400
+
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
2401
+
"integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
2402
+
"dev": true,
2403
+
"license": "MIT",
2404
+
"engines": {
2405
+
"node": ">=14"
2406
+
},
2407
+
"funding": {
2408
+
"type": "github",
2409
+
"url": "https://github.com/sponsors/gregberge"
2410
+
},
2411
+
"peerDependencies": {
2412
+
"@babel/core": "^7.0.0-0"
2413
+
}
2414
+
},
2415
+
"node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
2416
+
"version": "8.0.0",
2417
+
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
2418
+
"integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
2419
+
"dev": true,
2420
+
"license": "MIT",
2421
+
"engines": {
2422
+
"node": ">=14"
2423
+
},
2424
+
"funding": {
2425
+
"type": "github",
2426
+
"url": "https://github.com/sponsors/gregberge"
2427
+
},
2428
+
"peerDependencies": {
2429
+
"@babel/core": "^7.0.0-0"
2430
+
}
2431
+
},
2432
+
"node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
2433
+
"version": "8.0.0",
2434
+
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
2435
+
"integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
2436
+
"dev": true,
2437
+
"license": "MIT",
2438
+
"engines": {
2439
+
"node": ">=14"
2440
+
},
2441
+
"funding": {
2442
+
"type": "github",
2443
+
"url": "https://github.com/sponsors/gregberge"
2444
+
},
2445
+
"peerDependencies": {
2446
+
"@babel/core": "^7.0.0-0"
2447
+
}
2448
+
},
2449
+
"node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
2450
+
"version": "8.0.0",
2451
+
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
2452
+
"integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
2453
+
"dev": true,
2454
+
"license": "MIT",
2455
+
"engines": {
2456
+
"node": ">=14"
2457
+
},
2458
+
"funding": {
2459
+
"type": "github",
2460
+
"url": "https://github.com/sponsors/gregberge"
2461
+
},
2462
+
"peerDependencies": {
2463
+
"@babel/core": "^7.0.0-0"
2464
+
}
2465
+
},
2466
+
"node_modules/@svgr/babel-plugin-svg-dynamic-title": {
2467
+
"version": "8.0.0",
2468
+
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
2469
+
"integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
2470
+
"dev": true,
2471
+
"license": "MIT",
2472
+
"engines": {
2473
+
"node": ">=14"
2474
+
},
2475
+
"funding": {
2476
+
"type": "github",
2477
+
"url": "https://github.com/sponsors/gregberge"
2478
+
},
2479
+
"peerDependencies": {
2480
+
"@babel/core": "^7.0.0-0"
2481
+
}
2482
+
},
2483
+
"node_modules/@svgr/babel-plugin-svg-em-dimensions": {
2484
+
"version": "8.0.0",
2485
+
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
2486
+
"integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
2487
+
"dev": true,
2488
+
"license": "MIT",
2489
+
"engines": {
2490
+
"node": ">=14"
2491
+
},
2492
+
"funding": {
2493
+
"type": "github",
2494
+
"url": "https://github.com/sponsors/gregberge"
2495
+
},
2496
+
"peerDependencies": {
2497
+
"@babel/core": "^7.0.0-0"
2498
+
}
2499
+
},
2500
+
"node_modules/@svgr/babel-plugin-transform-react-native-svg": {
2501
+
"version": "8.1.0",
2502
+
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
2503
+
"integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
2504
+
"dev": true,
2505
+
"license": "MIT",
2506
+
"engines": {
2507
+
"node": ">=14"
2508
+
},
2509
+
"funding": {
2510
+
"type": "github",
2511
+
"url": "https://github.com/sponsors/gregberge"
2512
+
},
2513
+
"peerDependencies": {
2514
+
"@babel/core": "^7.0.0-0"
2515
+
}
2516
+
},
2517
+
"node_modules/@svgr/babel-plugin-transform-svg-component": {
2518
+
"version": "8.0.0",
2519
+
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
2520
+
"integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
2521
+
"dev": true,
2522
+
"license": "MIT",
2523
+
"engines": {
2524
+
"node": ">=12"
2525
+
},
2526
+
"funding": {
2527
+
"type": "github",
2528
+
"url": "https://github.com/sponsors/gregberge"
2529
+
},
2530
+
"peerDependencies": {
2531
+
"@babel/core": "^7.0.0-0"
2532
+
}
2533
+
},
2534
+
"node_modules/@svgr/babel-preset": {
2535
+
"version": "8.1.0",
2536
+
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
2537
+
"integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
2538
+
"dev": true,
2539
+
"license": "MIT",
2540
+
"dependencies": {
2541
+
"@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
2542
+
"@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
2543
+
"@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
2544
+
"@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
2545
+
"@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
2546
+
"@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
2547
+
"@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
2548
+
"@svgr/babel-plugin-transform-svg-component": "8.0.0"
2549
+
},
2550
+
"engines": {
2551
+
"node": ">=14"
2552
+
},
2553
+
"funding": {
2554
+
"type": "github",
2555
+
"url": "https://github.com/sponsors/gregberge"
2556
+
},
2557
+
"peerDependencies": {
2558
+
"@babel/core": "^7.0.0-0"
2559
+
}
2560
+
},
2561
+
"node_modules/@svgr/core": {
2562
+
"version": "8.1.0",
2563
+
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
2564
+
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
2565
+
"dev": true,
2566
+
"license": "MIT",
2567
+
"peer": true,
2568
+
"dependencies": {
2569
+
"@babel/core": "^7.21.3",
2570
+
"@svgr/babel-preset": "8.1.0",
2571
+
"camelcase": "^6.2.0",
2572
+
"cosmiconfig": "^8.1.3",
2573
+
"snake-case": "^3.0.4"
2574
+
},
2575
+
"engines": {
2576
+
"node": ">=14"
2577
+
},
2578
+
"funding": {
2579
+
"type": "github",
2580
+
"url": "https://github.com/sponsors/gregberge"
2581
+
}
2582
+
},
2583
+
"node_modules/@svgr/hast-util-to-babel-ast": {
2584
+
"version": "8.0.0",
2585
+
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
2586
+
"integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
2587
+
"dev": true,
2588
+
"license": "MIT",
2589
+
"dependencies": {
2590
+
"@babel/types": "^7.21.3",
2591
+
"entities": "^4.4.0"
2592
+
},
2593
+
"engines": {
2594
+
"node": ">=14"
2595
+
},
2596
+
"funding": {
2597
+
"type": "github",
2598
+
"url": "https://github.com/sponsors/gregberge"
2599
+
}
2600
+
},
2601
+
"node_modules/@svgr/plugin-jsx": {
2602
+
"version": "8.1.0",
2603
+
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
2604
+
"integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
2605
+
"dev": true,
2606
+
"license": "MIT",
2607
+
"dependencies": {
2608
+
"@babel/core": "^7.21.3",
2609
+
"@svgr/babel-preset": "8.1.0",
2610
+
"@svgr/hast-util-to-babel-ast": "8.0.0",
2611
+
"svg-parser": "^2.0.4"
2612
+
},
2613
+
"engines": {
2614
+
"node": ">=14"
2615
+
},
2616
+
"funding": {
2617
+
"type": "github",
2618
+
"url": "https://github.com/sponsors/gregberge"
2619
+
},
2620
+
"peerDependencies": {
2621
+
"@svgr/core": "*"
2622
+
}
2623
+
},
2417
2624
"node_modules/@types/babel__core": {
2418
2625
"version": "7.20.5",
2419
2626
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
···
2507
2714
"integrity": "sha512-ukd93VGzaNPMAUPy0gRDSC57UuQbnH9Kussp7HBjM06YFi9uZTFhOvMSO2OKqXm1rSgzOE+pVx1k1PYHGwlc8Q==",
2508
2715
"dev": true,
2509
2716
"license": "MIT",
2717
+
"peer": true,
2510
2718
"dependencies": {
2511
2719
"csstype": "^3.0.2"
2512
2720
}
···
2860
3068
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
2861
3069
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
2862
3070
"license": "MIT",
3071
+
"peer": true,
2863
3072
"bin": {
2864
3073
"acorn": "bin/acorn"
2865
3074
},
···
3084
3293
"dev": true,
3085
3294
"license": "MIT"
3086
3295
},
3296
+
"node_modules/argparse": {
3297
+
"version": "2.0.1",
3298
+
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
3299
+
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
3300
+
"dev": true,
3301
+
"license": "Python-2.0"
3302
+
},
3087
3303
"node_modules/array-union": {
3088
3304
"version": "2.1.0",
3089
3305
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
···
3264
3480
}
3265
3481
],
3266
3482
"license": "MIT",
3483
+
"peer": true,
3267
3484
"dependencies": {
3268
3485
"baseline-browser-mapping": "^2.8.3",
3269
3486
"caniuse-lite": "^1.0.30001741",
···
3325
3542
"node": "*"
3326
3543
}
3327
3544
},
3545
+
"node_modules/callsites": {
3546
+
"version": "3.1.0",
3547
+
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
3548
+
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
3549
+
"dev": true,
3550
+
"license": "MIT",
3551
+
"engines": {
3552
+
"node": ">=6"
3553
+
}
3554
+
},
3555
+
"node_modules/camelcase": {
3556
+
"version": "6.3.0",
3557
+
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
3558
+
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
3559
+
"dev": true,
3560
+
"license": "MIT",
3561
+
"engines": {
3562
+
"node": ">=10"
3563
+
},
3564
+
"funding": {
3565
+
"url": "https://github.com/sponsors/sindresorhus"
3566
+
}
3567
+
},
3328
3568
"node_modules/camelcase-css": {
3329
3569
"version": "2.0.1",
3330
3570
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
···
3693
3933
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
3694
3934
"license": "MIT"
3695
3935
},
3936
+
"node_modules/cosmiconfig": {
3937
+
"version": "8.3.6",
3938
+
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
3939
+
"integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
3940
+
"dev": true,
3941
+
"license": "MIT",
3942
+
"dependencies": {
3943
+
"import-fresh": "^3.3.0",
3944
+
"js-yaml": "^4.1.0",
3945
+
"parse-json": "^5.2.0",
3946
+
"path-type": "^4.0.0"
3947
+
},
3948
+
"engines": {
3949
+
"node": ">=14"
3950
+
},
3951
+
"funding": {
3952
+
"url": "https://github.com/sponsors/d-fischer"
3953
+
},
3954
+
"peerDependencies": {
3955
+
"typescript": ">=4.9.5"
3956
+
},
3957
+
"peerDependenciesMeta": {
3958
+
"typescript": {
3959
+
"optional": true
3960
+
}
3961
+
}
3962
+
},
3963
+
"node_modules/cosmiconfig/node_modules/parse-json": {
3964
+
"version": "5.2.0",
3965
+
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
3966
+
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
3967
+
"dev": true,
3968
+
"license": "MIT",
3969
+
"dependencies": {
3970
+
"@babel/code-frame": "^7.0.0",
3971
+
"error-ex": "^1.3.1",
3972
+
"json-parse-even-better-errors": "^2.3.0",
3973
+
"lines-and-columns": "^1.1.6"
3974
+
},
3975
+
"engines": {
3976
+
"node": ">=8"
3977
+
},
3978
+
"funding": {
3979
+
"url": "https://github.com/sponsors/sindresorhus"
3980
+
}
3981
+
},
3696
3982
"node_modules/crc-32": {
3697
3983
"version": "1.2.2",
3698
3984
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
···
4009
4295
"dev": true,
4010
4296
"license": "MIT"
4011
4297
},
4298
+
"node_modules/dot-case": {
4299
+
"version": "3.0.4",
4300
+
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
4301
+
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
4302
+
"dev": true,
4303
+
"license": "MIT",
4304
+
"dependencies": {
4305
+
"no-case": "^3.0.4",
4306
+
"tslib": "^2.0.3"
4307
+
}
4308
+
},
4012
4309
"node_modules/dot-prop": {
4013
4310
"version": "9.0.0",
4014
4311
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
···
4110
4407
"url": "https://github.com/sponsors/sindresorhus"
4111
4408
}
4112
4409
},
4410
+
"node_modules/error-ex": {
4411
+
"version": "1.3.4",
4412
+
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
4413
+
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
4414
+
"dev": true,
4415
+
"license": "MIT",
4416
+
"dependencies": {
4417
+
"is-arrayish": "^0.2.1"
4418
+
}
4419
+
},
4113
4420
"node_modules/es-module-lexer": {
4114
4421
"version": "1.7.0",
4115
4422
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
···
4550
4857
"version": "2.3.3",
4551
4858
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
4552
4859
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
4553
-
"dev": true,
4554
4860
"hasInstallScript": true,
4555
4861
"license": "MIT",
4556
4862
"optional": true,
···
4817
5123
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
4818
5124
"license": "MIT"
4819
5125
},
5126
+
"node_modules/import-fresh": {
5127
+
"version": "3.3.1",
5128
+
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
5129
+
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
5130
+
"dev": true,
5131
+
"license": "MIT",
5132
+
"dependencies": {
5133
+
"parent-module": "^1.0.0",
5134
+
"resolve-from": "^4.0.0"
5135
+
},
5136
+
"engines": {
5137
+
"node": ">=6"
5138
+
},
5139
+
"funding": {
5140
+
"url": "https://github.com/sponsors/sindresorhus"
5141
+
}
5142
+
},
5143
+
"node_modules/import-fresh/node_modules/resolve-from": {
5144
+
"version": "4.0.0",
5145
+
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
5146
+
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
5147
+
"dev": true,
5148
+
"license": "MIT",
5149
+
"engines": {
5150
+
"node": ">=4"
5151
+
}
5152
+
},
4820
5153
"node_modules/imurmurhash": {
4821
5154
"version": "0.1.4",
4822
5155
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
···
4852
5185
"engines": {
4853
5186
"node": ">= 10"
4854
5187
}
5188
+
},
5189
+
"node_modules/is-arrayish": {
5190
+
"version": "0.2.1",
5191
+
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
5192
+
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
5193
+
"dev": true,
5194
+
"license": "MIT"
4855
5195
},
4856
5196
"node_modules/is-binary-path": {
4857
5197
"version": "2.1.0",
···
5044
5384
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
5045
5385
"license": "MIT"
5046
5386
},
5387
+
"node_modules/js-yaml": {
5388
+
"version": "4.1.1",
5389
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
5390
+
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
5391
+
"dev": true,
5392
+
"license": "MIT",
5393
+
"dependencies": {
5394
+
"argparse": "^2.0.1"
5395
+
},
5396
+
"bin": {
5397
+
"js-yaml": "bin/js-yaml.js"
5398
+
}
5399
+
},
5047
5400
"node_modules/jsesc": {
5048
5401
"version": "3.1.0",
5049
5402
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
···
5057
5410
"node": ">=6"
5058
5411
}
5059
5412
},
5413
+
"node_modules/json-parse-even-better-errors": {
5414
+
"version": "2.3.1",
5415
+
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
5416
+
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
5417
+
"dev": true,
5418
+
"license": "MIT"
5419
+
},
5060
5420
"node_modules/json5": {
5061
5421
"version": "2.2.3",
5062
5422
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
···
5232
5592
},
5233
5593
"bin": {
5234
5594
"loose-envify": "cli.js"
5595
+
}
5596
+
},
5597
+
"node_modules/lower-case": {
5598
+
"version": "2.0.2",
5599
+
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
5600
+
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
5601
+
"dev": true,
5602
+
"license": "MIT",
5603
+
"dependencies": {
5604
+
"tslib": "^2.0.3"
5235
5605
}
5236
5606
},
5237
5607
"node_modules/lru-cache": {
···
5442
5812
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
5443
5813
}
5444
5814
},
5815
+
"node_modules/no-case": {
5816
+
"version": "3.0.4",
5817
+
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
5818
+
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
5819
+
"dev": true,
5820
+
"license": "MIT",
5821
+
"dependencies": {
5822
+
"lower-case": "^2.0.2",
5823
+
"tslib": "^2.0.3"
5824
+
}
5825
+
},
5445
5826
"node_modules/node-fetch": {
5446
5827
"version": "2.7.0",
5447
5828
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
···
5722
6103
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
5723
6104
"license": "(MIT AND Zlib)"
5724
6105
},
6106
+
"node_modules/parent-module": {
6107
+
"version": "1.0.1",
6108
+
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
6109
+
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
6110
+
"dev": true,
6111
+
"license": "MIT",
6112
+
"dependencies": {
6113
+
"callsites": "^3.0.0"
6114
+
},
6115
+
"engines": {
6116
+
"node": ">=6"
6117
+
}
6118
+
},
5725
6119
"node_modules/parse-gitignore": {
5726
6120
"version": "2.0.0",
5727
6121
"resolved": "https://registry.npmjs.org/parse-gitignore/-/parse-gitignore-2.0.0.tgz",
···
5912
6306
}
5913
6307
],
5914
6308
"license": "MIT",
6309
+
"peer": true,
5915
6310
"dependencies": {
5916
6311
"nanoid": "^3.3.11",
5917
6312
"picocolors": "^1.1.1",
···
6211
6606
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
6212
6607
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
6213
6608
"license": "MIT",
6609
+
"peer": true,
6214
6610
"dependencies": {
6215
6611
"loose-envify": "^1.1.0"
6216
6612
},
···
6545
6941
"node": ">=8"
6546
6942
}
6547
6943
},
6944
+
"node_modules/snake-case": {
6945
+
"version": "3.0.4",
6946
+
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
6947
+
"integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
6948
+
"dev": true,
6949
+
"license": "MIT",
6950
+
"dependencies": {
6951
+
"dot-case": "^3.0.4",
6952
+
"tslib": "^2.0.3"
6953
+
}
6954
+
},
6548
6955
"node_modules/source-map": {
6549
6956
"version": "0.6.1",
6550
6957
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
···
6800
7207
"url": "https://github.com/sponsors/ljharb"
6801
7208
}
6802
7209
},
7210
+
"node_modules/svg-parser": {
7211
+
"version": "2.0.4",
7212
+
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
7213
+
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
7214
+
"dev": true,
7215
+
"license": "MIT"
7216
+
},
6803
7217
"node_modules/tailwindcss": {
6804
7218
"version": "3.4.0",
6805
7219
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz",
···
7068
7482
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
7069
7483
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
7070
7484
"license": "Apache-2.0",
7485
+
"peer": true,
7071
7486
"bin": {
7072
7487
"tsc": "bin/tsc",
7073
7488
"tsserver": "bin/tsserver"
···
7218
7633
"integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==",
7219
7634
"dev": true,
7220
7635
"license": "MIT",
7636
+
"peer": true,
7221
7637
"dependencies": {
7222
7638
"esbuild": "^0.21.3",
7223
7639
"postcss": "^8.4.43",
···
7270
7686
"terser": {
7271
7687
"optional": true
7272
7688
}
7689
+
}
7690
+
},
7691
+
"node_modules/vite-plugin-svgr": {
7692
+
"version": "4.5.0",
7693
+
"resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.5.0.tgz",
7694
+
"integrity": "sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==",
7695
+
"dev": true,
7696
+
"license": "MIT",
7697
+
"dependencies": {
7698
+
"@rollup/pluginutils": "^5.2.0",
7699
+
"@svgr/core": "^8.1.0",
7700
+
"@svgr/plugin-jsx": "^8.1.0"
7701
+
},
7702
+
"peerDependencies": {
7703
+
"vite": ">=2.6.0"
7273
7704
}
7274
7705
},
7275
7706
"node_modules/webidl-conversions": {
+2
-1
package.json
+2
-1
package.json
public/favicon/apple-touch-icon.png
public/favicon/apple-touch-icon.png
This is a binary file and will not be displayed.
public/favicon/favicon-96x96.png
public/favicon/favicon-96x96.png
This is a binary file and will not be displayed.
public/favicon/favicon.ico
public/favicon/favicon.ico
This is a binary file and will not be displayed.
public/favicon/icon.png
public/favicon/icon.png
This is a binary file and will not be displayed.
+21
public/favicon/site.webmanifest
+21
public/favicon/site.webmanifest
···
1
+
{
2
+
"name": "ATLast - Your favs in the ATmosphere",
3
+
"short_name": "ATlast",
4
+
"icons": [
5
+
{
6
+
"src": "/web-app-manifest-192x192.png",
7
+
"sizes": "192x192",
8
+
"type": "image/png",
9
+
"purpose": "maskable"
10
+
},
11
+
{
12
+
"src": "/web-app-manifest-512x512.png",
13
+
"sizes": "512x512",
14
+
"type": "image/png",
15
+
"purpose": "maskable"
16
+
}
17
+
],
18
+
"theme_color": "#0891b2",
19
+
"background_color": "#ffffff",
20
+
"display": "standalone"
21
+
}
public/favicon/web-app-manifest-192x192.png
public/favicon/web-app-manifest-192x192.png
This is a binary file and will not be displayed.
public/favicon/web-app-manifest-512x512.png
public/favicon/web-app-manifest-512x512.png
This is a binary file and will not be displayed.
+176
src/assets/at-firefly-logo.svg
+176
src/assets/at-firefly-logo.svg
···
1
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+
<svg
5
+
width="149.09583"
6
+
height="62.405361"
7
+
viewBox="0 0 39.448271 16.511417"
8
+
version="1.1"
9
+
id="svg1"
10
+
xml:space="preserve"
11
+
xmlns:xlink="http://www.w3.org/1999/xlink"
12
+
xmlns="http://www.w3.org/2000/svg"
13
+
xmlns:svg="http://www.w3.org/2000/svg"><defs
14
+
id="defs1"><linearGradient
15
+
id="linearGradient5"><stop
16
+
style="stop-color:#06b6d4;stop-opacity:1;"
17
+
offset="0.20974289"
18
+
id="stop3" /><stop
19
+
style="stop-color:#6d28d9;stop-opacity:1;"
20
+
offset="0.38791069"
21
+
id="stop4" /><stop
22
+
style="stop-color:#ec4899;stop-opacity:1;"
23
+
offset="0.60860217"
24
+
id="stop6" /><stop
25
+
style="stop-color:#f97316;stop-opacity:1;"
26
+
offset="0.8159675"
27
+
id="stop5" /></linearGradient><linearGradient
28
+
id="linearGradient30"><stop
29
+
style="stop-color:#fef08a;stop-opacity:1;"
30
+
offset="0"
31
+
id="stop29" /><stop
32
+
style="stop-color:#eab308;stop-opacity:1;"
33
+
offset="1"
34
+
id="stop30" /></linearGradient><linearGradient
35
+
id="linearGradient25"><stop
36
+
style="stop-color:#fef08a;stop-opacity:1;"
37
+
offset="0"
38
+
id="stop25" /><stop
39
+
style="stop-color:#ffce00;stop-opacity:1;"
40
+
offset="1"
41
+
id="stop26" /></linearGradient><linearGradient
42
+
xlink:href="#linearGradient6"
43
+
id="linearGradient8"
44
+
x1="15.083038"
45
+
y1="9.7583179"
46
+
x2="17.143671"
47
+
y2="9.3141584"
48
+
gradientUnits="userSpaceOnUse"
49
+
gradientTransform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /><linearGradient
50
+
id="linearGradient6"><stop
51
+
style="stop-color:#ea580c;stop-opacity:1;"
52
+
offset="0"
53
+
id="stop7" /><stop
54
+
style="stop-color:#ff8c36;stop-opacity:1;"
55
+
offset="1"
56
+
id="stop8" /></linearGradient><linearGradient
57
+
xlink:href="#linearGradient175"
58
+
id="linearGradient176"
59
+
x1="1.0581446"
60
+
y1="9.2239799"
61
+
x2="3.3587396"
62
+
y2="9.369256"
63
+
gradientUnits="userSpaceOnUse"
64
+
gradientTransform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /><linearGradient
65
+
id="linearGradient175"><stop
66
+
style="stop-color:#00c9db;stop-opacity:1;"
67
+
offset="0"
68
+
id="stop175" /><stop
69
+
style="stop-color:#0891b2;stop-opacity:1;"
70
+
offset="1"
71
+
id="stop176" /></linearGradient><linearGradient
72
+
xlink:href="#linearGradient1"
73
+
id="linearGradient2"
74
+
x1="4.1462159"
75
+
y1="10.224313"
76
+
x2="5.5784869"
77
+
y2="10.895846"
78
+
gradientUnits="userSpaceOnUse" /><linearGradient
79
+
id="linearGradient1"><stop
80
+
style="stop-color:#06b6d4;stop-opacity:1;"
81
+
offset="0"
82
+
id="stop1" /><stop
83
+
style="stop-color:#39e8ff;stop-opacity:1;"
84
+
offset="1"
85
+
id="stop2" /></linearGradient><filter
86
+
style="color-interpolation-filters:sRGB"
87
+
id="filter187"
88
+
x="0"
89
+
y="0"
90
+
width="1"
91
+
height="1"><feFlood
92
+
result="flood1"
93
+
flood-color="rgb(198,0,0)"
94
+
flood-opacity="0.172549"
95
+
id="feFlood187" /><feBlend
96
+
result="blend1"
97
+
in="flood1"
98
+
in2="SourceGraphic"
99
+
mode="hard-light"
100
+
id="feBlend187" /><feComposite
101
+
operator="in"
102
+
in="blend1"
103
+
in2="SourceGraphic"
104
+
id="feComposite187" /></filter><linearGradient
105
+
xlink:href="#linearGradient5"
106
+
id="linearGradient7"
107
+
gradientUnits="userSpaceOnUse"
108
+
gradientTransform="matrix(0.90157488,0,0,1,0.94161522,0)"
109
+
x1="0.84473455"
110
+
y1="8.6206245"
111
+
x2="18.150963"
112
+
y2="8.6206245" /><radialGradient
113
+
xlink:href="#linearGradient30"
114
+
id="radialGradient15"
115
+
gradientUnits="userSpaceOnUse"
116
+
gradientTransform="matrix(2.0224824,0.82216334,-1.2014709,2.9555607,125.44482,81.267746)"
117
+
cx="2.9812629"
118
+
cy="10.872172"
119
+
fx="2.9812629"
120
+
fy="10.872172"
121
+
r="1.9679179" /><radialGradient
122
+
xlink:href="#linearGradient25"
123
+
id="radialGradient22"
124
+
gradientUnits="userSpaceOnUse"
125
+
gradientTransform="matrix(0.89210545,-0.38362324,0.41746133,0.97079503,-2.8830544,6.222786)"
126
+
cx="15.283519"
127
+
cy="11.701982"
128
+
fx="15.283519"
129
+
fy="11.701982"
130
+
r="1.8067596" /></defs><g
131
+
id="layer1"
132
+
transform="translate(-112.53832,-102.7461)"><path
133
+
id="use5"
134
+
clip-path="none"
135
+
style="display:inline;vector-effect:none;fill:url(#linearGradient7);fill-rule:evenodd;stroke-width:0.12119;stop-color:#000000"
136
+
d="m 9.5464118,5.3948093 c -1.7679049,0 -3.2585419,1.1196005 -3.6993808,2.7341191 C 5.5744869,8.7906914 5.1569858,9.2553125 4.8412936,9.5361086 4.7716625,9.3005651 4.7002206,9.1621229 4.7002205,9.1621229 5.2868814,8.9135379 6.0419642,7.6530245 4.9833135,7.0114688 5.1395775,6.476825 5.5741161,6.150355 5.5741161,6.150355 a 0.1322915,0.1322915 0 0 0 0.029351,-0.1855726 0.1322915,0.1322915 0 0 0 -0.087816,-0.052784 0.1322915,0.1322915 0 0 0 -0.097757,0.025564 c 0,0 -0.4809256,0.3494235 -0.6743577,0.95319 -0.02849,-0.011951 -0.056285,-0.024258 -0.086632,-0.035505 -0.04629,-0.017155 -0.090502,-0.031021 -0.1339721,-0.043789 V 6.8112208 C 4.4384266,6.2290341 4.0220757,5.8012228 4.0220757,5.8012228 a 0.15000001,0.15000001 0 0 0 -0.1036745,-0.046866 0.15000001,0.15000001 0 0 0 -0.107225,0.039055 0.15000001,0.15000001 0 0 0 -0.00781,0.2130298 c 0,0 0.3005764,0.3327274 0.4021529,0.7465511 C 2.9819341,6.6795482 3.19481,8.6965345 3.19481,8.6965345 c 0,0 -1.5666732,0.6527946 -1.6135824,1.9818875 -0.03249,0.920542 0.3289793,1.637733 0.9962694,1.86259 0.7638643,0.2574 1.8081292,0.05383 2.1984675,-1.007157 C 4.9377224,11.094173 4.9807228,10.683434 4.9684014,10.33 5.1571925,10.19256 5.4477957,9.9545824 5.74454,9.6092488 5.9827317,11.52521 7.6333364,12.842568 9.6058234,12.842568 c 0.9976536,0 2.4990766,-0.622169 2.4990766,-1.155569 0,-0.207431 -0.17785,-0.385347 -0.375406,-0.385347 -0.335844,0 -0.642006,0.652108 -2.1236706,0.652108 -1.6001972,0 -2.8250118,-1.067007 -2.8250118,-2.8252484 0,-1.5705638 1.2148375,-2.8446579 2.7360127,-2.8446579 1.5211747,0 2.6176627,0.8593501 2.6176627,2.4595473 0,1.0667982 -0.513563,1.52127 -0.869161,1.52127 -0.138289,0 -0.187703,-0.07911 -0.187703,-0.197645 0,-0.079019 0.03941,-0.2666231 0.05917,-0.3555229 l 0.306289,-1.7189137 c 0.02963,-0.1382887 0.03953,-0.2271401 0.03953,-0.2864068 0,-0.2765773 -0.197515,-0.4246394 -0.434581,-0.4246394 -0.1778,0 -0.405201,0.049313 -0.474346,0.3555231 l -0.03929,0.1777615 C 10.247941,7.3999618 9.9120187,7.2024855 9.3094753,7.2024855 c -0.9778982,0 -1.8767925,0.9977126 -1.8767925,2.3608436 0,0.9778989 0.6123067,1.7383229 1.4519164,1.7383229 0.4543769,0 0.7902106,-0.227119 1.027277,-0.503697 h 0.019883 c 0.00988,0.335845 0.2074648,0.503697 0.5926958,0.503697 0.259292,0 0.770004,-0.115173 1.262557,-0.432687 1.45e-4,-9.4e-5 3.28e-4,-1.43e-4 4.73e-4,-2.37e-4 a 0.25,0.25 0 0 0 0.0095,-0.0036 c 0,0 0.717037,-0.3987 1.433927,-0.9823042 -0.04094,0.3418032 -0.02,0.7710522 0.141074,1.2947482 0.373127,1.213185 1.498051,2.045055 2.707845,1.702581 0.95218,-0.269547 1.218765,-1.27536 1.131425,-1.997036 -0.202999,-1.6773421 -1.819985,-2.4697254 -1.819985,-2.4697254 0,0 0.48517,-1.4969048 -0.720987,-1.5629287 0.01314,-0.6264143 0.327829,-1.0166255 0.327829,-1.0166255 a 0.1322915,0.1322915 0 0 0 -0.02154,-0.1853359 0.1322915,0.1322915 0 0 0 -0.09563,-0.029351 0.1322915,0.1322915 0 0 0 -0.08995,0.04876 c 0,0 -0.372292,0.4671488 -0.385584,1.1894163 -0.02876,0.00276 -0.05663,0.00492 -0.08687,0.00899 -0.05471,0.00737 -0.106452,0.01695 -0.155749,0.027931 C 14.08742,6.7190018 13.984099,6.5462677 13.884882,6.3924989 13.714131,6.1278667 13.548768,5.9257269 13.548768,5.9257269 a 0.1322915,0.1322915 0 0 0 -0.185572,-0.019409 0.1322915,0.1322915 0 0 0 -0.01941,0.1874663 c 0,0 0.156273,0.1902354 0.318361,0.441445 0.08882,0.1376604 0.177279,0.2954109 0.242618,0.4419185 -0.962673,0.4198663 -0.489726,1.7135552 -0.319072,1.8957285 -0.218753,0.2391489 -0.479826,0.468665 -0.74087,0.6753045 0.111973,-0.311095 0.178708,-0.6725034 0.178708,-1.091186 0,-1.8866522 -1.590467,-3.062185 -3.4771192,-3.0621854 z m -0.059175,2.8744821 c 0.493888,0 0.7605162,0.3457497 0.7605162,0.8495155 0,0.6321767 -0.3951282,1.1160391 -0.9581605,1.1160391 -0.4543769,0 -0.7901039,-0.3160703 -0.7901039,-0.8791026 0,-0.582788 0.4148382,-1.086452 0.9877482,-1.086452 z"
137
+
transform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /><path
138
+
id="use15"
139
+
clip-path="none"
140
+
style="display:inline;fill:url(#radialGradient15);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
141
+
d="m 115.82132,112.56617 c -0.27625,0.49935 -0.46301,1.0707 -0.48575,1.71514 -0.0709,2.00974 0.71823,3.57551 2.17506,4.06642 1.65648,0.55819 3.9162,0.12254 4.78162,-2.15336 -1.36863,-0.43607 -4.93494,-1.70122 -6.39548,-3.54241 -0.0265,-0.0334 -0.0503,-0.0569 -0.0755,-0.0858 z" /><path
142
+
id="use22"
143
+
clip-path="none"
144
+
style="display:inline;fill:url(#radialGradient22);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.12119px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
145
+
d="m 17.070151,10.283606 c -0.0309,0.02427 -0.06368,0.05336 -0.100361,0.09279 -0.882008,0.947916 -3.175453,1.419561 -3.307879,1.446235 0.514178,0.841867 1.438112,1.334934 2.417888,1.057575 0.952181,-0.269547 1.218765,-1.275359 1.131425,-1.997036 -0.02594,-0.214294 -0.0753,-0.413961 -0.141073,-0.599561 z"
146
+
transform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /><path
147
+
id="use26"
148
+
mask="none"
149
+
style="display:inline;fill:#ffffff;fill-opacity:0.3;fill-rule:nonzero;stroke:none;stroke-width:0.12119px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
150
+
d="m 16.85144,10.150581 c -0.508591,0.461156 -1.35481,0.813378 -2.071833,1.043136 -0.666634,0.213612 -1.199253,0.320379 -1.273681,0.33493 0.028,0.0612 0.05779,0.121292 0.08971,0.179655 0.173364,-0.03571 0.656809,-0.141604 1.243147,-0.329486 0.729056,-0.233619 1.59345,-0.584877 2.144263,-1.084321 z"
151
+
transform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /><path
152
+
id="use24"
153
+
mask="none"
154
+
style="display:inline;fill:#f7c800;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.121381;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
155
+
d="m 16.989436,10.244551 -0.05018,0.04639 c -0.51324,0.478709 -1.339021,0.831881 -2.035382,1.060178 -0.696356,0.228298 -1.261373,0.333747 -1.261373,0.333747 l -0.05373,0.01254 c 0.02313,0.04283 0.04895,0.08542 0.07408,0.126634 l 0.0047,-0.0017 c 0,0 0.573109,-0.109383 1.278415,-0.340611 0.705306,-0.231233 1.545195,-0.584212 2.087219,-1.089766 l 0.04994,-0.04734 z"
156
+
transform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /><path
157
+
id="use27"
158
+
mask="none"
159
+
style="display:inline;fill:#ffffff;fill-opacity:0.3;fill-rule:nonzero;stroke:none;stroke-width:0.121381;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
160
+
d="m 2.7273279,10.277689 -0.1162196,0.155512 c 0,0 0.5251165,0.391878 1.2900138,0.686192 l 0.069826,-0.181548 C 3.2321275,10.653561 2.7273279,10.277689 2.7273279,10.277689 Z"
161
+
transform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /><path
162
+
id="use28"
163
+
mask="none"
164
+
style="display:inline;fill:#f7c800;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.121381;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
165
+
d="m 2.5081439,10.365268 -0.076927,0.113616 c 0,0 0.3023891,0.20541 0.6710439,0.40807 0.3686547,0.202656 0.794261,0.408616 1.0940263,0.393395 l -0.0071,-0.136812 C 3.9659775,11.15485 3.5305678,10.965962 3.1685363,10.766947 2.8065048,10.567933 2.5081439,10.365268 2.5081439,10.365268 Z"
166
+
transform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /><path
167
+
style="fill:url(#linearGradient8);fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
168
+
d="m 144.63508,109.85058 c -0.33259,1.91237 3.00927,7.85057 6.59868,4.76675 3.48964,-2.99811 -6.2661,-6.67912 -6.59868,-4.76675 z"
169
+
id="path119" /><path
170
+
style="fill:url(#linearGradient176);fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
171
+
d="m 120.19851,109.84357 c -2.04144,-0.5332 -9.22765,0.28065 -7.35052,3.89918 2.20679,4.254 9.39196,-3.36597 7.35052,-3.89918 z"
172
+
id="path117" /><path
173
+
style="display:inline;fill:url(#linearGradient2);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.12119px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter187)"
174
+
d="M 4.6747412,8.8087186 C 3.871122,8.4952424 2.6835491,12.528444 5.3401882,12.520739 6.3667683,12.517762 5.4783605,9.1221948 4.6747412,8.8087186 Z"
175
+
id="path118"
176
+
transform="matrix(2.1832058,0,0,2.1832058,111.88342,90.968121)" /></g></svg>
+68
-40
src/components/AppHeader.tsx
+68
-40
src/components/AppHeader.tsx
···
1
1
import { useState, useEffect, useRef } from "react";
2
2
import { Heart, Home, LogOut, ChevronDown } from "lucide-react";
3
3
import ThemeControls from "./ThemeControls";
4
+
import FireflyLogo from "../assets/at-firefly-logo.svg?react";
4
5
5
6
interface atprotoSession {
6
7
did: string;
···
13
14
interface AppHeaderProps {
14
15
session: atprotoSession | null;
15
16
onLogout: () => void;
16
-
onNavigate: (step: 'home' | 'login') => void;
17
+
onNavigate: (step: "home" | "login") => void;
17
18
currentStep: string;
18
19
isDark?: boolean;
19
20
reducedMotion?: boolean;
···
21
22
onToggleMotion?: () => void;
22
23
}
23
24
24
-
export default function AppHeader({
25
-
session,
26
-
onLogout,
27
-
onNavigate,
25
+
export default function AppHeader({
26
+
session,
27
+
onLogout,
28
+
onNavigate,
28
29
currentStep,
29
30
isDark = false,
30
31
reducedMotion = false,
31
32
onToggleTheme,
32
-
onToggleMotion
33
+
onToggleMotion,
33
34
}: AppHeaderProps) {
34
35
const [showMenu, setShowMenu] = useState(false);
35
36
const menuRef = useRef<HTMLDivElement>(null);
···
40
41
setShowMenu(false);
41
42
}
42
43
}
43
-
document.addEventListener('mousedown', handleClickOutside);
44
-
return () => document.removeEventListener('mousedown', handleClickOutside);
44
+
document.addEventListener("mousedown", handleClickOutside);
45
+
return () => document.removeEventListener("mousedown", handleClickOutside);
45
46
}, []);
46
47
47
48
return (
48
-
<div className="bg-white/95 dark:bg-slate-800/95 border-b-2 border-slate-200 dark:border-slate-700 backdrop-blur-sm relative z-[100]">
49
-
<div className="max-w-6xl mx-auto px-4 py-3">
49
+
<div className="bg-white/50 dark:bg-slate-900/50 backdrop-blur-xl relative z-[100]">
50
+
<div className="max-w-6xl mx-auto px-4 py-1">
50
51
<div className="flex items-center justify-between">
51
-
<button
52
-
onClick={() => onNavigate(session ? 'home' : 'login')}
53
-
className="flex items-center space-x-3 hover:opacity-80 transition-opacity focus:outline-none focus:ring-2 focus:ring-firefly-orange rounded-lg px-2 py-1"
52
+
<button
53
+
onClick={() => onNavigate(session ? "home" : "login")}
54
+
className="flex items-center space-x-3 hover:opacity-80 transition-opacity focus:outline-none focus:ring-2 focus:ring-orange-500 rounded-lg px-2 py-1"
54
55
>
55
-
<div className="w-10 h-10 bg-gradient-to-br from-firefly-amber via-firefly-orange to-firefly-pink rounded-xl flex items-center justify-center shadow-md">
56
-
<Heart className="w-5 h-5 text-slate-900" />
57
-
</div>
58
-
<h1 className="text-xl font-bold text-slate-900 dark:text-slate-100">ATlast</h1>
56
+
<FireflyLogo className="w-12 h-12" />
57
+
<h1 className="font-display text-2xl font-bold text-purple-950 dark:text-cyan-50">
58
+
ATlast
59
+
</h1>
59
60
</button>
60
61
61
62
<div className="flex items-center space-x-4">
···
69
70
)}
70
71
{session && (
71
72
<div className="relative z-[9999]" ref={menuRef}>
72
-
<button
73
-
onClick={() => setShowMenu(!showMenu)}
74
-
className="flex items-center space-x-3 px-3 py-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors focus:outline-none focus:ring-2 focus:ring-firefly-orange"
73
+
<button
74
+
onClick={() => setShowMenu(!showMenu)}
75
+
className="flex items-center space-x-3 px-3 py-1 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors focus:outline-none focus:ring-2 focus:ring-firefly-orange"
75
76
>
76
77
{session?.avatar ? (
77
-
<img src={session.avatar} alt="" className="w-8 h-8 rounded-full object-cover" />
78
+
<img
79
+
src={session.avatar}
80
+
alt=""
81
+
className="w-8 h-8 rounded-full object-cover"
82
+
/>
78
83
) : (
79
-
<div className="w-8 h-8 bg-gradient-to-br from-firefly-cyan to-blue-500 rounded-full flex items-center justify-center shadow-sm">
80
-
<span className="text-white font-bold text-sm">{session?.handle?.charAt(0).toUpperCase()}</span>
84
+
<div className="w-8 h-8 bg-gradient-to-br from-cyan-500 to-purple-500 rounded-full flex items-center justify-center shadow-sm">
85
+
<span className="text-white font-bold text-sm">
86
+
{session?.handle?.charAt(0).toUpperCase()}
87
+
</span>
81
88
</div>
82
89
)}
83
-
<span className="text-sm font-medium text-slate-900 dark:text-slate-100 hidden sm:inline">@{session?.handle}</span>
84
-
<ChevronDown className={`w-4 h-4 text-slate-600 dark:text-slate-400 transition-transform ${showMenu ? 'rotate-180' : ''}`} />
90
+
<span className="text-sm font-medium text-purple-950 dark:text-cyan-50 hidden sm:inline">
91
+
@{session?.handle}
92
+
</span>
93
+
<ChevronDown
94
+
className={`w-4 h-4 text-slate-600 dark:text-slate-400 transition-transform ${showMenu ? "rotate-180" : ""}`}
95
+
/>
85
96
</button>
86
97
87
98
{showMenu && (
88
-
<div className="absolute right-0 mt-2 w-64 bg-white dark:bg-slate-800 rounded-lg shadow-lg border-2 border-slate-200 dark:border-slate-700 py-2 z-[9999]">
89
-
<div className="px-4 py-3 border-b-2 border-slate-200 dark:border-slate-700">
90
-
<div className="font-semibold text-slate-900 dark:text-slate-100">{session?.displayName || session.handle}</div>
91
-
<div className="text-sm text-slate-600 dark:text-slate-400">@{session?.handle}</div>
99
+
<div className="absolute right-0 mt-2 w-64 bg-white dark:bg-slate-800 rounded-lg shadow-lg border-2 border-cyan-500/30 dark:border-purple-500/30 py-2 z-[9999]">
100
+
<div className="px-4 py-3">
101
+
<div className="font-semibold text-purple-950 dark:text-cyan-50">
102
+
{session?.displayName || session.handle}
103
+
</div>
104
+
<div className="text-sm text-slate-600 dark:text-slate-400">
105
+
@{session?.handle}
106
+
</div>
92
107
</div>
93
-
<button
94
-
onClick={() => { setShowMenu(false); onNavigate('home'); }}
108
+
<button
109
+
onClick={() => {
110
+
setShowMenu(false);
111
+
onNavigate("home");
112
+
}}
95
113
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors text-left"
96
114
>
97
115
<Home className="w-4 h-4 text-slate-600 dark:text-slate-400" />
98
-
<span className="text-slate-900 dark:text-slate-100">Dashboard</span>
116
+
<span className="text-slate-900 dark:text-slate-100">
117
+
Dashboard
118
+
</span>
99
119
</button>
100
-
<button
101
-
onClick={() => { setShowMenu(false); onNavigate('login'); }}
120
+
<button
121
+
onClick={() => {
122
+
setShowMenu(false);
123
+
onNavigate("login");
124
+
}}
102
125
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors text-left"
103
126
>
104
127
<Heart className="w-4 h-4 text-slate-600 dark:text-slate-400" />
105
-
<span className="text-slate-900 dark:text-slate-100">About</span>
128
+
<span className="text-slate-900 dark:text-slate-100">
129
+
About
130
+
</span>
106
131
</button>
107
-
<div className="border-t-2 border-slate-200 dark:border-slate-700 my-2"></div>
108
-
<button
109
-
onClick={() => { setShowMenu(false); onLogout(); }}
110
-
className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors text-left text-red-600 dark:text-red-400"
132
+
<div className="my-2"></div>
133
+
<button
134
+
onClick={() => {
135
+
setShowMenu(false);
136
+
onLogout();
137
+
}}
138
+
className="w-full flex items-center space-x-3 px-4 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors text-left text-red-600 dark:text-red-400"
111
139
>
112
140
<LogOut className="w-4 h-4" />
113
141
<span>Log out</span>
···
121
149
</div>
122
150
</div>
123
151
);
124
-
}
152
+
}
+110
-36
src/index.css
+110
-36
src/index.css
···
3
3
@tailwind utilities;
4
4
5
5
@layer base {
6
-
body {
7
-
font-family: system-ui, sans-serif;
8
-
@apply bg-gradient-to-br from-amber-50 via-orange-50 to-pink-50
9
-
dark:from-indigo-950 dark:via-purple-900 dark:to-slate-900
10
-
text-slate-900 dark:text-slate-100
6
+
body {
7
+
font-family: system-ui, sans-serif;
8
+
@apply bg-gradient-to-br from-amber-50 via-orange-50 to-pink-50
9
+
dark:from-indigo-950 dark:via-purple-900 dark:to-slate-900
10
+
text-slate-900 dark:text-slate-100
11
11
transition-colors duration-300;
12
-
}
13
-
14
-
button {
15
-
cursor: pointer;
16
-
}
12
+
}
13
+
14
+
button {
15
+
cursor: pointer;
16
+
}
17
17
}
18
18
19
19
/* Hide scrollbar but allow scrolling */
20
20
.scrollbar-hide {
21
-
-ms-overflow-style: none;
22
-
scrollbar-width: none;
21
+
-ms-overflow-style: none;
22
+
scrollbar-width: none;
23
23
}
24
24
25
25
.scrollbar-hide::-webkit-scrollbar {
26
-
display: none;
26
+
display: none;
27
27
}
28
28
29
29
/* Firefly animation keyframes */
30
30
@keyframes float {
31
-
0%, 100% {
32
-
transform: translate(0, 0) scale(1);
33
-
opacity: 0.3;
34
-
}
35
-
25% {
36
-
transform: translate(10px, -20px) scale(1.2);
37
-
opacity: 0.8;
38
-
}
39
-
50% {
40
-
transform: translate(-5px, -40px) scale(1);
41
-
opacity: 0.5;
42
-
}
43
-
75% {
44
-
transform: translate(15px, -25px) scale(1.1);
45
-
opacity: 0.9;
46
-
}
31
+
0%,
32
+
100% {
33
+
transform: translate(0, 0) scale(1);
34
+
opacity: 0.3;
35
+
}
36
+
25% {
37
+
transform: translate(10px, -20px) scale(1.2);
38
+
opacity: 0.8;
39
+
}
40
+
50% {
41
+
transform: translate(-5px, -40px) scale(1);
42
+
opacity: 0.5;
43
+
}
44
+
75% {
45
+
transform: translate(15px, -25px) scale(1.1);
46
+
opacity: 0.9;
47
+
}
47
48
}
48
49
49
50
@keyframes glow-pulse {
50
-
0%, 100% {
51
-
box-shadow: 0 0 20px rgba(251, 191, 36, 0.3);
52
-
}
53
-
50% {
54
-
box-shadow: 0 0 40px rgba(251, 191, 36, 0.6), 0 0 60px rgba(251, 191, 36, 0.3);
55
-
}
56
-
}
51
+
0%,
52
+
100% {
53
+
box-shadow: 0 0 20px rgba(251, 191, 36, 0.3);
54
+
}
55
+
50% {
56
+
box-shadow:
57
+
0 0 40px rgba(251, 191, 36, 0.6),
58
+
0 0 60px rgba(251, 191, 36, 0.3);
59
+
}
60
+
}
61
+
62
+
@keyframes svg-glow-left {
63
+
0%,
64
+
100% {
65
+
opacity: 0.5;
66
+
filter: blur(10px);
67
+
transform: scale(1);
68
+
}
69
+
50% {
70
+
opacity: 1;
71
+
filter: blur(15px);
72
+
transform: scale(1.2);
73
+
}
74
+
}
75
+
76
+
@keyframes svg-glow-right {
77
+
0%,
78
+
100% {
79
+
opacity: 0.5;
80
+
filter: blur(10px);
81
+
transform: scale(1);
82
+
}
83
+
50% {
84
+
opacity: 1;
85
+
filter: blur(15px);
86
+
transform: scale(1.2);
87
+
}
88
+
}
89
+
90
+
.logo-glow-container {
91
+
position: relative;
92
+
display: inline-block;
93
+
}
94
+
95
+
.logo-glow-container::before,
96
+
.logo-glow-container::after {
97
+
content: "";
98
+
position: absolute;
99
+
width: 30px;
100
+
height: 30px;
101
+
border-radius: 50%;
102
+
pointer-events: none;
103
+
z-index: -1; /* Places glow behind the SVG */
104
+
}
105
+
106
+
/* Left firefly glow - cyan tint */
107
+
.logo-glow-container::before {
108
+
bottom: 0;
109
+
left: 6%;
110
+
background: radial-gradient(
111
+
circle,
112
+
rgba(253, 224, 71, 1) 0%,
113
+
rgba(245, 158, 11, 0.8) 30%,
114
+
transparent 80%
115
+
);
116
+
animation: svg-glow-left 3s ease-in-out infinite;
117
+
}
118
+
119
+
/* Right firefly glow - orange/amber tint */
120
+
.logo-glow-container::after {
121
+
bottom: 0;
122
+
right: 4%;
123
+
background: radial-gradient(
124
+
circle,
125
+
rgba(253, 224, 71, 1) 0%,
126
+
rgba(245, 158, 11, 0.8) 30%,
127
+
transparent 80%
128
+
);
129
+
animation: svg-glow-right 3s ease-in-out infinite 0.5s; /* offset timing */
130
+
}
+151
-109
src/pages/Home.tsx
+151
-109
src/pages/Home.tsx
···
1
-
import { Upload, History, Settings, BookOpen, Grid3x3, ChevronRight, Sparkles } from "lucide-react";
1
+
import {
2
+
Upload,
3
+
History,
4
+
Settings,
5
+
BookOpen,
6
+
Grid3x3,
7
+
ChevronRight,
8
+
Sparkles,
9
+
} from "lucide-react";
2
10
import { useState, useEffect, useRef } from "react";
3
11
import AppHeader from "../components/AppHeader";
4
12
import PlatformSelector from "../components/PlatformSelector";
···
20
28
interface HomePageProps {
21
29
session: atprotoSession | null;
22
30
onLogout: () => void;
23
-
onNavigate: (step: 'home' | 'login') => void;
24
-
onFileUpload: (e: React.ChangeEvent<HTMLInputElement>, platform: string) => void;
31
+
onNavigate: (step: "home" | "login") => void;
32
+
onFileUpload: (
33
+
e: React.ChangeEvent<HTMLInputElement>,
34
+
platform: string,
35
+
) => void;
25
36
onLoadUpload: (uploadId: string) => void;
26
37
currentStep: string;
27
38
userSettings: UserSettings;
···
33
44
onToggleMotion?: () => void;
34
45
}
35
46
36
-
type TabId = 'upload' | 'history' | 'settings' | 'guides' | 'apps';
47
+
type TabId = "upload" | "history" | "settings" | "guides" | "apps";
37
48
38
-
export default function HomePage({
39
-
session,
40
-
onLogout,
41
-
onNavigate,
42
-
onFileUpload,
49
+
export default function HomePage({
50
+
session,
51
+
onLogout,
52
+
onNavigate,
53
+
onFileUpload,
43
54
onLoadUpload,
44
55
currentStep,
45
56
userSettings,
46
57
onSettingsUpdate,
47
-
// New props
48
58
reducedMotion = false,
49
59
isDark = false,
50
60
onToggleTheme,
51
-
onToggleMotion
61
+
onToggleMotion,
52
62
}: HomePageProps) {
53
-
const [activeTab, setActiveTab] = useState<TabId>('upload');
63
+
const [activeTab, setActiveTab] = useState<TabId>("upload");
54
64
const [uploads, setUploads] = useState<UploadType[]>([]);
55
65
const [isLoading, setIsLoading] = useState(true);
56
-
const [selectedPlatform, setSelectedPlatform] = useState<string>('');
66
+
const [selectedPlatform, setSelectedPlatform] = useState<string>("");
57
67
const [showWizard, setShowWizard] = useState(false);
58
68
const fileInputRef = useRef<HTMLInputElement>(null);
59
69
···
61
71
if (session) {
62
72
loadUploads();
63
73
}
64
-
74
+
65
75
// Show wizard on first visit
66
76
if (!userSettings.wizardCompleted) {
67
77
setShowWizard(true);
···
74
84
const data = await apiClient.getUploads();
75
85
setUploads(data.uploads);
76
86
} catch (error) {
77
-
console.error('Failed to load uploads:', error);
87
+
console.error("Failed to load uploads:", error);
78
88
} finally {
79
89
setIsLoading(false);
80
90
}
···
87
97
88
98
const formatDate = (dateString: string) => {
89
99
const date = new Date(dateString);
90
-
return date.toLocaleDateString('en-US', {
91
-
month: 'short',
92
-
day: 'numeric',
93
-
year: 'numeric',
94
-
hour: '2-digit',
95
-
minute: '2-digit'
100
+
return date.toLocaleDateString("en-US", {
101
+
month: "short",
102
+
day: "numeric",
103
+
year: "numeric",
104
+
hour: "2-digit",
105
+
minute: "2-digit",
96
106
});
97
107
};
98
108
99
109
const getPlatformColor = (platform: string) => {
100
110
const colors: Record<string, string> = {
101
-
tiktok: 'from-black via-gray-800 to-cyan-400',
102
-
twitter: 'from-blue-400 to-blue-600',
103
-
instagram: 'from-pink-500 via-purple-500 to-orange-500',
111
+
tiktok: "from-black via-gray-800 to-cyan-400",
112
+
twitter: "from-blue-400 to-blue-600",
113
+
instagram: "from-pink-500 via-purple-500 to-orange-500",
104
114
};
105
-
return colors[platform] || 'from-gray-400 to-gray-600';
115
+
return colors[platform] || "from-gray-400 to-gray-600";
106
116
};
107
117
108
118
const tabs = [
109
-
{ id: 'upload' as TabId, icon: Upload, label: 'Upload' },
110
-
{ id: 'history' as TabId, icon: History, label: 'History' },
111
-
{ id: 'settings' as TabId, icon: Settings, label: 'Settings' },
112
-
{ id: 'guides' as TabId, icon: BookOpen, label: 'Guides' },
113
-
{ id: 'apps' as TabId, icon: Grid3x3, label: 'Apps' },
119
+
{ id: "upload" as TabId, icon: Upload, label: "Upload" },
120
+
{ id: "history" as TabId, icon: History, label: "History" },
121
+
{ id: "settings" as TabId, icon: Settings, label: "Settings" },
122
+
{ id: "guides" as TabId, icon: BookOpen, label: "Guides" },
123
+
{ id: "apps" as TabId, icon: Grid3x3, label: "Apps" },
114
124
];
115
125
116
126
return (
···
123
133
/>
124
134
125
135
{/* Header */}
126
-
<div className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
127
-
<AppHeader
128
-
session={session}
129
-
onLogout={onLogout}
130
-
onNavigate={onNavigate}
136
+
<div className="bg-white dark:bg-slate-900 border-b-2 border-cyan-500/50 dark:border-purple-500/50 overflow-x-auto">
137
+
<AppHeader
138
+
session={session}
139
+
onLogout={onLogout}
140
+
onNavigate={onNavigate}
131
141
currentStep={currentStep}
132
142
isDark={isDark}
133
143
reducedMotion={reducedMotion}
···
139
149
<div className="max-w-6xl mx-auto">
140
150
<div className="overflow-x-auto scrollbar-hide px-4">
141
151
<div className="flex space-x-1 border-b border-gray-200 dark:border-gray-700 min-w-max">
142
-
{tabs.map(tab => {
152
+
{tabs.map((tab) => {
143
153
const Icon = tab.icon;
144
154
return (
145
155
<button
···
147
157
onClick={() => setActiveTab(tab.id)}
148
158
className={`flex items-center space-x-2 px-4 py-3 border-b-2 transition-all whitespace-nowrap ${
149
159
activeTab === tab.id
150
-
? 'border-blue-500 text-blue-600 dark:text-blue-400'
151
-
: 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100'
160
+
? "border-orange-500 dark:border-amber-500 text-orange-650 dark:text-amber-400"
161
+
: "border-transparent text-purple-750 dark:text-cyan-250 hover:text-purple-900 dark:hover:text-cyan-100"
152
162
}`}
153
163
>
154
164
<Icon className="w-4 h-4" />
···
163
173
164
174
{/* Tab Content */}
165
175
<div className="max-w-6xl mx-auto px-4 py-8">
166
-
{activeTab === 'upload' && (
176
+
{activeTab === "upload" && (
167
177
<div className="space-y-6">
168
178
{/* Setup Assistant Banner - Only show if wizard not completed */}
169
179
{!userSettings.wizardCompleted && (
170
180
<div className="bg-firefly-banner dark:bg-firefly-banner-dark rounded-2xl p-6 text-white">
171
181
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
172
182
<div className="flex-1">
173
-
<h2 className="text-2xl font-bold mb-2">Need help getting started?</h2>
174
-
<p className="text-white/90">Run the setup assistant to configure your preferences in minutes.</p>
183
+
<h2 className="text-2xl font-bold mb-2">
184
+
Need help getting started?
185
+
</h2>
186
+
<p className="text-white/90">
187
+
Run the setup assistant to configure your preferences in
188
+
minutes.
189
+
</p>
175
190
</div>
176
191
<button
177
192
onClick={() => setShowWizard(true)}
···
210
225
</button>
211
226
)}
212
227
</div>
213
-
228
+
214
229
<PlatformSelector onPlatformSelect={handlePlatformSelect} />
215
-
230
+
216
231
<input
217
232
id="file-upload"
218
233
ref={fileInputRef}
219
234
type="file"
220
235
accept=".txt,.json,.html,.zip"
221
-
onChange={(e) => onFileUpload(e, selectedPlatform || 'tiktok')}
236
+
onChange={(e) => onFileUpload(e, selectedPlatform || "tiktok")}
222
237
className="sr-only"
223
238
aria-label="Upload following data file"
224
239
/>
···
227
242
)}
228
243
229
244
{/* History Tab */}
230
-
{activeTab === 'history' && (
245
+
{activeTab === "history" && (
231
246
<div className="bg-white/95 dark:bg-slate-800/95 backdrop-blur-xl rounded-2xl shadow-lg p-6 border-2 border-slate-200 dark:border-slate-700">
232
247
<div className="flex items-center space-x-3 mb-6">
233
248
<Sparkles className="w-6 h-6 text-firefly-amber" />
···
236
251
</h2>
237
252
</div>
238
253
239
-
{isLoading ? (
240
-
<div className="space-y-3">
241
-
{[...Array(3)].map((_, i) => (
242
-
<div key={i} className="animate-pulse flex items-center space-x-4 p-4 bg-slate-50 dark:bg-slate-700 rounded-xl">
243
-
<div className="w-12 h-12 bg-slate-200 dark:bg-slate-600 rounded-xl" />
244
-
<div className="flex-1 space-y-2">
245
-
<div className="h-4 bg-slate-200 dark:bg-slate-600 rounded w-3/4" />
246
-
<div className="h-3 bg-slate-200 dark:bg-slate-600 rounded w-1/2" />
247
-
</div>
248
-
</div>
249
-
))}
250
-
</div>
251
-
) : uploads.length === 0 ? (
252
-
<div className="text-center py-12">
253
-
<Upload className="w-16 h-16 text-slate-300 dark:text-slate-600 mx-auto mb-4" />
254
-
<p className="text-slate-600 dark:text-slate-400 font-medium">No previous uploads yet</p>
255
-
<p className="text-sm text-slate-500 dark:text-slate-500 mt-2">
256
-
Upload your first file to get started
257
-
</p>
258
-
</div>
259
-
) : (
260
-
<div className="space-y-3">
261
-
{uploads.map((upload) => {
262
-
const destApp = ATPROTO_APPS[userSettings.platformDestinations[upload.sourcePlatform as keyof typeof userSettings.platformDestinations]];
263
-
return (
264
-
<button
265
-
key={upload.uploadId}
266
-
onClick={() => onLoadUpload(upload.uploadId)}
267
-
className="w-full flex items-start space-x-4 p-4 bg-slate-50 dark:bg-slate-900/50 hover:bg-slate-100 dark:hover:bg-slate-900/70 rounded-xl transition-all text-left border-2 border-slate-200 dark:border-slate-700 hover:border-firefly-orange dark:hover:border-firefly-orange shadow-md hover:shadow-lg"
254
+
{isLoading ? (
255
+
<div className="space-y-3">
256
+
{[...Array(3)].map((_, i) => (
257
+
<div
258
+
key={i}
259
+
className="animate-pulse flex items-center space-x-4 p-4 bg-slate-50 dark:bg-slate-700 rounded-xl"
268
260
>
269
-
<div className={`w-12 h-12 bg-gradient-to-r ${getPlatformColor(upload.sourcePlatform)} rounded-xl flex items-center justify-center flex-shrink-0 shadow-md`}>
270
-
<Sparkles className="w-6 h-6 text-white" />
261
+
<div className="w-12 h-12 bg-slate-200 dark:bg-slate-600 rounded-xl" />
262
+
<div className="flex-1 space-y-2">
263
+
<div className="h-4 bg-slate-200 dark:bg-slate-600 rounded w-3/4" />
264
+
<div className="h-3 bg-slate-200 dark:bg-slate-600 rounded w-1/2" />
271
265
</div>
272
-
<div className="flex-1 min-w-0">
273
-
<div className="flex flex-wrap items-start justify-between gap-x-4 gap-y-2 mb-1">
274
-
<div className="font-semibold text-slate-900 dark:text-slate-100 capitalize">
275
-
{upload.sourcePlatform}
276
-
</div>
277
-
<div className="flex items-center gap-2 flex-shrink-0">
278
-
<span className="text-xs px-2 py-0.5 bg-firefly-amber/20 dark:bg-firefly-amber/30 text-amber-900 dark:text-firefly-glow rounded-full font-medium border border-firefly-amber/20 dark:border-firefly-amber/50 whitespace-nowrap">
279
-
{upload.matchedUsers} {upload.matchedUsers === 1 ? 'firefly' : 'fireflies'}
280
-
</span>
281
-
<div className="text-sm text-slate-600 dark:text-slate-400 font-medium whitespace-nowrap">
282
-
{Math.round((upload.matchedUsers / upload.totalUsers) * 100)}%
266
+
</div>
267
+
))}
268
+
</div>
269
+
) : uploads.length === 0 ? (
270
+
<div className="text-center py-12">
271
+
<Upload className="w-16 h-16 text-slate-300 dark:text-slate-600 mx-auto mb-4" />
272
+
<p className="text-slate-600 dark:text-slate-400 font-medium">
273
+
No previous uploads yet
274
+
</p>
275
+
<p className="text-sm text-slate-500 dark:text-slate-500 mt-2">
276
+
Upload your first file to get started
277
+
</p>
278
+
</div>
279
+
) : (
280
+
<div className="space-y-3">
281
+
{uploads.map((upload) => {
282
+
const destApp =
283
+
ATPROTO_APPS[
284
+
userSettings.platformDestinations[
285
+
upload.sourcePlatform as keyof typeof userSettings.platformDestinations
286
+
]
287
+
];
288
+
return (
289
+
<button
290
+
key={upload.uploadId}
291
+
onClick={() => onLoadUpload(upload.uploadId)}
292
+
className="w-full flex items-start space-x-4 p-4 bg-slate-50 dark:bg-slate-900/50 hover:bg-slate-100 dark:hover:bg-slate-900/70 rounded-xl transition-all text-left border-2 border-slate-200 dark:border-slate-700 hover:border-firefly-orange dark:hover:border-firefly-orange shadow-md hover:shadow-lg"
293
+
>
294
+
<div
295
+
className={`w-12 h-12 bg-gradient-to-r ${getPlatformColor(upload.sourcePlatform)} rounded-xl flex items-center justify-center flex-shrink-0 shadow-md`}
296
+
>
297
+
<Sparkles className="w-6 h-6 text-white" />
298
+
</div>
299
+
<div className="flex-1 min-w-0">
300
+
<div className="flex flex-wrap items-start justify-between gap-x-4 gap-y-2 mb-1">
301
+
<div className="font-semibold text-slate-900 dark:text-slate-100 capitalize">
302
+
{upload.sourcePlatform}
283
303
</div>
304
+
<div className="flex items-center gap-2 flex-shrink-0">
305
+
<span className="text-xs px-2 py-0.5 bg-firefly-amber/20 dark:bg-firefly-amber/30 text-amber-900 dark:text-firefly-glow rounded-full font-medium border border-firefly-amber/20 dark:border-firefly-amber/50 whitespace-nowrap">
306
+
{upload.matchedUsers}{" "}
307
+
{upload.matchedUsers === 1
308
+
? "firefly"
309
+
: "fireflies"}
310
+
</span>
311
+
<div className="text-sm text-slate-600 dark:text-slate-400 font-medium whitespace-nowrap">
312
+
{Math.round(
313
+
(upload.matchedUsers / upload.totalUsers) * 100,
314
+
)}
315
+
%
316
+
</div>
317
+
</div>
318
+
</div>
319
+
<div className="text-sm text-slate-700 dark:text-slate-300">
320
+
{upload.totalUsers} users •{" "}
321
+
{formatDate(upload.createdAt)}
284
322
</div>
285
-
</div>
286
-
<div className="text-sm text-slate-700 dark:text-slate-300">
287
-
{upload.totalUsers} users • {formatDate(upload.createdAt)}
323
+
{destApp && (
324
+
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
325
+
Sent to {destApp.icon} {destApp.name}
326
+
</div>
327
+
)}
288
328
</div>
289
-
{destApp && (
290
-
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
291
-
Sent to {destApp.icon} {destApp.name}
292
-
</div>
293
-
)}
294
-
</div>
295
-
</button>
296
-
);
297
-
})}
298
-
</div>
299
-
)}
329
+
</button>
330
+
);
331
+
})}
332
+
</div>
333
+
)}
300
334
</div>
301
335
)}
302
336
303
337
{/* Settings Tab */}
304
-
{activeTab === 'settings' && (
338
+
{activeTab === "settings" && (
305
339
<SettingsPage
306
340
userSettings={userSettings}
307
341
onSettingsUpdate={onSettingsUpdate}
···
310
344
)}
311
345
312
346
{/* Guides Tab - Placeholder */}
313
-
{activeTab === 'guides' && (
347
+
{activeTab === "guides" && (
314
348
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-lg p-6">
315
349
<div className="flex items-center space-x-3 mb-6">
316
350
<BookOpen className="w-6 h-6 text-gray-600 dark:text-gray-400" />
317
-
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100">Platform Guides</h2>
351
+
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100">
352
+
Platform Guides
353
+
</h2>
318
354
</div>
319
-
<p className="text-gray-600 dark:text-gray-400">Export guides coming soon...</p>
355
+
<p className="text-gray-600 dark:text-gray-400">
356
+
Export guides coming soon...
357
+
</p>
320
358
</div>
321
359
)}
322
360
323
361
{/* Apps Tab - Placeholder */}
324
-
{activeTab === 'apps' && (
362
+
{activeTab === "apps" && (
325
363
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-lg p-6">
326
364
<div className="flex items-center space-x-3 mb-6">
327
365
<Grid3x3 className="w-6 h-6 text-gray-600 dark:text-gray-400" />
328
-
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100">ATmosphere Apps</h2>
366
+
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100">
367
+
ATmosphere Apps
368
+
</h2>
329
369
</div>
330
-
<p className="text-gray-600 dark:text-gray-400">Apps directory coming soon...</p>
370
+
<p className="text-gray-600 dark:text-gray-400">
371
+
Apps directory coming soon...
372
+
</p>
331
373
</div>
332
374
)}
333
375
</div>
334
376
</div>
335
377
);
336
-
}
378
+
}
+110
-53
src/pages/Login.tsx
+110
-53
src/pages/Login.tsx
···
1
1
import { useState } from "react";
2
2
import { Heart, Upload, Search, ArrowRight } from "lucide-react";
3
+
import FireflyLogo from "../assets/at-firefly-logo.svg?react";
3
4
4
5
interface LoginPageProps {
5
6
onSubmit: (handle: string) => void;
6
7
session?: { handle: string } | null;
7
-
onNavigate?: (step: 'home') => void;
8
+
onNavigate?: (step: "home") => void;
8
9
reducedMotion?: boolean;
9
10
}
10
11
11
-
export default function LoginPage({ onSubmit, session, onNavigate, reducedMotion = false }: LoginPageProps) {
12
+
export default function LoginPage({
13
+
onSubmit,
14
+
session,
15
+
onNavigate,
16
+
reducedMotion = false,
17
+
}: LoginPageProps) {
12
18
const [handle, setHandle] = useState("");
13
-
19
+
14
20
const handleSubmit = (e: React.FormEvent) => {
15
21
e.preventDefault();
16
22
onSubmit(handle);
17
23
};
18
-
24
+
19
25
return (
20
26
<div className="min-h-screen">
21
27
<div className="max-w-6xl mx-auto px-4 py-8 md:py-12">
22
-
23
28
{/* Hero Section - Side by side on desktop */}
24
29
<div className="grid md:grid-cols-2 gap-8 md:gap-12 items-start mb-12 md:mb-16">
25
30
{/* Left: Welcome */}
26
31
<div className="text-center md:text-left">
27
-
<div className="inline-flex items-center justify-center mb-6 relative">
28
-
<div
29
-
className={`w-20 h-20 md:w-24 md:h-24 bg-gradient-to-br from-firefly-amber via-firefly-orange to-firefly-pink rounded-3xl flex items-center justify-center relative shadow-xl ${
30
-
reducedMotion ? '' : 'animate-glow-pulse'
31
-
}`}
32
-
>
33
-
<Heart className="w-10 h-10 md:w-12 md:h-12 text-slate-900" aria-hidden="true" />
34
-
{/* Firefly mascot hint */}
35
-
<div
36
-
className={`absolute -top-2 -right-2 w-8 h-8 bg-firefly-glow rounded-full flex items-center justify-center shadow-lg ${
37
-
reducedMotion ? '' : 'animate-bounce'
38
-
}`}
39
-
aria-hidden="true"
40
-
>
41
-
<div className="w-4 h-4 bg-firefly-amber rounded-full" />
42
-
</div>
32
+
<div className="justify-center md:justify-start mb-4">
33
+
<div className="logo-glow-container">
34
+
<img
35
+
src="src/assets/at-firefly-logo.svg"
36
+
className="w-50 h-15"
37
+
alt="ATlast logo"
38
+
/>
43
39
</div>
44
40
</div>
45
-
41
+
46
42
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold text-slate-900 dark:text-white mb-3 md:mb-4">
47
43
ATlast
48
44
</h1>
···
52
48
<p className="text-slate-700 dark:text-slate-300 mb-6">
53
49
Reconnect with your internet, one firefly at a time ✨
54
50
</p>
55
-
51
+
56
52
{/* Decorative firefly trail - only show if motion enabled */}
57
53
{!reducedMotion && (
58
-
<div className="mt-8 flex justify-center md:justify-start space-x-2" aria-hidden="true">
54
+
<div
55
+
className="mt-8 flex justify-center md:justify-start space-x-2"
56
+
aria-hidden="true"
57
+
>
59
58
{[...Array(5)].map((_, i) => (
60
59
<div
61
60
key={i}
···
63
62
style={{
64
63
opacity: 1 - i * 0.15,
65
64
animation: `float ${2 + i * 0.3}s ease-in-out infinite`,
66
-
animationDelay: `${i * 0.2}s`
65
+
animationDelay: `${i * 0.2}s`,
67
66
}}
68
67
/>
69
68
))}
70
69
</div>
71
70
)}
72
-
71
+
73
72
{/* Privacy Notice - visible on mobile */}
74
73
<div className="md:hidden mt-6">
75
74
<p className="text-sm text-slate-600 dark:text-slate-400">
76
-
Your data is processed and stored by our servers. This helps you find matches and reconnect with your community.
75
+
Your data is processed and stored by our servers. This helps you
76
+
find matches and reconnect with your community.
77
77
</p>
78
78
</div>
79
79
</div>
···
95
95
</div>
96
96
97
97
<button
98
-
onClick={() => onNavigate?.('home')}
98
+
onClick={() => onNavigate?.("home")}
99
99
className="w-full bg-gradient-to-r from-firefly-amber via-firefly-orange to-firefly-pink hover:from-amber-600 hover:via-orange-600 hover:to-pink-600 text-white py-4 rounded-xl font-bold text-lg transition-all shadow-lg hover:shadow-xl focus:ring-4 focus:ring-orange-300 dark:focus:ring-orange-800 focus:outline-none flex items-center justify-center space-x-2"
100
100
>
101
101
<span>Go to Dashboard</span>
···
111
111
Connect your ATmosphere account to begin
112
112
</p>
113
113
114
-
<form onSubmit={handleSubmit} className="space-y-4" method="post">
114
+
<form
115
+
onSubmit={handleSubmit}
116
+
className="space-y-4"
117
+
method="post"
118
+
>
115
119
<div>
116
-
<label htmlFor="atproto-handle" className="block text-sm font-semibold text-slate-900 dark:text-slate-100 mb-2">
120
+
<label
121
+
htmlFor="atproto-handle"
122
+
className="block text-sm font-semibold text-slate-900 dark:text-slate-100 mb-2"
123
+
>
117
124
Your ATmosphere Handle
118
125
</label>
119
126
<input
···
126
133
aria-required="true"
127
134
aria-describedby="handle-description"
128
135
/>
129
-
<p id="handle-description" className="text-xs text-slate-600 dark:text-slate-400 mt-2">
130
-
Enter your full ATmosphere handle (e.g., username.bsky.social or yourname.com)
136
+
<p
137
+
id="handle-description"
138
+
className="text-xs text-slate-600 dark:text-slate-400 mt-2"
139
+
>
140
+
Enter your full ATmosphere handle (e.g.,
141
+
username.bsky.social or yourname.com)
131
142
</p>
132
143
</div>
133
144
···
142
153
143
154
<div className="mt-6 pt-6 border-t-2 border-slate-200 dark:border-slate-700">
144
155
<div className="flex items-start space-x-2 text-sm text-slate-700 dark:text-slate-300">
145
-
<svg className="w-5 h-5 text-green-500 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
146
-
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
156
+
<svg
157
+
className="w-5 h-5 text-green-500 flex-shrink-0 mt-0.5"
158
+
fill="currentColor"
159
+
viewBox="0 0 20 20"
160
+
aria-hidden="true"
161
+
>
162
+
<path
163
+
fillRule="evenodd"
164
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
165
+
clipRule="evenodd"
166
+
/>
147
167
</svg>
148
168
<div>
149
-
<p className="font-semibold text-slate-900 dark:text-slate-100">Secure OAuth Connection</p>
150
-
<p className="text-xs mt-1">We use official AT Protocol OAuth. We never see your password and you can revoke access anytime.</p>
169
+
<p className="font-semibold text-slate-900 dark:text-slate-100">
170
+
Secure OAuth Connection
171
+
</p>
172
+
<p className="text-xs mt-1">
173
+
We use official AT Protocol OAuth. We never see your
174
+
password and you can revoke access anytime.
175
+
</p>
151
176
</div>
152
177
</div>
153
178
</div>
···
166
191
Share Your Light
167
192
</h3>
168
193
<p className="text-slate-700 dark:text-slate-300 text-sm leading-relaxed">
169
-
Import your following lists. Your data stays private, your connections shine bright.
194
+
Import your following lists. Your data stays private, your
195
+
connections shine bright.
170
196
</p>
171
197
</div>
172
198
···
178
204
Find Your Swarm
179
205
</h3>
180
206
<p className="text-slate-700 dark:text-slate-300 text-sm leading-relaxed">
181
-
Watch as fireflies light up - discover which friends have already migrated to the ATmosphere.
207
+
Watch as fireflies light up - discover which friends have already
208
+
migrated to the ATmosphere.
182
209
</p>
183
210
</div>
184
211
···
190
217
Sync Your Glow
191
218
</h3>
192
219
<p className="text-slate-700 dark:text-slate-300 text-sm leading-relaxed">
193
-
Reconnect instantly. Follow everyone at once or pick and choose - light up together.
220
+
Reconnect instantly. Follow everyone at once or pick and choose -
221
+
light up together.
194
222
</p>
195
223
</div>
196
224
</div>
···
198
226
{/* Privacy Notice - desktop only */}
199
227
<div className="hidden md:block text-center mb-8">
200
228
<p className="text-sm text-slate-600 dark:text-slate-400 max-w-2xl mx-auto">
201
-
Your data is processed and stored by our servers. This helps you find matches and reconnect with your community.
229
+
Your data is processed and stored by our servers. This helps you
230
+
find matches and reconnect with your community.
202
231
</p>
203
232
</div>
204
233
···
209
238
</h2>
210
239
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
211
240
<div className="text-center">
212
-
<div className="w-12 h-12 bg-firefly-orange text-white rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md" aria-hidden="true">
241
+
<div
242
+
className="w-12 h-12 bg-firefly-orange text-white rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md"
243
+
aria-hidden="true"
244
+
>
213
245
1
214
246
</div>
215
-
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">Connect</h3>
216
-
<p className="text-sm text-slate-700 dark:text-slate-300">Sign in with your ATmosphere account</p>
247
+
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">
248
+
Connect
249
+
</h3>
250
+
<p className="text-sm text-slate-700 dark:text-slate-300">
251
+
Sign in with your ATmosphere account
252
+
</p>
217
253
</div>
218
254
<div className="text-center">
219
-
<div className="w-12 h-12 bg-firefly-pink text-white rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md" aria-hidden="true">
255
+
<div
256
+
className="w-12 h-12 bg-firefly-pink text-white rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md"
257
+
aria-hidden="true"
258
+
>
220
259
2
221
260
</div>
222
-
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">Upload</h3>
223
-
<p className="text-sm text-slate-700 dark:text-slate-300">Import your following data from other platforms</p>
261
+
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">
262
+
Upload
263
+
</h3>
264
+
<p className="text-sm text-slate-700 dark:text-slate-300">
265
+
Import your following data from other platforms
266
+
</p>
224
267
</div>
225
268
<div className="text-center">
226
-
<div className="w-12 h-12 bg-firefly-cyan text-white rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md" aria-hidden="true">
269
+
<div
270
+
className="w-12 h-12 bg-firefly-cyan text-white rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md"
271
+
aria-hidden="true"
272
+
>
227
273
3
228
274
</div>
229
-
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">Match</h3>
230
-
<p className="text-sm text-slate-700 dark:text-slate-300">We find your fireflies in the ATmosphere</p>
275
+
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">
276
+
Match
277
+
</h3>
278
+
<p className="text-sm text-slate-700 dark:text-slate-300">
279
+
We find your fireflies in the ATmosphere
280
+
</p>
231
281
</div>
232
282
<div className="text-center">
233
-
<div className="w-12 h-12 bg-firefly-amber text-slate-900 rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md" aria-hidden="true">
283
+
<div
284
+
className="w-12 h-12 bg-firefly-amber text-slate-900 rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md"
285
+
aria-hidden="true"
286
+
>
234
287
4
235
288
</div>
236
-
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">Follow</h3>
237
-
<p className="text-sm text-slate-700 dark:text-slate-300">Reconnect with your community</p>
289
+
<h3 className="font-semibold text-slate-900 dark:text-slate-100 mb-1">
290
+
Follow
291
+
</h3>
292
+
<p className="text-sm text-slate-700 dark:text-slate-300">
293
+
Reconnect with your community
294
+
</p>
238
295
</div>
239
296
</div>
240
297
</div>
241
298
</div>
242
299
</div>
243
300
);
244
-
}
301
+
}
+5
src/svg.d.ts
+5
src/svg.d.ts
+34
-28
tailwind.config.js
+34
-28
tailwind.config.js
···
1
1
/** @type {import('tailwindcss').Config} */
2
2
export default {
3
-
darkMode: 'class', // Changed from 'media' to 'class' for manual control
4
-
content: [
5
-
"./index.html",
6
-
"./src/**/*.{js,ts,jsx,tsx}",
7
-
],
3
+
darkMode: "class", // Changed from 'media' to 'class' for manual control
4
+
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
8
5
theme: {
9
6
extend: {
10
7
colors: {
11
8
firefly: {
12
-
glow: '#FCD34D', // close to amber-300
13
-
amber: '#F59E0B', // close to amber-500
14
-
orange: '#F97316', // close to orange-500
15
-
pink: '#EC4899', // close to tailwind pink-500
16
-
cyan: '#10D2F4', // close to tailwind cyan-300
17
-
}
18
-
},
19
-
backgroundImage: {
20
-
'firefly-banner':
21
-
'linear-gradient(90deg, rgba(9,163,190,1) 0%, rgba(91,33,182,1) 33%, rgba(236,72,153,1) 67%, rgba(244,105,6,1) 100%)',
22
-
'firefly-banner-dark':
23
-
'linear-gradient(90deg, rgba(24,21,60,1) 0%, rgba(55,20,94,1) 33%, rgba(104,25,98,1) 67%, rgba(36,16,54,1) 100%)',
9
+
glow: "#FCD34D", // close to amber-300
10
+
amber: "#F59E0B", // close to amber-500
11
+
orange: "#F97316", // close to orange-500
12
+
pink: "#EC4899", // close to tailwind pink-500
13
+
cyan: "#10D2F4", // close to tailwind cyan-300
14
+
},
15
+
cyan: { 250: "#72EEFD" },
16
+
purple: { 750: "#6A1DD1" },
17
+
orange: { 650: "#DF3F00" },
18
+
yellow: { 650: "#C56508" },
19
+
orange: { 650: "#F26611" },
20
+
pink: { 650: "#CD206A" },
24
21
},
22
+
backgroundImage: ({ theme }) => ({
23
+
"firefly-banner": `linear-gradient(to right, ${theme("colors.yellow.400")}, ${theme("colors.orange.500")}, ${theme("colors.pink.600")})`,
24
+
"firefly-banner-dark": `linear-gradient(to right, ${theme("colors.yellow.600")}, ${theme("colors.orange.600")}, ${theme("colors.pink.700")})`,
25
+
}),
25
26
animation: {
26
-
'float': 'float 3s ease-in-out infinite',
27
-
'glow-pulse': 'glow-pulse 3s ease-in-out infinite',
27
+
float: "float 3s ease-in-out infinite",
28
28
},
29
29
keyframes: {
30
30
float: {
31
-
'0%, 100%': { transform: 'translate(0, 0) scale(1)', opacity: '0.3' },
32
-
'25%': { transform: 'translate(10px, -20px) scale(1.2)', opacity: '0.8' },
33
-
'50%': { transform: 'translate(-5px, -40px) scale(1)', opacity: '0.5' },
34
-
'75%': { transform: 'translate(15px, -25px) scale(1.1)', opacity: '0.9' },
35
-
},
36
-
'glow-pulse': {
37
-
'0%, 100%': { boxShadow: '0 0 20px rgba(251, 191, 36, 0.3)' },
38
-
'50%': { boxShadow: '0 0 40px rgba(251, 191, 36, 0.6), 0 0 60px rgba(251, 191, 36, 0.3)' },
31
+
"0%, 100%": { transform: "translate(0, 0) scale(1)", opacity: "0.3" },
32
+
"25%": {
33
+
transform: "translate(10px, -20px) scale(1.2)",
34
+
opacity: "0.8",
35
+
},
36
+
"50%": {
37
+
transform: "translate(-5px, -40px) scale(1)",
38
+
opacity: "0.5",
39
+
},
40
+
"75%": {
41
+
transform: "translate(15px, -25px) scale(1.1)",
42
+
opacity: "0.9",
43
+
},
44
+
}
39
45
},
40
46
},
41
47
},
42
48
},
43
49
plugins: [],
44
-
}
50
+
};
+6
-5
vite.config.ts
+6
-5
vite.config.ts
···
1
-
import { defineConfig } from 'vite'
2
-
import react from '@vitejs/plugin-react'
1
+
import { defineConfig } from "vite";
2
+
import react from "@vitejs/plugin-react";
3
+
import svgr from "vite-plugin-svgr";
3
4
4
5
export default defineConfig({
5
-
base: '/',
6
-
plugins: [react()],
7
-
})
6
+
base: "/",
7
+
plugins: [react(), svgr()],
8
+
});