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

+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 + });