Store your runs on ATProto

Add tailwind and improve styling

+2 -1
index.html
··· 5 5 <meta charset="UTF-8" /> 6 6 <link rel="icon" type="image/svg+xml" href="/vite.svg" /> 7 7 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 8 - <title>Vite + React + TS</title> 8 + <title>MappedAt</title> 9 + <link href="/src/styles.css" rel="stylesheet"> 9 10 <link rel="icon" 10 11 href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🏃</text></svg>"> 11 12 <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
+3 -1
package.json
··· 14 14 "dependencies": { 15 15 "@atproto/api": "^0.15.6", 16 16 "@atproto/oauth-client-browser": "^0.3.16", 17 + "@tailwindcss/vite": "^4.1.6", 17 18 "hono": "^4.7.9", 18 19 "react": "^19.0.0", 19 - "react-dom": "^19.0.0" 20 + "react-dom": "^19.0.0", 21 + "tailwindcss": "^4.1.6" 20 22 }, 21 23 "devDependencies": { 22 24 "@atproto/oauth-types": "^0.2.7",
+404 -40
pnpm-lock.yaml
··· 14 14 '@atproto/oauth-client-browser': 15 15 specifier: ^0.3.16 16 16 version: 0.3.16 17 + '@tailwindcss/vite': 18 + specifier: ^4.1.6 19 + version: 4.1.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)) 17 20 hono: 18 21 specifier: ^4.7.9 19 22 version: 4.7.9 ··· 23 26 react-dom: 24 27 specifier: ^19.0.0 25 28 version: 19.1.0(react@19.1.0) 29 + tailwindcss: 30 + specifier: ^4.1.6 31 + version: 4.1.6 26 32 devDependencies: 27 33 '@atproto/oauth-types': 28 34 specifier: ^0.2.7 29 35 version: 0.2.7 30 36 '@cloudflare/vite-plugin': 31 37 specifier: ^1.1.1 32 - version: 1.1.1(rollup@4.40.2)(vite@6.3.5)(workerd@1.20250507.0)(wrangler@4.14.4(@cloudflare/workers-types@4.20250510.0)) 38 + version: 1.1.1(rollup@4.40.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))(workerd@1.20250507.0)(wrangler@4.14.4(@cloudflare/workers-types@4.20250510.0)) 33 39 '@cloudflare/workers-types': 34 40 specifier: ^4.20250510.0 35 41 version: 4.20250510.0 ··· 44 50 version: 19.1.3(@types/react@19.1.3) 45 51 '@vitejs/plugin-react': 46 52 specifier: ^4.3.4 47 - version: 4.4.1(vite@6.3.5) 53 + version: 4.4.1(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2)) 48 54 eslint: 49 55 specifier: ^9.22.0 50 - version: 9.26.0 56 + version: 9.26.0(jiti@2.4.2) 51 57 eslint-plugin-react-hooks: 52 58 specifier: ^5.2.0 53 - version: 5.2.0(eslint@9.26.0) 59 + version: 5.2.0(eslint@9.26.0(jiti@2.4.2)) 54 60 eslint-plugin-react-refresh: 55 61 specifier: ^0.4.19 56 - version: 0.4.20(eslint@9.26.0) 62 + version: 0.4.20(eslint@9.26.0(jiti@2.4.2)) 57 63 globals: 58 64 specifier: ^16.0.0 59 65 version: 16.1.0 ··· 62 68 version: 5.7.3 63 69 typescript-eslint: 64 70 specifier: ^8.26.1 65 - version: 8.32.0(eslint@9.26.0)(typescript@5.7.3) 71 + version: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) 66 72 vite: 67 73 specifier: ^6.3.1 68 - version: 6.3.5 74 + version: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2) 69 75 wrangler: 70 76 specifier: ^4.14.4 71 77 version: 4.14.4(@cloudflare/workers-types@4.20250510.0) ··· 604 610 cpu: [x64] 605 611 os: [win32] 606 612 613 + '@isaacs/fs-minipass@4.0.1': 614 + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} 615 + engines: {node: '>=18.0.0'} 616 + 607 617 '@jridgewell/gen-mapping@0.3.8': 608 618 resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 609 619 engines: {node: '>=6.0.0'} ··· 762 772 cpu: [x64] 763 773 os: [win32] 764 774 775 + '@tailwindcss/node@4.1.6': 776 + resolution: {integrity: sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==} 777 + 778 + '@tailwindcss/oxide-android-arm64@4.1.6': 779 + resolution: {integrity: sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==} 780 + engines: {node: '>= 10'} 781 + cpu: [arm64] 782 + os: [android] 783 + 784 + '@tailwindcss/oxide-darwin-arm64@4.1.6': 785 + resolution: {integrity: sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==} 786 + engines: {node: '>= 10'} 787 + cpu: [arm64] 788 + os: [darwin] 789 + 790 + '@tailwindcss/oxide-darwin-x64@4.1.6': 791 + resolution: {integrity: sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==} 792 + engines: {node: '>= 10'} 793 + cpu: [x64] 794 + os: [darwin] 795 + 796 + '@tailwindcss/oxide-freebsd-x64@4.1.6': 797 + resolution: {integrity: sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==} 798 + engines: {node: '>= 10'} 799 + cpu: [x64] 800 + os: [freebsd] 801 + 802 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6': 803 + resolution: {integrity: sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==} 804 + engines: {node: '>= 10'} 805 + cpu: [arm] 806 + os: [linux] 807 + 808 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.6': 809 + resolution: {integrity: sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==} 810 + engines: {node: '>= 10'} 811 + cpu: [arm64] 812 + os: [linux] 813 + 814 + '@tailwindcss/oxide-linux-arm64-musl@4.1.6': 815 + resolution: {integrity: sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==} 816 + engines: {node: '>= 10'} 817 + cpu: [arm64] 818 + os: [linux] 819 + 820 + '@tailwindcss/oxide-linux-x64-gnu@4.1.6': 821 + resolution: {integrity: sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==} 822 + engines: {node: '>= 10'} 823 + cpu: [x64] 824 + os: [linux] 825 + 826 + '@tailwindcss/oxide-linux-x64-musl@4.1.6': 827 + resolution: {integrity: sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==} 828 + engines: {node: '>= 10'} 829 + cpu: [x64] 830 + os: [linux] 831 + 832 + '@tailwindcss/oxide-wasm32-wasi@4.1.6': 833 + resolution: {integrity: sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==} 834 + engines: {node: '>=14.0.0'} 835 + cpu: [wasm32] 836 + bundledDependencies: 837 + - '@napi-rs/wasm-runtime' 838 + - '@emnapi/core' 839 + - '@emnapi/runtime' 840 + - '@tybys/wasm-util' 841 + - '@emnapi/wasi-threads' 842 + - tslib 843 + 844 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.6': 845 + resolution: {integrity: sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==} 846 + engines: {node: '>= 10'} 847 + cpu: [arm64] 848 + os: [win32] 849 + 850 + '@tailwindcss/oxide-win32-x64-msvc@4.1.6': 851 + resolution: {integrity: sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==} 852 + engines: {node: '>= 10'} 853 + cpu: [x64] 854 + os: [win32] 855 + 856 + '@tailwindcss/oxide@4.1.6': 857 + resolution: {integrity: sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==} 858 + engines: {node: '>= 10'} 859 + 860 + '@tailwindcss/vite@4.1.6': 861 + resolution: {integrity: sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w==} 862 + peerDependencies: 863 + vite: ^5.2.0 || ^6 864 + 765 865 '@types/babel__core@7.20.5': 766 866 resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} 767 867 ··· 943 1043 chalk@4.1.2: 944 1044 resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 945 1045 engines: {node: '>=10'} 1046 + 1047 + chownr@3.0.0: 1048 + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} 1049 + engines: {node: '>=18'} 946 1050 947 1051 color-convert@2.0.1: 948 1052 resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} ··· 1031 1135 resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} 1032 1136 engines: {node: '>= 0.8'} 1033 1137 1138 + enhanced-resolve@5.18.1: 1139 + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} 1140 + engines: {node: '>=10.13.0'} 1141 + 1034 1142 es-define-property@1.0.1: 1035 1143 resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 1036 1144 engines: {node: '>= 0.4'} ··· 1258 1366 gopd@1.2.0: 1259 1367 resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 1260 1368 engines: {node: '>= 0.4'} 1369 + 1370 + graceful-fs@4.2.11: 1371 + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 1261 1372 1262 1373 graphemer@1.4.0: 1263 1374 resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} ··· 1329 1440 iso-datestring-validator@2.2.2: 1330 1441 resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 1331 1442 1443 + jiti@2.4.2: 1444 + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} 1445 + hasBin: true 1446 + 1332 1447 jose@5.10.0: 1333 1448 resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} 1334 1449 ··· 1365 1480 resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 1366 1481 engines: {node: '>= 0.8.0'} 1367 1482 1483 + lightningcss-darwin-arm64@1.29.2: 1484 + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} 1485 + engines: {node: '>= 12.0.0'} 1486 + cpu: [arm64] 1487 + os: [darwin] 1488 + 1489 + lightningcss-darwin-x64@1.29.2: 1490 + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} 1491 + engines: {node: '>= 12.0.0'} 1492 + cpu: [x64] 1493 + os: [darwin] 1494 + 1495 + lightningcss-freebsd-x64@1.29.2: 1496 + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} 1497 + engines: {node: '>= 12.0.0'} 1498 + cpu: [x64] 1499 + os: [freebsd] 1500 + 1501 + lightningcss-linux-arm-gnueabihf@1.29.2: 1502 + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} 1503 + engines: {node: '>= 12.0.0'} 1504 + cpu: [arm] 1505 + os: [linux] 1506 + 1507 + lightningcss-linux-arm64-gnu@1.29.2: 1508 + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} 1509 + engines: {node: '>= 12.0.0'} 1510 + cpu: [arm64] 1511 + os: [linux] 1512 + 1513 + lightningcss-linux-arm64-musl@1.29.2: 1514 + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} 1515 + engines: {node: '>= 12.0.0'} 1516 + cpu: [arm64] 1517 + os: [linux] 1518 + 1519 + lightningcss-linux-x64-gnu@1.29.2: 1520 + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} 1521 + engines: {node: '>= 12.0.0'} 1522 + cpu: [x64] 1523 + os: [linux] 1524 + 1525 + lightningcss-linux-x64-musl@1.29.2: 1526 + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} 1527 + engines: {node: '>= 12.0.0'} 1528 + cpu: [x64] 1529 + os: [linux] 1530 + 1531 + lightningcss-win32-arm64-msvc@1.29.2: 1532 + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} 1533 + engines: {node: '>= 12.0.0'} 1534 + cpu: [arm64] 1535 + os: [win32] 1536 + 1537 + lightningcss-win32-x64-msvc@1.29.2: 1538 + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} 1539 + engines: {node: '>= 12.0.0'} 1540 + cpu: [x64] 1541 + os: [win32] 1542 + 1543 + lightningcss@1.29.2: 1544 + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} 1545 + engines: {node: '>= 12.0.0'} 1546 + 1368 1547 locate-path@6.0.0: 1369 1548 resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 1370 1549 engines: {node: '>=10'} ··· 1434 1613 resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 1435 1614 engines: {node: '>=16 || 14 >=14.17'} 1436 1615 1616 + minipass@7.1.2: 1617 + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 1618 + engines: {node: '>=16 || 14 >=14.17'} 1619 + 1620 + minizlib@3.0.2: 1621 + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} 1622 + engines: {node: '>= 18'} 1623 + 1624 + mkdirp@3.0.1: 1625 + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} 1626 + engines: {node: '>=10'} 1627 + hasBin: true 1628 + 1437 1629 ms@2.1.3: 1438 1630 resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1439 1631 ··· 1691 1883 resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1692 1884 engines: {node: '>=8'} 1693 1885 1886 + tailwindcss@4.1.6: 1887 + resolution: {integrity: sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==} 1888 + 1889 + tapable@2.2.1: 1890 + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} 1891 + engines: {node: '>=6'} 1892 + 1893 + tar@7.4.3: 1894 + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} 1895 + engines: {node: '>=18'} 1896 + 1694 1897 tinyglobby@0.2.13: 1695 1898 resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} 1696 1899 engines: {node: '>=12.0.0'} ··· 1850 2053 1851 2054 yallist@3.1.1: 1852 2055 resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} 2056 + 2057 + yallist@5.0.0: 2058 + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} 2059 + engines: {node: '>=18'} 1853 2060 1854 2061 yocto-queue@0.1.0: 1855 2062 resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} ··· 2115 2322 optionalDependencies: 2116 2323 workerd: 1.20250507.0 2117 2324 2118 - '@cloudflare/vite-plugin@1.1.1(rollup@4.40.2)(vite@6.3.5)(workerd@1.20250507.0)(wrangler@4.14.4(@cloudflare/workers-types@4.20250510.0))': 2325 + '@cloudflare/vite-plugin@1.1.1(rollup@4.40.2)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))(workerd@1.20250507.0)(wrangler@4.14.4(@cloudflare/workers-types@4.20250510.0))': 2119 2326 dependencies: 2120 2327 '@cloudflare/unenv-preset': 2.3.1(unenv@2.0.0-rc.15)(workerd@1.20250507.0) 2121 2328 '@hattip/adapter-node': 0.0.49 ··· 2125 2332 picocolors: 1.1.1 2126 2333 tinyglobby: 0.2.13 2127 2334 unenv: 2.0.0-rc.15 2128 - vite: 6.3.5 2335 + vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2) 2129 2336 wrangler: 4.14.4(@cloudflare/workers-types@4.20250510.0) 2130 2337 ws: 8.18.0 2131 2338 transitivePeerDependencies: ··· 2235 2442 '@esbuild/win32-x64@0.25.4': 2236 2443 optional: true 2237 2444 2238 - '@eslint-community/eslint-utils@4.7.0(eslint@9.26.0)': 2445 + '@eslint-community/eslint-utils@4.7.0(eslint@9.26.0(jiti@2.4.2))': 2239 2446 dependencies: 2240 - eslint: 9.26.0 2447 + eslint: 9.26.0(jiti@2.4.2) 2241 2448 eslint-visitor-keys: 3.4.3 2242 2449 2243 2450 '@eslint-community/regexpp@4.12.1': {} ··· 2393 2600 '@img/sharp-win32-x64@0.33.5': 2394 2601 optional: true 2395 2602 2603 + '@isaacs/fs-minipass@4.0.1': 2604 + dependencies: 2605 + minipass: 7.1.2 2606 + 2396 2607 '@jridgewell/gen-mapping@0.3.8': 2397 2608 dependencies: 2398 2609 '@jridgewell/set-array': 1.2.1 ··· 2519 2730 '@rollup/rollup-win32-x64-msvc@4.40.2': 2520 2731 optional: true 2521 2732 2733 + '@tailwindcss/node@4.1.6': 2734 + dependencies: 2735 + '@ampproject/remapping': 2.3.0 2736 + enhanced-resolve: 5.18.1 2737 + jiti: 2.4.2 2738 + lightningcss: 1.29.2 2739 + magic-string: 0.30.17 2740 + source-map-js: 1.2.1 2741 + tailwindcss: 4.1.6 2742 + 2743 + '@tailwindcss/oxide-android-arm64@4.1.6': 2744 + optional: true 2745 + 2746 + '@tailwindcss/oxide-darwin-arm64@4.1.6': 2747 + optional: true 2748 + 2749 + '@tailwindcss/oxide-darwin-x64@4.1.6': 2750 + optional: true 2751 + 2752 + '@tailwindcss/oxide-freebsd-x64@4.1.6': 2753 + optional: true 2754 + 2755 + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6': 2756 + optional: true 2757 + 2758 + '@tailwindcss/oxide-linux-arm64-gnu@4.1.6': 2759 + optional: true 2760 + 2761 + '@tailwindcss/oxide-linux-arm64-musl@4.1.6': 2762 + optional: true 2763 + 2764 + '@tailwindcss/oxide-linux-x64-gnu@4.1.6': 2765 + optional: true 2766 + 2767 + '@tailwindcss/oxide-linux-x64-musl@4.1.6': 2768 + optional: true 2769 + 2770 + '@tailwindcss/oxide-wasm32-wasi@4.1.6': 2771 + optional: true 2772 + 2773 + '@tailwindcss/oxide-win32-arm64-msvc@4.1.6': 2774 + optional: true 2775 + 2776 + '@tailwindcss/oxide-win32-x64-msvc@4.1.6': 2777 + optional: true 2778 + 2779 + '@tailwindcss/oxide@4.1.6': 2780 + dependencies: 2781 + detect-libc: 2.0.4 2782 + tar: 7.4.3 2783 + optionalDependencies: 2784 + '@tailwindcss/oxide-android-arm64': 4.1.6 2785 + '@tailwindcss/oxide-darwin-arm64': 4.1.6 2786 + '@tailwindcss/oxide-darwin-x64': 4.1.6 2787 + '@tailwindcss/oxide-freebsd-x64': 4.1.6 2788 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.6 2789 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.6 2790 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.6 2791 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.6 2792 + '@tailwindcss/oxide-linux-x64-musl': 4.1.6 2793 + '@tailwindcss/oxide-wasm32-wasi': 4.1.6 2794 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.6 2795 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.6 2796 + 2797 + '@tailwindcss/vite@4.1.6(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))': 2798 + dependencies: 2799 + '@tailwindcss/node': 4.1.6 2800 + '@tailwindcss/oxide': 4.1.6 2801 + tailwindcss: 4.1.6 2802 + vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2) 2803 + 2522 2804 '@types/babel__core@7.20.5': 2523 2805 dependencies: 2524 2806 '@babel/parser': 7.27.2 ··· 2552 2834 dependencies: 2553 2835 csstype: 3.1.3 2554 2836 2555 - '@typescript-eslint/eslint-plugin@8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0)(typescript@5.7.3))(eslint@9.26.0)(typescript@5.7.3)': 2837 + '@typescript-eslint/eslint-plugin@8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3)': 2556 2838 dependencies: 2557 2839 '@eslint-community/regexpp': 4.12.1 2558 - '@typescript-eslint/parser': 8.32.0(eslint@9.26.0)(typescript@5.7.3) 2840 + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) 2559 2841 '@typescript-eslint/scope-manager': 8.32.0 2560 - '@typescript-eslint/type-utils': 8.32.0(eslint@9.26.0)(typescript@5.7.3) 2561 - '@typescript-eslint/utils': 8.32.0(eslint@9.26.0)(typescript@5.7.3) 2842 + '@typescript-eslint/type-utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) 2843 + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) 2562 2844 '@typescript-eslint/visitor-keys': 8.32.0 2563 - eslint: 9.26.0 2845 + eslint: 9.26.0(jiti@2.4.2) 2564 2846 graphemer: 1.4.0 2565 2847 ignore: 5.3.2 2566 2848 natural-compare: 1.4.0 ··· 2569 2851 transitivePeerDependencies: 2570 2852 - supports-color 2571 2853 2572 - '@typescript-eslint/parser@8.32.0(eslint@9.26.0)(typescript@5.7.3)': 2854 + '@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3)': 2573 2855 dependencies: 2574 2856 '@typescript-eslint/scope-manager': 8.32.0 2575 2857 '@typescript-eslint/types': 8.32.0 2576 2858 '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.7.3) 2577 2859 '@typescript-eslint/visitor-keys': 8.32.0 2578 2860 debug: 4.4.0 2579 - eslint: 9.26.0 2861 + eslint: 9.26.0(jiti@2.4.2) 2580 2862 typescript: 5.7.3 2581 2863 transitivePeerDependencies: 2582 2864 - supports-color ··· 2586 2868 '@typescript-eslint/types': 8.32.0 2587 2869 '@typescript-eslint/visitor-keys': 8.32.0 2588 2870 2589 - '@typescript-eslint/type-utils@8.32.0(eslint@9.26.0)(typescript@5.7.3)': 2871 + '@typescript-eslint/type-utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3)': 2590 2872 dependencies: 2591 2873 '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.7.3) 2592 - '@typescript-eslint/utils': 8.32.0(eslint@9.26.0)(typescript@5.7.3) 2874 + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) 2593 2875 debug: 4.4.0 2594 - eslint: 9.26.0 2876 + eslint: 9.26.0(jiti@2.4.2) 2595 2877 ts-api-utils: 2.1.0(typescript@5.7.3) 2596 2878 typescript: 5.7.3 2597 2879 transitivePeerDependencies: ··· 2613 2895 transitivePeerDependencies: 2614 2896 - supports-color 2615 2897 2616 - '@typescript-eslint/utils@8.32.0(eslint@9.26.0)(typescript@5.7.3)': 2898 + '@typescript-eslint/utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3)': 2617 2899 dependencies: 2618 - '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0) 2900 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) 2619 2901 '@typescript-eslint/scope-manager': 8.32.0 2620 2902 '@typescript-eslint/types': 8.32.0 2621 2903 '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.7.3) 2622 - eslint: 9.26.0 2904 + eslint: 9.26.0(jiti@2.4.2) 2623 2905 typescript: 5.7.3 2624 2906 transitivePeerDependencies: 2625 2907 - supports-color ··· 2629 2911 '@typescript-eslint/types': 8.32.0 2630 2912 eslint-visitor-keys: 4.2.0 2631 2913 2632 - '@vitejs/plugin-react@4.4.1(vite@6.3.5)': 2914 + '@vitejs/plugin-react@4.4.1(vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2))': 2633 2915 dependencies: 2634 2916 '@babel/core': 7.27.1 2635 2917 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.1) 2636 2918 '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.1) 2637 2919 '@types/babel__core': 7.20.5 2638 2920 react-refresh: 0.17.0 2639 - vite: 6.3.5 2921 + vite: 6.3.5(jiti@2.4.2)(lightningcss@1.29.2) 2640 2922 transitivePeerDependencies: 2641 2923 - supports-color 2642 2924 ··· 2751 3033 ansi-styles: 4.3.0 2752 3034 supports-color: 7.2.0 2753 3035 3036 + chownr@3.0.0: {} 3037 + 2754 3038 color-convert@2.0.1: 2755 3039 dependencies: 2756 3040 color-name: 1.1.4 ··· 2808 3092 2809 3093 depd@2.0.0: {} 2810 3094 2811 - detect-libc@2.0.4: 2812 - optional: true 3095 + detect-libc@2.0.4: {} 2813 3096 2814 3097 dunder-proto@1.0.1: 2815 3098 dependencies: ··· 2823 3106 2824 3107 encodeurl@2.0.0: {} 2825 3108 3109 + enhanced-resolve@5.18.1: 3110 + dependencies: 3111 + graceful-fs: 4.2.11 3112 + tapable: 2.2.1 3113 + 2826 3114 es-define-property@1.0.1: {} 2827 3115 2828 3116 es-errors@1.3.0: {} ··· 2865 3153 2866 3154 escape-string-regexp@4.0.0: {} 2867 3155 2868 - eslint-plugin-react-hooks@5.2.0(eslint@9.26.0): 3156 + eslint-plugin-react-hooks@5.2.0(eslint@9.26.0(jiti@2.4.2)): 2869 3157 dependencies: 2870 - eslint: 9.26.0 3158 + eslint: 9.26.0(jiti@2.4.2) 2871 3159 2872 - eslint-plugin-react-refresh@0.4.20(eslint@9.26.0): 3160 + eslint-plugin-react-refresh@0.4.20(eslint@9.26.0(jiti@2.4.2)): 2873 3161 dependencies: 2874 - eslint: 9.26.0 3162 + eslint: 9.26.0(jiti@2.4.2) 2875 3163 2876 3164 eslint-scope@8.3.0: 2877 3165 dependencies: ··· 2882 3170 2883 3171 eslint-visitor-keys@4.2.0: {} 2884 3172 2885 - eslint@9.26.0: 3173 + eslint@9.26.0(jiti@2.4.2): 2886 3174 dependencies: 2887 - '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0) 3175 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) 2888 3176 '@eslint-community/regexpp': 4.12.1 2889 3177 '@eslint/config-array': 0.20.0 2890 3178 '@eslint/config-helpers': 0.2.2 ··· 2921 3209 natural-compare: 1.4.0 2922 3210 optionator: 0.9.4 2923 3211 zod: 3.24.4 3212 + optionalDependencies: 3213 + jiti: 2.4.2 2924 3214 transitivePeerDependencies: 2925 3215 - supports-color 2926 3216 ··· 3105 3395 3106 3396 gopd@1.2.0: {} 3107 3397 3398 + graceful-fs@4.2.11: {} 3399 + 3108 3400 graphemer@1.4.0: {} 3109 3401 3110 3402 has-flag@4.0.0: {} ··· 3159 3451 3160 3452 iso-datestring-validator@2.2.2: {} 3161 3453 3454 + jiti@2.4.2: {} 3455 + 3162 3456 jose@5.10.0: {} 3163 3457 3164 3458 js-tokens@4.0.0: {} ··· 3186 3480 prelude-ls: 1.2.1 3187 3481 type-check: 0.4.0 3188 3482 3483 + lightningcss-darwin-arm64@1.29.2: 3484 + optional: true 3485 + 3486 + lightningcss-darwin-x64@1.29.2: 3487 + optional: true 3488 + 3489 + lightningcss-freebsd-x64@1.29.2: 3490 + optional: true 3491 + 3492 + lightningcss-linux-arm-gnueabihf@1.29.2: 3493 + optional: true 3494 + 3495 + lightningcss-linux-arm64-gnu@1.29.2: 3496 + optional: true 3497 + 3498 + lightningcss-linux-arm64-musl@1.29.2: 3499 + optional: true 3500 + 3501 + lightningcss-linux-x64-gnu@1.29.2: 3502 + optional: true 3503 + 3504 + lightningcss-linux-x64-musl@1.29.2: 3505 + optional: true 3506 + 3507 + lightningcss-win32-arm64-msvc@1.29.2: 3508 + optional: true 3509 + 3510 + lightningcss-win32-x64-msvc@1.29.2: 3511 + optional: true 3512 + 3513 + lightningcss@1.29.2: 3514 + dependencies: 3515 + detect-libc: 2.0.4 3516 + optionalDependencies: 3517 + lightningcss-darwin-arm64: 1.29.2 3518 + lightningcss-darwin-x64: 1.29.2 3519 + lightningcss-freebsd-x64: 1.29.2 3520 + lightningcss-linux-arm-gnueabihf: 1.29.2 3521 + lightningcss-linux-arm64-gnu: 1.29.2 3522 + lightningcss-linux-arm64-musl: 1.29.2 3523 + lightningcss-linux-x64-gnu: 1.29.2 3524 + lightningcss-linux-x64-musl: 1.29.2 3525 + lightningcss-win32-arm64-msvc: 1.29.2 3526 + lightningcss-win32-x64-msvc: 1.29.2 3527 + 3189 3528 locate-path@6.0.0: 3190 3529 dependencies: 3191 3530 p-locate: 5.0.0 ··· 3253 3592 minimatch@9.0.5: 3254 3593 dependencies: 3255 3594 brace-expansion: 2.0.1 3595 + 3596 + minipass@7.1.2: {} 3597 + 3598 + minizlib@3.0.2: 3599 + dependencies: 3600 + minipass: 7.1.2 3601 + 3602 + mkdirp@3.0.1: {} 3256 3603 3257 3604 ms@2.1.3: {} 3258 3605 ··· 3534 3881 dependencies: 3535 3882 has-flag: 4.0.0 3536 3883 3884 + tailwindcss@4.1.6: {} 3885 + 3886 + tapable@2.2.1: {} 3887 + 3888 + tar@7.4.3: 3889 + dependencies: 3890 + '@isaacs/fs-minipass': 4.0.1 3891 + chownr: 3.0.0 3892 + minipass: 7.1.2 3893 + minizlib: 3.0.2 3894 + mkdirp: 3.0.1 3895 + yallist: 5.0.0 3896 + 3537 3897 tinyglobby@0.2.13: 3538 3898 dependencies: 3539 3899 fdir: 6.4.4(picomatch@4.0.2) ··· 3563 3923 media-typer: 1.1.0 3564 3924 mime-types: 3.0.1 3565 3925 3566 - typescript-eslint@8.32.0(eslint@9.26.0)(typescript@5.7.3): 3926 + typescript-eslint@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3): 3567 3927 dependencies: 3568 - '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0)(typescript@5.7.3))(eslint@9.26.0)(typescript@5.7.3) 3569 - '@typescript-eslint/parser': 8.32.0(eslint@9.26.0)(typescript@5.7.3) 3570 - '@typescript-eslint/utils': 8.32.0(eslint@9.26.0)(typescript@5.7.3) 3571 - eslint: 9.26.0 3928 + '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) 3929 + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) 3930 + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) 3931 + eslint: 9.26.0(jiti@2.4.2) 3572 3932 typescript: 5.7.3 3573 3933 transitivePeerDependencies: 3574 3934 - supports-color ··· 3609 3969 3610 3970 vary@1.1.2: {} 3611 3971 3612 - vite@6.3.5: 3972 + vite@6.3.5(jiti@2.4.2)(lightningcss@1.29.2): 3613 3973 dependencies: 3614 3974 esbuild: 0.25.4 3615 3975 fdir: 6.4.4(picomatch@4.0.2) ··· 3619 3979 tinyglobby: 0.2.13 3620 3980 optionalDependencies: 3621 3981 fsevents: 2.3.3 3982 + jiti: 2.4.2 3983 + lightningcss: 1.29.2 3622 3984 3623 3985 which@2.0.2: 3624 3986 dependencies: ··· 3657 4019 ws@8.18.0: {} 3658 4020 3659 4021 yallist@3.1.1: {} 4022 + 4023 + yallist@5.0.0: {} 3660 4024 3661 4025 yocto-queue@0.1.0: {} 3662 4026
+66 -39
src/components/App.tsx
··· 1 - import { useContext, useState } from "react"; 1 + import { useContext, useState, useEffect } from "react"; 2 2 import { ATProtoContext } from "../context.tsx"; 3 3 import { RunForm } from "./RunForm.tsx"; 4 4 import { SignIn } from "./SignIn.tsx"; 5 - import { AtUri } from "@atproto/api" 5 + import { AtUri, BlobRef } from "@atproto/api" 6 6 import { Run } from "../../shared/run_lexicon.ts"; 7 7 8 8 type RunsResponse = { ··· 22 22 } 23 23 } 24 24 25 - export function App() { 25 + function RunList() { 26 26 const { session, agent } = useContext(ATProtoContext); 27 - const [runsResponse, setRunsResponse] = useState<RunsResponse>({ records: [] }); 27 + const [runsResponse, setRunsResponse] = useState<RunsResponse | undefined>(undefined); 28 28 29 29 const fetchRuns = () => session && agent?.com.atproto.repo 30 30 .listRecords({ collection: "me.wilb.test.run", repo: session.did }) ··· 34 34 .deleteRecord({ collection: "me.wilb.test.run", repo: session.did, rkey }) 35 35 .then(() => fetchRuns()) 36 36 37 + useEffect(() => { 38 + if (!runsResponse) { 39 + fetchRuns(); 40 + } 41 + }, [session, agent]) 42 + 43 + const loadGPX = (uri: string, gpx: BlobRef) => { 44 + const map = window.L.map(`map-${uri}`); 45 + window.L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 46 + attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>' 47 + }).addTo(map); 48 + 49 + const options = { 50 + async: true, 51 + polyline_options: { color: 'red' }, 52 + }; 53 + 54 + new window.L.GPX(`${session?.server.issuer}/xrpc/com.atproto.sync.getBlob?did=${session?.did}&cid=${gpx.ref}`, options).on('loaded', (e: any) => { 55 + map.fitBounds(e.target.getBounds()); 56 + }).addTo(map); 57 + }; 58 + 37 59 return ( 38 - <> 39 - <h1>ProtoRuns: your runs, on ATProto</h1> 40 - <p><a target="_blank" href="https://www.val.town/x/wilhelm/ProtoRuns/code/README.md">[src]</a></p> 41 - {session && <RunForm />} 42 - <SignIn /> 43 - {session && <button 44 - type="button" 45 - onClick={fetchRuns}> 46 - Get my runs 47 - </button>} 48 - <div id="map"></div> 60 + <div className="flex flex-col gap-2 p-2"> 49 61 {runsResponse?.records.map(({ uri, value: { note, date_iso, distance_meters, duration_seconds, gpx } }) => ( 50 - <div> 51 - <p>distance (kilometers): {distance_meters / 1000}</p> 52 - <p>duration (minutes): {duration_seconds ?? 0 / 60}</p> 53 - <p>date: {date_iso}</p> 54 - <p>note: {note}</p> 55 - {gpx && <p><button type="button" onClick={() => { 56 - const map = window.L.map('map'); 57 - window.L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 58 - attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>' 59 - }).addTo(map); 60 - 61 - const options = { 62 - async: true, 63 - polyline_options: { color: 'red' }, 64 - }; 65 - 66 - new window.L.GPX(`${session?.server.issuer}/xrpc/com.atproto.sync.getBlob?did=${session?.did}&cid=${gpx.ref}`, options).on('loaded', (e: any) => { 67 - map.fitBounds(e.target.getBounds()); 68 - }).addTo(map); 69 - }}>Load gpx</button></p>} 70 - <button type="button" onClick={() => deleteRun(new AtUri(uri).rkey)}> 71 - delete 72 - </button> 62 + <div className="flex flex-row" key={uri}> 63 + <div className="flex flex-col w-full gap-2 p-2"> 64 + <p>distance (kilometers): {distance_meters / 1000}</p> 65 + <p>duration (minutes): {duration_seconds ?? 0 / 60}</p> 66 + <p>date: {date_iso}</p> 67 + <p>note: {note}</p> 68 + <div className="flex flex-row gap-2 p-2"> 69 + {gpx && <button type="button" onClick={() => loadGPX(uri, gpx)}>Load gpx</button>} 70 + <button type="button" onClick={() => deleteRun(new AtUri(uri).rkey)}> 71 + delete 72 + </button> 73 + </div> 74 + </div> 75 + <div id={`map-${uri}`} data-active={!!gpx} className="flex w-full h-2 data-[active=true]:h-[180px]"></div> 73 76 </div> 74 77 ))} 75 - </> 78 + </div> 76 79 ); 80 + } 81 + 82 + export function App() { 83 + const { session } = useContext(ATProtoContext); 84 + 85 + return ( 86 + <div className="flex flex-col gap-2 p-2 items-center"> 87 + <aside className="w-full flex justify-between bg-amber-50 gap-2"> 88 + <h1 className="text-xl">MappedAt: map your runs and more</h1> 89 + <SignIn /> 90 + </aside> 91 + <main className="w-full flex flex-col gap-2 p-2 max-w-3xl"> 92 + {session && 93 + <details> 94 + <summary> 95 + New run form 96 + </summary> 97 + <RunForm /> 98 + </details> 99 + } 100 + <RunList /> 101 + </main> 102 + </div> 103 + ) 77 104 }
+22 -10
src/components/RunForm.tsx
··· 49 49 }, null 50 50 ) 51 51 return ( 52 - <> 53 - <form action={submitRun}> 54 - <input required name="distance_kilometers" type="number" placeholder="distance (in kilometers)" /> 55 - <input required name="date_iso" type="date" placeholder="date" /> 56 - <input required name="duration_minutes" type="number" placeholder="duration (in minutes)" /> 57 - <input name="note" type="text" placeholder="note" /> 58 - <span>GPX file: <input name="gpx" type="file" /></span> 59 - <input type="submit" value="Submit run" /> 52 + <div className="flex justify-center"> 53 + <form action={submitRun} className="flex flex-col gap-2 p-2 w-xl"> 54 + <div className="outline-1 p-2 rounded-sm"> 55 + <input required name="distance_kilometers" type="number" placeholder="distance (in kilometers)" /> 56 + </div> 57 + <div className="outline-1 p-2 rounded-sm"> 58 + <span>Date: <input required name="date_iso" type="date" placeholder="date" /></span> 59 + </div> 60 + <div className="outline-1 p-2 rounded-sm"> 61 + <input required name="duration_minutes" type="number" placeholder="duration (in minutes)" /> 62 + </div> 63 + <div className="outline-1 p-2 rounded-sm"> 64 + <input name="note" type="text" placeholder="note" /> 65 + </div> 66 + <div className="outline-1 p-2 rounded-sm"> 67 + <span>GPX file: <input name="gpx" type="file" /></span> 68 + </div> 69 + <div className="outline-1 p-2 rounded-sm"> 70 + <input type="submit" value="Submit run" /> 71 + </div> 60 72 </form> 61 73 {isPending && <p>submitting...</p>} 62 - {error && <pre>{error as any}</pre>} 63 - </> 74 + {error ? <pre>{(error as Error).message}</pre> : ""} 75 + </div> 64 76 ) 65 77 }
+1 -18
src/index.css
··· 1 - html { 2 - margin: 0; 3 - padding: 0; 4 - } 5 - 6 - body, 7 - form { 8 - max-width: 400px; 9 - margin: auto; 10 - display: flex; 11 - flex-direction: column; 12 - gap: 2rem; 13 - margin-bottom: 2rem; 14 - } 15 - 16 - #map { 17 - height: 180px; 18 - } 1 + @import "tailwindcss";
+2 -1
vite.config.ts
··· 2 2 import react from "@vitejs/plugin-react"; 3 3 4 4 import { cloudflare } from "@cloudflare/vite-plugin"; 5 + import tailwindcss from "@tailwindcss/vite"; 5 6 6 7 // https://vite.dev/config/ 7 8 export default defineConfig({ 8 - plugins: [react(), cloudflare()], 9 + plugins: [react(), cloudflare(), tailwindcss()], 9 10 server: { 10 11 allowedHosts: [ 11 12 "currency-houston-arranged-scotia.trycloudflare.com",