atmosphere explorer

new homepage showcase

handle.invalid 2526464f 80992ef7

verified
+118 -75
public/avatar/angelicimouto.moe.observer.jpg

This is a binary file and will not be displayed.

public/avatar/anyaustin.bsky.social.jpg

This is a binary file and will not be displayed.

public/avatar/aylac.top.jpg

This is a binary file and will not be displayed.

public/avatar/blooym.dev.jpg

This is a binary file and will not be displayed.

public/avatar/coil-habdle.ebil.club.jpg

This is a binary file and will not be displayed.

public/avatar/computer.fish.jpg

This is a binary file and will not be displayed.

public/avatar/cwonus.org.jpg

This is a binary file and will not be displayed.

public/avatar/dreary.blacksky.app.jpg

This is a binary file and will not be displayed.

public/avatar/drunk.moe.jpg

This is a binary file and will not be displayed.

public/avatar/emilia.wtf.jpg

This is a binary file and will not be displayed.

public/avatar/futanari.observer.jpg

This is a binary file and will not be displayed.

public/avatar/futur.blue.jpg

This is a binary file and will not be displayed.

public/avatar/hailey.at.jpg

This is a binary file and will not be displayed.

public/avatar/isabelroses.com.jpg

This is a binary file and will not be displayed.

public/avatar/isla.pet.jpg

This is a binary file and will not be displayed.

public/avatar/isuggest.selfce.st.jpg

This is a binary file and will not be displayed.

public/avatar/jaz.sh.jpg

This is a binary file and will not be displayed.

public/avatar/mary.my.id.jpg

This is a binary file and will not be displayed.

public/avatar/mofu.run.jpg

This is a binary file and will not be displayed.

public/avatar/natalie.sh.jpg

This is a binary file and will not be displayed.

public/avatar/neko.moe.observer.jpg

This is a binary file and will not be displayed.

public/avatar/nekomimi.pet.jpg

This is a binary file and will not be displayed.

public/avatar/nel.pet.jpg

This is a binary file and will not be displayed.

public/avatar/notnite.com.jpg

This is a binary file and will not be displayed.

public/avatar/nullekko.moe.jpg

This is a binary file and will not be displayed.

public/avatar/number-one-warned.rat.mom.jpg

This is a binary file and will not be displayed.

public/avatar/olaren.dev.jpg

This is a binary file and will not be displayed.

public/avatar/paizuri.moe.jpg

This is a binary file and will not be displayed.

public/avatar/ptr.pet.jpg

This is a binary file and will not be displayed.

public/avatar/quilling.dev.jpg

This is a binary file and will not be displayed.

public/avatar/rainy.pet.jpg

This is a binary file and will not be displayed.

public/avatar/retr0.id.jpg

This is a binary file and will not be displayed.

public/avatar/sapphic.moe.jpg

This is a binary file and will not be displayed.

public/avatar/shi.gg.jpg

This is a binary file and will not be displayed.

public/avatar/slug.moe.jpg

This is a binary file and will not be displayed.

+118 -75
src/views/home.tsx
··· 1 1 import { A } from "@solidjs/router"; 2 - import { For, JSX } from "solid-js"; 2 + import { createSignal, For, JSX, onCleanup, onMount } from "solid-js"; 3 3 import { setOpenManager, setShowAddAccount } from "../auth/state"; 4 4 import { Button } from "../components/button"; 5 + import { Favicon } from "../components/favicon"; 6 + import { JSONValue } from "../components/json"; 5 7 import { SearchButton } from "../components/search"; 6 8 7 - type ProfileData = { 8 - did: string; 9 - handle: string; 9 + const SLIDES = ["Record", "Repository", "PDS"] as const; 10 + 11 + const slideLinks = [ 12 + "/at://did:plc:ia76kvnndjutgedggx2ibrem/app.bsky.feed.post/3kenlltlvus2u", 13 + "/at://did:plc:vwzwgnygau7ed7b7wt5ux7y2", 14 + "/npmx.social", 15 + ] as const; 16 + 17 + const exampleRecord = { 18 + text: "ma'am do you know where the petard is, i'd like to hoist myself with it", 19 + $type: "app.bsky.feed.post", 20 + langs: ["en"], 21 + createdAt: "2023-11-20T21:44:21.000Z", 22 + }; 23 + 24 + const exampleCollections = [ 25 + { authority: "app.bsky", nsids: ["actor.profile", "feed.post", "feed.like", "graph.follow"] }, 26 + { authority: "sh.tangled", nsids: ["actor.profile", "repo.pull"] }, 27 + { authority: "place.stream", nsids: ["chat.message"] }, 28 + ]; 29 + 30 + const exampleRepos = [ 31 + "did:plc:ty2jdjtqqq4jn7kk7p3mpwae", 32 + "did:plc:byfvayavc7z2ldyu6bu5myz2", 33 + "did:plc:n34gdj7o3o6ktuxp5qfbgllu", 34 + "did:plc:vh7y4mqklsu2uui5tlwl42dy", 35 + "did:plc:uz76j2yr2ps7apdxtlgqljwk", 36 + ]; 37 + 38 + const ExplorerShowcase = () => { 39 + const [slide, setSlide] = createSignal(0); 40 + 41 + onMount(() => { 42 + const id = setInterval(() => setSlide((s) => (s + 1) % SLIDES.length), 5000); 43 + onCleanup(() => clearInterval(id)); 44 + }); 45 + 46 + return ( 47 + <div class="flex flex-col gap-1.5"> 48 + <A 49 + href={slideLinks[slide()]} 50 + class="relative block h-42 overflow-hidden rounded-lg border border-neutral-200 bg-neutral-50 transition-colors hover:border-neutral-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600" 51 + > 52 + {/* Record slide */} 53 + <div 54 + class="pointer-events-none absolute inset-0 overflow-hidden px-3 py-2 font-mono text-xs wrap-anywhere whitespace-pre-wrap transition-opacity duration-700 sm:text-sm" 55 + classList={{ "opacity-0": slide() !== 0 }} 56 + > 57 + <JSONValue data={exampleRecord as any} repo="did:plc:ia76kvnndjutgedggx2ibrem" /> 58 + </div> 59 + 60 + {/* Collections slide */} 61 + <div 62 + class="pointer-events-none absolute inset-0 flex flex-col gap-1 overflow-hidden px-3 py-2 text-sm wrap-anywhere transition-opacity duration-700" 63 + classList={{ "opacity-0": slide() !== 1 }} 64 + > 65 + <For each={exampleCollections}> 66 + {({ authority, nsids }) => ( 67 + <div class="flex items-start gap-2"> 68 + <Favicon authority={authority} /> 69 + <div class="flex flex-col"> 70 + <For each={nsids}> 71 + {(nsid) => ( 72 + <span> 73 + <span class="text-neutral-500 dark:text-neutral-400">{authority}.</span> 74 + <span>{nsid}</span> 75 + </span> 76 + )} 77 + </For> 78 + </div> 79 + </div> 80 + )} 81 + </For> 82 + </div> 83 + 84 + {/* Repos slide */} 85 + <div 86 + class="pointer-events-none absolute inset-0 overflow-hidden py-0.5 transition-opacity duration-700" 87 + classList={{ "opacity-0": slide() !== 2 }} 88 + > 89 + <For each={exampleRepos}> 90 + {(did) => ( 91 + <div class="flex min-w-0 items-center gap-2 p-1.5 font-mono text-sm"> 92 + <span class="flex shrink-0 items-center text-neutral-400 dark:text-neutral-500"> 93 + <span class="iconify lucide--chevron-right" /> 94 + </span> 95 + <span class="truncate text-blue-500 dark:text-blue-400">{did}</span> 96 + </div> 97 + )} 98 + </For> 99 + </div> 100 + </A> 101 + 102 + {/* Slide indicator */} 103 + <div class="flex items-center justify-between px-0.5"> 104 + <span class="text-xs text-neutral-400 dark:text-neutral-500">{SLIDES[slide()]}</span> 105 + <div class="flex gap-1"> 106 + <For each={SLIDES}> 107 + {(_, i) => ( 108 + <button 109 + onClick={() => setSlide(i())} 110 + class="h-1 rounded-full transition-all duration-300" 111 + classList={{ 112 + "w-4 bg-neutral-400 dark:bg-neutral-500": slide() === i(), 113 + "w-1.5 bg-neutral-300 dark:bg-neutral-600": slide() !== i(), 114 + }} 115 + /> 116 + )} 117 + </For> 118 + </div> 119 + </div> 120 + </div> 121 + ); 10 122 }; 11 123 12 124 export const Home = () => { ··· 25 137 </a> 26 138 ); 27 139 28 - const allExampleProfiles: ProfileData[] = [ 29 - { did: "did:plc:oisofpd7lj26yvgiivf3lxsi", handle: "hailey.at" }, 30 - { did: "did:plc:vwzwgnygau7ed7b7wt5ux7y2", handle: "retr0.id" }, 31 - { did: "did:plc:uu5axsmbm2or2dngy4gwchec", handle: "futur.blue" }, 32 - { did: "did:plc:ia76kvnndjutgedggx2ibrem", handle: "mary.my.id" }, 33 - { did: "did:plc:q6gjnaw2blty4crticxkmujt", handle: "jaz.sh" }, 34 - { did: "did:plc:jrtgsidnmxaen4offglr5lsh", handle: "quilling.dev" }, 35 - { did: "did:plc:3c6vkaq7xf5kz3va3muptjh5", handle: "aylac.top" }, 36 - { did: "did:plc:gwd5r7dbg3zv6dhv75hboa3f", handle: "mofu.run" }, 37 - { did: "did:plc:tzrpqyerzt37pyj54hh52xrz", handle: "rainy.pet" }, 38 - { did: "did:plc:qx7in36j344d7qqpebfiqtew", handle: "futanari.observer" }, 39 - { did: "did:plc:ucaezectmpny7l42baeyooxi", handle: "sapphic.moe" }, 40 - { did: "did:plc:6v6jqsy7swpzuu53rmzaybjy", handle: "computer.fish" }, 41 - { did: "did:plc:w4nvvt6feq2l3qgnwl6a7g7d", handle: "emilia.wtf" }, 42 - { did: "did:plc:xwhsmuozq3mlsp56dyd7copv", handle: "paizuri.moe" }, 43 - { did: "did:plc:aokggmp5jzj4nc5jifhiplqc", handle: "dreary.blacksky.app" }, 44 - { did: "did:plc:k644h4rq5bjfzcetgsa6tuby", handle: "natalie.sh" }, 45 - { did: "did:plc:ttdrpj45ibqunmfhdsb4zdwq", handle: "nekomimi.pet" }, 46 - { did: "did:plc:fz2tul67ziakfukcwa3vdd5d", handle: "nullekko.moe" }, 47 - { did: "did:plc:qxichs7jsycphrsmbujwqbfb", handle: "isabelroses.com" }, 48 - { did: "did:plc:fnvdhaoe7b5abgrtvzf4ttl5", handle: "isuggest.selfce.st" }, 49 - { did: "did:plc:p5yjdr64h7mk5l3kh6oszryk", handle: "blooym.dev" }, 50 - { did: "did:plc:hvakvedv6byxhufjl23mfmsd", handle: "number-one-warned.rat.mom" }, 51 - { did: "did:plc:6if5m2yo6kroprmmency3gt5", handle: "olaren.dev" }, 52 - { did: "did:plc:w7adfxpixpi77e424cjjxnxy", handle: "anyaustin.bsky.social" }, 53 - { did: "did:plc:h6as5sk7tfqvvnqvfrlnnwqn", handle: "cwonus.org" }, 54 - { did: "did:plc:73gqgbnvpx5syidcponjrics", handle: "coil-habdle.ebil.club" }, 55 - { did: "did:plc:gy5roooborfiyvl2xadsam3e", handle: "slug.moe" }, 56 - { did: "did:plc:dadnngq7hpnuglhxm556wgzi", handle: "drunk.moe" }, 57 - { did: "did:plc:ra3gxl2udc22odfbvcfslcn3", handle: "notnite.com" }, 58 - { did: "did:plc:h5wsnqetncv6lu2weom35lg2", handle: "nel.pet" }, 59 - { did: "did:plc:irs2tcoeuvuwj3m4yampbuco", handle: "shi.gg" }, 60 - { did: "did:plc:vafqb3yhndyawabm2t2zhw5z", handle: "neko.moe.observer" }, 61 - { did: "did:plc:mppmy46nmna6j4y7o3qlk7vn", handle: "isla.pet" }, 62 - { did: "did:plc:3l526xzfvz5pnwepjxx3inq5", handle: "angelicimouto.moe.observer" }, 63 - { did: "did:plc:dfl62fgb7wtjj3fcbb72naae", handle: "ptr.pet" }, 64 - ]; 65 - 66 - for (let i = allExampleProfiles.length - 1; i > 0; i--) { 67 - const j = Math.floor(Math.random() * (i + 1)); 68 - [allExampleProfiles[i], allExampleProfiles[j]] = [allExampleProfiles[j], allExampleProfiles[i]]; 69 - } 70 - const profiles = allExampleProfiles.slice(0, 3); 71 - 72 140 return ( 73 141 <div class="flex w-full flex-col gap-5 px-2 wrap-break-word"> 74 142 {/* Welcome Section */} ··· 89 157 </div> 90 158 </div> 91 159 92 - {/* Example Repos */} 93 - <section class="mb-1 flex flex-col gap-3"> 94 - <div class="flex justify-between"> 95 - <For each={profiles}> 96 - {(profile) => ( 97 - <A 98 - href={`/at://${profile.did}`} 99 - class="group flex min-w-0 basis-1/3 flex-col items-center gap-1.5 transition-transform hover:scale-105 active:scale-105" 100 - > 101 - <img 102 - src={`/avatar/${profile.handle}.jpg`} 103 - alt={`Bluesky profile picture of ${profile.handle}`} 104 - class="size-16 rounded-full ring-2 ring-transparent transition-all group-hover:ring-blue-500 active:ring-blue-500 dark:group-hover:ring-blue-400 dark:active:ring-blue-400" 105 - classList={{ 106 - "animate-[spin_5s_linear_infinite] [animation-play-state:paused] group-hover:[animation-play-state:running]": 107 - profile.handle === "coil-habdle.ebil.club", 108 - "rounded-lg": profile.handle === "ptr.pet", 109 - }} 110 - /> 111 - <span class="w-full truncate px-0.5 text-center text-xs text-neutral-600 dark:text-neutral-300"> 112 - @{profile.handle} 113 - </span> 114 - </A> 115 - )} 116 - </For> 117 - </div> 118 - </section> 160 + <ExplorerShowcase /> 161 + 119 162 <div class="flex items-center gap-1.5 text-xs text-neutral-500 dark:text-neutral-400"> 120 163 <SearchButton /> 121 164 <span>to find any account</span>