ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto

using the logo! started moving ui changes over from mockup

authored by byarielm.fyi and committed by byarielm.fyi d216f8d7 702ca8db

verified
+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
··· 38 38 "postcss": "^8.5.6", 39 39 "tailwindcss": "^3.4.0", 40 40 "typescript": "^5.3.3", 41 - "vite": "^5.4.0" 41 + "vite": "^5.4.0", 42 + "vite-plugin-svgr": "^4.5.0" 42 43 } 43 44 }
public/favicon/apple-touch-icon.png

This is a binary file and will not be displayed.

public/favicon/favicon-96x96.png

This is a binary file and will not be displayed.

public/favicon/favicon.ico

This is a binary file and will not be displayed.

public/favicon/icon.png

This is a binary file and will not be displayed.

+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

This is a binary file and will not be displayed.

public/favicon/web-app-manifest-512x512.png

This is a binary file and will not be displayed.

+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
··· 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
··· 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
··· 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
··· 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
··· 1 + declare module "*.svg?react" { 2 + import * as React from "react"; 3 + const Component: React.FC<React.SVGProps<SVGSVGElement>>; 4 + export default Component; 5 + }
+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
··· 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 + });