atmo.rsvp
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

all these changes

Florian 2d8e557b 2074a5c8

+697 -560
+1 -1
lexicons/README.md
··· 3 3 this directory contains lexicon documents pulled from the following sources: 4 4 5 5 - https://github.com/flo-bit/contrail.git 6 - - commit: 15d8e9ae46f8d5e69421cb99ddb4d2f66a4fe57d 6 + - commit: 2c94dd5211fdaa8b2224c0c2bb7e487151ac85bb
+4
lexicons/community/lexicon/calendar/event/listRecords.json
··· 26 26 "type": "boolean", 27 27 "description": "Include profile + identity info keyed by DID" 28 28 }, 29 + "search": { 30 + "type": "string", 31 + "description": "Full-text search across: mode, name, status, description" 32 + }, 29 33 "mode": { 30 34 "type": "string", 31 35 "description": "Filter by mode"
+4
lexicons/community/lexicon/calendar/rsvp/listRecords.json
··· 26 26 "type": "boolean", 27 27 "description": "Include profile + identity info keyed by DID" 28 28 }, 29 + "search": { 30 + "type": "string", 31 + "description": "Full-text search across: status, subject.uri" 32 + }, 29 33 "status": { 30 34 "type": "string", 31 35 "description": "Filter by status"
+56
lexicons/rsvp/atmo/notifyOfUpdate.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "rsvp.atmo.notifyOfUpdate", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Notify of a record change for immediate indexing. Fetches the record from the user's PDS and indexes (or deletes) it.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "properties": { 13 + "uri": { 14 + "type": "string", 15 + "format": "at-uri", 16 + "description": "Single AT URI to fetch and index" 17 + }, 18 + "uris": { 19 + "type": "array", 20 + "items": { 21 + "type": "string", 22 + "format": "at-uri" 23 + }, 24 + "maxLength": 25, 25 + "description": "Batch of AT URIs to fetch and index (max 25)" 26 + } 27 + } 28 + } 29 + }, 30 + "output": { 31 + "encoding": "application/json", 32 + "schema": { 33 + "type": "object", 34 + "required": ["indexed", "deleted"], 35 + "properties": { 36 + "indexed": { 37 + "type": "integer", 38 + "description": "Number of records created or updated" 39 + }, 40 + "deleted": { 41 + "type": "integer", 42 + "description": "Number of records deleted (not found on PDS)" 43 + }, 44 + "errors": { 45 + "type": "array", 46 + "items": { 47 + "type": "string" 48 + }, 49 + "description": "Errors for individual URIs that could not be processed" 50 + } 51 + } 52 + } 53 + } 54 + } 55 + } 56 + }
+3
package.json
··· 61 61 "@foxui/social": "^0.5.1", 62 62 "@foxui/time": "^0.5.1", 63 63 "@internationalized/date": "^3.12.0", 64 + "@tailwindcss/typography": "^0.5.19", 64 65 "dompurify": "^3.3.3", 66 + "maplibre-gl": "^5.20.2", 65 67 "marked": "^17.0.4", 66 68 "svelte-boring-avatars": "^1.2.6", 69 + "svelte-maplibre-gl": "^1.0.3", 67 70 "valibot": "^1.2.0" 68 71 } 69 72 }
+236
pnpm-lock.yaml
··· 29 29 '@internationalized/date': 30 30 specifier: ^3.12.0 31 31 version: 3.12.0 32 + '@tailwindcss/typography': 33 + specifier: ^0.5.19 34 + version: 0.5.19(tailwindcss@4.2.1) 32 35 dompurify: 33 36 specifier: ^3.3.3 34 37 version: 3.3.3 38 + maplibre-gl: 39 + specifier: ^5.20.2 40 + version: 5.20.2 35 41 marked: 36 42 specifier: ^17.0.4 37 43 version: 17.0.4 38 44 svelte-boring-avatars: 39 45 specifier: ^1.2.6 40 46 version: 1.2.6 47 + svelte-maplibre-gl: 48 + specifier: ^1.0.3 49 + version: 1.0.3(maplibre-gl@5.20.2)(svelte@5.48.0) 41 50 valibot: 42 51 specifier: ^1.2.0 43 52 version: 1.2.0(typescript@5.9.3) ··· 888 897 '@jridgewell/trace-mapping@0.3.9': 889 898 resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==, tarball: https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz} 890 899 900 + '@mapbox/jsonlint-lines-primitives@2.0.2': 901 + resolution: {integrity: sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==, tarball: https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz} 902 + engines: {node: '>= 0.6'} 903 + 904 + '@mapbox/point-geometry@1.1.0': 905 + resolution: {integrity: sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==, tarball: https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz} 906 + 907 + '@mapbox/tiny-sdf@2.0.7': 908 + resolution: {integrity: sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==, tarball: https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz} 909 + 910 + '@mapbox/unitbezier@0.0.1': 911 + resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==, tarball: https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz} 912 + 913 + '@mapbox/vector-tile@2.0.4': 914 + resolution: {integrity: sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==, tarball: https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.4.tgz} 915 + 916 + '@mapbox/whoots-js@3.1.0': 917 + resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==, tarball: https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz} 918 + engines: {node: '>=6.0.0'} 919 + 920 + '@maplibre/geojson-vt@5.0.4': 921 + resolution: {integrity: sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ==, tarball: https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-5.0.4.tgz} 922 + 923 + '@maplibre/geojson-vt@6.0.3': 924 + resolution: {integrity: sha512-tJ8df2SAIacER7pWTiSlDjIULBBAfZnzAURvWb1d8kVzx/pmSJcG0L2p0DTAB6nEu8Lmsx5zAc8JFDcs2DTwaw==, tarball: https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.0.3.tgz} 925 + 926 + '@maplibre/maplibre-gl-style-spec@24.7.0': 927 + resolution: {integrity: sha512-Ed7rcKYU5iELfablg9Mj+TVCsXsPBgdMyXPRAxb2v7oWg9YJnpQdZ5msDs1LESu/mtXy3Z48Vdppv2t/x5kAhw==, tarball: https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.7.0.tgz} 928 + hasBin: true 929 + 930 + '@maplibre/mlt@1.1.7': 931 + resolution: {integrity: sha512-HZSsXrgn2V6T3o0qklMwKERfKaAxjO8shmiFnVygCtXTg4SPKWVX+U99RkvxUfCsjYBEcT4ltor8lSlBSCca7Q==, tarball: https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.7.tgz} 932 + 933 + '@maplibre/vt-pbf@4.3.0': 934 + resolution: {integrity: sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w==, tarball: https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.0.tgz} 935 + 891 936 '@mary-ext/event-iterator@1.0.0': 892 937 resolution: {integrity: sha512-l6gCPsWJ8aRCe/s7/oCmero70kDHgIK5m4uJvYgwEYTqVxoBOIXbKr5tnkLqUHEg6mNduB4IWvms3h70Hp9ADQ==, tarball: https://registry.npmjs.org/@mary-ext/event-iterator/-/event-iterator-1.0.0.tgz} 893 938 ··· 1201 1246 resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==, tarball: https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz} 1202 1247 engines: {node: '>= 10'} 1203 1248 1249 + '@tailwindcss/typography@0.5.19': 1250 + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==, tarball: https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz} 1251 + peerDependencies: 1252 + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' 1253 + 1204 1254 '@tailwindcss/vite@4.1.18': 1205 1255 resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==, tarball: https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz} 1206 1256 peerDependencies: ··· 1276 1326 '@types/estree@1.0.8': 1277 1327 resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, tarball: https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz} 1278 1328 1329 + '@types/geojson@7946.0.16': 1330 + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==, tarball: https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz} 1331 + 1279 1332 '@types/json-schema@7.0.15': 1280 1333 resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, tarball: https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz} 1281 1334 1282 1335 '@types/node@25.0.10': 1283 1336 resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==, tarball: https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz} 1337 + 1338 + '@types/supercluster@7.1.3': 1339 + resolution: {integrity: sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==, tarball: https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz} 1284 1340 1285 1341 '@types/trusted-types@2.0.7': 1286 1342 resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, tarball: https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz} ··· 1519 1575 1520 1576 dompurify@3.3.3: 1521 1577 resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==, tarball: https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz} 1578 + 1579 + earcut@3.0.2: 1580 + resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==, tarball: https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz} 1522 1581 1523 1582 emoji-picker-element@1.29.0: 1524 1583 resolution: {integrity: sha512-lQm8YayfwIP5j+Xe1O2Fjul7hv2b4spPS16X99O4qKQdzDQaEeiAqqYaRONHncewcpisUf6qGFJkhM2G3riEdA==, tarball: https://registry.npmjs.org/emoji-picker-element/-/emoji-picker-element-1.29.0.tgz} ··· 1664 1723 get-tsconfig@4.13.6: 1665 1724 resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==, tarball: https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz} 1666 1725 1726 + gl-matrix@3.4.4: 1727 + resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==, tarball: https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz} 1728 + 1667 1729 glob-parent@6.0.2: 1668 1730 resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, tarball: https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz} 1669 1731 engines: {node: '>=10.13.0'} ··· 1750 1812 json-stable-stringify-without-jsonify@1.0.1: 1751 1813 resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, tarball: https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz} 1752 1814 1815 + json-stringify-pretty-compact@4.0.0: 1816 + resolution: {integrity: sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==, tarball: https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz} 1817 + 1818 + kdbush@4.0.2: 1819 + resolution: {integrity: sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==, tarball: https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz} 1820 + 1753 1821 keyv@4.5.4: 1754 1822 resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, tarball: https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz} 1755 1823 ··· 1864 1932 magic-string@0.30.21: 1865 1933 resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, tarball: https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz} 1866 1934 1935 + maplibre-gl@5.20.2: 1936 + resolution: {integrity: sha512-0UzMWOe+GZmIUmOA99yTI1vRh15YcGnHxADVB2s+JF3etpjj2/MBCqbPEuu4BP9mLsJWJcpHH0Nzr9uuimmbuQ==, tarball: https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.20.2.tgz} 1937 + engines: {node: '>=16.14.0', npm: '>=8.1.0'} 1938 + 1867 1939 marked@17.0.4: 1868 1940 resolution: {integrity: sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==, tarball: https://registry.npmjs.org/marked/-/marked-17.0.4.tgz} 1869 1941 engines: {node: '>= 20'} ··· 1885 1957 resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==, tarball: https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz} 1886 1958 engines: {node: '>=16 || 14 >=14.17'} 1887 1959 1960 + minimist@1.2.8: 1961 + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, tarball: https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz} 1962 + 1888 1963 mlly@1.8.1: 1889 1964 resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==, tarball: https://registry.npmjs.org/mlly/-/mlly-1.8.1.tgz} 1890 1965 ··· 1906 1981 1907 1982 multiformats@9.9.0: 1908 1983 resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==, tarball: https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz} 1984 + 1985 + murmurhash-js@1.0.0: 1986 + resolution: {integrity: sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==, tarball: https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz} 1909 1987 1910 1988 nanoid@3.3.11: 1911 1989 resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, tarball: https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz} ··· 1974 2052 pathe@2.0.3: 1975 2053 resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, tarball: https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz} 1976 2054 2055 + pbf@4.0.1: 2056 + resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==, tarball: https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz} 2057 + hasBin: true 2058 + 1977 2059 picocolors@1.1.1: 1978 2060 resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, tarball: https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz} 1979 2061 ··· 2013 2095 engines: {node: '>=12.0'} 2014 2096 peerDependencies: 2015 2097 postcss: ^8.4.29 2098 + 2099 + postcss-selector-parser@6.0.10: 2100 + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, tarball: https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz} 2101 + engines: {node: '>=4'} 2016 2102 2017 2103 postcss-selector-parser@7.1.1: 2018 2104 resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==, tarball: https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz} ··· 2025 2111 resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, tarball: https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz} 2026 2112 engines: {node: ^10 || ^12 || >=14} 2027 2113 2114 + potpack@2.1.0: 2115 + resolution: {integrity: sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==, tarball: https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz} 2116 + 2028 2117 prelude-ls@1.2.1: 2029 2118 resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, tarball: https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz} 2030 2119 engines: {node: '>= 0.8.0'} ··· 2095 2184 engines: {node: '>=14'} 2096 2185 hasBin: true 2097 2186 2187 + protocol-buffers-schema@3.6.0: 2188 + resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==, tarball: https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz} 2189 + 2098 2190 punycode@2.3.1: 2099 2191 resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, tarball: https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz} 2100 2192 engines: {node: '>=6'} 2193 + 2194 + quickselect@3.0.0: 2195 + resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==, tarball: https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz} 2101 2196 2102 2197 rangetouch@2.0.1: 2103 2198 resolution: {integrity: sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==, tarball: https://registry.npmjs.org/rangetouch/-/rangetouch-2.0.1.tgz} ··· 2117 2212 resolve-pkg-maps@1.0.0: 2118 2213 resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, tarball: https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz} 2119 2214 2215 + resolve-protobuf-schema@2.1.0: 2216 + resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==, tarball: https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz} 2217 + 2120 2218 rollup@4.56.0: 2121 2219 resolution: {integrity: sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==, tarball: https://registry.npmjs.org/rollup/-/rollup-4.56.0.tgz} 2122 2220 engines: {node: '>=18.0.0', npm: '>=8.0.0'} ··· 2146 2244 '@sveltejs/kit': 2147 2245 optional: true 2148 2246 2247 + rw@1.3.3: 2248 + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==, tarball: https://registry.npmjs.org/rw/-/rw-1.3.3.tgz} 2249 + 2149 2250 sade@1.8.1: 2150 2251 resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==, tarball: https://registry.npmjs.org/sade/-/sade-1.8.1.tgz} 2151 2252 engines: {node: '>=6'} ··· 2198 2299 style-to-object@1.0.14: 2199 2300 resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==, tarball: https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz} 2200 2301 2302 + supercluster@8.0.1: 2303 + resolution: {integrity: sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==, tarball: https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz} 2304 + 2201 2305 supports-color@10.2.2: 2202 2306 resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==, tarball: https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz} 2203 2307 engines: {node: '>=18'} ··· 2226 2330 svelte: 2227 2331 optional: true 2228 2332 2333 + svelte-maplibre-gl@1.0.3: 2334 + resolution: {integrity: sha512-pGZMLN5EcJoe6lREyaY2lsbkvrnKjIGHJRM2ijN+TUDeJbV9oecjBxbDXQZd8b5X4JJztSG915L731dFdhhSwA==, tarball: https://registry.npmjs.org/svelte-maplibre-gl/-/svelte-maplibre-gl-1.0.3.tgz} 2335 + peerDependencies: 2336 + maplibre-gl: ^5.0.0 || ^4.0.0 2337 + svelte: '>=5.0.0' 2338 + 2229 2339 svelte-sonner@1.0.7: 2230 2340 resolution: {integrity: sha512-1EUFYmd7q/xfs2qCHwJzGPh9n5VJ3X6QjBN10fof2vxgy8fYE7kVfZ7uGnd7i6fQaWIr5KvXcwYXE/cmTEjk5A==, tarball: https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.0.7.tgz} 2231 2341 peerDependencies: ··· 2279 2389 tinyglobby@0.2.15: 2280 2390 resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, tarball: https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz} 2281 2391 engines: {node: '>=12.0.0'} 2392 + 2393 + tinyqueue@3.0.0: 2394 + resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==, tarball: https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz} 2282 2395 2283 2396 tlds@1.261.0: 2284 2397 resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==, tarball: https://registry.npmjs.org/tlds/-/tlds-1.261.0.tgz} ··· 3168 3281 '@jridgewell/resolve-uri': 3.1.2 3169 3282 '@jridgewell/sourcemap-codec': 1.5.5 3170 3283 3284 + '@mapbox/jsonlint-lines-primitives@2.0.2': {} 3285 + 3286 + '@mapbox/point-geometry@1.1.0': {} 3287 + 3288 + '@mapbox/tiny-sdf@2.0.7': {} 3289 + 3290 + '@mapbox/unitbezier@0.0.1': {} 3291 + 3292 + '@mapbox/vector-tile@2.0.4': 3293 + dependencies: 3294 + '@mapbox/point-geometry': 1.1.0 3295 + '@types/geojson': 7946.0.16 3296 + pbf: 4.0.1 3297 + 3298 + '@mapbox/whoots-js@3.1.0': {} 3299 + 3300 + '@maplibre/geojson-vt@5.0.4': {} 3301 + 3302 + '@maplibre/geojson-vt@6.0.3': 3303 + dependencies: 3304 + kdbush: 4.0.2 3305 + 3306 + '@maplibre/maplibre-gl-style-spec@24.7.0': 3307 + dependencies: 3308 + '@mapbox/jsonlint-lines-primitives': 2.0.2 3309 + '@mapbox/unitbezier': 0.0.1 3310 + json-stringify-pretty-compact: 4.0.0 3311 + minimist: 1.2.8 3312 + quickselect: 3.0.0 3313 + rw: 1.3.3 3314 + tinyqueue: 3.0.0 3315 + 3316 + '@maplibre/mlt@1.1.7': 3317 + dependencies: 3318 + '@mapbox/point-geometry': 1.1.0 3319 + 3320 + '@maplibre/vt-pbf@4.3.0': 3321 + dependencies: 3322 + '@mapbox/point-geometry': 1.1.0 3323 + '@mapbox/vector-tile': 2.0.4 3324 + '@maplibre/geojson-vt': 5.0.4 3325 + '@types/geojson': 7946.0.16 3326 + '@types/supercluster': 7.1.3 3327 + pbf: 4.0.1 3328 + supercluster: 8.0.1 3329 + 3171 3330 '@mary-ext/event-iterator@1.0.0': 3172 3331 dependencies: 3173 3332 yocto-queue: 1.2.2 ··· 3409 3568 '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 3410 3569 '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 3411 3570 3571 + '@tailwindcss/typography@0.5.19(tailwindcss@4.2.1)': 3572 + dependencies: 3573 + postcss-selector-parser: 6.0.10 3574 + tailwindcss: 4.2.1 3575 + 3412 3576 '@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': 3413 3577 dependencies: 3414 3578 '@tailwindcss/node': 4.1.18 ··· 3469 3633 3470 3634 '@types/estree@1.0.8': {} 3471 3635 3636 + '@types/geojson@7946.0.16': {} 3637 + 3472 3638 '@types/json-schema@7.0.15': {} 3473 3639 3474 3640 '@types/node@25.0.10': 3475 3641 dependencies: 3476 3642 undici-types: 7.16.0 3643 + 3644 + '@types/supercluster@7.1.3': 3645 + dependencies: 3646 + '@types/geojson': 7946.0.16 3477 3647 3478 3648 '@types/trusted-types@2.0.7': 3479 3649 optional: true ··· 3720 3890 optionalDependencies: 3721 3891 '@types/trusted-types': 2.0.7 3722 3892 3893 + earcut@3.0.2: {} 3894 + 3723 3895 emoji-picker-element@1.29.0: {} 3724 3896 3725 3897 emoji-regex@10.6.0: {} ··· 3928 4100 dependencies: 3929 4101 resolve-pkg-maps: 1.0.0 3930 4102 4103 + gl-matrix@3.4.4: {} 4104 + 3931 4105 glob-parent@6.0.2: 3932 4106 dependencies: 3933 4107 is-glob: 4.0.3 ··· 3987 4161 3988 4162 json-stable-stringify-without-jsonify@1.0.1: {} 3989 4163 4164 + json-stringify-pretty-compact@4.0.0: {} 4165 + 4166 + kdbush@4.0.2: {} 4167 + 3990 4168 keyv@4.5.4: 3991 4169 dependencies: 3992 4170 json-buffer: 3.0.1 ··· 4074 4252 dependencies: 4075 4253 '@jridgewell/sourcemap-codec': 1.5.5 4076 4254 4255 + maplibre-gl@5.20.2: 4256 + dependencies: 4257 + '@mapbox/jsonlint-lines-primitives': 2.0.2 4258 + '@mapbox/point-geometry': 1.1.0 4259 + '@mapbox/tiny-sdf': 2.0.7 4260 + '@mapbox/unitbezier': 0.0.1 4261 + '@mapbox/vector-tile': 2.0.4 4262 + '@mapbox/whoots-js': 3.1.0 4263 + '@maplibre/geojson-vt': 6.0.3 4264 + '@maplibre/maplibre-gl-style-spec': 24.7.0 4265 + '@maplibre/mlt': 1.1.7 4266 + '@maplibre/vt-pbf': 4.3.0 4267 + '@types/geojson': 7946.0.16 4268 + earcut: 3.0.2 4269 + gl-matrix: 3.4.4 4270 + kdbush: 4.0.2 4271 + murmurhash-js: 1.0.0 4272 + pbf: 4.0.1 4273 + potpack: 2.1.0 4274 + quickselect: 3.0.0 4275 + tinyqueue: 3.0.0 4276 + 4077 4277 marked@17.0.4: {} 4078 4278 4079 4279 mini-svg-data-uri@1.4.4: {} ··· 4098 4298 dependencies: 4099 4299 brace-expansion: 2.0.2 4100 4300 4301 + minimist@1.2.8: {} 4302 + 4101 4303 mlly@1.8.1: 4102 4304 dependencies: 4103 4305 acorn: 8.16.0 ··· 4118 4320 ms@2.1.3: {} 4119 4321 4120 4322 multiformats@9.9.0: {} 4323 + 4324 + murmurhash-js@1.0.0: {} 4121 4325 4122 4326 nanoid@3.3.11: {} 4123 4327 ··· 4173 4377 4174 4378 pathe@2.0.3: {} 4175 4379 4380 + pbf@4.0.1: 4381 + dependencies: 4382 + resolve-protobuf-schema: 2.1.0 4383 + 4176 4384 picocolors@1.1.1: {} 4177 4385 4178 4386 picomatch@4.0.3: {} ··· 4212 4420 dependencies: 4213 4421 postcss: 8.5.6 4214 4422 4423 + postcss-selector-parser@6.0.10: 4424 + dependencies: 4425 + cssesc: 3.0.0 4426 + util-deprecate: 1.0.2 4427 + 4215 4428 postcss-selector-parser@7.1.1: 4216 4429 dependencies: 4217 4430 cssesc: 3.0.0 ··· 4224 4437 nanoid: 3.3.11 4225 4438 picocolors: 1.1.1 4226 4439 source-map-js: 1.2.1 4440 + 4441 + potpack@2.1.0: {} 4227 4442 4228 4443 prelude-ls@1.2.1: {} 4229 4444 ··· 4240 4455 4241 4456 prettier@3.8.1: {} 4242 4457 4458 + protocol-buffers-schema@3.6.0: {} 4459 + 4243 4460 punycode@2.3.1: {} 4461 + 4462 + quickselect@3.0.0: {} 4244 4463 4245 4464 rangetouch@2.0.1: {} 4246 4465 ··· 4252 4471 4253 4472 resolve-pkg-maps@1.0.0: {} 4254 4473 4474 + resolve-protobuf-schema@2.1.0: 4475 + dependencies: 4476 + protocol-buffers-schema: 3.6.0 4477 + 4255 4478 rollup@4.56.0: 4256 4479 dependencies: 4257 4480 '@types/estree': 1.0.8 ··· 4306 4529 svelte: 5.48.0 4307 4530 optionalDependencies: 4308 4531 '@sveltejs/kit': 2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.0)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)))(svelte@5.48.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) 4532 + 4533 + rw@1.3.3: {} 4309 4534 4310 4535 sade@1.8.1: 4311 4536 dependencies: ··· 4386 4611 style-to-object@1.0.14: 4387 4612 dependencies: 4388 4613 inline-style-parser: 0.2.7 4614 + 4615 + supercluster@8.0.1: 4616 + dependencies: 4617 + kdbush: 4.0.2 4389 4618 4390 4619 supports-color@10.2.2: {} 4391 4620 ··· 4418 4647 optionalDependencies: 4419 4648 svelte: 5.48.0 4420 4649 4650 + svelte-maplibre-gl@1.0.3(maplibre-gl@5.20.2)(svelte@5.48.0): 4651 + dependencies: 4652 + maplibre-gl: 5.20.2 4653 + svelte: 5.48.0 4654 + 4421 4655 svelte-sonner@1.0.7(svelte@5.48.0): 4422 4656 dependencies: 4423 4657 runed: 0.28.0(svelte@5.48.0) ··· 4479 4713 dependencies: 4480 4714 fdir: 6.5.0(picomatch@4.0.3) 4481 4715 picomatch: 4.0.3 4716 + 4717 + tinyqueue@3.0.0: {} 4482 4718 4483 4719 tlds@1.261.0: {} 4484 4720
+23 -22
src/app.css
··· 1 1 @import 'tailwindcss'; 2 2 3 3 @plugin '@tailwindcss/forms'; 4 + @plugin '@tailwindcss/typography'; 4 5 @source "../node_modules/@foxui"; 5 6 6 7 ··· 32 33 33 34 @layer base { 34 35 :root { 35 - --accent-50: var(--color-fuchsia-50); 36 - --accent-100: var(--color-fuchsia-100); 37 - --accent-200: var(--color-fuchsia-200); 38 - --accent-300: var(--color-fuchsia-300); 39 - --accent-400: var(--color-fuchsia-400); 40 - --accent-500: var(--color-fuchsia-500); 41 - --accent-600: var(--color-fuchsia-600); 42 - --accent-700: var(--color-fuchsia-700); 43 - --accent-800: var(--color-fuchsia-800); 44 - --accent-900: var(--color-fuchsia-900); 45 - --accent-950: var(--color-fuchsia-950); 36 + --accent-50: var(--color-teal-50); 37 + --accent-100: var(--color-teal-100); 38 + --accent-200: var(--color-teal-200); 39 + --accent-300: var(--color-teal-300); 40 + --accent-400: var(--color-teal-400); 41 + --accent-500: var(--color-teal-500); 42 + --accent-600: var(--color-teal-600); 43 + --accent-700: var(--color-teal-700); 44 + --accent-800: var(--color-teal-800); 45 + --accent-900: var(--color-teal-900); 46 + --accent-950: var(--color-teal-950); 46 47 47 - --base-50: var(--color-mauve-50); 48 - --base-100: var(--color-mauve-100); 49 - --base-200: var(--color-mauve-200); 50 - --base-300: var(--color-mauve-300); 51 - --base-400: var(--color-mauve-400); 52 - --base-500: var(--color-mauve-500); 53 - --base-600: var(--color-mauve-600); 54 - --base-700: var(--color-mauve-700); 55 - --base-800: var(--color-mauve-800); 56 - --base-900: var(--color-mauve-900); 57 - --base-950: var(--color-mauve-950); 48 + --base-50: var(--color-mist-50); 49 + --base-100: var(--color-mist-100); 50 + --base-200: var(--color-mist-200); 51 + --base-300: var(--color-mist-300); 52 + --base-400: var(--color-mist-400); 53 + --base-500: var(--color-mist-500); 54 + --base-600: var(--color-mist-600); 55 + --base-700: var(--color-mist-700); 56 + --base-800: var(--color-mist-800); 57 + --base-900: var(--color-mist-900); 58 + --base-950: var(--color-mist-950); 58 59 } 59 60 }
+17 -25
src/lexicon-types/index.ts
··· 1 - export * as AppBskyActorProfile from "./types/app/bsky/actor/profile.js"; 2 - export * as ComExampleAdminGetCursor from "./types/com/example/admin/getCursor.js"; 3 - export * as ComExampleAdminGetOverview from "./types/com/example/admin/getOverview.js"; 4 - export * as ComExampleAdminReset from "./types/com/example/admin/reset.js"; 5 - export * as ComExampleAdminSync from "./types/com/example/admin/sync.js"; 6 - export * as ComExampleGetProfile from "./types/com/example/getProfile.js"; 7 - export * as CommunityLexiconCalendarEvent from "./types/community/lexicon/calendar/event.js"; 8 - export * as CommunityLexiconCalendarEventGetRecord from "./types/community/lexicon/calendar/event/getRecord.js"; 9 - export * as CommunityLexiconCalendarEventListRecords from "./types/community/lexicon/calendar/event/listRecords.js"; 10 - export * as CommunityLexiconCalendarRsvp from "./types/community/lexicon/calendar/rsvp.js"; 11 - export * as CommunityLexiconCalendarRsvpGetRecord from "./types/community/lexicon/calendar/rsvp/getRecord.js"; 12 - export * as CommunityLexiconCalendarRsvpListRecords from "./types/community/lexicon/calendar/rsvp/listRecords.js"; 13 - export * as CommunityLexiconLocationAddress from "./types/community/lexicon/location/address.js"; 14 - export * as CommunityLexiconLocationFsq from "./types/community/lexicon/location/fsq.js"; 15 - export * as CommunityLexiconLocationGeo from "./types/community/lexicon/location/geo.js"; 16 - export * as CommunityLexiconLocationHthree from "./types/community/lexicon/location/hthree.js"; 17 - export * as ContrailAdminGetCursor from "./types/contrail/admin/getCursor.js"; 18 - export * as ContrailAdminGetOverview from "./types/contrail/admin/getOverview.js"; 19 - export * as ContrailAdminReset from "./types/contrail/admin/reset.js"; 20 - export * as ContrailAdminSync from "./types/contrail/admin/sync.js"; 21 - export * as RsvpAtmoAdminGetCursor from "./types/rsvp/atmo/admin/getCursor.js"; 22 - export * as RsvpAtmoAdminGetOverview from "./types/rsvp/atmo/admin/getOverview.js"; 23 - export * as RsvpAtmoAdminReset from "./types/rsvp/atmo/admin/reset.js"; 24 - export * as RsvpAtmoAdminSync from "./types/rsvp/atmo/admin/sync.js"; 25 - export * as RsvpAtmoGetProfile from "./types/rsvp/atmo/getProfile.js"; 1 + export * as AppBskyActorProfile from './types/app/bsky/actor/profile.js'; 2 + export * as CommunityLexiconCalendarEvent from './types/community/lexicon/calendar/event.js'; 3 + export * as CommunityLexiconCalendarEventGetRecord from './types/community/lexicon/calendar/event/getRecord.js'; 4 + export * as CommunityLexiconCalendarEventListRecords from './types/community/lexicon/calendar/event/listRecords.js'; 5 + export * as CommunityLexiconCalendarRsvp from './types/community/lexicon/calendar/rsvp.js'; 6 + export * as CommunityLexiconCalendarRsvpGetRecord from './types/community/lexicon/calendar/rsvp/getRecord.js'; 7 + export * as CommunityLexiconCalendarRsvpListRecords from './types/community/lexicon/calendar/rsvp/listRecords.js'; 8 + export * as CommunityLexiconLocationAddress from './types/community/lexicon/location/address.js'; 9 + export * as CommunityLexiconLocationFsq from './types/community/lexicon/location/fsq.js'; 10 + export * as CommunityLexiconLocationGeo from './types/community/lexicon/location/geo.js'; 11 + export * as CommunityLexiconLocationHthree from './types/community/lexicon/location/hthree.js'; 12 + export * as RsvpAtmoAdminGetCursor from './types/rsvp/atmo/admin/getCursor.js'; 13 + export * as RsvpAtmoAdminGetOverview from './types/rsvp/atmo/admin/getOverview.js'; 14 + export * as RsvpAtmoAdminReset from './types/rsvp/atmo/admin/reset.js'; 15 + export * as RsvpAtmoAdminSync from './types/rsvp/atmo/admin/sync.js'; 16 + export * as RsvpAtmoGetProfile from './types/rsvp/atmo/getProfile.js'; 17 + export * as RsvpAtmoNotifyOfUpdate from './types/rsvp/atmo/notifyOfUpdate.js';
-30
src/lexicon-types/types/com/example/admin/getCursor.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _mainSchema = /*#__PURE__*/ v.query("com.example.admin.getCursor", { 6 - params: null, 7 - output: { 8 - type: "lex", 9 - schema: /*#__PURE__*/ v.object({ 10 - date: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 11 - seconds_ago: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 12 - time_us: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 13 - }), 14 - }, 15 - }); 16 - 17 - type main$schematype = typeof _mainSchema; 18 - 19 - export interface mainSchema extends main$schematype {} 20 - 21 - export const mainSchema = _mainSchema as mainSchema; 22 - 23 - export interface $params {} 24 - export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 25 - 26 - declare module "@atcute/lexicons/ambient" { 27 - interface XRPCQueries { 28 - "com.example.admin.getCursor": mainSchema; 29 - } 30 - }
-47
src/lexicon-types/types/com/example/admin/getOverview.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _collectionStatsSchema = /*#__PURE__*/ v.object({ 6 - $type: /*#__PURE__*/ v.optional( 7 - /*#__PURE__*/ v.literal("com.example.admin.getOverview#collectionStats"), 8 - ), 9 - collection: /*#__PURE__*/ v.string(), 10 - records: /*#__PURE__*/ v.integer(), 11 - unique_users: /*#__PURE__*/ v.integer(), 12 - }); 13 - const _mainSchema = /*#__PURE__*/ v.query("com.example.admin.getOverview", { 14 - params: null, 15 - output: { 16 - type: "lex", 17 - schema: /*#__PURE__*/ v.object({ 18 - get collections() { 19 - return /*#__PURE__*/ v.array(collectionStatsSchema); 20 - }, 21 - total_records: /*#__PURE__*/ v.integer(), 22 - }), 23 - }, 24 - }); 25 - 26 - type collectionStats$schematype = typeof _collectionStatsSchema; 27 - type main$schematype = typeof _mainSchema; 28 - 29 - export interface collectionStatsSchema extends collectionStats$schematype {} 30 - export interface mainSchema extends main$schematype {} 31 - 32 - export const collectionStatsSchema = 33 - _collectionStatsSchema as collectionStatsSchema; 34 - export const mainSchema = _mainSchema as mainSchema; 35 - 36 - export interface CollectionStats extends v.InferInput< 37 - typeof collectionStatsSchema 38 - > {} 39 - 40 - export interface $params {} 41 - export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 42 - 43 - declare module "@atcute/lexicons/ambient" { 44 - interface XRPCQueries { 45 - "com.example.admin.getOverview": mainSchema; 46 - } 47 - }
-28
src/lexicon-types/types/com/example/admin/reset.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _mainSchema = /*#__PURE__*/ v.query("com.example.admin.reset", { 6 - params: null, 7 - output: { 8 - type: "lex", 9 - schema: /*#__PURE__*/ v.object({ 10 - ok: /*#__PURE__*/ v.boolean(), 11 - }), 12 - }, 13 - }); 14 - 15 - type main$schematype = typeof _mainSchema; 16 - 17 - export interface mainSchema extends main$schematype {} 18 - 19 - export const mainSchema = _mainSchema as mainSchema; 20 - 21 - export interface $params {} 22 - export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 23 - 24 - declare module "@atcute/lexicons/ambient" { 25 - interface XRPCQueries { 26 - "com.example.admin.reset": mainSchema; 27 - } 28 - }
-43
src/lexicon-types/types/com/example/admin/sync.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _mainSchema = /*#__PURE__*/ v.query("com.example.admin.sync", { 6 - params: /*#__PURE__*/ v.object({ 7 - /** 8 - * @minimum 1 9 - * @maximum 50 10 - * @default 10 11 - */ 12 - concurrency: /*#__PURE__*/ v.optional( 13 - /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 14 - /*#__PURE__*/ v.integerRange(1, 50), 15 - ]), 16 - 10, 17 - ), 18 - }), 19 - output: { 20 - type: "lex", 21 - schema: /*#__PURE__*/ v.object({ 22 - backfilled: /*#__PURE__*/ v.integer(), 23 - discovered: /*#__PURE__*/ v.integer(), 24 - done: /*#__PURE__*/ v.boolean(), 25 - remaining: /*#__PURE__*/ v.integer(), 26 - }), 27 - }, 28 - }); 29 - 30 - type main$schematype = typeof _mainSchema; 31 - 32 - export interface mainSchema extends main$schematype {} 33 - 34 - export const mainSchema = _mainSchema as mainSchema; 35 - 36 - export interface $params extends v.InferInput<mainSchema["params"]> {} 37 - export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 38 - 39 - declare module "@atcute/lexicons/ambient" { 40 - interface XRPCQueries { 41 - "com.example.admin.sync": mainSchema; 42 - } 43 - }
-126
src/lexicon-types/types/com/example/getProfile.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 - import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 - 7 - const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 8 - $type: /*#__PURE__*/ v.optional( 9 - /*#__PURE__*/ v.literal("com.example.getProfile#appBskyActorProfile"), 10 - ), 11 - /** 12 - * Small image to be displayed next to posts from account. AKA, 'profile picture' 13 - * @accept image/png, image/jpeg 14 - * @maxSize 1000000 15 - */ 16 - avatar: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.blob()), 17 - /** 18 - * Larger horizontal image to display behind profile view. 19 - * @accept image/png, image/jpeg 20 - * @maxSize 1000000 21 - */ 22 - banner: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.blob()), 23 - createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 24 - /** 25 - * Free-form profile description text. 26 - * @maxLength 2560 27 - * @maxGraphemes 256 28 - */ 29 - description: /*#__PURE__*/ v.optional( 30 - /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 31 - /*#__PURE__*/ v.stringLength(0, 2560), 32 - /*#__PURE__*/ v.stringGraphemes(0, 256), 33 - ]), 34 - ), 35 - /** 36 - * @maxLength 640 37 - * @maxGraphemes 64 38 - */ 39 - displayName: /*#__PURE__*/ v.optional( 40 - /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 41 - /*#__PURE__*/ v.stringLength(0, 640), 42 - /*#__PURE__*/ v.stringGraphemes(0, 64), 43 - ]), 44 - ), 45 - get joinedViaStarterPack() { 46 - return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 47 - }, 48 - /** 49 - * Self-label values, specific to the Bluesky application, on the overall account. 50 - */ 51 - get labels() { 52 - return /*#__PURE__*/ v.optional( 53 - /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 54 - ); 55 - }, 56 - get pinnedPost() { 57 - return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 58 - }, 59 - /** 60 - * Free-form pronouns text. 61 - * @maxLength 200 62 - * @maxGraphemes 20 63 - */ 64 - pronouns: /*#__PURE__*/ v.optional( 65 - /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 66 - /*#__PURE__*/ v.stringLength(0, 200), 67 - /*#__PURE__*/ v.stringGraphemes(0, 20), 68 - ]), 69 - ), 70 - website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 71 - }); 72 - const _mainSchema = /*#__PURE__*/ v.query("com.example.getProfile", { 73 - params: /*#__PURE__*/ v.object({ 74 - /** 75 - * DID or handle of the user 76 - */ 77 - actor: /*#__PURE__*/ v.actorIdentifierString(), 78 - }), 79 - output: { 80 - type: "lex", 81 - get schema() { 82 - return profileEntrySchema; 83 - }, 84 - }, 85 - }); 86 - const _profileEntrySchema = /*#__PURE__*/ v.object({ 87 - $type: /*#__PURE__*/ v.optional( 88 - /*#__PURE__*/ v.literal("com.example.getProfile#profileEntry"), 89 - ), 90 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 91 - collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 92 - did: /*#__PURE__*/ v.didString(), 93 - handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 94 - get record() { 95 - return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 96 - }, 97 - rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 98 - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 99 - }); 100 - 101 - type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 102 - type main$schematype = typeof _mainSchema; 103 - type profileEntry$schematype = typeof _profileEntrySchema; 104 - 105 - export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 106 - export interface mainSchema extends main$schematype {} 107 - export interface profileEntrySchema extends profileEntry$schematype {} 108 - 109 - export const appBskyActorProfileSchema = 110 - _appBskyActorProfileSchema as appBskyActorProfileSchema; 111 - export const mainSchema = _mainSchema as mainSchema; 112 - export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 113 - 114 - export interface AppBskyActorProfile extends v.InferInput< 115 - typeof appBskyActorProfileSchema 116 - > {} 117 - export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 118 - 119 - export interface $params extends v.InferInput<mainSchema["params"]> {} 120 - export type $output = v.InferXRPCBodyInput<mainSchema["output"]>; 121 - 122 - declare module "@atcute/lexicons/ambient" { 123 - interface XRPCQueries { 124 - "com.example.getProfile": mainSchema; 125 - } 126 - }
+4
src/lexicon-types/types/community/lexicon/calendar/event/listRecords.ts
··· 205 205 /*#__PURE__*/ v.integer(), 206 206 ), 207 207 /** 208 + * Full-text search across: mode, name, status, description 209 + */ 210 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 211 + /** 208 212 * Field to sort by (default: time_us) 209 213 */ 210 214 sort: /*#__PURE__*/ v.optional(
+4
src/lexicon-types/types/community/lexicon/calendar/rsvp/listRecords.ts
··· 108 108 */ 109 109 profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 110 110 /** 111 + * Full-text search across: status, subject.uri 112 + */ 113 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 114 + /** 111 115 * Field to sort by (default: time_us) 112 116 */ 113 117 sort: /*#__PURE__*/ v.optional(
-30
src/lexicon-types/types/contrail/admin/getCursor.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _mainSchema = /*#__PURE__*/ v.query("contrail.admin.getCursor", { 6 - params: null, 7 - output: { 8 - type: "lex", 9 - schema: /*#__PURE__*/ v.object({ 10 - date: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 11 - seconds_ago: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 12 - time_us: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 13 - }), 14 - }, 15 - }); 16 - 17 - type main$schematype = typeof _mainSchema; 18 - 19 - export interface mainSchema extends main$schematype {} 20 - 21 - export const mainSchema = _mainSchema as mainSchema; 22 - 23 - export interface $params {} 24 - export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 25 - 26 - declare module "@atcute/lexicons/ambient" { 27 - interface XRPCQueries { 28 - "contrail.admin.getCursor": mainSchema; 29 - } 30 - }
-47
src/lexicon-types/types/contrail/admin/getOverview.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _collectionStatsSchema = /*#__PURE__*/ v.object({ 6 - $type: /*#__PURE__*/ v.optional( 7 - /*#__PURE__*/ v.literal("contrail.admin.getOverview#collectionStats"), 8 - ), 9 - collection: /*#__PURE__*/ v.string(), 10 - records: /*#__PURE__*/ v.integer(), 11 - unique_users: /*#__PURE__*/ v.integer(), 12 - }); 13 - const _mainSchema = /*#__PURE__*/ v.query("contrail.admin.getOverview", { 14 - params: null, 15 - output: { 16 - type: "lex", 17 - schema: /*#__PURE__*/ v.object({ 18 - get collections() { 19 - return /*#__PURE__*/ v.array(collectionStatsSchema); 20 - }, 21 - total_records: /*#__PURE__*/ v.integer(), 22 - }), 23 - }, 24 - }); 25 - 26 - type collectionStats$schematype = typeof _collectionStatsSchema; 27 - type main$schematype = typeof _mainSchema; 28 - 29 - export interface collectionStatsSchema extends collectionStats$schematype {} 30 - export interface mainSchema extends main$schematype {} 31 - 32 - export const collectionStatsSchema = 33 - _collectionStatsSchema as collectionStatsSchema; 34 - export const mainSchema = _mainSchema as mainSchema; 35 - 36 - export interface CollectionStats extends v.InferInput< 37 - typeof collectionStatsSchema 38 - > {} 39 - 40 - export interface $params {} 41 - export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 42 - 43 - declare module "@atcute/lexicons/ambient" { 44 - interface XRPCQueries { 45 - "contrail.admin.getOverview": mainSchema; 46 - } 47 - }
-28
src/lexicon-types/types/contrail/admin/reset.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _mainSchema = /*#__PURE__*/ v.query("contrail.admin.reset", { 6 - params: null, 7 - output: { 8 - type: "lex", 9 - schema: /*#__PURE__*/ v.object({ 10 - ok: /*#__PURE__*/ v.boolean(), 11 - }), 12 - }, 13 - }); 14 - 15 - type main$schematype = typeof _mainSchema; 16 - 17 - export interface mainSchema extends main$schematype {} 18 - 19 - export const mainSchema = _mainSchema as mainSchema; 20 - 21 - export interface $params {} 22 - export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 23 - 24 - declare module "@atcute/lexicons/ambient" { 25 - interface XRPCQueries { 26 - "contrail.admin.reset": mainSchema; 27 - } 28 - }
-43
src/lexicon-types/types/contrail/admin/sync.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _mainSchema = /*#__PURE__*/ v.query("contrail.admin.sync", { 6 - params: /*#__PURE__*/ v.object({ 7 - /** 8 - * @minimum 1 9 - * @maximum 50 10 - * @default 10 11 - */ 12 - concurrency: /*#__PURE__*/ v.optional( 13 - /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 14 - /*#__PURE__*/ v.integerRange(1, 50), 15 - ]), 16 - 10, 17 - ), 18 - }), 19 - output: { 20 - type: "lex", 21 - schema: /*#__PURE__*/ v.object({ 22 - backfilled: /*#__PURE__*/ v.integer(), 23 - discovered: /*#__PURE__*/ v.integer(), 24 - done: /*#__PURE__*/ v.boolean(), 25 - remaining: /*#__PURE__*/ v.integer(), 26 - }), 27 - }, 28 - }); 29 - 30 - type main$schematype = typeof _mainSchema; 31 - 32 - export interface mainSchema extends main$schematype {} 33 - 34 - export const mainSchema = _mainSchema as mainSchema; 35 - 36 - export interface $params extends v.InferInput<mainSchema["params"]> {} 37 - export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 38 - 39 - declare module "@atcute/lexicons/ambient" { 40 - interface XRPCQueries { 41 - "contrail.admin.sync": mainSchema; 42 - } 43 - }
+61
src/lexicon-types/types/rsvp/atmo/notifyOfUpdate.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + 5 + const _mainSchema = /*#__PURE__*/ v.procedure("rsvp.atmo.notifyOfUpdate", { 6 + params: null, 7 + input: { 8 + type: "lex", 9 + schema: /*#__PURE__*/ v.object({ 10 + /** 11 + * Single AT URI to fetch and index 12 + */ 13 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 14 + /** 15 + * Batch of AT URIs to fetch and index (max 25) 16 + * @maxLength 25 17 + */ 18 + uris: /*#__PURE__*/ v.optional( 19 + /*#__PURE__*/ v.constrain( 20 + /*#__PURE__*/ v.array(/*#__PURE__*/ v.resourceUriString()), 21 + [/*#__PURE__*/ v.arrayLength(0, 25)], 22 + ), 23 + ), 24 + }), 25 + }, 26 + output: { 27 + type: "lex", 28 + schema: /*#__PURE__*/ v.object({ 29 + /** 30 + * Number of records deleted (not found on PDS) 31 + */ 32 + deleted: /*#__PURE__*/ v.integer(), 33 + /** 34 + * Errors for individual URIs that could not be processed 35 + */ 36 + errors: /*#__PURE__*/ v.optional( 37 + /*#__PURE__*/ v.array(/*#__PURE__*/ v.string()), 38 + ), 39 + /** 40 + * Number of records created or updated 41 + */ 42 + indexed: /*#__PURE__*/ v.integer(), 43 + }), 44 + }, 45 + }); 46 + 47 + type main$schematype = typeof _mainSchema; 48 + 49 + export interface mainSchema extends main$schematype {} 50 + 51 + export const mainSchema = _mainSchema as mainSchema; 52 + 53 + export interface $params {} 54 + export interface $input extends v.InferXRPCBodyInput<mainSchema["input"]> {} 55 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 56 + 57 + declare module "@atcute/lexicons/ambient" { 58 + interface XRPCProcedures { 59 + "rsvp.atmo.notifyOfUpdate": mainSchema; 60 + } 61 + }
+4 -2
src/lib/atproto/methods.ts
··· 324 324 */ 325 325 export function getCDNImageBlobUrl({ 326 326 did, 327 - blob 327 + blob, 328 + format = 'webp' 328 329 }: { 329 330 did?: string; 330 331 blob: { ··· 333 334 $link: string; 334 335 }; 335 336 }; 337 + format?: 'webp' | 'jpeg' | 'png'; 336 338 }) { 337 339 did ??= user.did ?? undefined; 338 340 339 - return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${blob.ref.$link}@webp`; 341 + return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${blob.ref.$link}@${format}`; 340 342 } 341 343 342 344 /**
+21 -2
src/lib/components/EventEditor.svelte
··· 3 3 import { atProtoLoginModalState } from '@foxui/social'; 4 4 import { uploadBlob, putRecord, deleteRecord, resolveHandle } from '$lib/atproto/methods'; 5 5 import { getCDNImageBlobUrl } from '$lib/atproto'; 6 + import { notifyContrailOfUpdate } from '$lib/contrail'; 6 7 import { compressImage } from '$lib/atproto/image-helper'; 7 8 import { validateLink } from '$lib/cal/helper'; 8 9 import { ··· 489 490 if (isNew || thumbnailChanged) { 490 491 if (thumbnailFile) { 491 492 const compressed = await compressImage(thumbnailFile); 492 - const blobRef = await uploadBlob({ blob: compressed.blob }); 493 - if (blobRef) { 493 + const result = await uploadBlob({ blob: compressed.blob }); 494 + if (result) { 495 + const { aspectRatio: _ar, ...blobRef } = result as Record<string, unknown> & { aspectRatio?: unknown }; 494 496 media = [ 495 497 { 496 498 role: 'thumbnail', ··· 563 565 }); 564 566 565 567 if (response.ok) { 568 + const eventUri = `at://${user.did}/community.lexicon.calendar.event/${rkey}`; 569 + await notifyContrailOfUpdate(eventUri); 566 570 localStorage.removeItem(DRAFT_KEY); 567 571 if (thumbnailKey) deleteImage(thumbnailKey); 568 572 const handle = ··· 591 595 collection: 'community.lexicon.calendar.event', 592 596 rkey 593 597 }); 598 + const eventUri = `at://${user.did}/community.lexicon.calendar.event/${rkey}`; 599 + await notifyContrailOfUpdate(eventUri); 594 600 localStorage.removeItem(DRAFT_KEY); 595 601 if (thumbnailKey) deleteImage(thumbnailKey); 596 602 const handle = ··· 710 716 {/if} 711 717 </div> 712 718 </div> 719 + <Button 720 + type="submit" 721 + class="mt-3 w-full" 722 + disabled={submitting || !name.trim() || !startsAt} 723 + > 724 + {submitting 725 + ? isNew 726 + ? 'Creating...' 727 + : 'Saving...' 728 + : isNew 729 + ? 'Create Event' 730 + : 'Save Event'} 731 + </Button> 713 732 714 733 <!-- Right column: event details --> 715 734 <div class="order-2 min-w-0 md:order-0 md:col-start-2 md:row-span-5 md:row-start-1">
+128
src/lib/components/Map.svelte
··· 1 + <script lang="ts"> 2 + import { MapLibre, Projection, Marker } from 'svelte-maplibre-gl'; 3 + import type maplibregl from 'maplibre-gl'; 4 + 5 + let { 6 + lat, 7 + lng, 8 + zoom = 14 9 + }: { 10 + lat: number; 11 + lng: number; 12 + zoom?: number; 13 + } = $props(); 14 + 15 + let center = $state({ lng, lat }); 16 + let showAttribution = $state(false); 17 + let map: maplibregl.Map | undefined = $state(); 18 + 19 + const fixedCenter = { lng, lat }; 20 + 21 + function handleZoom() { 22 + if (map) { 23 + map.setCenter(fixedCenter); 24 + } 25 + } 26 + 27 + $effect(() => { 28 + if (map) { 29 + map.getCanvas().style.touchAction = 'pan-x pan-y'; 30 + } 31 + }); 32 + </script> 33 + 34 + <div 35 + class="relative isolate h-full w-full overflow-hidden rounded-xl" 36 + onfocusin={(e) => { 37 + if (e.target instanceof HTMLElement) e.target.blur(); 38 + }} 39 + > 40 + <MapLibre 41 + bind:map 42 + class="h-full w-full" 43 + style="https://tiles.openfreemap.org/styles/liberty" 44 + {zoom} 45 + {center} 46 + attributionControl={false} 47 + dragPan={false} 48 + dragRotate={false} 49 + keyboard={false} 50 + touchZoomRotate={true} 51 + scrollZoom={true} 52 + boxZoom={false} 53 + pitchWithRotate={false} 54 + touchPitch={false} 55 + onzoom={handleZoom} 56 + > 57 + <Projection type="globe" /> 58 + 59 + <Marker bind:lnglat={center}> 60 + {#snippet content()} 61 + <div class="from-accent-400 size-10 rounded-full bg-radial via-transparent p-3"> 62 + <div class="bg-accent-500 size-4 rounded-full ring-2 ring-white"></div> 63 + </div> 64 + {/snippet} 65 + </Marker> 66 + </MapLibre> 67 + 68 + {#snippet infoIcon()} 69 + <svg 70 + xmlns="http://www.w3.org/2000/svg" 71 + width="24" 72 + height="24" 73 + fill-rule="evenodd" 74 + viewBox="0 0 20 20" 75 + > 76 + <path 77 + d="M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0" 78 + /> 79 + </svg> 80 + {/snippet} 81 + 82 + {#if showAttribution} 83 + <div 84 + class="absolute right-2.5 bottom-2.5 z-10 rounded-xl bg-white text-black" 85 + style="width: calc(100% - 20px); max-width: 12rem;" 86 + > 87 + <button 88 + class="float-right flex size-6 cursor-pointer items-center justify-center rounded-full shadow-[0_0_6px_rgba(59,130,246,0.6)]" 89 + onclick={() => (showAttribution = false)} 90 + aria-label="Toggle attribution" 91 + > 92 + {@render infoIcon()} 93 + </button> 94 + <div class="p-2 text-left text-xs leading-snug text-black/75"> 95 + <a 96 + href="https://openfreemap.org" 97 + target="_blank" 98 + rel="noopener noreferrer" 99 + class="text-black/75 no-underline hover:underline" 100 + onclick={(e) => e.stopPropagation()}>OpenFreeMap</a 101 + > 102 + <a 103 + href="https://openmaptiles.org" 104 + target="_blank" 105 + rel="noopener noreferrer" 106 + class="text-black/75 no-underline hover:underline" 107 + onclick={(e) => e.stopPropagation()}>© OpenMapTiles</a 108 + > 109 + Data from 110 + <a 111 + href="https://www.openstreetmap.org/copyright" 112 + target="_blank" 113 + rel="noopener noreferrer" 114 + class="text-black/75 no-underline hover:underline" 115 + onclick={(e) => e.stopPropagation()}>OpenStreetMap</a 116 + > 117 + </div> 118 + </div> 119 + {:else} 120 + <button 121 + class="absolute right-2.5 bottom-2.5 z-10 flex size-6 items-center justify-center rounded-full bg-white text-black" 122 + onclick={() => (showAttribution = true)} 123 + aria-label="Toggle attribution" 124 + > 125 + {@render infoIcon()} 126 + </button> 127 + {/if} 128 + </div>
+10
src/lib/contrail.ts
··· 8 8 } from '../lexicon-types'; 9 9 import { Client, simpleFetchHandler } from '@atcute/client'; 10 10 import type { ActorIdentifier } from '@atcute/lexicons'; 11 + import { isResourceUri } from '@atcute/lexicons'; 11 12 12 13 export const CONTRAIL_URL = 'https://contrail.atmo.tools'; 13 14 export const RSVP_HYDRATE_LIMIT = 50; ··· 191 192 if (status === RSVP_INTERESTED || status.endsWith('#interested')) return 'interested'; 192 193 if (status.endsWith('#notgoing')) return 'notgoing'; 193 194 return null; 195 + } 196 + 197 + export async function notifyContrailOfUpdate(uri: string) { 198 + if (!isResourceUri(uri)) return; 199 + try { 200 + await contrail.post('rsvp.atmo.notifyOfUpdate', { input: { uri } }); 201 + } catch { 202 + // best-effort, don't block on failure 203 + } 194 204 } 195 205 196 206 export async function getProfileFromContrail(
+3 -3
src/routes/+layout.svelte
··· 48 48 /> 49 49 50 50 <Head 51 - title="statusphere" 52 - emojiFavicon="🥳" 53 - description="svelte + cloudflare workers statusphere" 51 + title="atmo.rsvp" 52 + emojiFavicon="🗓️" 53 + description="discover and attend events" 54 54 image="/og.png" 55 55 />
+5 -1
src/routes/calendar/+page.svelte
··· 21 21 <Button onclick={() => atProtoLoginModalState.show()}>Login</Button> 22 22 </div> 23 23 {:else if data.events.length === 0} 24 - <p class="text-base-500 text-center text-lg">No upcoming events on your calendar.</p> 24 + <p class="text-base-600 dark:text-base-400 text-center text-sm">No upcoming events on your calendar.</p> 25 + <div class="mt-6 flex justify-center gap-3"> 26 + <Button href="/">Join events</Button> 27 + <Button href="/create" variant="secondary">Create event</Button> 28 + </div> 25 29 {:else} 26 30 <div class="grid gap-6 sm:grid-cols-2"> 27 31 {#each data.events as event (event.uri)}
-19
src/routes/login/+page.svelte
··· 13 13 <meta name="description" content="Log in with your Bluesky account" /> 14 14 </svelte:head> 15 15 16 - <div class="min-h-screen px-6 py-12"> 17 - <div class="mx-auto flex min-h-[70vh] max-w-md items-center"> 18 - <div> 19 - <p 20 - class="text-base-500 dark:text-base-400 mb-2 text-sm font-semibold tracking-[0.18em] uppercase" 21 - > 22 - Account 23 - </p> 24 - <h1 class="text-base-900 dark:text-base-50 text-3xl font-bold tracking-tight">Log in</h1> 25 - <p class="text-base-500 dark:text-base-400 mt-3 text-sm leading-relaxed"> 26 - The login modal should open automatically. If it does not, open it manually. 27 - </p> 28 - 29 - <div class="mt-6"> 30 - <Button onclick={() => atProtoLoginModalState.show()}>Open login modal</Button> 31 - </div> 32 - </div> 33 - </div> 34 - </div>
+85 -39
src/routes/p/[actor]/e/[rkey]/+page.svelte
··· 3 3 import { getCDNImageBlobUrl } from '$lib/atproto'; 4 4 import { user } from '$lib/atproto/auth.svelte'; 5 5 import { Avatar as FoxAvatar, Badge, Button } from '@foxui/core'; 6 + import Map from '$lib/components/Map.svelte'; 6 7 import Avatar from 'svelte-boring-avatars'; 7 8 import EventRsvp from './EventRsvp.svelte'; 8 9 import EventAttendees from './EventAttendees.svelte'; ··· 62 63 return 'secondary'; 63 64 } 64 65 65 - function getLocationString(locations: FlatEventRecord['locations']): string | undefined { 66 - if (!locations || locations.length === 0) return undefined; 66 + function getLocationData(locations: FlatEventRecord['locations']) { 67 + if (!locations || locations.length === 0) return null; 67 68 68 69 const loc = locations.find((v) => v.$type === 'community.lexicon.location.address') as 69 - | { street?: string; locality?: string; region?: string } 70 + | { name?: string; street?: string; locality?: string; region?: string; country?: string } 70 71 | undefined; 71 - if (!loc) return undefined; 72 + if (!loc) return null; 72 73 73 - const street = loc.street || undefined; 74 - const locality = loc.locality || undefined; 75 - const region = loc.region || undefined; 74 + const shortParts = [loc.street, loc.locality].filter(Boolean); 75 + const fullParts = [loc.street, loc.locality, loc.region, loc.country].filter(Boolean); 76 + if (fullParts.length === 0) return null; 76 77 77 - const parts = [street, locality, region].filter(Boolean); 78 - return parts.length > 0 ? parts.join(', ') : undefined; 78 + const shortAddress = shortParts.join(', '); 79 + const fullAddress = fullParts.join(', '); 80 + const displayName = loc.name || undefined; 81 + const fullString = displayName ? `${displayName}, ${fullAddress}` : fullAddress; 82 + const mapsUrl = `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(fullString)}`; 83 + 84 + return { name: displayName, shortAddress, fullAddress, fullString, mapsUrl }; 79 85 } 80 86 81 - let location = $derived(getLocationString(eventData.locations)); 87 + let locationData = $derived(getLocationData(eventData.locations)); 88 + let location = $derived(locationData?.fullString); 89 + 90 + let geoLocation: { lat: number; lng: number } | null = $state(null); 91 + 92 + function initGeoLocation() { 93 + if (!eventData.locations || eventData.locations.length === 0) return; 94 + 95 + // Check for explicit geo coordinates first 96 + const geo = eventData.locations.find((v) => v.$type === 'community.lexicon.location.geo') as 97 + | { latitude?: string; longitude?: string } 98 + | undefined; 99 + if (geo?.latitude && geo?.longitude) { 100 + const lat = parseFloat(geo.latitude); 101 + const lng = parseFloat(geo.longitude); 102 + if (!isNaN(lat) && !isNaN(lng)) { 103 + geoLocation = { lat, lng }; 104 + return; 105 + } 106 + } 107 + 108 + // Geocode from address if available 109 + const addressQuery = locationData?.fullAddress; 110 + if (addressQuery) { 111 + fetch(`/api/geocoding?q=${encodeURIComponent(addressQuery)}`) 112 + .then((r) => (r.ok ? r.json() : null)) 113 + .then((data: unknown) => { 114 + const d = data as Record<string, unknown> | null; 115 + if (!d) return; 116 + if (d.lat && d.lon) { 117 + geoLocation = { lat: parseFloat(d.lat as string), lng: parseFloat(d.lon as string) }; 118 + } 119 + }) 120 + .catch(() => {}); 121 + } 122 + } 123 + 124 + import { onMount } from 'svelte'; 125 + onMount(initGeoLocation); 82 126 83 127 let thumbnailImage = $derived.by(() => { 84 128 if (!eventData.media || eventData.media.length === 0) return null; ··· 267 311 </div> 268 312 {/if} 269 313 {#if isOwner} 270 - <Button href="./{rkey}/edit" size="sm" class="mt-3 w-full">Edit Event</Button> 314 + <Button href="./{rkey}/edit" class="mt-9 w-full">Edit Event</Button> 271 315 {/if} 272 316 </div> 273 317 {/if} ··· 318 362 </div> 319 363 320 364 <!-- Location row --> 321 - {#if location} 322 - <div class="mb-6 flex items-center gap-4"> 365 + {#if locationData} 366 + <a href={locationData.mapsUrl} target="_blank" rel="noopener noreferrer" class="mb-6 flex items-center gap-4 hover:opacity-80 transition-opacity"> 323 367 <div 324 368 class="border-base-200 dark:border-base-700 bg-base-100 dark:bg-base-950/30 flex size-12 shrink-0 items-center justify-center rounded-xl border" 325 369 > ··· 343 387 /> 344 388 </svg> 345 389 </div> 346 - <p class="text-base-900 dark:text-base-50 font-semibold">{location}</p> 347 - </div> 390 + <div> 391 + {#if locationData.name} 392 + <p class="text-base-900 dark:text-base-50 font-semibold">{locationData.name}</p> 393 + <p class="text-base-500 dark:text-base-400 text-sm">{locationData.shortAddress}</p> 394 + {:else} 395 + <p class="text-base-900 dark:text-base-50 font-semibold">{locationData.shortAddress}</p> 396 + {/if} 397 + </div> 398 + </a> 348 399 {/if} 349 400 350 401 <EventRsvp ··· 371 422 </div> 372 423 </div> 373 424 {/if} 425 + 426 + <!-- Map --> 427 + {#if geoLocation && locationData} 428 + <div class="mt-8 mb-8"> 429 + <p 430 + class="text-base-500 dark:text-base-400 mb-3 text-xs font-semibold tracking-wider uppercase" 431 + > 432 + Location 433 + </p> 434 + <a href={locationData.mapsUrl} target="_blank" rel="noopener noreferrer" class="block hover:opacity-80 transition-opacity"> 435 + <div class="h-64 w-full overflow-hidden rounded-xl"> 436 + <Map lat={geoLocation.lat} lng={geoLocation.lng} /> 437 + </div> 438 + <p class="text-base-500 dark:text-base-400 mt-2 text-sm"> 439 + {locationData.fullString} 440 + </p> 441 + </a> 442 + </div> 443 + {/if} 374 444 </div> 375 445 376 446 <!-- Left column: sidebar info --> ··· 465 535 interestedCount={attendees.interestedCount} 466 536 /> 467 537 </div> 468 - 469 - <!-- View on Smoke Signal link, currently disabled as some events dont work on smokesignal --> 470 - <!-- <a 471 - href={smokesignalUrl} 472 - target="_blank" 473 - rel="noopener noreferrer" 474 - class="text-base-500 dark:text-base-400 hover:text-base-700 dark:hover:text-base-200 order-6 inline-flex items-center gap-1.5 text-sm transition-colors md:order-0 md:col-start-2" 475 - > 476 - View on Smoke Signal 477 - <svg 478 - xmlns="http://www.w3.org/2000/svg" 479 - fill="none" 480 - viewBox="0 0 24 24" 481 - stroke-width="2" 482 - stroke="currentColor" 483 - class="size-3.5" 484 - > 485 - <path 486 - stroke-linecap="round" 487 - stroke-linejoin="round" 488 - d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" 489 - /> 490 - </svg> 491 - </a> --> 492 538 </div> 493 539 </div> 494 540 </div>
+8 -3
src/routes/p/[actor]/e/[rkey]/EventRsvp.svelte
··· 1 1 <script lang="ts"> 2 2 import { user } from '$lib/atproto/auth.svelte'; 3 3 import { putRecord, deleteRecord, createTID } from '$lib/atproto/methods'; 4 + import { notifyContrailOfUpdate } from '$lib/contrail'; 4 5 import { atProtoLoginModalState } from '@foxui/social'; 5 6 import { Avatar, Button } from '@foxui/core'; 6 7 ··· 26 27 let rsvpRkeyOverride: string | null | undefined = $state(undefined); 27 28 let rsvpSubmitting = $state(false); 28 29 29 - let rsvpStatus = $derived(rsvpStatusOverride ?? initialRsvpStatus); 30 - let rsvpRkey = $derived(rsvpRkeyOverride ?? initialRsvpRkey); 30 + let rsvpStatus = $derived(rsvpStatusOverride !== undefined ? rsvpStatusOverride : initialRsvpStatus); 31 + let rsvpRkey = $derived(rsvpRkeyOverride !== undefined ? rsvpRkeyOverride : initialRsvpRkey); 31 32 32 33 async function submitRsvp(status: 'going' | 'interested') { 33 34 if (!user.isLoggedIn || !user.did) return; ··· 50 51 }); 51 52 52 53 if (response.ok) { 54 + const rsvpUri = `at://${user.did}/community.lexicon.calendar.rsvp/${key}`; 55 + notifyContrailOfUpdate(rsvpUri); 53 56 rsvpStatusOverride = status; 54 57 rsvpRkeyOverride = key; 55 58 onrsvp?.(status); ··· 65 68 if (!user.isLoggedIn || !user.did || !rsvpRkey) return; 66 69 rsvpSubmitting = true; 67 70 try { 71 + const rsvpUri = `at://${user.did}/community.lexicon.calendar.rsvp/${rsvpRkey}`; 68 72 await deleteRecord({ 69 73 collection: 'community.lexicon.calendar.rsvp', 70 74 rkey: rsvpRkey 71 75 }); 76 + notifyContrailOfUpdate(rsvpUri); 72 77 rsvpStatusOverride = null; 73 78 rsvpRkeyOverride = null; 74 79 oncancel?.(); ··· 159 164 {:else} 160 165 {#if user.profile} 161 166 <div class="mb-4 flex items-center gap-2"> 162 - <span class="text-base-500 dark:text-base-400 text-sm">RSVPing as</span> 167 + <span class="text-base-500 dark:text-base-400 text-sm">Attend as</span> 163 168 <Avatar 164 169 src={user.profile.avatar} 165 170 alt={user.profile.displayName || user.profile.handle}
+1 -2
src/routes/p/[actor]/e/[rkey]/og.png/+server.ts
··· 44 44 eventData.media.find((m) => m.role === 'thumbnail') ?? 45 45 eventData.media.find((m) => m.role === 'header'); 46 46 if (media?.content) { 47 - thumbnailUrl = getCDNImageBlobUrl({ did, blob: media.content }) ?? null; 47 + thumbnailUrl = getCDNImageBlobUrl({ did, blob: media.content, format: 'png' }) ?? null; 48 48 } 49 49 } 50 - 51 50 return new ImageResponse( 52 51 EventOgImage, 53 52 { width: 1200, height: 630, debug: false },
+19 -19
src/routes/p/[actor]/e/[rkey]/og.png/EventOgImage.svelte
··· 14 14 } = $props(); 15 15 </script> 16 16 17 - <div class="flex h-full w-full bg-neutral-900 p-0"> 18 - <div class="flex h-full shrink-0 items-center px-8"> 19 - <div class="flex overflow-hidden rounded-3xl"> 20 - {#if thumbnailUrl} 21 - <img src={thumbnailUrl} alt={name} width="420" height="420" style="object-fit: cover;" /> 22 - {:else} 23 - <Avatar 24 - size={420} 25 - name={rkey} 26 - variant="marble" 27 - colors={['#92A1C6', '#146A7C', '#F0AB3D', '#C271B4', '#C20D90']} 28 - square 29 - /> 30 - {/if} 31 - </div> 32 - </div> 33 - 17 + <div class="flex h-full w-full p-0" style="background: radial-gradient(ellipse 100% 80% at 30% 120%, #14b8a6 0%, #042f2e 25%, #090b0c 65%);"> 34 18 <div class="flex min-w-0 flex-1 flex-col justify-center p-12"> 35 19 <h1 36 - class="text-7xl leading-tight font-bold text-neutral-50" 37 - style="display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; word-break: break-word;" 20 + class="text-5xl leading-tight font-bold text-neutral-50" 21 + style="overflow: hidden; word-break: break-word; max-height: 3.6em;" 38 22 > 39 23 {name} 40 24 </h1> ··· 57 41 /> 58 42 </svg> 59 43 <span class="ml-3 text-2xl text-neutral-300">{dateStr}</span> 44 + </div> 45 + </div> 46 + 47 + <div class="flex h-full shrink-0 items-center px-8"> 48 + <div class="flex overflow-hidden rounded-3xl"> 49 + {#if thumbnailUrl} 50 + <img src={thumbnailUrl} alt={name} width="420" height="420" style="object-fit: cover;" /> 51 + {:else} 52 + <Avatar 53 + size={420} 54 + name={rkey} 55 + variant="marble" 56 + colors={['#92A1C6', '#146A7C', '#F0AB3D', '#C271B4', '#C20D90']} 57 + square 58 + /> 59 + {/if} 60 60 </div> 61 61 </div> 62 62 </div>
static/og.png

This is a binary file and will not be displayed.