atmo.rsvp
4
fork

Configure Feed

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

commiaats

Florian 57349a9b 2d8e557b

+486 -34
+1
package.json
··· 60 60 "@foxui/core": "^0.5.1", 61 61 "@foxui/social": "^0.5.1", 62 62 "@foxui/time": "^0.5.1", 63 + "@foxui/visual": "^0.8.0", 63 64 "@internationalized/date": "^3.12.0", 64 65 "@tailwindcss/typography": "^0.5.19", 65 66 "dompurify": "^3.3.3",
+303
pnpm-lock.yaml
··· 26 26 '@foxui/time': 27 27 specifier: ^0.5.1 28 28 version: 0.5.1(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1) 29 + '@foxui/visual': 30 + specifier: ^0.8.0 31 + version: 0.8.0(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1) 29 32 '@internationalized/date': 30 33 specifier: ^3.12.0 31 34 version: 3.12.0 ··· 704 707 '@floating-ui/utils@0.2.10': 705 708 resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==, tarball: https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz} 706 709 710 + '@foxui/colors@0.8.0': 711 + resolution: {integrity: sha512-4LIsmfuLM0lgIyy+NDxEElv9ZwM7xLl/7ZdAuzSukqIUeXMdtfWNyMLV6QcAFEc2LQyJAGXBpSK5H620BB01Hg==, tarball: https://registry.npmjs.org/@foxui/colors/-/colors-0.8.0.tgz} 712 + peerDependencies: 713 + svelte: '>=5' 714 + tailwindcss: '>=3' 715 + 707 716 '@foxui/core@0.5.1': 708 717 resolution: {integrity: sha512-WhcwTYy2bipsfrBmNjHF88gfmigr10KeWqNBVrwWmkFjAeXy8J9rj9CSLpeRQCHMmrNDqjVq7VahxWRuz0NO9g==, tarball: https://registry.npmjs.org/@foxui/core/-/core-0.5.1.tgz} 709 718 peerDependencies: 710 719 svelte: '>=5' 711 720 tailwindcss: '>=3' 712 721 722 + '@foxui/core@0.8.0': 723 + resolution: {integrity: sha512-1/vE7eftrQit7XyLVod6FksqCZKYj9VbRqktc4BkTdKnf3/iJZJrGyeDD3hJ0+q6qEi7DImCgSOEnFLoAqTecw==, tarball: https://registry.npmjs.org/@foxui/core/-/core-0.8.0.tgz} 724 + peerDependencies: 725 + svelte: '>=5' 726 + tailwindcss: '>=3' 727 + 713 728 '@foxui/social@0.5.1': 714 729 resolution: {integrity: sha512-PupbByfLdykQqzxjTZkMpy43LHaLrivI0DEb9Psew0+1PCMDqFSoDk00BVl5vtaPOlBfOwdQsDICa4kR0LApHg==, tarball: https://registry.npmjs.org/@foxui/social/-/social-0.5.1.tgz} 715 730 peerDependencies: ··· 722 737 svelte: '>=5' 723 738 tailwindcss: '>=3' 724 739 740 + '@foxui/visual@0.8.0': 741 + resolution: {integrity: sha512-IGeocy5B9kyVPshiZ1aqbs+2kjFIucr2Vl3KYxVu4As89WePwJKz6WlmhBoHaLspbdoIaSls0MVTqAs5ICli+w==, tarball: https://registry.npmjs.org/@foxui/visual/-/visual-0.8.0.tgz} 742 + peerDependencies: 743 + svelte: '>=5' 744 + tailwindcss: '>=3' 745 + 725 746 '@humanfs/core@0.19.1': 726 747 resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, tarball: https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz} 727 748 engines: {node: '>=18.18.0'} ··· 944 965 945 966 '@number-flow/svelte@0.3.13': 946 967 resolution: {integrity: sha512-mvbxDeSFa1o/E4vGhrWuawAFCgcn5qTQ/s++FIoD88es5+JQa/aMQUypTy7qXIreTtTvncpIbkKdw9DMnweaSw==, tarball: https://registry.npmjs.org/@number-flow/svelte/-/svelte-0.3.13.tgz} 968 + peerDependencies: 969 + svelte: ^4 || ^5 970 + 971 + '@number-flow/svelte@0.4.0': 972 + resolution: {integrity: sha512-9tnowrlZlBV3IVe3Gm1V7yXSf4Ugag2k7iW45xqb04HXSa1ApEImopvGWAjJpHDvS849o+UCb0YH461Mtde9lA==, tarball: https://registry.npmjs.org/@number-flow/svelte/-/svelte-0.4.0.tgz} 947 973 peerDependencies: 948 974 svelte: ^4 || ^5 949 975 ··· 1316 1342 1317 1343 '@takumi-rs/wasm@0.55.4': 1318 1344 resolution: {integrity: sha512-/iOhQW+nJW0hhv2viu6806JehiAKWFvJ4LXux6CW4XBpP1xWdr4H+VBS7OYMbQu/7XaPITyL7B10lSTtRUAHoA==, tarball: https://registry.npmjs.org/@takumi-rs/wasm/-/wasm-0.55.4.tgz} 1345 + 1346 + '@texel/color@1.1.11': 1347 + resolution: {integrity: sha512-/3kKgfBqzrRfLl4RsEccx+Yfj1kVL6Bh6DejVWZ+DPg/jJdcfdYZ5fpD1nXFwWd8OQNATjz+WqsfQfUynSsgRg==, tarball: https://registry.npmjs.org/@texel/color/-/color-1.1.11.tgz} 1319 1348 1320 1349 '@types/bun@1.3.6': 1321 1350 resolution: {integrity: sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA==, tarball: https://registry.npmjs.org/@types/bun/-/bun-1.3.6.tgz} ··· 1466 1495 blake3-wasm@2.1.5: 1467 1496 resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==, tarball: https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz} 1468 1497 1498 + boolbase@1.0.0: 1499 + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, tarball: https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz} 1500 + 1469 1501 brace-expansion@1.1.12: 1470 1502 resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, tarball: https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz} 1471 1503 ··· 1482 1514 camelize@1.0.1: 1483 1515 resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==, tarball: https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz} 1484 1516 1517 + canvas-confetti@1.9.4: 1518 + resolution: {integrity: sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==, tarball: https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.9.4.tgz} 1519 + 1485 1520 chalk@4.1.2: 1486 1521 resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, tarball: https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz} 1487 1522 engines: {node: '>=10'} 1488 1523 1524 + cheerio-select@2.1.0: 1525 + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==, tarball: https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz} 1526 + 1527 + cheerio@1.2.0: 1528 + resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==, tarball: https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz} 1529 + engines: {node: '>=20.18.1'} 1530 + 1489 1531 chokidar@4.0.3: 1490 1532 resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==, tarball: https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz} 1491 1533 engines: {node: '>= 14.16.0'} ··· 1535 1577 resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==, tarball: https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz} 1536 1578 engines: {node: '>=4'} 1537 1579 1580 + css-select@5.2.2: 1581 + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==, tarball: https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz} 1582 + 1538 1583 css-to-react-native@3.2.0: 1539 1584 resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==, tarball: https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz} 1540 1585 1586 + css-what@6.2.2: 1587 + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==, tarball: https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz} 1588 + engines: {node: '>= 6'} 1589 + 1541 1590 cssesc@3.0.0: 1542 1591 resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, tarball: https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz} 1543 1592 engines: {node: '>=4'} ··· 1573 1622 devalue@5.6.2: 1574 1623 resolution: {integrity: sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==, tarball: https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz} 1575 1624 1625 + dom-serializer@2.0.0: 1626 + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, tarball: https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz} 1627 + 1628 + domelementtype@2.3.0: 1629 + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, tarball: https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz} 1630 + 1631 + domhandler@5.0.3: 1632 + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, tarball: https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz} 1633 + engines: {node: '>= 4'} 1634 + 1576 1635 dompurify@3.3.3: 1577 1636 resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==, tarball: https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz} 1637 + 1638 + domutils@3.2.2: 1639 + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==, tarball: https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz} 1578 1640 1579 1641 earcut@3.0.2: 1580 1642 resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==, tarball: https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz} ··· 1585 1647 emoji-regex@10.6.0: 1586 1648 resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==, tarball: https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz} 1587 1649 1650 + encoding-sniffer@0.2.1: 1651 + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==, tarball: https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz} 1652 + 1588 1653 enhanced-resolve@5.18.4: 1589 1654 resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==, tarball: https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz} 1590 1655 engines: {node: '>=10.13.0'} 1591 1656 1657 + entities@4.5.0: 1658 + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==, tarball: https://registry.npmjs.org/entities/-/entities-4.5.0.tgz} 1659 + engines: {node: '>=0.12'} 1660 + 1661 + entities@6.0.1: 1662 + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, tarball: https://registry.npmjs.org/entities/-/entities-6.0.1.tgz} 1663 + engines: {node: '>=0.12'} 1664 + 1665 + entities@7.0.1: 1666 + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==, tarball: https://registry.npmjs.org/entities/-/entities-7.0.1.tgz} 1667 + engines: {node: '>=0.12'} 1668 + 1592 1669 error-stack-parser-es@1.0.5: 1593 1670 resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==, tarball: https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz} 1594 1671 ··· 1755 1832 1756 1833 hls.js@1.6.15: 1757 1834 resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==, tarball: https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz} 1835 + 1836 + htmlparser2@10.1.0: 1837 + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==, tarball: https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz} 1838 + 1839 + iconv-lite@0.6.3: 1840 + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==, tarball: https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz} 1841 + engines: {node: '>=0.10.0'} 1758 1842 1759 1843 ignore@5.3.2: 1760 1844 resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, tarball: https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz} ··· 2002 2086 resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==, tarball: https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz} 2003 2087 hasBin: true 2004 2088 2089 + nth-check@2.1.1: 2090 + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==, tarball: https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz} 2091 + 2005 2092 number-flow@0.5.12: 2006 2093 resolution: {integrity: sha512-CIs21h2JkfYG4rfgERaUNAk0Cz+Ef14fNJfSCbGGhgRgconQc9b7rcCQfi9SZ36kNjVXmsl2BrzDbjGtEgumAA==, tarball: https://registry.npmjs.org/number-flow/-/number-flow-0.5.12.tgz} 2094 + 2095 + number-flow@0.6.0: 2096 + resolution: {integrity: sha512-K8flNq2Wqus53vjp/btVo3qXFkagF8dIdYavreBfE7hlvFFG/b1HMGEH6nZL+mlrJ+4lbLP9OmPv3t2rmRkpSQ==, tarball: https://registry.npmjs.org/number-flow/-/number-flow-0.6.0.tgz} 2007 2097 2008 2098 obug@2.1.1: 2009 2099 resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==, tarball: https://registry.npmjs.org/obug/-/obug-2.1.1.tgz} ··· 2030 2120 parse-css-color@0.2.1: 2031 2121 resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==, tarball: https://registry.npmjs.org/parse-css-color/-/parse-css-color-0.2.1.tgz} 2032 2122 2123 + parse5-htmlparser2-tree-adapter@7.1.0: 2124 + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==, tarball: https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz} 2125 + 2126 + parse5-parser-stream@7.1.2: 2127 + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==, tarball: https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz} 2128 + 2129 + parse5@7.3.0: 2130 + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==, tarball: https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz} 2131 + 2033 2132 partysocket@1.1.16: 2034 2133 resolution: {integrity: sha512-d7xFv+ZC7x0p/DAHWJ5FhxQhimIx+ucyZY+kxL0cKddLBmK9c4p2tEA/L+dOOrWm6EYrRwrBjKQV0uSzOY9x1w==, tarball: https://registry.npmjs.org/partysocket/-/partysocket-1.1.16.tgz} 2035 2134 peerDependencies: ··· 2251 2350 resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==, tarball: https://registry.npmjs.org/sade/-/sade-1.8.1.tgz} 2252 2351 engines: {node: '>=6'} 2253 2352 2353 + safer-buffer@2.1.2: 2354 + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, tarball: https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz} 2355 + 2254 2356 satori-html@0.3.2: 2255 2357 resolution: {integrity: sha512-wjTh14iqADFKDK80e51/98MplTGfxz2RmIzh0GqShlf4a67+BooLywF17TvJPD6phO0Hxm7Mf1N5LtRYvdkYRA==, tarball: https://registry.npmjs.org/satori-html/-/satori-html-0.3.2.tgz} 2256 2358 ··· 2451 2553 resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==, tarball: https://registry.npmjs.org/undici/-/undici-7.18.2.tgz} 2452 2554 engines: {node: '>=20.18.1'} 2453 2555 2556 + undici@7.24.4: 2557 + resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==, tarball: https://registry.npmjs.org/undici/-/undici-7.24.4.tgz} 2558 + engines: {node: '>=20.18.1'} 2559 + 2454 2560 unenv@2.0.0-rc.24: 2455 2561 resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==, tarball: https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz} 2456 2562 ··· 2528 2634 vite: 2529 2635 optional: true 2530 2636 2637 + web-haptics@0.0.6: 2638 + resolution: {integrity: sha512-eCzcf1LDi20+Fr0x9V3OkX92k0gxEQXaHajmhXHitsnk6SxPeshv8TBtBRqxyst8HI1uf2FyFVE7QS3jo1gkrw==, tarball: https://registry.npmjs.org/web-haptics/-/web-haptics-0.0.6.tgz} 2639 + peerDependencies: 2640 + react: '>=18' 2641 + react-dom: '>=18' 2642 + svelte: '>=4' 2643 + vue: '>=3' 2644 + peerDependenciesMeta: 2645 + react: 2646 + optional: true 2647 + react-dom: 2648 + optional: true 2649 + svelte: 2650 + optional: true 2651 + vue: 2652 + optional: true 2653 + 2654 + whatwg-encoding@3.1.1: 2655 + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==, tarball: https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz} 2656 + engines: {node: '>=18'} 2657 + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation 2658 + 2659 + whatwg-mimetype@4.0.0: 2660 + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==, tarball: https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz} 2661 + engines: {node: '>=18'} 2662 + 2531 2663 which@2.0.2: 2532 2664 resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, tarball: https://registry.npmjs.org/which/-/which-2.0.2.tgz} 2533 2665 engines: {node: '>= 8'} ··· 3103 3235 3104 3236 '@floating-ui/utils@0.2.10': {} 3105 3237 3238 + '@foxui/colors@0.8.0(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1)': 3239 + dependencies: 3240 + '@foxui/core': 0.8.0(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1) 3241 + '@texel/color': 1.1.11 3242 + '@use-gesture/vanilla': 10.3.1 3243 + bits-ui: 2.16.2(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0) 3244 + svelte: 5.48.0 3245 + tailwindcss: 4.2.1 3246 + transitivePeerDependencies: 3247 + - '@internationalized/date' 3248 + - '@sveltejs/kit' 3249 + - react 3250 + - react-dom 3251 + - vue 3252 + 3106 3253 '@foxui/core@0.5.1(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1)': 3107 3254 dependencies: 3108 3255 '@number-flow/svelte': 0.3.13(svelte@5.48.0) ··· 3118 3265 - '@internationalized/date' 3119 3266 - '@sveltejs/kit' 3120 3267 3268 + '@foxui/core@0.8.0(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1)': 3269 + dependencies: 3270 + '@number-flow/svelte': 0.4.0(svelte@5.48.0) 3271 + bits-ui: 2.16.2(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0) 3272 + clsx: 2.1.1 3273 + dompurify: 3.3.3 3274 + mode-watcher: 1.1.0(svelte@5.48.0) 3275 + svelte: 5.48.0 3276 + svelte-sonner: 1.0.7(svelte@5.48.0) 3277 + tailwind-merge: 3.5.0 3278 + tailwind-variants: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1) 3279 + tailwindcss: 4.2.1 3280 + web-haptics: 0.0.6(svelte@5.48.0) 3281 + transitivePeerDependencies: 3282 + - '@internationalized/date' 3283 + - '@sveltejs/kit' 3284 + - react 3285 + - react-dom 3286 + - vue 3287 + 3121 3288 '@foxui/social@0.5.1(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1)': 3122 3289 dependencies: 3123 3290 '@atproto/api': 0.18.21 ··· 3145 3312 transitivePeerDependencies: 3146 3313 - '@internationalized/date' 3147 3314 - '@sveltejs/kit' 3315 + 3316 + '@foxui/visual@0.8.0(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1)': 3317 + dependencies: 3318 + '@foxui/colors': 0.8.0(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1) 3319 + '@foxui/core': 0.8.0(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0)(tailwindcss@4.2.1) 3320 + bits-ui: 2.16.2(@internationalized/date@3.12.0)(@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)))(svelte@5.48.0) 3321 + canvas-confetti: 1.9.4 3322 + cheerio: 1.2.0 3323 + svelte: 5.48.0 3324 + tailwindcss: 4.2.1 3325 + transitivePeerDependencies: 3326 + - '@internationalized/date' 3327 + - '@sveltejs/kit' 3328 + - react 3329 + - react-dom 3330 + - vue 3148 3331 3149 3332 '@humanfs/core@0.19.1': {} 3150 3333 ··· 3339 3522 dependencies: 3340 3523 esm-env: 1.2.2 3341 3524 number-flow: 0.5.12 3525 + svelte: 5.48.0 3526 + 3527 + '@number-flow/svelte@0.4.0(svelte@5.48.0)': 3528 + dependencies: 3529 + esm-env: 1.2.2 3530 + number-flow: 0.6.0 3342 3531 svelte: 5.48.0 3343 3532 3344 3533 '@optique/core@0.6.11': {} ··· 3625 3814 3626 3815 '@takumi-rs/wasm@0.55.4': {} 3627 3816 3817 + '@texel/color@1.1.11': {} 3818 + 3628 3819 '@types/bun@1.3.6': 3629 3820 dependencies: 3630 3821 bun-types: 1.3.6 ··· 3804 3995 3805 3996 blake3-wasm@2.1.5: {} 3806 3997 3998 + boolbase@1.0.0: {} 3999 + 3807 4000 brace-expansion@1.1.12: 3808 4001 dependencies: 3809 4002 balanced-match: 1.0.2 ··· 3821 4014 3822 4015 camelize@1.0.1: {} 3823 4016 4017 + canvas-confetti@1.9.4: {} 4018 + 3824 4019 chalk@4.1.2: 3825 4020 dependencies: 3826 4021 ansi-styles: 4.3.0 3827 4022 supports-color: 7.2.0 3828 4023 4024 + cheerio-select@2.1.0: 4025 + dependencies: 4026 + boolbase: 1.0.0 4027 + css-select: 5.2.2 4028 + css-what: 6.2.2 4029 + domelementtype: 2.3.0 4030 + domhandler: 5.0.3 4031 + domutils: 3.2.2 4032 + 4033 + cheerio@1.2.0: 4034 + dependencies: 4035 + cheerio-select: 2.1.0 4036 + dom-serializer: 2.0.0 4037 + domhandler: 5.0.3 4038 + domutils: 3.2.2 4039 + encoding-sniffer: 0.2.1 4040 + htmlparser2: 10.1.0 4041 + parse5: 7.3.0 4042 + parse5-htmlparser2-tree-adapter: 7.1.0 4043 + parse5-parser-stream: 7.1.2 4044 + undici: 7.24.4 4045 + whatwg-mimetype: 4.0.0 4046 + 3829 4047 chokidar@4.0.3: 3830 4048 dependencies: 3831 4049 readdirp: 4.1.2 ··· 3862 4080 3863 4081 css-color-keywords@1.0.0: {} 3864 4082 4083 + css-select@5.2.2: 4084 + dependencies: 4085 + boolbase: 1.0.0 4086 + css-what: 6.2.2 4087 + domhandler: 5.0.3 4088 + domutils: 3.2.2 4089 + nth-check: 2.1.1 4090 + 3865 4091 css-to-react-native@3.2.0: 3866 4092 dependencies: 3867 4093 camelize: 1.0.1 3868 4094 css-color-keywords: 1.0.0 3869 4095 postcss-value-parser: 4.2.0 4096 + 4097 + css-what@6.2.2: {} 3870 4098 3871 4099 cssesc@3.0.0: {} 3872 4100 ··· 3886 4114 3887 4115 devalue@5.6.2: {} 3888 4116 4117 + dom-serializer@2.0.0: 4118 + dependencies: 4119 + domelementtype: 2.3.0 4120 + domhandler: 5.0.3 4121 + entities: 4.5.0 4122 + 4123 + domelementtype@2.3.0: {} 4124 + 4125 + domhandler@5.0.3: 4126 + dependencies: 4127 + domelementtype: 2.3.0 4128 + 3889 4129 dompurify@3.3.3: 3890 4130 optionalDependencies: 3891 4131 '@types/trusted-types': 2.0.7 3892 4132 4133 + domutils@3.2.2: 4134 + dependencies: 4135 + dom-serializer: 2.0.0 4136 + domelementtype: 2.3.0 4137 + domhandler: 5.0.3 4138 + 3893 4139 earcut@3.0.2: {} 3894 4140 3895 4141 emoji-picker-element@1.29.0: {} 3896 4142 3897 4143 emoji-regex@10.6.0: {} 3898 4144 4145 + encoding-sniffer@0.2.1: 4146 + dependencies: 4147 + iconv-lite: 0.6.3 4148 + whatwg-encoding: 3.1.1 4149 + 3899 4150 enhanced-resolve@5.18.4: 3900 4151 dependencies: 3901 4152 graceful-fs: 4.2.11 3902 4153 tapable: 2.3.0 4154 + 4155 + entities@4.5.0: {} 4156 + 4157 + entities@6.0.1: {} 4158 + 4159 + entities@7.0.1: {} 3903 4160 3904 4161 error-stack-parser-es@1.0.5: {} 3905 4162 ··· 4120 4377 4121 4378 hls.js@1.6.15: {} 4122 4379 4380 + htmlparser2@10.1.0: 4381 + dependencies: 4382 + domelementtype: 2.3.0 4383 + domhandler: 5.0.3 4384 + domutils: 3.2.2 4385 + entities: 7.0.1 4386 + 4387 + iconv-lite@0.6.3: 4388 + dependencies: 4389 + safer-buffer: 2.1.2 4390 + 4123 4391 ignore@5.3.2: {} 4124 4392 4125 4393 ignore@7.0.5: {} ··· 4331 4599 4332 4600 node-gyp-build@4.8.4: {} 4333 4601 4602 + nth-check@2.1.1: 4603 + dependencies: 4604 + boolbase: 1.0.0 4605 + 4334 4606 number-flow@0.5.12: 4607 + dependencies: 4608 + esm-env: 1.2.2 4609 + 4610 + number-flow@0.6.0: 4335 4611 dependencies: 4336 4612 esm-env: 1.2.2 4337 4613 ··· 4365 4641 color-name: 1.1.4 4366 4642 hex-rgb: 4.3.0 4367 4643 4644 + parse5-htmlparser2-tree-adapter@7.1.0: 4645 + dependencies: 4646 + domhandler: 5.0.3 4647 + parse5: 7.3.0 4648 + 4649 + parse5-parser-stream@7.1.2: 4650 + dependencies: 4651 + parse5: 7.3.0 4652 + 4653 + parse5@7.3.0: 4654 + dependencies: 4655 + entities: 6.0.1 4656 + 4368 4657 partysocket@1.1.16: 4369 4658 dependencies: 4370 4659 event-target-polyfill: 0.0.4 ··· 4535 4824 sade@1.8.1: 4536 4825 dependencies: 4537 4826 mri: 1.2.0 4827 + 4828 + safer-buffer@2.1.2: {} 4538 4829 4539 4830 satori-html@0.3.2: 4540 4831 dependencies: ··· 4764 5055 4765 5056 undici@7.18.2: {} 4766 5057 5058 + undici@7.24.4: {} 5059 + 4767 5060 unenv@2.0.0-rc.24: 4768 5061 dependencies: 4769 5062 pathe: 2.0.3 ··· 4814 5107 vitefu@1.1.1(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): 4815 5108 optionalDependencies: 4816 5109 vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) 5110 + 5111 + web-haptics@0.0.6(svelte@5.48.0): 5112 + optionalDependencies: 5113 + svelte: 5.48.0 5114 + 5115 + whatwg-encoding@3.1.1: 5116 + dependencies: 5117 + iconv-lite: 0.6.3 5118 + 5119 + whatwg-mimetype@4.0.0: {} 4817 5120 4818 5121 which@2.0.2: 4819 5122 dependencies:
+9 -2
src/lib/components/EventCard.svelte
··· 1 1 <script lang="ts"> 2 2 import { getCDNImageBlobUrl } from '$lib/atproto'; 3 - import type { FlatEventRecord } from '$lib/contrail'; 3 + import { isEventOngoing, type FlatEventRecord } from '$lib/contrail'; 4 4 import Avatar from 'svelte-boring-avatars'; 5 5 6 6 let { ··· 59 59 let thumbnail = $derived(getThumbnail(event)); 60 60 let location = $derived(getLocationString(event.locations)); 61 61 let mode = $derived(getModeLabel(event.mode)); 62 + let isOngoing = $derived(isEventOngoing(event.startsAt, event.endsAt)); 62 63 </script> 63 64 64 65 <a ··· 88 89 </div> 89 90 90 91 <div class="min-w-0 self-center"> 91 - <p class="text-base-500 dark:text-base-400 text-xs font-medium"> 92 + <p class="text-base-500 dark:text-base-400 flex items-center gap-1.5 text-xs font-medium"> 92 93 {formatDateTime(event.startsAt)} 94 + {#if isOngoing} 95 + <span class="inline-flex items-center gap-1 rounded-full bg-accent-100 px-1.5 py-0.5 text-[10px] font-semibold text-accent-700 dark:bg-accent-900/30 dark:text-accent-400"> 96 + <span class="size-1.5 rounded-full bg-accent-500 animate-pulse"></span> 97 + Live 98 + </span> 99 + {/if} 93 100 </p> 94 101 <h3 95 102 class="text-base-900 dark:text-base-50 group-hover:text-base-700 dark:group-hover:text-base-200 mt-0.5 line-clamp-2 text-sm leading-snug font-semibold transition-colors sm:text-base"
+1
src/lib/components/EventEditor.svelte
··· 519 519 520 520 const record: Record<string, unknown> = { 521 521 $type: 'community.lexicon.calendar.event', 522 + createdWith: 'https://atmo.rsvp', 522 523 name: name.trim(), 523 524 mode: `community.lexicon.calendar.event#${mode}`, 524 525 status: 'community.lexicon.calendar.event#scheduled',
+75
src/lib/components/UserProfile.svelte
··· 1 + <script lang="ts"> 2 + import { Avatar, cn } from '@foxui/core'; 3 + import { sanitize } from '$lib/cal/sanitize'; 4 + import type { Snippet } from 'svelte'; 5 + 6 + let { 7 + profile, 8 + actions, 9 + class: className 10 + }: { 11 + profile: { 12 + banner?: string; 13 + avatar?: string; 14 + displayName?: string; 15 + handle?: string; 16 + description?: string; 17 + }; 18 + actions?: Snippet; 19 + class?: string; 20 + } = $props(); 21 + </script> 22 + 23 + {#if profile} 24 + <div class={cn('mx-auto max-w-full sm:max-w-2xl sm:py-6', className)}> 25 + <div> 26 + {#if profile.banner} 27 + <img 28 + class="border-base-800 aspect-3/1 w-full border-b object-cover sm:rounded-xl sm:border" 29 + src={profile.banner} 30 + alt="" 31 + /> 32 + {:else} 33 + <div class="aspect-8/1 w-full"></div> 34 + {/if} 35 + </div> 36 + <div 37 + class={cn( 38 + profile.banner ? '-mt-11' : '-mt-8', 39 + 'flex max-w-full items-end space-x-5 px-4 sm:-mt-16 sm:px-6 lg:px-8' 40 + )} 41 + > 42 + <Avatar 43 + src={profile.avatar} 44 + class="border-base-50 dark:border-base-800 size-24 border-2 sm:size-32" 45 + /> 46 + <div 47 + class="flex min-w-0 flex-1 flex-row sm:flex-row sm:items-center sm:justify-end sm:space-x-6 sm:pb-1" 48 + > 49 + <div 50 + class={cn( 51 + profile.banner ? 'mt-4 sm:mt-0' : '-mt-18 sm:-mt-26', 52 + 'flex max-w-full min-w-0 flex-1 flex-col items-baseline' 53 + )} 54 + > 55 + <h1 56 + class="text-base-900 dark:text-base-100 max-w-full truncate text-lg font-bold sm:text-xl" 57 + > 58 + {profile.displayName || profile.handle} 59 + </h1> 60 + <div class="text-base-900 dark:text-base-400 truncate text-xs sm:text-sm"> 61 + {profile.handle} 62 + </div> 63 + </div> 64 + </div> 65 + 66 + {#if actions} 67 + {@render actions()} 68 + {/if} 69 + </div> 70 + 71 + <div class="text-base-800 dark:text-base-200 px-4 py-4 text-xs sm:px-6 sm:text-sm lg:px-8"> 72 + {@html sanitize(profile.description?.replaceAll('\n', '<br/>') ?? '')} 73 + </div> 74 + </div> 75 + {/if}
+6
src/lib/contrail.ts
··· 194 194 return null; 195 195 } 196 196 197 + export function isEventOngoing(startsAt: string, endsAt?: string | null): boolean { 198 + if (!endsAt) return false; 199 + const now = new Date(); 200 + return new Date(startsAt) <= now && new Date(endsAt) >= now; 201 + } 202 + 197 203 export async function notifyContrailOfUpdate(uri: string) { 198 204 if (!isResourceUri(uri)) return; 199 205 try {
+37 -7
src/routes/calendar/+page.server.ts
··· 1 - import { flattenEventRecord, contrail } from '$lib/contrail'; 1 + import { 2 + flattenEventRecord, 3 + flattenEventRecords, 4 + contrail, 5 + listEventRecordsFromContrail 6 + } from '$lib/contrail'; 2 7 import type { PageServerLoad } from './$types'; 3 8 4 9 export const load: PageServerLoad = async ({ locals }) => { ··· 6 11 return { events: [], loggedIn: false }; 7 12 } 8 13 9 - const response = await contrail.get('community.lexicon.calendar.rsvp.listRecords', { 10 - params: { actor: locals.did, hydrateEvent: true, limit: 100 } 11 - }); 14 + const now = new Date().toISOString(); 12 15 13 - const now = new Date(); 14 - const events = (response.ok ? (response.data.records ?? []) : []) 16 + const [rsvpResponse, hostingResponse] = await Promise.all([ 17 + contrail.get('community.lexicon.calendar.rsvp.listRecords', { 18 + params: { actor: locals.did, hydrateEvent: true, limit: 100 } 19 + }), 20 + listEventRecordsFromContrail({ 21 + actor: locals.did, 22 + startsAtMin: now, 23 + sort: 'startsAt', 24 + order: 'asc', 25 + limit: 100 26 + }) 27 + ]); 28 + 29 + const nowDate = new Date(); 30 + 31 + // Events from RSVPs 32 + const rsvpEvents = (rsvpResponse.ok ? (rsvpResponse.data.records ?? []) : []) 15 33 .filter((r) => { 16 34 const status = r.record?.status; 17 35 return status?.endsWith('#going') || status?.endsWith('#interested'); ··· 21 39 const flat = flattenEventRecord(r.event); 22 40 return flat ? [flat] : []; 23 41 }) 24 - .filter((e) => new Date(e.endsAt || e.startsAt) >= now) 42 + .filter((e) => new Date(e.endsAt || e.startsAt) >= nowDate); 43 + 44 + // Events the user is hosting 45 + const hostingEvents = hostingResponse ? flattenEventRecords(hostingResponse.records) : []; 46 + 47 + // Merge and deduplicate 48 + const seen = new Set<string>(); 49 + const events = [...rsvpEvents, ...hostingEvents] 50 + .filter((e) => { 51 + if (seen.has(e.uri)) return false; 52 + seen.add(e.uri); 53 + return true; 54 + }) 25 55 .sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime()); 26 56 27 57 return { events, loggedIn: true };
+16 -7
src/routes/calendar/+page.svelte
··· 11 11 </svelte:head> 12 12 13 13 <div class="mx-auto max-w-3xl px-6 py-8 sm:py-12"> 14 - <h1 class="text-base-900 dark:text-base-50 mb-8 text-2xl font-bold">Calendar</h1> 14 + <h1 class="text-base-900 dark:text-base-50 mb-2 text-2xl font-bold">Calendar</h1> 15 + <h1 class="text-base-700 dark:text-base-300 mb-8 text-sm"> 16 + Upcoming events you're hosting, attending or interested in 17 + </h1> 15 18 16 19 {#if !data.loggedIn} 17 20 <div 18 - class="border-base-200 dark:border-base-800 bg-base-100 dark:bg-base-900/50 rounded-2xl border p-8 text-center" 21 + class="border-base-200 dark:border-base-800 bg-base-100 dark:bg-base-950/50 rounded-2xl border p-8 text-center" 19 22 > 20 - <p class="text-base-600 dark:text-base-400 mb-4">Log in to see events you're attending.</p> 23 + <p class="text-base-600 dark:text-base-400 mb-4">Log in to see your events</p> 21 24 <Button onclick={() => atProtoLoginModalState.show()}>Login</Button> 22 25 </div> 23 26 {:else if data.events.length === 0} 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> 27 + <div 28 + class="border-base-200 dark:border-base-800 bg-base-100 dark:bg-base-950/50 rounded-2xl border p-8 text-center" 29 + > 30 + <p class="text-base-600 dark:text-base-400 text-center text-sm"> 31 + No upcoming events on your calendar. 32 + </p> 33 + <div class="mt-6 flex justify-center gap-3"> 34 + <Button href="/">Join events</Button> 35 + <Button href="/create" variant="secondary">Create event</Button> 36 + </div> 28 37 </div> 29 38 {:else} 30 39 <div class="grid gap-6 sm:grid-cols-2">
-1
src/routes/login/+page.svelte
··· 1 1 <script lang="ts"> 2 - import { Button } from '@foxui/core'; 3 2 import { atProtoLoginModalState } from '@foxui/social'; 4 3 import { onMount } from 'svelte'; 5 4
+4 -1
src/routes/p/[actor]/+page.server.ts
··· 43 43 listAttendingEventsFromContrail(actor) 44 44 ]); 45 45 46 + const nowDate = new Date(now); 46 47 const upcomingEvents = upcomingResponse ? flattenEventRecords(upcomingResponse.records) : []; 47 - const pastEvents = pastResponse ? flattenEventRecords(pastResponse.records) : []; 48 + const pastEvents = (pastResponse ? flattenEventRecords(pastResponse.records) : []).filter( 49 + (e) => new Date(e.endsAt || e.startsAt) < nowDate 50 + ); 48 51 49 52 return { 50 53 upcomingEvents: upcomingEvents.slice(0, PREVIEW_LIMIT),
+9 -7
src/routes/p/[actor]/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import { getProfileBlobUrl, type FlatEventRecord } from '$lib/contrail'; 3 - import { user } from '$lib/atproto/auth.svelte'; 4 - import { UserProfile } from '@foxui/social'; 3 + import { user, logout } from '$lib/atproto/auth.svelte'; 4 + import UserProfile from '$lib/components/UserProfile.svelte'; 5 5 import { Button } from '@foxui/core'; 6 6 import EventCard from '$lib/components/EventCard.svelte'; 7 7 ··· 42 42 <div class="mx-auto max-w-2xl"> 43 43 <!-- Header --> 44 44 <UserProfile 45 - class="" 46 45 profile={{ 47 46 handle: hostProfile?.handle, 48 47 displayName: hostName, 49 48 avatar: hostProfile?.record?.avatar 50 49 ? getProfileBlobUrl(hostDid, hostProfile.record.avatar) 51 - : undefined, 52 - banner: hostProfile?.record?.banner 53 - ? getProfileBlobUrl(hostDid, hostProfile.record.banner) 54 50 : undefined 55 51 }} 56 - /> 52 + > 53 + {#snippet actions()} 54 + {#if isOwnProfile} 55 + <Button onclick={logout} variant="rose">Logout</Button> 56 + {/if} 57 + {/snippet} 58 + </UserProfile> 57 59 58 60 {#if isOwnProfile} 59 61 <Button href="/create" class="-mt-6 mb-6" size="lg">Create Event</Button>
+18 -8
src/routes/p/[actor]/e/[rkey]/+page.svelte
··· 1 1 <script lang="ts"> 2 - import type { FlatEventRecord } from '$lib/contrail'; 2 + import { isEventOngoing, type FlatEventRecord } from '$lib/contrail'; 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'; ··· 152 152 startDate.getMonth() === endDate.getMonth() && 153 153 startDate.getDate() === endDate.getDate() 154 154 ); 155 + 156 + let isOngoing = $derived(isEventOngoing(eventData.startsAt, eventData.endsAt)); 155 157 156 158 const renderer = new marked.Renderer(); 157 159 renderer.link = ({ href, text }) => ··· 295 297 <img 296 298 src={displayImage.url} 297 299 alt={displayImage.alt} 298 - class="border-base-200 dark:border-base-800 aspect-square w-full rounded-2xl border object-cover" 300 + class="border-base-200 dark:border-base-800 bg-base-200 dark:bg-base-950/50 aspect-square w-full rounded-2xl border object-cover" 299 301 /> 300 302 {:else} 301 303 <div ··· 324 326 </h1> 325 327 </div> 326 328 327 - <!-- Mode badge --> 328 - {#if eventData.mode} 329 - <div class="mb-8"> 330 - <Badge size="md" variant={getModeColor(eventData.mode)} 331 - >{getModeLabel(eventData.mode)}</Badge 332 - > 329 + <!-- Badges --> 330 + {#if eventData.mode || isOngoing} 331 + <div class="mb-8 flex items-center gap-2"> 332 + {#if isOngoing} 333 + <Badge size="md" variant="primary"> 334 + <span class="mr-1 inline-block size-1.5 animate-pulse rounded-full bg-accent-500"></span> 335 + Live 336 + </Badge> 337 + {/if} 338 + {#if eventData.mode} 339 + <Badge size="md" variant={getModeColor(eventData.mode)} 340 + >{getModeLabel(eventData.mode)}</Badge 341 + > 342 + {/if} 333 343 </div> 334 344 {/if} 335 345
+1
src/routes/p/[actor]/e/[rkey]/EventRsvp.svelte
··· 41 41 rkey: key, 42 42 record: { 43 43 $type: 'community.lexicon.calendar.rsvp', 44 + createdWith: 'https://atmo.rsvp', 44 45 status: `community.lexicon.calendar.rsvp#${status}`, 45 46 subject: { 46 47 uri: eventUri,
+6 -1
src/routes/p/[actor]/past-events/+page.server.ts
··· 33 33 }) 34 34 ]); 35 35 36 + const nowDate = new Date(now); 37 + const events = (response ? flattenEventRecords(response.records) : []).filter( 38 + (e) => new Date(e.endsAt || e.startsAt) < nowDate 39 + ); 40 + 36 41 return { 37 - events: response ? flattenEventRecords(response.records) : [], 42 + events, 38 43 cursor: response?.cursor ?? null, 39 44 actorProfile: profile, 40 45 actor,