atproto explorer

MY BRAIN ON 3 HOURS OF SLEEP AND INDUSTRIAL MUSIC (wide layout)

handle.invalid 223630d7 b5787c9f

verified
+228 -195
+1 -1
src/components/account.tsx
··· 68 68 return ( 69 69 <> 70 70 <Modal open={openManager()} onClose={() => setOpenManager(false)}> 71 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-12 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-300 sm:w-[24rem] dark:border-neutral-700 starting:opacity-0"> 71 + <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-16 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 72 72 <div class="mb-2 flex items-center gap-1 font-semibold"> 73 73 <span class="iconify lucide--user-round"></span> 74 74 <span>Manage accounts</span>
+1 -1
src/components/create.tsx
··· 164 164 return ( 165 165 <> 166 166 <Modal open={openDialog()} onClose={() => setOpenDialog(false)} closeOnClick={false}> 167 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-12 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 shadow-md transition-opacity duration-300 sm:w-xl sm:p-4 lg:w-[48rem] dark:border-neutral-700 starting:opacity-0"> 167 + <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-16 left-[50%] w-screen -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-xl lg:w-[48rem] dark:border-neutral-700 starting:opacity-0"> 168 168 <div class="mb-2 flex w-full justify-between"> 169 169 <div class="flex items-center gap-1 font-semibold"> 170 170 <span
+1 -1
src/components/navbar.tsx
··· 52 52 }); 53 53 54 54 return ( 55 - <nav class="flex w-[22rem] flex-col text-sm wrap-anywhere sm:w-[24rem]"> 55 + <nav class="flex w-full flex-col px-2 text-sm wrap-anywhere"> 56 56 <div class="relative flex items-center justify-between gap-1"> 57 57 <div class="flex min-h-[1.25rem] basis-full items-center gap-2"> 58 58 <Tooltip text="PDS">
+4 -4
src/components/search.tsx
··· 25 25 return ( 26 26 <button 27 27 onclick={() => setShowSearch(!showSearch())} 28 - class={`flex items-center gap-0.5 rounded-lg ${isTouchDevice ? "p-1 text-xl hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" : "dark:bg-dark-200 bg-neutral-200 p-1.5 text-xs hover:bg-neutral-300/80 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"}`} 28 + class={`flex items-center gap-0.5 rounded-lg ${isTouchDevice ? "p-1 text-xl hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" : "dark:bg-dark-100 box-border h-7 border-[0.5px] border-neutral-300 bg-neutral-100 p-1.5 text-xs hover:bg-neutral-200 active:bg-neutral-300 dark:border-neutral-700 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"}`} 29 29 > 30 30 <span class="iconify lucide--search"></span> 31 31 <Show when={!isTouchDevice}> ··· 90 90 91 91 return ( 92 92 <form 93 - class="relative w-[22rem] sm:w-[24rem]" 93 + class="relative w-full" 94 94 onsubmit={(e) => { 95 95 e.preventDefault(); 96 96 processInput(searchInput.value); ··· 125 125 </Show> 126 126 </div> 127 127 <Show when={search()?.length && input()}> 128 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute z-30 mt-1 flex w-full flex-col rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-1 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 128 + <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute z-30 mt-1 flex w-full flex-col rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 129 129 <For each={search()}> 130 130 {(actor) => ( 131 131 <A ··· 135 135 > 136 136 <img 137 137 src={actor.avatar?.replace("img/avatar/", "img/avatar_thumbnail/")} 138 - class="size-6 rounded-full" 138 + class="size-8 rounded-full" 139 139 /> 140 140 <span>{actor.handle}</span> 141 141 </A>
+1 -1
src/components/sticky.tsx
··· 29 29 /> 30 30 31 31 <div 32 - class="sticky top-2 z-10 flex flex-col items-center justify-center gap-2 rounded-lg p-3 transition-colors" 32 + class="sticky top-2 z-10 flex w-full flex-col items-center justify-center gap-2 rounded-lg p-3 transition-colors" 33 33 classList={{ 34 34 "bg-neutral-50 dark:bg-dark-300 border-[0.5px] border-neutral-300 dark:border-neutral-700 shadow-md": 35 35 filterStuck(),
+4 -4
src/layout.tsx
··· 45 45 return ( 46 46 <div 47 47 id="main" 48 - class="m-4 mb-8 flex flex-col items-center text-neutral-900 dark:text-neutral-200" 48 + class="mx-auto mb-8 flex max-w-lg flex-col items-center p-4 text-neutral-900 dark:text-neutral-200" 49 49 > 50 50 <MetaProvider> 51 51 <Show when={location.pathname !== "/"}> 52 52 <Meta name="robots" content="noindex, nofollow" /> 53 53 </Show> 54 54 </MetaProvider> 55 - <header class="mb-4 flex w-[22rem] items-center justify-between sm:w-[24rem]"> 55 + <header class="dark:shadow-dark-800 dark:bg-dark-300 mb-4 flex w-full items-center justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 shadow-xs dark:border-neutral-700"> 56 56 <A 57 57 href="/" 58 58 style='font-feature-settings: "cv05"' ··· 73 73 <DropdownMenu 74 74 icon="lucide--menu text-xl" 75 75 buttonClass="rounded-lg p-1" 76 - menuClass="top-8 p-3 text-sm" 76 + menuClass="top-10 p-3" 77 77 > 78 78 <NavMenu href="/jetstream" label="Jetstream" icon="lucide--radio-tower" /> 79 79 <NavMenu href="/firehose" label="Firehose" icon="lucide--waves" /> ··· 83 83 </MenuProvider> 84 84 </div> 85 85 </header> 86 - <div class="flex max-w-full min-w-[22rem] flex-col items-center gap-4 text-pretty sm:min-w-[24rem] md:max-w-[48rem]"> 86 + <div class="flex w-full flex-col items-center gap-4 text-pretty"> 87 87 <Show when={showSearch() || location.pathname === "/"}> 88 88 <Search /> 89 89 </Show>
+120 -118
src/views/collection.tsx
··· 183 183 <Show when={records.length || response()}> 184 184 <div class="-mt-2 flex w-full flex-col items-center"> 185 185 <StickyOverlay> 186 - <div class="flex w-[22rem] items-center gap-1 sm:w-[24rem]"> 187 - <Show when={agent() && agent()?.sub === did}> 188 - <div class="flex items-center"> 189 - <Tooltip 190 - text={batchDelete() ? "Cancel" : "Delete"} 191 - children={ 192 - <button 193 - onclick={() => { 194 - setRecords( 195 - { from: 0, to: untrack(() => records.length) - 1 }, 196 - "toDelete", 197 - false, 198 - ); 199 - setLastSelected(undefined); 200 - setBatchDelete(!batchDelete()); 201 - }} 202 - class="-ml-1 flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 203 - > 204 - <span 205 - class={`iconify text-lg ${batchDelete() ? "lucide--circle-x" : "lucide--trash-2"} `} 206 - ></span> 207 - </button> 208 - } 209 - /> 210 - <Show when={batchDelete()}> 211 - <Tooltip 212 - text="Select all" 213 - children={ 214 - <button 215 - onclick={() => selectAll()} 216 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 217 - > 218 - <span class="iconify lucide--copy-check text-lg"></span> 219 - </button> 220 - } 221 - /> 186 + <div class="flex w-full flex-col gap-2"> 187 + <div class="flex items-center gap-1"> 188 + <Show when={agent() && agent()?.sub === did}> 189 + <div class="flex items-center"> 222 190 <Tooltip 223 - text="Recreate" 191 + text={batchDelete() ? "Cancel" : "Delete"} 224 192 children={ 225 193 <button 226 194 onclick={() => { 227 - setRecreate(true); 228 - setOpenDelete(true); 195 + setRecords( 196 + { from: 0, to: untrack(() => records.length) - 1 }, 197 + "toDelete", 198 + false, 199 + ); 200 + setLastSelected(undefined); 201 + setBatchDelete(!batchDelete()); 229 202 }} 230 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 203 + class="-ml-1 flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 231 204 > 232 - <span class="iconify lucide--recycle text-lg text-green-500 dark:text-green-400"></span> 205 + <span 206 + class={`iconify text-lg ${batchDelete() ? "lucide--circle-x" : "lucide--trash-2"} `} 207 + ></span> 233 208 </button> 234 209 } 235 210 /> 236 - <Tooltip 237 - text="Delete" 238 - children={ 239 - <button 240 - onclick={() => { 241 - setRecreate(false); 242 - setOpenDelete(true); 243 - }} 244 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 211 + <Show when={batchDelete()}> 212 + <Tooltip 213 + text="Select all" 214 + children={ 215 + <button 216 + onclick={() => selectAll()} 217 + class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 218 + > 219 + <span class="iconify lucide--copy-check text-lg"></span> 220 + </button> 221 + } 222 + /> 223 + <Tooltip 224 + text="Recreate" 225 + children={ 226 + <button 227 + onclick={() => { 228 + setRecreate(true); 229 + setOpenDelete(true); 230 + }} 231 + class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 232 + > 233 + <span class="iconify lucide--recycle text-lg text-green-500 dark:text-green-400"></span> 234 + </button> 235 + } 236 + /> 237 + <Tooltip 238 + text="Delete" 239 + children={ 240 + <button 241 + onclick={() => { 242 + setRecreate(false); 243 + setOpenDelete(true); 244 + }} 245 + class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 246 + > 247 + <span class="iconify lucide--trash-2 text-lg text-red-500 dark:text-red-400"></span> 248 + </button> 249 + } 250 + /> 251 + </Show> 252 + </div> 253 + <Modal open={openDelete()} onClose={() => setOpenDelete(false)}> 254 + <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 255 + <h2 class="mb-2 font-semibold"> 256 + {recreate() ? "Recreate" : "Delete"}{" "} 257 + {records.filter((r) => r.toDelete).length} records? 258 + </h2> 259 + <div class="flex justify-end gap-2"> 260 + <Button onClick={() => setOpenDelete(false)}>Cancel</Button> 261 + <Button 262 + onClick={deleteRecords} 263 + class={`dark:shadow-dark-800 rounded-lg px-2 py-1.5 text-xs font-semibold text-neutral-200 shadow-xs select-none ${recreate() ? "bg-green-500 hover:bg-green-400 dark:bg-green-600 dark:hover:bg-green-500" : "bg-red-500 hover:bg-red-400 active:bg-red-400"}`} 245 264 > 246 - <span class="iconify lucide--trash-2 text-lg text-red-500 dark:text-red-400"></span> 247 - </button> 248 - } 249 - /> 250 - </Show> 251 - </div> 252 - <Modal open={openDelete()} onClose={() => setOpenDelete(false)}> 253 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-300 dark:border-neutral-700 starting:opacity-0"> 254 - <h2 class="mb-2 font-semibold"> 255 - {recreate() ? "Recreate" : "Delete"} {records.filter((r) => r.toDelete).length}{" "} 256 - records? 257 - </h2> 258 - <div class="flex justify-end gap-2"> 259 - <Button onClick={() => setOpenDelete(false)}>Cancel</Button> 260 - <Button 261 - onClick={deleteRecords} 262 - class={`dark:shadow-dark-800 rounded-lg px-2 py-1.5 text-xs font-semibold text-neutral-200 shadow-xs select-none ${recreate() ? "bg-green-500 hover:bg-green-400 dark:bg-green-600 dark:hover:bg-green-500" : "bg-red-500 hover:bg-red-400 active:bg-red-400"}`} 263 - > 264 - {recreate() ? "Recreate" : "Delete"} 265 - </Button> 265 + {recreate() ? "Recreate" : "Delete"} 266 + </Button> 267 + </div> 266 268 </div> 269 + </Modal> 270 + </Show> 271 + <Tooltip text="Jetstream"> 272 + <A 273 + href={`/jetstream?collections=${params.collection}&dids=${params.repo}`} 274 + class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 275 + > 276 + <span class="iconify lucide--radio-tower text-lg"></span> 277 + </A> 278 + </Tooltip> 279 + <TextInput 280 + placeholder="Filter by substring" 281 + onInput={(e) => setFilter(e.currentTarget.value)} 282 + class="grow" 283 + /> 284 + </div> 285 + <Show when={records.length > 1}> 286 + <div class="flex items-center justify-between gap-x-2"> 287 + <Button 288 + onClick={() => { 289 + setReverse(!reverse()); 290 + setRecords([]); 291 + setCursor(undefined); 292 + refetch(); 293 + }} 294 + > 295 + <span 296 + class={`iconify ${reverse() ? "lucide--rotate-ccw" : "lucide--rotate-cw"} text-sm`} 297 + ></span> 298 + Reverse 299 + </Button> 300 + <div> 301 + <Show when={batchDelete()}> 302 + <span>{records.filter((rec) => rec.toDelete).length}</span> 303 + <span>/</span> 304 + </Show> 305 + <span>{records.length} records</span> 267 306 </div> 268 - </Modal> 307 + <div class="flex w-[5rem] items-center justify-end"> 308 + <Show when={cursor()}> 309 + <Show when={!response.loading}> 310 + <Button onClick={() => refetch()}>Load More</Button> 311 + </Show> 312 + <Show when={response.loading}> 313 + <div class="iconify lucide--loader-circle w-[5rem] animate-spin text-xl" /> 314 + </Show> 315 + </Show> 316 + </div> 317 + </div> 269 318 </Show> 270 - <Tooltip text="Jetstream"> 271 - <A 272 - href={`/jetstream?collections=${params.collection}&dids=${params.repo}`} 273 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 274 - > 275 - <span class="iconify lucide--radio-tower text-lg"></span> 276 - </A> 277 - </Tooltip> 278 - <TextInput 279 - placeholder="Filter by substring" 280 - onInput={(e) => setFilter(e.currentTarget.value)} 281 - class="grow" 282 - /> 283 319 </div> 284 - <Show when={records.length > 1}> 285 - <div class="flex w-[22rem] items-center justify-between gap-x-2 sm:w-[24rem]"> 286 - <Button 287 - onClick={() => { 288 - setReverse(!reverse()); 289 - setRecords([]); 290 - setCursor(undefined); 291 - refetch(); 292 - }} 293 - > 294 - <span 295 - class={`iconify ${reverse() ? "lucide--rotate-ccw" : "lucide--rotate-cw"} text-sm`} 296 - ></span> 297 - Reverse 298 - </Button> 299 - <div> 300 - <Show when={batchDelete()}> 301 - <span>{records.filter((rec) => rec.toDelete).length}</span> 302 - <span>/</span> 303 - </Show> 304 - <span>{records.length} records</span> 305 - </div> 306 - <div class="flex w-[5rem] items-center justify-end"> 307 - <Show when={cursor()}> 308 - <Show when={!response.loading}> 309 - <Button onClick={() => refetch()}>Load More</Button> 310 - </Show> 311 - <Show when={response.loading}> 312 - <div class="iconify lucide--loader-circle w-[5rem] animate-spin text-xl" /> 313 - </Show> 314 - </Show> 315 - </div> 316 - </div> 317 - </Show> 318 320 </StickyOverlay> 319 321 <div class="flex max-w-full flex-col font-mono"> 320 322 <For
+2 -10
src/views/home.tsx
··· 2 2 3 3 const Home = () => { 4 4 return ( 5 - <div class="flex w-[22rem] flex-col gap-4 break-words sm:w-[24rem]"> 5 + <div class="flex w-full flex-col gap-4 break-words"> 6 6 <div> 7 7 <div> 8 8 <span class="text-lg font-semibold">AT Protocol Explorer</span> ··· 54 54 </div> 55 55 <div class="flex items-center gap-1"> 56 56 <div class="iconify lucide--tag" /> 57 - <span> 58 - <A 59 - href="/at://did:plc:ar7c4by46qjdydhdevvrndac/labels" 60 - class="text-blue-400 hover:underline active:underline" 61 - > 62 - Query labels 63 - </A>{" "} 64 - from moderation services. 65 - </span> 57 + <span>Query labels from moderation services.</span> 66 58 </div> 67 59 </div> 68 60 <div class="flex gap-2 text-xl">
+9 -8
src/views/labels.tsx
··· 62 62 return ( 63 63 <div class="flex w-full flex-col items-center"> 64 64 <form 65 - class="flex w-[22rem] flex-col items-center gap-y-1 sm:w-[24rem]" 65 + class="flex w-full flex-col items-center gap-y-1" 66 66 onsubmit={(e) => { 67 67 e.preventDefault(); 68 68 initQuery(); ··· 102 102 <StickyOverlay> 103 103 <TextInput 104 104 placeholder="Filter by label" 105 + name="filter" 105 106 onInput={(e) => setFilter(e.currentTarget.value)} 106 - class="w-[22rem] sm:w-[24rem]" 107 + class="w-full" 107 108 /> 108 109 <div class="flex items-center gap-x-2"> 109 110 <Show when={labelCount() && labels().length}> ··· 126 127 </div> 127 128 </StickyOverlay> 128 129 <Show when={labels().length}> 129 - <div class="flex max-w-full min-w-[22rem] flex-col gap-2 divide-y-[0.5px] divide-neutral-400 text-sm wrap-anywhere whitespace-pre-wrap sm:min-w-[24rem] dark:divide-neutral-600"> 130 + <div class="flex flex-col gap-2 divide-y-[0.5px] divide-neutral-400 text-sm wrap-anywhere whitespace-pre-wrap dark:divide-neutral-600"> 130 131 <For each={filterLabels()}> 131 132 {(label) => ( 132 133 <div class="flex items-center justify-between gap-2 pb-2"> 133 134 <div class="flex flex-col"> 134 135 <div class="flex items-center gap-x-2"> 135 - <div class="min-w-[5rem] font-semibold">URI</div> 136 + <div class="min-w-[4rem] font-semibold">URI</div> 136 137 <A 137 138 href={`/at://${label.uri.replace("at://", "")}`} 138 139 class="text-blue-400 hover:underline active:underline" ··· 142 143 </div> 143 144 <Show when={label.cid}> 144 145 <div class="flex items-center gap-x-2"> 145 - <div class="min-w-[5rem] font-semibold">CID</div> 146 + <div class="min-w-[4rem] font-semibold">CID</div> 146 147 {label.cid} 147 148 </div> 148 149 </Show> 149 150 <div class="flex items-center gap-x-2"> 150 - <div class="min-w-[5rem] font-semibold">Label</div> 151 + <div class="min-w-[4rem] font-semibold">Label</div> 151 152 {label.val} 152 153 </div> 153 154 <div class="flex items-center gap-x-2"> 154 - <div class="min-w-[5rem] font-semibold">Created</div> 155 + <div class="min-w-[4rem] font-semibold">Created</div> 155 156 {localDateFromTimestamp(new Date(label.cts).getTime())} 156 157 </div> 157 158 <Show when={label.exp}> 158 159 {(exp) => ( 159 160 <div class="flex items-center gap-x-2"> 160 - <div class="min-w-[5rem] font-semibold">Expires</div> 161 + <div class="min-w-[4rem] font-semibold">Expires</div> 161 162 {localDateFromTimestamp(new Date(exp()).getTime())} 162 163 </div> 163 164 )}
+63 -27
src/views/pds.tsx
··· 5 5 import { A, useParams } from "@solidjs/router"; 6 6 import { createResource, createSignal, For, Show } from "solid-js"; 7 7 import { Button } from "../components/button"; 8 + import { Modal } from "../components/modal"; 8 9 import { setPDS } from "../components/navbar"; 9 10 import Tooltip from "../components/tooltip"; 10 11 import { localDateFromTimestamp } from "../utils/date"; ··· 44 45 const [response, { refetch }] = createResource(fetchRepos); 45 46 const [repos, setRepos] = createSignal<ComAtprotoSyncListRepos.Repo[]>(); 46 47 48 + const RepoCard = (repo: ComAtprotoSyncListRepos.Repo) => { 49 + const [openInfo, setOpenInfo] = createSignal(false); 50 + 51 + return ( 52 + <div class="flex items-center gap-1"> 53 + <A 54 + href={`/at://${repo.did}`} 55 + class="grow truncate rounded py-0.5 font-mono text-blue-400 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 56 + > 57 + {repo.did} 58 + </A> 59 + <Show when={!repo.active}> 60 + <Tooltip text={repo.status ?? "Unknown status"}> 61 + <span class="iconify lucide--unplug"></span> 62 + </Tooltip> 63 + </Show> 64 + <button 65 + onclick={() => setOpenInfo(true)} 66 + class="flex items-center rounded-lg p-1.5 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 67 + > 68 + <span class="iconify lucide--info"></span> 69 + </button> 70 + <Modal open={openInfo()} onClose={() => setOpenInfo(false)}> 71 + <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] w-max max-w-full -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-3 break-words shadow-md transition-opacity duration-200 sm:max-w-[32rem] dark:border-neutral-700 starting:opacity-0"> 72 + <div class="mb-1 flex justify-between gap-2"> 73 + <div class="flex items-center gap-1"> 74 + <span class="iconify lucide--info"></span> 75 + <span class="font-semibold">{repo.did}</span> 76 + </div> 77 + <button 78 + onclick={() => setOpenInfo(false)} 79 + class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 80 + > 81 + <span class="iconify lucide--x"></span> 82 + </button> 83 + </div> 84 + <div class="flex flex-col text-sm"> 85 + <span> 86 + Head: <span class="text-xs">{repo.head}</span> 87 + </span> 88 + <Show when={TID.validate(repo.rev)}> 89 + <span> 90 + Rev: {repo.rev} ({localDateFromTimestamp(TID.parse(repo.rev).timestamp / 1000)}) 91 + </span> 92 + </Show> 93 + <Show when={repo.active !== undefined}> 94 + <span>Active: {repo.active ? "true" : "false"}</span> 95 + </Show> 96 + <Show when={repo.status}> 97 + <span>Status: {repo.status}</span> 98 + </Show> 99 + </div> 100 + </div> 101 + </Modal> 102 + </div> 103 + ); 104 + }; 105 + 47 106 return ( 48 107 <Show when={repos() || response()}> 49 - <div class="flex w-[22rem] flex-col sm:w-[24rem]"> 108 + <div class="flex w-full flex-col"> 50 109 <Show when={version()}> 51 110 {(version) => ( 52 111 <div class="flex items-baseline gap-x-1"> ··· 76 135 )} 77 136 </Show> 78 137 <p class="w-full font-semibold">{repos()?.length} Repositories</p> 79 - <For each={repos()}> 80 - {(repo) => ( 81 - <A 82 - href={`/at://${repo.did}`} 83 - classList={{ 84 - "rounded items-center text-sm gap-1 flex justify-between font-mono relative hover:bg-neutral-200 dark:hover:bg-neutral-700 active:bg-neutral-300 dark:active:bg-neutral-600": true, 85 - "text-blue-400": repo.active, 86 - "text-neutral-400 dark:text-neutral-500": !repo.active, 87 - }} 88 - > 89 - <Show when={!repo.active}> 90 - <div class="absolute -left-4"> 91 - <Tooltip text={repo.status ?? "Unknown status"}> 92 - <span class="iconify lucide--unplug"></span> 93 - </Tooltip> 94 - </div> 95 - </Show> 96 - <span class="text-sm">{repo.did}</span> 97 - <Show when={TID.validate(repo.rev)}> 98 - <span class="text-xs text-neutral-500 dark:text-neutral-400"> 99 - {localDateFromTimestamp(TID.parse(repo.rev).timestamp / 1000).split(" ")[0]} 100 - </span> 101 - </Show> 102 - </A> 103 - )} 104 - </For> 138 + <div class="flex flex-col divide-y-[0.5px] divide-neutral-300 dark:divide-neutral-700"> 139 + <For each={repos()}>{(repo) => <RepoCard {...repo} />}</For> 140 + </div> 105 141 </div> 106 142 <Show when={cursor()}> 107 143 <div class="dark:bg-dark-500 fixed bottom-0 z-5 flex w-screen justify-center bg-neutral-100 py-3">
+3 -3
src/views/record.tsx
··· 120 120 return ( 121 121 <Show when={record()} keyed> 122 122 <div class="flex w-full flex-col items-center"> 123 - <div class="dark:shadow-dark-800 dark:bg-dark-300 mb-3 flex w-[22rem] justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 shadow-xs sm:w-[24rem] dark:border-neutral-700"> 123 + <div class="dark:shadow-dark-800 dark:bg-dark-300 mb-3 flex w-full justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 shadow-xs dark:border-neutral-700"> 124 124 <div class="flex gap-3 text-sm"> 125 125 <A 126 126 classList={{ ··· 157 157 </button> 158 158 </Tooltip> 159 159 <Modal open={openDelete()} onClose={() => setOpenDelete(false)}> 160 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-300 dark:border-neutral-700 starting:opacity-0"> 160 + <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 161 161 <h2 class="mb-2 font-semibold">Delete this record?</h2> 162 162 <div class="flex justify-end gap-2"> 163 163 <Button onClick={() => setOpenDelete(false)}>Cancel</Button> ··· 206 206 <Show when={validRecord() === false}> 207 207 <div class="mb-2 break-words text-red-500 dark:text-red-400">{notice()}</div> 208 208 </Show> 209 - <div class="w-[22rem] font-mono text-xs wrap-anywhere whitespace-pre-wrap sm:w-full sm:text-sm"> 209 + <div class="w-max max-w-screen px-4 font-mono text-xs wrap-anywhere whitespace-pre-wrap sm:text-sm md:max-w-[48rem]"> 210 210 <JSONValue data={record()?.value as any} repo={record()!.uri.split("/")[2]} /> 211 211 </div> 212 212 </Show>
+15 -13
src/views/repo.tsx
··· 169 169 const did = params.repo; 170 170 171 171 const RepoTab = (props: { tab: Tab; label: string; icon: string }) => ( 172 - <A 173 - classList={{ 174 - "flex items-center border-b-2 gap-1": true, 175 - "border-transparent hover:border-neutral-400 dark:hover:border-neutral-600": 176 - (location.hash !== `#${props.tab}` && !!location.hash) || 177 - (!location.hash && props.tab !== "collections"), 178 - }} 179 - href={`/at://${params.repo}#${props.tab}`} 180 - > 181 - <div class={"iconify " + props.icon} /> 182 - {props.label} 172 + <A class="group flex flex-1 justify-center" href={`/at://${params.repo}#${props.tab}`}> 173 + <span 174 + classList={{ 175 + "flex gap-1 items-center border-b-2": true, 176 + "border-transparent group-hover:border-neutral-400 dark:group-hover:border-neutral-600": 177 + (location.hash !== `#${props.tab}` && !!location.hash) || 178 + (!location.hash && props.tab !== "collections"), 179 + }} 180 + > 181 + <span class={"iconify " + props.icon}></span> 182 + {props.label} 183 + </span> 183 184 </A> 184 185 ); 185 186 ··· 266 267 267 268 return ( 268 269 <Show when={repo()}> 269 - <div class="flex w-[22rem] flex-col gap-2 break-words sm:w-[24rem]"> 270 + <div class="flex w-full flex-col gap-2 break-words"> 270 271 <Show when={error()}> 271 272 <div class="rounded-lg bg-red-100 p-2 text-sm text-red-700 dark:bg-red-200 dark:text-red-600"> 272 273 {error()} ··· 317 318 </A> 318 319 </Tooltip> 319 320 <TextInput 321 + name="filter" 320 322 placeholder="Filter collections" 321 323 onInput={(e) => setFilter(e.currentTarget.value)} 322 324 class="grow" ··· 454 456 </ErrorBoundary> 455 457 </span> 456 458 </span> 457 - <span class="truncate text-xs">{key()}</span> 459 + <span class="truncate">{key()}</span> 458 460 </li> 459 461 )} 460 462 </Show>
+1 -1
src/views/settings.tsx
··· 5 5 6 6 const Settings = () => { 7 7 return ( 8 - <div class="flex w-[22rem] flex-col gap-3 sm:w-[24rem]"> 8 + <div class="flex w-full flex-col gap-3"> 9 9 <div class="flex items-center gap-1 font-semibold"> 10 10 <span>Settings</span> 11 11 </div>
+3 -3
src/views/stream.tsx
··· 140 140 onCleanup(() => socket?.close()); 141 141 142 142 return ( 143 - <div class="flex flex-col items-center"> 143 + <div class="flex w-full flex-col items-center"> 144 144 <div class="flex gap-2 text-sm"> 145 145 <A 146 146 class="flex items-center gap-1 border-b-2 p-1" ··· 160 160 </A> 161 161 </div> 162 162 <StickyOverlay> 163 - <form ref={formRef} class="flex w-[22rem] flex-col gap-1 text-sm sm:w-[24rem]"> 163 + <form ref={formRef} class="flex w-full flex-col gap-1 text-sm"> 164 164 <Show when={!connected()}> 165 165 <label class="flex items-center justify-end gap-x-1"> 166 166 <span class="min-w-[5rem]">Instance</span> ··· 246 246 <Show when={notice().length}> 247 247 <div class="text-red-500 dark:text-red-400">{notice()}</div> 248 248 </Show> 249 - <div class="flex w-full flex-col gap-2 divide-y-[0.5px] divide-neutral-500 px-4 font-mono text-sm wrap-anywhere whitespace-pre-wrap md:w-[48rem]"> 249 + <div class="flex w-full flex-col gap-2 divide-y-[0.5px] divide-neutral-500 font-mono text-sm wrap-anywhere whitespace-pre-wrap md:w-[48rem]"> 250 250 <For each={records().toReversed()}> 251 251 {(rec) => ( 252 252 <div class="pb-2">