Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

I LOVE ATPROTO-UI AND YOU SHOULD USE IT TOO

+25
bun.lock
··· 20 20 "@radix-ui/react-slot": "^1.2.3", 21 21 "@radix-ui/react-tabs": "^1.1.13", 22 22 "@tanstack/react-query": "^5.90.2", 23 + "atproto-ui": "^0.11.1", 23 24 "class-variance-authority": "^0.7.1", 24 25 "clsx": "^2.1.1", 25 26 "elysia": "latest", ··· 51 52 "protobufjs", 52 53 ], 53 54 "packages": { 55 + "@atcute/atproto": ["@atcute/atproto@3.1.9", "", { "dependencies": { "@atcute/lexicons": "^1.2.2" } }, "sha512-DyWwHCTdR4hY2BPNbLXgVmm7lI+fceOwWbE4LXbGvbvVtSn+ejSVFaAv01Ra3kWDha0whsOmbJL8JP0QPpf1+w=="], 56 + 57 + "@atcute/bluesky": ["@atcute/bluesky@3.2.10", "", { "dependencies": { "@atcute/atproto": "^3.1.9", "@atcute/lexicons": "^1.2.2" } }, "sha512-qwQWTzRf3umnh2u41gdU+xWYkbzGlKDupc3zeOB+YjmuP1N9wEaUhwS8H7vgrqr0xC9SGNDjeUVcjC4m5BPLBg=="], 58 + 59 + "@atcute/client": ["@atcute/client@4.0.5", "", { "dependencies": { "@atcute/identity": "^1.1.1", "@atcute/lexicons": "^1.2.2" } }, "sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA=="], 60 + 61 + "@atcute/identity": ["@atcute/identity@1.1.1", "", { "dependencies": { "@atcute/lexicons": "^1.2.2", "@badrap/valita": "^0.4.6" } }, "sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q=="], 62 + 63 + "@atcute/identity-resolver": ["@atcute/identity-resolver@1.1.4", "", { "dependencies": { "@atcute/lexicons": "^1.2.2", "@atcute/util-fetch": "^1.0.3", "@badrap/valita": "^0.4.6" }, "peerDependencies": { "@atcute/identity": "^1.0.0" } }, "sha512-/SVh8vf2cXFJenmBnGeYF2aY3WGQm3cJeew5NWTlkqoy3LvJ5wkvKq9PWu4Tv653VF40rPOp6LOdVr9Fa+q5rA=="], 64 + 65 + "@atcute/lexicons": ["@atcute/lexicons@1.2.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "esm-env": "^1.2.2" } }, "sha512-bgEhJq5Z70/0TbK5sx+tAkrR8FsCODNiL2gUEvS5PuJfPxmFmRYNWaMGehxSPaXWpU2+Oa9ckceHiYbrItDTkA=="], 66 + 67 + "@atcute/tangled": ["@atcute/tangled@1.0.10", "", { "dependencies": { "@atcute/atproto": "^3.1.8", "@atcute/lexicons": "^1.2.2" } }, "sha512-DGconZIN5TpLBah+aHGbWI1tMsL7XzyVEbr/fW4CbcLWYKICU6SAUZ0YnZ+5GvltjlORWHUy7hfftvoh4zodIA=="], 68 + 69 + "@atcute/util-fetch": ["@atcute/util-fetch@1.0.3", "", { "dependencies": { "@badrap/valita": "^0.4.6" } }, "sha512-f8zzTb/xlKIwv2OQ31DhShPUNCmIIleX6p7qIXwWwEUjX6x8skUtpdISSjnImq01LXpltGV5y8yhV4/Mlb7CRQ=="], 70 + 54 71 "@atproto-labs/did-resolver": ["@atproto-labs/did-resolver@0.2.2", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.1", "zod": "^3.23.8" } }, "sha512-ca2B7xR43tVoQ8XxBvha58DXwIH8cIyKQl6lpOKGkPUrJuFoO4iCLlDiSDi2Ueh+yE1rMDPP/qveHdajgDX3WQ=="], 55 72 56 73 "@atproto-labs/fetch": ["@atproto-labs/fetch@0.2.3", "", { "dependencies": { "@atproto-labs/pipe": "0.1.1" } }, "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw=="], ··· 100 117 "@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA=="], 101 118 102 119 "@atproto/xrpc-server": ["@atproto/xrpc-server@0.9.5", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/crypto": "^0.4.4", "@atproto/lexicon": "^0.5.1", "@atproto/xrpc": "^0.7.5", "cbor-x": "^1.5.1", "express": "^4.17.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "rate-limiter-flexible": "^2.4.1", "uint8arrays": "3.0.0", "ws": "^8.12.0", "zod": "^3.23.8" } }, "sha512-V0srjUgy6mQ5yf9+MSNBLs457m4qclEaWZsnqIE7RfYywvntexTAbMoo7J7ONfTNwdmA9Gw4oLak2z2cDAET4w=="], 120 + 121 + "@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="], 103 122 104 123 "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="], 105 124 ··· 341 360 342 361 "@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="], 343 362 363 + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], 364 + 344 365 "@tanstack/query-core": ["@tanstack/query-core@5.90.7", "", {}, "sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ=="], 345 366 346 367 "@tanstack/react-query": ["@tanstack/react-query@5.90.7", "", { "dependencies": { "@tanstack/query-core": "5.90.7" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ=="], ··· 376 397 "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], 377 398 378 399 "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], 400 + 401 + "atproto-ui": ["atproto-ui@0.11.1", "", { "dependencies": { "@atcute/atproto": "^3.1.7", "@atcute/bluesky": "^3.2.3", "@atcute/client": "^4.0.3", "@atcute/identity-resolver": "^1.1.3", "@atcute/tangled": "^1.0.10" }, "peerDependencies": { "react": "^18.2.0 || ^19.0.0", "react-dom": "^18.2.0 || ^19.0.0" }, "optionalPeers": ["react-dom"] }, "sha512-RpX9OGx3GDw0uL2X0Lw0bgzqEKKhfMeFuTUIgJuAa3W3MlLBH6h4qOWzaHXdrVQpru+6SQ0HznfRlQHK6nYRkQ=="], 379 402 380 403 "await-lock": ["await-lock@2.2.2", "", {}, "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="], 381 404 ··· 468 491 "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 469 492 470 493 "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], 494 + 495 + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 471 496 472 497 "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], 473 498
+1
package.json
··· 24 24 "@radix-ui/react-slot": "^1.2.3", 25 25 "@radix-ui/react-tabs": "^1.1.13", 26 26 "@tanstack/react-query": "^5.90.2", 27 + "atproto-ui": "^0.11.1", 27 28 "class-variance-authority": "^0.7.1", 28 29 "clsx": "^2.1.1", 29 30 "elysia": "latest",
+1 -4
public/editor/editor.tsx
··· 19 19 import { Label } from '@public/components/ui/label' 20 20 import { Badge } from '@public/components/ui/badge' 21 21 import { 22 - Globe, 23 22 Loader2, 24 23 Trash2, 25 24 LogOut ··· 189 188 <header className="border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 190 189 <div className="container mx-auto px-4 py-4 flex items-center justify-between"> 191 190 <div className="flex items-center gap-2"> 192 - <div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center"> 193 - <Globe className="w-5 h-5 text-primary-foreground" /> 194 - </div> 191 + <img src="/transparent-full-size-ico.png" alt="wisp.place" className="w-8 h-8" /> 195 192 <span className="text-xl font-semibold text-foreground"> 196 193 wisp.place 197 194 </span>
+74 -6
public/index.tsx
··· 13 13 import Layout from '@public/layouts' 14 14 import { Button } from '@public/components/ui/button' 15 15 import { Card } from '@public/components/ui/card' 16 + import { BlueskyPostList, BlueskyProfile, BlueskyPost, AtProtoProvider, useLatestRecord, type AtProtoStyles, type FeedPostRecord } from 'atproto-ui' 17 + import 'atproto-ui/styles.css' 18 + 19 + const LatestPostWithPrefetch: React.FC<{ did: string }> = ({ did }) => { 20 + // Fetch once with the hook 21 + const { record, rkey, loading } = useLatestRecord<FeedPostRecord>( 22 + did, 23 + 'app.bsky.feed.post' 24 + ) 25 + 26 + if (loading) return <span>Loading…</span> 27 + if (!record || !rkey) return <span>No posts yet.</span> 28 + 29 + // Pass prefetched record—BlueskyPost won't re-fetch it 30 + return <BlueskyPost did={did} rkey={rkey} record={record} showParent={true} /> 31 + } 16 32 17 33 function App() { 18 34 const [showForm, setShowForm] = useState(false) ··· 63 79 <header className="border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 64 80 <div className="container mx-auto px-4 py-4 flex items-center justify-between"> 65 81 <div className="flex items-center gap-2"> 66 - <div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center"> 67 - <Globe className="w-5 h-5 text-primary-foreground" /> 68 - </div> 82 + <img src="/transparent-full-size-ico.png" alt="wisp.place" className="w-8 h-8" /> 69 83 <span className="text-xl font-semibold text-foreground"> 70 84 wisp.place 71 85 </span> ··· 81 95 <Button 82 96 size="sm" 83 97 className="bg-accent text-accent-foreground hover:bg-accent/90" 98 + onClick={() => setShowForm(true)} 84 99 > 85 100 Get Started 86 101 </Button> ··· 318 333 319 334 {/* CTA Section */} 320 335 <section className="container mx-auto px-4 py-20"> 336 + <div className="max-w-6xl mx-auto"> 337 + <div className="text-center mb-12"> 338 + <h2 className="text-3xl md:text-4xl font-bold"> 339 + Follow on Bluesky for updates 340 + </h2> 341 + </div> 342 + <div className="grid md:grid-cols-2 gap-8 items-center"> 343 + <Card 344 + className="shadow-lg border-2 border-border overflow-hidden !py-3" 345 + style={{ 346 + '--atproto-color-bg': 'var(--card)', 347 + '--atproto-color-bg-elevated': 'hsl(var(--muted) / 0.3)', 348 + '--atproto-color-text': 'hsl(var(--foreground))', 349 + '--atproto-color-text-secondary': 'hsl(var(--muted-foreground))', 350 + '--atproto-color-link': 'hsl(var(--accent))', 351 + '--atproto-color-link-hover': 'hsl(var(--accent))', 352 + '--atproto-color-border': 'transparent', 353 + } as AtProtoStyles} 354 + > 355 + <BlueskyPostList did="wisp.place" /> 356 + </Card> 357 + <div className="space-y-6 w-full max-w-md mx-auto"> 358 + <Card 359 + className="shadow-lg border-2 overflow-hidden relative !py-3" 360 + style={{ 361 + '--atproto-color-bg': 'var(--card)', 362 + '--atproto-color-bg-elevated': 'hsl(var(--muted) / 0.3)', 363 + '--atproto-color-text': 'hsl(var(--foreground))', 364 + '--atproto-color-text-secondary': 'hsl(var(--muted-foreground))', 365 + } as AtProtoStyles} 366 + > 367 + <BlueskyProfile did="wisp.place" /> 368 + </Card> 369 + <Card 370 + className="shadow-lg border-2 overflow-hidden relative !py-3" 371 + style={{ 372 + '--atproto-color-bg': 'var(--card)', 373 + '--atproto-color-bg-elevated': 'hsl(var(--muted) / 0.3)', 374 + '--atproto-color-text': 'hsl(var(--foreground))', 375 + '--atproto-color-text-secondary': 'hsl(var(--muted-foreground))', 376 + } as AtProtoStyles} 377 + > 378 + <LatestPostWithPrefetch did="wisp.place" /> 379 + </Card> 380 + </div> 381 + </div> 382 + </div> 383 + </section> 384 + 385 + {/* Ready to Deploy CTA */} 386 + <section className="container mx-auto px-4 py-20"> 321 387 <div className="max-w-3xl mx-auto text-center bg-accent/5 border border-accent/20 rounded-2xl p-12"> 322 388 <h2 className="text-3xl md:text-4xl font-bold mb-4"> 323 389 Ready to deploy? ··· 362 428 363 429 const root = createRoot(document.getElementById('elysia')!) 364 430 root.render( 365 - <Layout className="gap-6"> 366 - <App /> 367 - </Layout> 431 + <AtProtoProvider> 432 + <Layout className="gap-6"> 433 + <App /> 434 + </Layout> 435 + </AtProtoProvider> 368 436 )
public/transparent-full-size-ico.png

This is a binary file and will not be displayed.