atmosphere explorer pdsls.dev
atproto tool typescript

Compare changes

Choose any two refs to compare.

+1 -1
src/components/backlinks.tsx
··· 74 74 onClick={() => setMore(true)} 75 75 class="dark:hover:bg-dark-200 dark:shadow-dark-700 dark:active:bg-dark-100 box-border flex h-7 w-full items-center justify-center gap-1 rounded border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-xs shadow-xs select-none hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800" 76 76 > 77 - Load More 77 + Load more 78 78 </Button> 79 79 </div> 80 80 }
+1 -1
src/views/blob.tsx
··· 51 51 {blobs()?.length} blob{(blobs()?.length ?? 0 > 1) ? "s" : ""} 52 52 </p> 53 53 <Show when={!response.loading && cursor()}> 54 - <Button onClick={() => refetch()}>Load More</Button> 54 + <Button onClick={() => refetch()}>Load more</Button> 55 55 </Show> 56 56 <Show when={response.loading}> 57 57 <span class="iconify lucide--loader-circle animate-spin py-3.5 text-xl"></span>
+1 -1
src/views/collection.tsx
··· 398 398 <div class="flex w-20 items-center justify-end"> 399 399 <Show when={cursor()}> 400 400 <Show when={!response.loading}> 401 - <Button onClick={() => refetch()}>Load More</Button> 401 + <Button onClick={() => refetch()}>Load more</Button> 402 402 </Show> 403 403 <Show when={response.loading}> 404 404 <div class="iconify lucide--loader-circle w-20 animate-spin text-xl" />
+1 -1
src/views/pds.tsx
··· 260 260 <div class="flex flex-col items-center gap-1 pb-2"> 261 261 <p>{repos()?.length} loaded</p> 262 262 <Show when={!response.loading && cursor()}> 263 - <Button onClick={() => refetch()}>Load More</Button> 263 + <Button onClick={() => refetch()}>Load more</Button> 264 264 </Show> 265 265 <Show when={response.loading}> 266 266 <span class="iconify lucide--loader-circle animate-spin py-3.5 text-xl"></span>
+1 -8
src/components/create/file-upload.tsx
··· 2 2 import { remove } from "@mary/exif-rm"; 3 3 import { createSignal, onCleanup, Show } from "solid-js"; 4 4 import { agent } from "../../auth/state"; 5 + import { formatFileSize } from "../../utils/format"; 5 6 import { Button } from "../button.jsx"; 6 7 import { TextInput } from "../text-input.jsx"; 7 8 import { editorInstance } from "./state"; ··· 16 17 17 18 onCleanup(() => (props.blobInput.value = "")); 18 19 19 - const formatFileSize = (bytes: number) => { 20 - if (bytes === 0) return "0 Bytes"; 21 - const k = 1024; 22 - const sizes = ["Bytes", "KB", "MB", "GB"]; 23 - const i = Math.floor(Math.log(bytes) / Math.log(k)); 24 - return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i]; 25 - }; 26 - 27 20 const uploadBlob = async () => { 28 21 let blob: Blob; 29 22
+9
src/utils/format.ts
··· 1 + const formatFileSize = (bytes: number): string => { 2 + if (bytes === 0) return "0 B"; 3 + const k = 1024; 4 + const sizes = ["B", "KB", "MB", "GB"]; 5 + const i = Math.floor(Math.log(bytes) / Math.log(k)); 6 + return `${(bytes / Math.pow(k, i)).toFixed(i === 0 ? 0 : 1)} ${sizes[i]}`; 7 + }; 8 + 9 + export { formatFileSize };
+37 -4
src/views/car/explore.tsx
··· 1 1 import * as CAR from "@atcute/car"; 2 2 import * as CBOR from "@atcute/cbor"; 3 3 import * as CID from "@atcute/cid"; 4 + import { Did } from "@atcute/lexicons"; 4 5 import { fromStream, isCommit } from "@atcute/repo"; 5 6 import * as TID from "@atcute/tid"; 6 7 import { Title } from "@solidjs/meta"; ··· 10 11 import { JSONValue } from "../../components/json.jsx"; 11 12 import { TextInput } from "../../components/text-input.jsx"; 12 13 import { isTouchDevice } from "../../layout.jsx"; 14 + import { didDocCache, resolveDidDoc } from "../../utils/api.js"; 13 15 import { localDateFromTimestamp } from "../../utils/date.js"; 14 16 import { createDropHandler, createFileChangeHandler, handleDragOver } from "./file-handlers.js"; 15 17 import { ··· 39 41 const car = CAR.fromUint8Array(buffer); 40 42 41 43 // Get DID from commit block 42 - let did = ""; 44 + let did = "Repository"; 43 45 const rootCid = car.roots[0]?.$link; 44 46 if (rootCid) { 45 47 for (const entry of car) { ··· 98 100 await repo.dispose(); 99 101 } 100 102 103 + // Resolve DID document to populate handle in cache 104 + if (did !== "Repository") { 105 + try { 106 + const doc = await resolveDidDoc(did as Did); 107 + didDocCache[did] = doc; 108 + } catch (err) { 109 + console.error("Failed to resolve DID document:", err); 110 + } 111 + } 112 + 101 113 setArchive(result); 102 114 setView({ type: "repo" }); 103 115 } catch (err) { ··· 147 159 setView: (view: View) => void; 148 160 onClose: () => void; 149 161 }) => { 162 + const handle = 163 + didDocCache[props.archive.did]?.alsoKnownAs 164 + ?.filter((alias) => alias.startsWith("at://"))[0] 165 + ?.split("at://")[1] ?? props.archive.did; 166 + 150 167 return ( 151 168 <div class="flex w-full flex-col"> 152 169 <nav class="flex w-full flex-col text-sm wrap-anywhere sm:text-base"> ··· 157 174 fallback={ 158 175 <div class="flex min-h-6 min-w-0 basis-full items-center gap-2 px-2 sm:min-h-7"> 159 176 <span class="iconify lucide--book-user shrink-0 text-neutral-500 transition-colors duration-200 group-hover:text-neutral-700 dark:text-neutral-400 dark:group-hover:text-neutral-200" /> 160 - <span class="truncate py-0.5 font-medium">{props.archive.did || "Repository"}</span> 177 + <span class="flex min-w-0 gap-1 py-0.5 font-medium"> 178 + <Show 179 + when={handle !== props.archive.did} 180 + fallback={<span class="truncate">{props.archive.did}</span>} 181 + > 182 + <span class="shrink-0">{handle}</span> 183 + <span class="truncate text-neutral-500 dark:text-neutral-400"> 184 + ({props.archive.did}) 185 + </span> 186 + </Show> 187 + </span> 161 188 </div> 162 189 } 163 190 > ··· 167 194 class="flex min-h-6 min-w-0 basis-full items-center gap-2 px-2 sm:min-h-7" 168 195 > 169 196 <span class="iconify lucide--book-user shrink-0 text-neutral-500 transition-colors duration-200 group-hover:text-neutral-700 dark:text-neutral-400 dark:group-hover:text-neutral-200" /> 170 - <span class="truncate py-0.5 font-medium text-blue-500 transition-colors duration-150 group-hover:text-blue-600 dark:text-blue-400 dark:group-hover:text-blue-300"> 171 - {props.archive.did || "Repository"} 197 + <span class="flex min-w-0 gap-1 py-0.5 font-medium text-blue-500 transition-colors duration-150 group-hover:text-blue-600 dark:text-blue-400 dark:group-hover:text-blue-300"> 198 + <Show 199 + when={handle !== props.archive.did} 200 + fallback={<span class="truncate">{props.archive.did}</span>} 201 + > 202 + <span class="shrink-0">{handle}</span> 203 + <span class="truncate">({props.archive.did})</span> 204 + </Show> 172 205 </span> 173 206 </button> 174 207 </Show>
+99 -76
src/components/lexicon-schema.tsx
··· 124 124 }; 125 125 126 126 return ( 127 - <> 128 - <Show when={props.refType}> 129 - <button 130 - type="button" 131 - onClick={handleClick} 132 - class="inline-block cursor-pointer truncate rounded bg-blue-100 px-1.5 py-0.5 font-mono text-xs text-blue-800 hover:bg-blue-200 hover:underline active:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-300 dark:hover:bg-blue-900/50 dark:active:bg-blue-900/50" 133 - > 134 - {displayType} 135 - </button> 136 - </Show> 137 - <Show when={!props.refType}> 138 - <span class="inline-block rounded bg-blue-100 px-1.5 py-0.5 font-mono text-xs text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"> 139 - {displayType} 140 - </span> 141 - </Show> 142 - </> 127 + <Show 128 + when={props.refType} 129 + fallback={ 130 + <span class="font-mono text-xs text-neutral-600 dark:text-neutral-400">{displayType}</span> 131 + } 132 + > 133 + <button 134 + type="button" 135 + onClick={handleClick} 136 + class="inline-block cursor-pointer truncate font-mono text-xs text-blue-500 hover:underline dark:text-blue-400" 137 + > 138 + {displayType} 139 + </button> 140 + </Show> 143 141 ); 144 142 }; 145 143 146 144 const UnionBadges = (props: { refs: string[] }) => ( 147 - <div class="flex flex-wrap gap-2"> 145 + <div class="flex flex-col items-start gap-1"> 148 146 <For each={props.refs}>{(refType) => <TypeBadge type="union" refType={refType} />}</For> 149 147 </div> 150 148 ); 151 149 152 - const ConstraintsList = (props: { property: LexiconProperty }) => ( 153 - <div class="flex flex-wrap gap-x-4 gap-y-1 text-xs text-neutral-500 dark:text-neutral-400"> 154 - <Show when={props.property.minLength !== undefined}> 155 - <span>minLength: {props.property.minLength}</span> 156 - </Show> 157 - <Show when={props.property.maxLength !== undefined}> 158 - <span>maxLength: {props.property.maxLength}</span> 159 - </Show> 160 - <Show when={props.property.maxGraphemes !== undefined}> 161 - <span>maxGraphemes: {props.property.maxGraphemes}</span> 162 - </Show> 163 - <Show when={props.property.minGraphemes !== undefined}> 164 - <span>minGraphemes: {props.property.minGraphemes}</span> 165 - </Show> 166 - <Show when={props.property.minimum !== undefined}> 167 - <span>min: {props.property.minimum}</span> 168 - </Show> 169 - <Show when={props.property.maximum !== undefined}> 170 - <span>max: {props.property.maximum}</span> 171 - </Show> 172 - <Show when={props.property.maxSize !== undefined}> 173 - <span>maxSize: {props.property.maxSize}</span> 174 - </Show> 175 - <Show when={props.property.accept}> 176 - <span>accept: [{props.property.accept!.join(", ")}]</span> 177 - </Show> 178 - <Show when={props.property.enum}> 179 - <span>enum: [{props.property.enum!.join(", ")}]</span> 180 - </Show> 181 - <Show when={props.property.const}> 182 - <span>const: {props.property.const?.toString()}</span> 183 - </Show> 184 - <Show when={props.property.default !== undefined}> 185 - <span>default: {JSON.stringify(props.property.default)}</span> 186 - </Show> 187 - <Show when={props.property.knownValues}> 188 - <span>knownValues: [{props.property.knownValues!.join(", ")}]</span> 189 - </Show> 190 - <Show when={props.property.closed}> 191 - <span>closed: true</span> 192 - </Show> 193 - </div> 194 - ); 150 + const ConstraintsList = (props: { property: LexiconProperty }) => { 151 + const valueClass = "text-neutral-600 dark:text-neutral-400"; 152 + return ( 153 + <div class="flex flex-wrap gap-x-4 gap-y-1 text-xs"> 154 + <Show when={props.property.minLength !== undefined}> 155 + <span> 156 + minLength: <span class={valueClass}>{props.property.minLength}</span> 157 + </span> 158 + </Show> 159 + <Show when={props.property.maxLength !== undefined}> 160 + <span> 161 + maxLength: <span class={valueClass}>{props.property.maxLength}</span> 162 + </span> 163 + </Show> 164 + <Show when={props.property.maxGraphemes !== undefined}> 165 + <span> 166 + maxGraphemes: <span class={valueClass}>{props.property.maxGraphemes}</span> 167 + </span> 168 + </Show> 169 + <Show when={props.property.minGraphemes !== undefined}> 170 + <span> 171 + minGraphemes: <span class={valueClass}>{props.property.minGraphemes}</span> 172 + </span> 173 + </Show> 174 + <Show when={props.property.minimum !== undefined}> 175 + <span> 176 + min: <span class={valueClass}>{props.property.minimum}</span> 177 + </span> 178 + </Show> 179 + <Show when={props.property.maximum !== undefined}> 180 + <span> 181 + max: <span class={valueClass}>{props.property.maximum}</span> 182 + </span> 183 + </Show> 184 + <Show when={props.property.maxSize !== undefined}> 185 + <span> 186 + maxSize: <span class={valueClass}>{props.property.maxSize}</span> 187 + </span> 188 + </Show> 189 + <Show when={props.property.accept}> 190 + <span> 191 + accept: <span class={valueClass}>[{props.property.accept!.join(", ")}]</span> 192 + </span> 193 + </Show> 194 + <Show when={props.property.enum}> 195 + <span> 196 + enum: <span class={valueClass}>[{props.property.enum!.join(", ")}]</span> 197 + </span> 198 + </Show> 199 + <Show when={props.property.const}> 200 + <span> 201 + const: <span class={valueClass}>{props.property.const?.toString()}</span> 202 + </span> 203 + </Show> 204 + <Show when={props.property.default !== undefined}> 205 + <span> 206 + default: <span class={valueClass}>{JSON.stringify(props.property.default)}</span> 207 + </span> 208 + </Show> 209 + <Show when={props.property.knownValues}> 210 + <span> 211 + knownValues: <span class={valueClass}>[{props.property.knownValues!.join(", ")}]</span> 212 + </span> 213 + </Show> 214 + <Show when={props.property.closed}> 215 + <span> 216 + closed: <span class={valueClass}>true</span> 217 + </span> 218 + </Show> 219 + </div> 220 + ); 221 + }; 195 222 196 223 const PropertyRow = (props: { 197 224 name: string; ··· 217 244 return ( 218 245 <div class="flex flex-col gap-2 py-3"> 219 246 <Show when={!props.hideNameType}> 220 - <div class="flex flex-wrap items-center gap-2"> 221 - <span class="font-mono text-sm font-semibold">{props.name}</span> 247 + <div class="flex flex-wrap items-baseline gap-2"> 248 + <span class="font-semibold">{props.name}</span> 222 249 <Show when={!props.property.refs}> 223 250 <TypeBadge 224 251 type={props.property.type} ··· 227 254 /> 228 255 </Show> 229 256 <Show when={props.property.refs}> 230 - <span class="inline-block rounded bg-blue-100 px-1.5 py-0.5 font-mono text-xs text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"> 231 - union 232 - </span> 257 + <span class="font-mono text-xs text-neutral-600 dark:text-neutral-400">union</span> 233 258 </Show> 234 259 <Show when={props.required}> 235 260 <span class="text-xs font-semibold text-red-500 dark:text-red-400">required</span> ··· 244 269 </Show> 245 270 <Show when={props.property.items}> 246 271 <div class="flex flex-col gap-2"> 247 - <div class="flex items-center gap-2 text-xs text-neutral-500 dark:text-neutral-400"> 248 - <span class="font-semibold">items:</span> 272 + <div class="flex items-baseline gap-2 text-xs"> 273 + <span class="font-medium">items:</span> 249 274 <Show when={!props.property.items!.refs}> 250 275 <TypeBadge 251 276 type={props.property.items!.type} ··· 254 279 /> 255 280 </Show> 256 281 <Show when={props.property.items!.refs}> 257 - <span class="inline-block rounded bg-blue-100 px-1.5 py-0.5 font-mono text-xs text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"> 258 - union 259 - </span> 282 + <span class="font-mono text-xs text-neutral-600 dark:text-neutral-400">union</span> 260 283 </Show> 261 284 </div> 262 285 <Show when={props.property.items!.refs}> ··· 292 315 <button 293 316 type="button" 294 317 onClick={handleClick} 295 - class="cursor-pointer rounded bg-blue-100 px-1.5 py-0.5 font-mono text-xs text-blue-800 hover:bg-blue-200 hover:underline active:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-300 dark:hover:bg-blue-900/50 dark:active:bg-blue-900/50" 318 + class="cursor-pointer font-mono text-xs text-blue-500 hover:underline dark:text-blue-400" 296 319 > 297 320 {props.nsid} 298 321 </button> ··· 314 337 return ( 315 338 <div class="flex flex-col gap-2 py-3"> 316 339 <div class="flex flex-wrap items-center gap-2"> 317 - <span class="font-mono text-sm font-semibold">#{props.index + 1}</span> 340 + <span class="font-semibold">#{props.index + 1}</span> 318 341 <span 319 342 class={`rounded px-1.5 py-0.5 font-mono text-xs font-semibold ${resourceColor(props.permission.resource)}`} 320 343 > ··· 328 351 <span class="text-xs font-semibold text-neutral-500 dark:text-neutral-400"> 329 352 Collections: 330 353 </span> 331 - <div class="flex flex-wrap gap-1"> 354 + <div class="flex flex-col items-start gap-1"> 332 355 <For each={props.permission.collection}>{(col) => <NsidLink nsid={col} />}</For> 333 356 </div> 334 357 </div> ··· 356 379 <span class="text-xs font-semibold text-neutral-500 dark:text-neutral-400"> 357 380 Lexicon Methods: 358 381 </span> 359 - <div class="flex flex-wrap gap-1"> 382 + <div class="flex flex-col items-start gap-1"> 360 383 <For each={props.permission.lxm}>{(method) => <NsidLink nsid={method} />}</For> 361 384 </div> 362 385 </div> ··· 681 704 <For each={props.def.errors}> 682 705 {(error) => ( 683 706 <div class="flex flex-col gap-1 py-2"> 684 - <div class="font-mono text-sm font-semibold">{error.name}</div> 707 + <div class="font-semibold">{error.name}</div> 685 708 <Show when={error.description}> 686 709 <p class="text-sm text-neutral-700 dark:text-neutral-300"> 687 710 {error.description} ··· 738 761 <div class="flex gap-4 text-sm text-neutral-600 dark:text-neutral-400"> 739 762 <span> 740 763 <span class="font-semibold">Lexicon version: </span> 741 - <span class="font-mono">{props.schema.lexicon}</span> 764 + <span>{props.schema.lexicon}</span> 742 765 </span> 743 766 </div> 744 767 <Show when={props.schema.description}>
+1 -1
src/index.tsx
··· 13 13 import { RecordView } from "./views/record.tsx"; 14 14 import { RepoView } from "./views/repo.tsx"; 15 15 import { Settings } from "./views/settings.tsx"; 16 - import { StreamView } from "./views/stream.tsx"; 16 + import { StreamView } from "./views/stream"; 17 17 18 18 render( 19 19 () => (