forked from pdsls.dev/pdsls
this repo has no description

increase printWidth to 100

+1
.prettierrc
··· 1 1 { 2 + "printWidth": 100, 2 3 "experimentalTernaries": true, 3 4 "plugins": ["prettier-plugin-tailwindcss"] 4 5 }
+2 -6
index.html
··· 7 7 <meta property="og:title" content="PDSls" /> 8 8 <meta property="og:type" content="website" /> 9 9 <meta property="og:url" content="https://pdsls.dev" /> 10 - <meta 11 - property="og:description" 12 - content="Browse and manage atproto repositories" 13 - /> 10 + <meta property="og:description" content="Browse and manage atproto repositories" /> 14 11 <title>PDSls</title> 15 12 <script> 16 13 if ( 17 14 localStorage.theme === "dark" || 18 - (!("theme" in localStorage) && 19 - window.matchMedia("(prefers-color-scheme: dark)").matches) 15 + (!("theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches) 20 16 ) 21 17 document.documentElement.classList.add("dark"); 22 18 else document.documentElement.classList.remove("dark");
+3 -15
src/components/account.tsx
··· 1 - import { 2 - createSignal, 3 - onMount, 4 - Show, 5 - onCleanup, 6 - createEffect, 7 - For, 8 - } from "solid-js"; 1 + import { createSignal, onMount, Show, onCleanup, createEffect, For } from "solid-js"; 9 2 import Tooltip from "./tooltip.jsx"; 10 - import { 11 - deleteStoredSession, 12 - getSession, 13 - OAuthUserAgent, 14 - } from "@atcute/oauth-browser-client"; 3 + import { deleteStoredSession, getSession, OAuthUserAgent } from "@atcute/oauth-browser-client"; 15 4 import { agent, Login, setLoginState } from "./login.jsx"; 16 5 import { At } from "@atcute/client/lexicons"; 17 6 ··· 89 78 classList={{ 90 79 "basis-full text-left font-mono max-w-[32ch] text-sm truncate group-hover/select:bg-slate-300 dark:group-hover/select:bg-neutral-700": 91 80 true, 92 - "text-green-500 dark:text-green-400": 93 - session === agent?.sub, 81 + "text-green-500 dark:text-green-400": session === agent?.sub, 94 82 }} 95 83 onclick={() => resumeSession(session)} 96 84 >
+7 -32
src/components/backlinks.tsx
··· 45 45 {({ collection, path, matchesFilter, counts }) => ( 46 46 <div class="mt-2 font-mono text-sm sm:text-base"> 47 47 <p classList={{ "text-stone-400": matchesFilter }}> 48 - <span title="Collection containing linking records"> 49 - {collection} 50 - </span> 48 + <span title="Collection containing linking records">{collection}</span> 51 49 <span class="text-cyan-500">@</span> 52 - <span title="Record path where the link is found"> 53 - {path.slice(1)} 54 - </span> 55 - : 50 + <span title="Record path where the link is found">{path.slice(1)}</span>: 56 51 </p> 57 52 <div class="pl-2.5 font-sans"> 58 53 <p> ··· 78 73 href="#" 79 74 title="Show linking DIDs" 80 75 onclick={() => 81 - ( 82 - show()?.collection === collection && 83 - show()?.path === path && 84 - show()?.showDids 85 - ) ? 76 + show()?.collection === collection && show()?.path === path && show()?.showDids ? 86 77 setShow(null) 87 78 : setShow({ collection, path, showDids: true }) 88 79 } ··· 91 82 {counts.distinct_dids < 2 ? "" : "s"} 92 83 </a> 93 84 </p> 94 - <Show 95 - when={ 96 - show()?.collection === collection && show()?.path === path 97 - } 98 - > 85 + <Show when={show()?.collection === collection && show()?.path === path}> 99 86 <Show when={show()?.showDids}> 100 87 {/* putting this in the `dids` prop directly failed to re-render. idk how to solidjs. */} 101 88 <p class="w-full font-semibold text-stone-600 dark:text-stone-400"> 102 89 Distinct identities 103 90 </p> 104 - <BacklinkItems 105 - target={target} 106 - collection={collection} 107 - path={path} 108 - dids={true} 109 - /> 91 + <BacklinkItems target={target} collection={collection} path={path} dids={true} /> 110 92 </Show> 111 93 <Show when={!show()?.showDids}> 112 - <p class="w-full font-semibold text-stone-600 dark:text-stone-400"> 113 - Records 114 - </p> 115 - <BacklinkItems 116 - target={target} 117 - collection={collection} 118 - path={path} 119 - dids={false} 120 - /> 94 + <p class="w-full font-semibold text-stone-600 dark:text-stone-400">Records</p> 95 + <BacklinkItems target={target} collection={collection} path={path} dids={false} /> 121 96 </Show> 122 97 </Show> 123 98 </div>
+1 -3
src/components/create.tsx
··· 137 137 </div> 138 138 <Editor theme={theme().color} model={model!} /> 139 139 <div class="flex flex-col gap-x-2"> 140 - <div class="text-red-500 dark:text-red-400"> 141 - {createNotice()} 142 - </div> 140 + <div class="text-red-500 dark:text-red-400">{createNotice()}</div> 143 141 <div class="flex items-center justify-end gap-2"> 144 142 <button 145 143 onclick={() => setOpenCreate(false)}
+5 -29
src/components/json.tsx
··· 11 11 } 12 12 13 13 export const syntaxHighlight = (json: string) => { 14 - json = json 15 - .replace(/&/g, "&amp;") 16 - .replace(/</g, "&lt;") 17 - .replace(/>/g, "&gt;"); 14 + json = json.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); 18 15 19 16 return json.replace( 20 17 /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, ··· 70 67 ["http:", "https:", "web+at:"].includes(new URL(part).protocol) && 71 68 part.split("\n").length === 1 72 69 ) ? 73 - <a 74 - class="underline" 75 - href={part} 76 - target="_blank" 77 - rel="noopener noreferer" 78 - > 70 + <a class="underline" href={part} target="_blank" rel="noopener noreferer"> 79 71 {part} 80 72 </a> 81 73 : part} ··· 91 83 }; 92 84 93 85 const JSONBoolean = ({ data }: { data: boolean }) => { 94 - return ( 95 - <span class="text-[#f57d26] dark:text-orange-300"> 96 - {data ? "true" : "false"} 97 - </span> 98 - ); 86 + return <span class="text-[#f57d26] dark:text-orange-300">{data ? "true" : "false"}</span>; 99 87 }; 100 88 101 89 const JSONNull = () => { 102 90 return <span class="text-neutral-400 dark:text-neutral-500">null</span>; 103 91 }; 104 92 105 - const JSONObject = ({ 106 - data, 107 - repo, 108 - }: { 109 - data: { [x: string]: JSONType }; 110 - repo: string; 111 - }) => { 93 + const JSONObject = ({ data, repo }: { data: { [x: string]: JSONType }; repo: string }) => { 112 94 const [clip, setClip] = createSignal(false); 113 95 const rawObj = ( 114 96 <For each={Object.entries(data)}> ··· 214 196 return <JSONObject data={data} repo={repo} />; 215 197 }; 216 198 217 - export type JSONType = 218 - | string 219 - | number 220 - | boolean 221 - | null 222 - | { [x: string]: JSONType } 223 - | JSONType[]; 199 + export type JSONType = string | number | boolean | null | { [x: string]: JSONType } | JSONType[];
+4 -14
src/components/navbar.tsx
··· 6 6 export const [pds, setPDS] = createSignal<string>(); 7 7 export const [cid, setCID] = createSignal<string>(); 8 8 export const [isLabeler, setIsLabeler] = createSignal(false); 9 - export const [validRecord, setValidRecord] = createSignal<boolean | undefined>( 10 - undefined, 11 - ); 9 + export const [validRecord, setValidRecord] = createSignal<boolean | undefined>(undefined); 12 10 13 11 const NavBar = (props: { params: Params }) => { 14 12 const [openMenu, setOpenMenu] = createSignal(false); ··· 29 27 <Tooltip text="PDS"> 30 28 <div class="i-tabler-server mr-1 shrink-0" /> 31 29 </Tooltip> 32 - <A 33 - end 34 - href={pds()!} 35 - inactiveClass="text-lightblue-500 w-full hover:underline" 36 - > 30 + <A end href={pds()!} inactiveClass="text-lightblue-500 w-full hover:underline"> 37 31 {pds()} 38 32 </A> 39 33 </Show> ··· 49 43 <Show when={props.params.repo}> 50 44 <button 51 45 class="p-0.75 flex items-center rounded hover:bg-neutral-200 dark:hover:bg-neutral-600" 52 - onclick={() => 53 - navigator.clipboard.writeText(props.params.repo) 54 - } 46 + onclick={() => navigator.clipboard.writeText(props.params.repo)} 55 47 > 56 48 Copy DID 57 49 </button> ··· 121 113 </Show> 122 114 <Show 123 115 when={ 124 - props.params.repo in labelerCache && 125 - !props.params.collection && 126 - !props.params.rkey 116 + props.params.repo in labelerCache && !props.params.collection && !props.params.rkey 127 117 } 128 118 > 129 119 <div class="mt-1 flex items-center">
+3 -9
src/components/search.tsx
··· 13 13 !input.startsWith("https://main.bsky.dev/") && 14 14 (input.startsWith("https://") || input.startsWith("http://")) 15 15 ) 16 - throw redirect( 17 - `/${input.replace("https://", "").replace("http://", "").replace("/", "")}`, 18 - ); 16 + throw redirect(`/${input.replace("https://", "").replace("http://", "").replace("/", "")}`); 19 17 20 18 const uri = input 21 19 .replace("at://", "") ··· 30 28 } catch { 31 29 throw redirect(`/${actor}`); 32 30 } 33 - throw redirect( 34 - `/at://${did}${uriParts.length > 1 ? `/${uriParts.slice(1).join("/")}` : ""}`, 35 - ); 31 + throw redirect(`/at://${did}${uriParts.length > 1 ? `/${uriParts.slice(1).join("/")}` : ""}`); 36 32 }); 37 33 38 34 const Search = () => { ··· 77 73 </Show> 78 74 </div> 79 75 </form> 80 - <Show when={submission.error}> 81 - {(err) => <div class="mt-3">{err().message}</div>} 82 - </Show> 76 + <Show when={submission.error}>{(err) => <div class="mt-3">{err().message}</div>}</Show> 83 77 </> 84 78 ); 85 79 };
+10 -33
src/components/settings.tsx
··· 4 4 const getInitialTheme = () => { 5 5 const isDarkMode = 6 6 localStorage.theme === "dark" || 7 - (!("theme" in localStorage) && 8 - window.matchMedia("(prefers-color-scheme: dark)").matches); 7 + (!("theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches); 9 8 return { 10 9 color: isDarkMode ? "dark" : "light", 11 10 system: !("theme" in localStorage), ··· 13 12 }; 14 13 15 14 export const [theme, setTheme] = createSignal(getInitialTheme()); 16 - const [backlinksEnabled, setBacklinksEnabled] = createSignal( 17 - localStorage.backlinks === "true", 18 - ); 15 + const [backlinksEnabled, setBacklinksEnabled] = createSignal(localStorage.backlinks === "true"); 19 16 20 17 const Settings = () => { 21 18 const [modal, setModal] = createSignal<HTMLDialogElement>(); ··· 41 38 onMount(() => { 42 39 window.addEventListener("keydown", keyEvent); 43 40 window.addEventListener("click", clickEvent); 44 - window 45 - .matchMedia("(prefers-color-scheme: dark)") 46 - .addEventListener("change", themeEvent); 41 + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", themeEvent); 47 42 }); 48 43 49 44 onCleanup(() => { 50 45 window.removeEventListener("keydown", keyEvent); 51 46 window.removeEventListener("click", clickEvent); 52 - window 53 - .matchMedia("(prefers-color-scheme: dark)") 54 - .removeEventListener("change", themeEvent); 47 + window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", themeEvent); 55 48 }); 56 49 57 50 createEffect(() => { ··· 61 54 62 55 const updateTheme = (newTheme: { color: string; system: boolean }) => { 63 56 setTheme(newTheme); 64 - document.documentElement.classList.toggle( 65 - "dark", 66 - newTheme.color === "dark", 67 - ); 57 + document.documentElement.classList.toggle("dark", newTheme.color === "dark"); 68 58 if (newTheme.system) { 69 59 localStorage.removeItem("theme"); 70 60 } else { ··· 80 70 class="backdrop-brightness-60 fixed left-0 top-0 z-20 flex h-screen w-screen items-center justify-center bg-transparent" 81 71 > 82 72 <div class="dark:bg-dark-400 top-10% absolute rounded-md border border-slate-900 bg-slate-100 p-4 text-slate-900 dark:border-slate-100 dark:text-slate-100"> 83 - <h3 class="mb-2 border-b border-neutral-500 pb-2 text-xl font-bold"> 84 - Settings 85 - </h3> 73 + <h3 class="mb-2 border-b border-neutral-500 pb-2 text-xl font-bold">Settings</h3> 86 74 <h4 class="mb-1 font-semibold">Theme</h4> 87 75 <div class="w-xs flex divide-x divide-neutral-500 overflow-hidden rounded-lg border border-neutral-500"> 88 76 <button ··· 94 82 onclick={() => 95 83 updateTheme({ 96 84 color: 97 - ( 98 - window.matchMedia("(prefers-color-scheme: dark)") 99 - .matches 100 - ) ? 101 - "dark" 102 - : "light", 85 + window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light", 103 86 system: true, 104 87 }) 105 88 } ··· 111 94 "basis-1/3 p-2": true, 112 95 "hover:bg-slate-200 dark:hover:bg-dark-200": 113 96 theme().color !== "light" || theme().system, 114 - "bg-neutral-500 text-slate-100": 115 - theme().color === "light" && !theme().system, 97 + "bg-neutral-500 text-slate-100": theme().color === "light" && !theme().system, 116 98 }} 117 99 onclick={() => updateTheme({ color: "light", system: false })} 118 100 > ··· 161 143 name="constellation" 162 144 type="text" 163 145 spellcheck={false} 164 - value={ 165 - localStorage.constellationHost || 166 - "https://constellation.microcosm.blue" 167 - } 146 + value={localStorage.constellationHost || "https://constellation.microcosm.blue"} 168 147 disabled={!backlinksEnabled()} 169 148 class="dark:bg-dark-100 rounded-lg border border-gray-400 px-2 py-1 focus:outline-none focus:ring-1 focus:ring-gray-300 disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 dark:disabled:border-gray-700 dark:disabled:bg-gray-800/20" 170 - onInput={(e) => 171 - (localStorage.constellationHost = e.currentTarget.value) 172 - } 149 + onInput={(e) => (localStorage.constellationHost = e.currentTarget.value)} 173 150 /> 174 151 </div> 175 152 </div>
+5 -20
src/layout.tsx
··· 28 28 window.location.href = location.pathname.replace(params.repo, did); 29 29 } 30 30 await retrieveSession(); 31 - if (loginState() && location.pathname === "/") 32 - window.location.href = `/at://${agent.sub}`; 31 + if (loginState() && location.pathname === "/") window.location.href = `/at://${agent.sub}`; 33 32 }); 34 33 35 34 return ( 36 - <div 37 - id="main" 38 - class="m-5 flex flex-col items-center text-slate-900 dark:text-slate-100" 39 - > 35 + <div id="main" class="m-5 flex flex-col items-center text-slate-900 dark:text-slate-100"> 40 36 <Show when={location.pathname !== "/"}> 41 37 <MetaProvider> 42 38 <Meta name="robots" content="noindex, nofollow" /> ··· 69 65 </div> 70 66 </div> 71 67 <div class="mb-5 flex max-w-full flex-col items-center text-pretty md:max-w-screen-md"> 72 - <Show 73 - when={ 74 - location.pathname !== "/jetstream" && 75 - location.pathname !== "/firehose" 76 - } 77 - > 68 + <Show when={location.pathname !== "/jetstream" && location.pathname !== "/firehose"}> 78 69 <Search /> 79 70 </Show> 80 71 <Show when={params.pds}> ··· 82 73 </Show> 83 74 <Show keyed when={location.pathname}> 84 75 <ErrorBoundary 85 - fallback={(err) => ( 86 - <div class="mt-3 break-words">Error: {err.message}</div> 87 - )} 76 + fallback={(err) => <div class="mt-3 break-words">Error: {err.message}</div>} 88 77 > 89 - <Suspense 90 - fallback={ 91 - <div class="i-line-md-loading-twotone-loop mt-3 text-xl" /> 92 - } 93 - > 78 + <Suspense fallback={<div class="i-line-md-loading-twotone-loop mt-3 text-xl" />}> 94 79 {props.children} 95 80 </Suspense> 96 81 </ErrorBoundary>
+2 -2
src/styles/index.css
··· 4 4 samp, 5 5 pre { 6 6 font-family: 7 - "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 8 - "Liberation Mono", "Courier New", monospace; 7 + "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", 8 + "Courier New", monospace; 9 9 } 10 10 11 11 .string {
+11 -7
src/styles/tailwind.css
··· 15 15 16 16 ::before, 17 17 ::after { 18 - --un-content: ''; 18 + --un-content: ""; 19 19 } 20 20 21 21 /* ··· 34 34 -webkit-text-size-adjust: 100%; /* 2 */ 35 35 -moz-tab-size: 4; /* 3 */ 36 36 tab-size: 4; /* 3 */ 37 - font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ 37 + font-family: 38 + ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", 39 + "Noto Color Emoji"; /* 4 */ 38 40 font-feature-settings: normal; /* 5 */ 39 41 font-variation-settings: normal; /* 6 */ 40 42 -webkit-tap-highlight-color: transparent; /* 7 */ ··· 113 115 kbd, 114 116 samp, 115 117 pre { 116 - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */ 118 + font-family: 119 + ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", 120 + monospace; /* 1 */ 117 121 font-feature-settings: normal; /* 2 */ 118 122 font-variation-settings: normal; /* 3 */ 119 123 font-size: 1em; /* 4 */ ··· 196 200 */ 197 201 198 202 button, 199 - [type='button'], 200 - [type='reset'], 201 - [type='submit'] { 203 + [type="button"], 204 + [type="reset"], 205 + [type="submit"] { 202 206 -webkit-appearance: button; /* 1 */ 203 207 background-color: transparent; /* 2 */ 204 208 background-image: none; /* 2 */ ··· 242 246 2. Correct the outline style in Safari. 243 247 */ 244 248 245 - [type='search'] { 249 + [type="search"] { 246 250 -webkit-appearance: textfield; /* 1 */ 247 251 outline-offset: -2px; /* 2 */ 248 252 }
+5 -18
src/utils/api.ts
··· 90 90 cursor?: string, 91 91 limit?: number, 92 92 ) => { 93 - const url = new URL( 94 - localStorage.constellationHost || "https://constellation.microcosm.blue", 95 - ); 93 + const url = new URL(localStorage.constellationHost || "https://constellation.microcosm.blue"); 96 94 url.pathname = endpoint; 97 95 url.searchParams.set("target", target); 98 96 if (collection) { 99 - if (!path) 100 - throw new Error("collection and path must either both be set or neither"); 97 + if (!path) throw new Error("collection and path must either both be set or neither"); 101 98 url.searchParams.set("collection", collection); 102 99 url.searchParams.set("path", path); 103 100 } else { 104 - if (path) 105 - throw new Error("collection and path must either both be set or neither"); 101 + if (path) throw new Error("collection and path must either both be set or neither"); 106 102 } 107 103 if (limit) url.searchParams.set("limit", `${limit}`); 108 104 if (cursor) url.searchParams.set("cursor", `${cursor}`); ··· 111 107 return await res.json(); 112 108 }; 113 109 114 - const getAllBacklinks = (target: string) => 115 - getConstellation("/links/all", target); 110 + const getAllBacklinks = (target: string) => getConstellation("/links/all", target); 116 111 117 112 const getRecordBacklinks = ( 118 113 target: string, ··· 128 123 path: string, 129 124 cursor?: string, 130 125 limit?: number, 131 - ) => 132 - getConstellation( 133 - "/links/distinct-dids", 134 - target, 135 - collection, 136 - path, 137 - cursor, 138 - limit || 100, 139 - ); 126 + ) => getConstellation("/links/distinct-dids", target, collection, path, cursor, limit || 100); 140 127 141 128 export { 142 129 didDocCache,
+1 -3
src/utils/firehose.ts
··· 196 196 return this; 197 197 } 198 198 199 - private parseMessage( 200 - data: ArrayBuffer, 201 - ): ParsedCommit | { $type: string; seq?: number } { 199 + private parseMessage(data: ArrayBuffer): ParsedCommit | { $type: string; seq?: number } { 202 200 const [header, remainder] = decodeFirst(new Uint8Array(data)); 203 201 const [body, remainder2] = decodeFirst(remainder); 204 202 if (remainder2.length > 0) {
+2 -8
src/utils/types/at-uri.ts
··· 16 16 17 17 const isDid = (input: unknown): input is Did => { 18 18 return ( 19 - typeof input === "string" && 20 - input.length >= 7 && 21 - input.length <= 2048 && 22 - DID_RE.test(input) 19 + typeof input === "string" && input.length >= 7 && input.length <= 2048 && DID_RE.test(input) 23 20 ); 24 21 }; 25 22 26 23 const isNsid = (input: unknown): input is Nsid => { 27 24 return ( 28 - typeof input === "string" && 29 - input.length >= 5 && 30 - input.length <= 317 && 31 - NSID_RE.test(input) 25 + typeof input === "string" && input.length >= 5 && input.length <= 317 && NSID_RE.test(input) 32 26 ); 33 27 }; 34 28
+4 -15
src/utils/verify.ts
··· 4 4 import * as CAR from "@atcute/car"; 5 5 import * as CBOR from "@atcute/cbor"; 6 6 import * as CID from "@atcute/cid"; 7 - import { 8 - type FoundPublicKey, 9 - getPublicKeyFromDidController, 10 - verifySig, 11 - } from "@atcute/crypto"; 12 - import { 13 - type DidDocument, 14 - getAtprotoVerificationMaterial, 15 - } from "@atcute/identity"; 7 + import { type FoundPublicKey, getPublicKeyFromDidController, verifySig } from "@atcute/crypto"; 8 + import { type DidDocument, getAtprotoVerificationMaterial } from "@atcute/identity"; 16 9 import { toSha256 } from "@atcute/uint8array"; 17 10 18 11 import { type AddressedAtUri, parseAddressedAtUri } from "./types/at-uri"; ··· 34 27 didDoc: DidDocument; 35 28 } 36 29 37 - export const verifyRecord = async ( 38 - opts: VerifyOptions, 39 - ): Promise<VerifyResult> => { 30 + export const verifyRecord = async (opts: VerifyOptions): Promise<VerifyResult> => { 40 31 const errors: VerifyError[] = []; 41 32 42 33 // verify cid can be parsed ··· 125 116 const cidString = CID.toString(entry.cid); 126 117 127 118 // Verify that `bytes` matches its associated CID 128 - const expectedCid = CID.toString( 129 - await CID.create(entry.cid.codec as 85 | 113, entry.bytes), 130 - ); 119 + const expectedCid = CID.toString(await CID.create(entry.cid.codec as 85 | 113, entry.bytes)); 131 120 if (cidString !== expectedCid) { 132 121 errors.push({ 133 122 message: `cid does not match bytes`,
+1 -2
src/views/blob.tsx
··· 25 25 const fetchBlobs = async (): Promise<string[]> => { 26 26 if (!did.startsWith("did:")) did = await resolveHandle(params.repo); 27 27 if (!pds) pds = await resolvePDS(did); 28 - if (!rpc) 29 - rpc = new XRPC({ handler: new CredentialManager({ service: pds }) }); 28 + if (!rpc) rpc = new XRPC({ handler: new CredentialManager({ service: pds }) }); 30 29 const res = await listBlobs(did, cursor()); 31 30 setCursor(res.data.cids.length < 1000 ? undefined : res.data.cursor); 32 31 setBlobs(blobs()?.concat(res.data.cids) ?? res.data.cids);
+7 -18
src/views/collection.tsx
··· 50 50 onmouseleave={() => setHoverRk(undefined)} 51 51 > 52 52 <span class="text-lightblue-500">{props.record.rkey}</span> 53 - <Show 54 - when={props.record.timestamp && props.record.timestamp <= Date.now()} 55 - > 53 + <Show when={props.record.timestamp && props.record.timestamp <= Date.now()}> 56 54 <span class="ml-2 text-xs text-neutral-500 dark:text-neutral-400"> 57 55 {localDateFromTimestamp(props.record.timestamp!)} 58 56 </span> ··· 121 119 const fetchRecords = async () => { 122 120 if (!did.startsWith("did:")) did = await resolveHandle(params.repo); 123 121 if (!pds) pds = await resolvePDS(did); 124 - if (!rpc) 125 - rpc = new XRPC({ handler: new CredentialManager({ service: pds }) }); 122 + if (!rpc) rpc = new XRPC({ handler: new CredentialManager({ service: pds }) }); 126 123 const res = await listRecords(did, params.collection, cursor()); 127 124 setCursor(res.data.records.length < 100 ? undefined : res.data.cursor); 128 125 const tmpRecords: AtprotoRecord[] = []; ··· 131 128 tmpRecords.push({ 132 129 rkey: rkey, 133 130 record: record, 134 - timestamp: 135 - TID.validate(rkey) ? TID.parse(rkey).timestamp / 1000 : undefined, 131 + timestamp: TID.validate(rkey) ? TID.parse(rkey).timestamp / 1000 : undefined, 136 132 toDelete: false, 137 133 }); 138 134 }); ··· 183 179 setRecords( 184 180 records 185 181 .map((record, index) => 186 - JSON.stringify(record.record.value).includes(filter() ?? "") ? 187 - index 188 - : undefined, 182 + JSON.stringify(record.record.value).includes(filter() ?? "") ? index : undefined, 189 183 ) 190 184 .filter((i) => i !== undefined), 191 185 "toDelete", ··· 271 265 > 272 266 <div class="dark:bg-dark-400 rounded-md border border-neutral-500 bg-slate-100 p-3 text-slate-900 dark:text-slate-100"> 273 267 <h3 class="text-lg font-bold"> 274 - Delete {records.filter((rec) => rec.toDelete).length}{" "} 275 - records? 268 + Delete {records.filter((rec) => rec.toDelete).length} records? 276 269 </h3> 277 270 <div class="mt-2 inline-flex gap-2"> 278 271 <button ··· 334 327 <div class="flex flex-col font-mono"> 335 328 <For 336 329 each={records.filter((rec) => 337 - filter() ? 338 - JSON.stringify(rec.record.value).includes(filter()!) 339 - : true, 330 + filter() ? JSON.stringify(rec.record.value).includes(filter()!) : true, 340 331 )} 341 332 > 342 333 {(record, index) => ( ··· 349 340 <input 350 341 type="checkbox" 351 342 checked={record.toDelete} 352 - onchange={(e) => 353 - setRecords(index(), "toDelete", e.currentTarget.checked) 354 - } 343 + onchange={(e) => setRecords(index(), "toDelete", e.currentTarget.checked)} 355 344 /> 356 345 <RecordLink record={record} index={index()} /> 357 346 </label>
+2 -9
src/views/home.tsx
··· 6 6 <div class="mb-2"> 7 7 <p> 8 8 Browse the public data on{" "} 9 - <a 10 - class="text-lightblue-500 hover:underline" 11 - href="https://atproto.com" 12 - target="_blank" 13 - > 9 + <a class="text-lightblue-500 hover:underline" href="https://atproto.com" target="_blank"> 14 10 AT Protocol 15 11 </a> 16 12 . ··· 41 37 <div> 42 38 <span class="font-semibold text-orange-400">PDS</span> 43 39 <div> 44 - <A 45 - href="/pyramid-activation.today" 46 - class="text-lightblue-500 hover:underline" 47 - > 40 + <A href="/pyramid-activation.today" class="text-lightblue-500 hover:underline"> 48 41 https://pyramid-activation.today 49 42 </A> 50 43 </div>
+5 -15
src/views/labels.tsx
··· 24 24 }); 25 25 26 26 const fetchLabels = async () => { 27 - const uriPatterns = ( 28 - document.getElementById("patterns") as HTMLInputElement 29 - ).value; 27 + const uriPatterns = (document.getElementById("patterns") as HTMLInputElement).value; 30 28 if (!uriPatterns) return; 31 29 const res = await rpc.get("com.atproto.label.queryLabels", { 32 30 params: { ··· 46 44 setLabels([]); 47 45 setCursor(""); 48 46 setSearchParams({ 49 - uriPatterns: (document.getElementById("patterns") as HTMLInputElement) 50 - .value, 47 + uriPatterns: (document.getElementById("patterns") as HTMLInputElement).value, 51 48 }); 52 49 refetch(); 53 50 }; 54 51 55 52 const filterLabels = () => { 56 - const newFilter = labels().filter((label) => 57 - filter() ? filter() === label.val : true, 58 - ); 53 + const newFilter = labels().filter((label) => (filter() ? filter() === label.val : true)); 59 54 setLabelCount(newFilter.length); 60 55 return newFilter; 61 56 }; 62 57 63 58 return ( 64 59 <> 65 - <form 66 - class="mt-3 flex flex-col items-center gap-y-1" 67 - onsubmit={(e) => e.preventDefault()} 68 - > 60 + <form class="mt-3 flex flex-col items-center gap-y-1" onsubmit={(e) => e.preventDefault()}> 69 61 <div class="w-full"> 70 62 <label for="patterns" class="ml-0.5 text-sm"> 71 63 URI Patterns (comma-separated) ··· 191 183 </For> 192 184 </div> 193 185 </Show> 194 - <Show 195 - when={!labels().length && !response.loading && searchParams.uriPatterns} 196 - > 186 + <Show when={!labels().length && !response.loading && searchParams.uriPatterns}> 197 187 <div class="mt-2">No results</div> 198 188 </Show> 199 189 </>
+4 -12
src/views/pds.tsx
··· 11 11 const [version, setVersion] = createSignal<string>(); 12 12 const [cursor, setCursor] = createSignal<string>(); 13 13 setPDS(params.pds); 14 - const pds = 15 - params.pds.startsWith("localhost") ? 16 - `http://${params.pds}` 17 - : `https://${params.pds}`; 14 + const pds = params.pds.startsWith("localhost") ? `http://${params.pds}` : `https://${params.pds}`; 18 15 const rpc = new XRPC({ handler: new CredentialManager({ service: pds }) }); 19 16 20 17 const listRepos = async (cursor: string | undefined) => ··· 44 41 <div class="mt-3 flex flex-col"> 45 42 <Show when={version()}> 46 43 <div class="flex max-w-[21rem] gap-1"> 47 - <span class="font-semibold text-stone-600 dark:text-stone-400"> 48 - Version 49 - </span> 44 + <span class="font-semibold text-stone-600 dark:text-stone-400">Version</span> 50 45 <span class="break-anywhere">{version()}</span> 51 46 </div> 52 47 </Show> 53 - <p class="w-full font-semibold text-stone-600 dark:text-stone-400"> 54 - Repositories 55 - </p> 48 + <p class="w-full font-semibold text-stone-600 dark:text-stone-400">Repositories</p> 56 49 <For each={repos()}> 57 50 {(repo) => ( 58 51 <A ··· 60 53 classList={{ 61 54 "w-full flex font-mono relative": true, 62 55 "text-lightblue-500": repo.active, 63 - "text-gray-300 absolute -left-5 dark:text-gray-600": 64 - !repo.active, 56 + "text-gray-300 absolute -left-5 dark:text-gray-600": !repo.active, 65 57 }} 66 58 > 67 59 <Show when={!repo.active}>
+12 -42
src/views/record.tsx
··· 13 13 import { setCID, setValidRecord, validRecord } from "../components/navbar.jsx"; 14 14 import { theme } from "../components/settings.jsx"; 15 15 16 - import { 17 - didDocCache, 18 - getAllBacklinks, 19 - LinkData, 20 - resolveHandle, 21 - resolvePDS, 22 - } from "../utils/api.js"; 16 + import { didDocCache, getAllBacklinks, LinkData, resolveHandle, resolvePDS } from "../utils/api.js"; 23 17 import { AtUri, uriTemplates } from "../utils/templates.js"; 24 18 import { verifyRecord } from "../utils/verify.js"; 25 19 ··· 79 73 80 74 if (errors.length > 0) { 81 75 console.warn(errors); 82 - setNotice( 83 - `Invalid record: ${errors.map((e) => e.message).join("\n")}`, 84 - ); 76 + setNotice(`Invalid record: ${errors.map((e) => e.message).join("\n")}`); 85 77 } 86 78 setValidRecord(errors.length === 0); 87 79 } catch (err) { ··· 203 195 <div class="i-line-md-loading-twotone-loop mt-3 text-xl" /> 204 196 </Show> 205 197 <Show when={validRecord() === false}> 206 - <div class="w-20rem mb-2 mt-3 break-words text-red-500 dark:text-red-400"> 207 - {notice()} 208 - </div> 198 + <div class="w-20rem mb-2 mt-3 break-words text-red-500 dark:text-red-400">{notice()}</div> 209 199 </Show> 210 200 <Show when={record()}> 211 201 <div class="my-4 flex w-full justify-center gap-x-2"> ··· 221 211 target="_blank" 222 212 href={externalLink()?.link} 223 213 > 224 - {externalLink()?.label}{" "} 225 - <div class="i-tabler-external-link text-sm" /> 214 + {externalLink()?.label} <div class="i-tabler-external-link text-sm" /> 226 215 </a> 227 216 </Show> 228 - <Show 229 - when={loginState() && agent.sub === record()?.uri.split("/")[2]} 230 - > 217 + <Show when={loginState() && agent.sub === record()?.uri.split("/")[2]}> 231 218 <Show when={openEdit()}> 232 219 <dialog 233 220 ref={setModal} ··· 252 239 </div> 253 240 <Editor theme={theme().color} model={model!} /> 254 241 <div class="mt-2 flex flex-col gap-2"> 255 - <div class="text-red-500 dark:text-red-400"> 256 - {editNotice()} 257 - </div> 242 + <div class="text-red-500 dark:text-red-400">{editNotice()}</div> 258 243 <div class="flex items-center justify-end gap-2"> 259 244 <div class="flex items-center gap-1"> 260 - <input 261 - id="recreate" 262 - class="size-4" 263 - name="recreate" 264 - type="checkbox" 265 - /> 245 + <input id="recreate" class="size-4" name="recreate" type="checkbox" /> 266 246 <label for="recreate" class="select-none"> 267 247 Recreate record 268 248 </label> ··· 288 268 </Show> 289 269 <button 290 270 onclick={() => { 291 - model = editor.createModel( 292 - JSON.stringify(record()?.value, null, 2), 293 - "json", 294 - ); 271 + model = editor.createModel(JSON.stringify(record()?.value, null, 2), "json"); 295 272 setOpenEdit(true); 296 273 }} 297 274 class="dark:bg-dark-700 dark:hover:bg-dark-800 rounded-lg border border-slate-400 bg-white px-2.5 py-1.5 text-sm font-bold hover:bg-slate-100 focus:outline-none focus:ring-1 focus:ring-slate-700 dark:focus:ring-slate-300" ··· 335 312 </div> 336 313 <div 337 314 classList={{ 338 - "break-anywhere mb-2 whitespace-pre-wrap pb-3 font-mono text-sm sm:text-base": 339 - true, 315 + "break-anywhere mb-2 whitespace-pre-wrap pb-3 font-mono text-sm sm:text-base": true, 340 316 "border-b border-neutral-500": !!backlinks(), 341 317 }} 342 318 > 343 319 <Show when={!JSONSyntax()}> 344 - <JSONValue 345 - data={record()?.value as any} 346 - repo={record()!.uri.split("/")[2]} 347 - /> 320 + <JSONValue data={record()?.value as any} repo={record()!.uri.split("/")[2]} /> 348 321 </Show> 349 322 <Show when={JSONSyntax()}> 350 323 <span 351 324 innerHTML={syntaxHighlight( 352 325 JSON.stringify(record()?.value, null, 2).replace( 353 326 /[\u007F-\uFFFF]/g, 354 - (chr) => 355 - "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).slice(-4), 327 + (chr) => "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).slice(-4), 356 328 ), 357 329 )} 358 330 ></span> 359 331 </Show> 360 332 </div> 361 333 <Show when={backlinks()}> 362 - {(backlinks) => ( 363 - <Backlinks links={backlinks().links} target={backlinks().target} /> 364 - )} 334 + {(backlinks) => <Backlinks links={backlinks().links} target={backlinks().target} />} 365 335 </Show> 366 336 </Show> 367 337 </>
+12 -38
src/views/repo.tsx
··· 1 1 import { createSignal, For, Show, createResource } from "solid-js"; 2 2 import { CredentialManager, XRPC } from "@atcute/client"; 3 3 import { A, query, useParams } from "@solidjs/router"; 4 - import { 5 - didDocCache, 6 - getAllBacklinks, 7 - LinkData, 8 - resolveHandle, 9 - resolvePDS, 10 - } from "../utils/api.js"; 4 + import { didDocCache, getAllBacklinks, LinkData, resolveHandle, resolvePDS } from "../utils/api.js"; 11 5 import { DidDocument } from "@atcute/client/utils/did"; 12 6 import { Backlinks } from "../components/backlinks.jsx"; 13 7 ··· 24 18 let did = params.repo; 25 19 26 20 const describeRepo = query( 27 - (repo: string) => 28 - rpc.get("com.atproto.repo.describeRepo", { params: { repo: repo } }), 21 + (repo: string) => rpc.get("com.atproto.repo.describeRepo", { params: { repo: repo } }), 29 22 "describeRepo", 30 23 ); 31 24 ··· 51 44 const downloadRepo = async () => { 52 45 try { 53 46 setDownloading(true); 54 - const response = await fetch( 55 - `${pds}/xrpc/com.atproto.sync.getRepo?did=${did}`, 56 - ); 47 + const response = await fetch(`${pds}/xrpc/com.atproto.sync.getRepo?did=${did}`); 57 48 if (!response.ok) { 58 49 throw new Error(`HTTP error status: ${response.status}`); 59 50 } ··· 78 69 <Show when={repo()}> 79 70 <div class="mt-3 flex w-[21rem] flex-col gap-2 break-words"> 80 71 <div class="flex flex-col border-b border-neutral-500 pb-2 font-mono"> 81 - <p class="font-sans font-semibold text-stone-600 dark:text-stone-400"> 82 - Collections 83 - </p> 72 + <p class="font-sans font-semibold text-stone-600 dark:text-stone-400">Collections</p> 84 73 <For each={repo()?.collections}> 85 74 {(collection) => ( 86 75 <A ··· 96 85 {(didDocument) => ( 97 86 <div class="flex flex-col gap-y-1"> 98 87 <div> 99 - <span class="font-semibold text-stone-600 dark:text-stone-400"> 100 - ID{" "} 101 - </span> 88 + <span class="font-semibold text-stone-600 dark:text-stone-400">ID </span> 102 89 <span>{didDocument().id}</span> 103 90 </div> 104 91 <div> 105 - <p class="font-semibold text-stone-600 dark:text-stone-400"> 106 - Identities 107 - </p> 92 + <p class="font-semibold text-stone-600 dark:text-stone-400">Identities</p> 108 93 <ul class="ml-3"> 109 - <For each={didDocument().alsoKnownAs}> 110 - {(alias) => <li>{alias}</li>} 111 - </For> 94 + <For each={didDocument().alsoKnownAs}>{(alias) => <li>{alias}</li>}</For> 112 95 </ul> 113 96 </div> 114 97 <div> 115 - <p class="font-semibold text-stone-600 dark:text-stone-400"> 116 - Services 117 - </p> 98 + <p class="font-semibold text-stone-600 dark:text-stone-400">Services</p> 118 99 <ul class="ml-3"> 119 100 <For each={didDocument().service}> 120 101 {(service) => ( ··· 133 114 </ul> 134 115 </div> 135 116 <div> 136 - <p class="font-semibold text-stone-600 dark:text-stone-400"> 137 - Verification methods 138 - </p> 117 + <p class="font-semibold text-stone-600 dark:text-stone-400">Verification methods</p> 139 118 <ul class="ml-3"> 140 119 <For each={didDocument().verificationMethod}> 141 120 {(verif) => ( ··· 156 135 } 157 136 target="_blank" 158 137 > 159 - DID document{" "} 160 - <div class="i-tabler-external-link ml-0.5 text-xs" /> 138 + DID document <div class="i-tabler-external-link ml-0.5 text-xs" /> 161 139 </a> 162 140 <Show when={repo()?.did.startsWith("did:plc")}> 163 141 <a ··· 165 143 href={`https://boat.kelinci.net/plc-oplogs?q=${repo()?.did}`} 166 144 target="_blank" 167 145 > 168 - PLC operation logs{" "} 169 - <div class="i-tabler-external-link ml-0.5 text-xs" /> 146 + PLC operation logs <div class="i-tabler-external-link ml-0.5 text-xs" /> 170 147 </a> 171 148 </Show> 172 149 <div class="flex items-center gap-1"> ··· 183 160 <Show when={backlinks()}> 184 161 {(backlinks) => ( 185 162 <div class="mt-2 border-t border-neutral-500 pt-2"> 186 - <Backlinks 187 - links={backlinks().links} 188 - target={backlinks().target} 189 - /> 163 + <Backlinks links={backlinks().links} target={backlinks().target} /> 190 164 </div> 191 165 )} 192 166 </Show>
+9 -25
src/views/stream.tsx
··· 14 14 const [searchParams, setSearchParams] = useSearchParams(); 15 15 const [parameters, setParameters] = createSignal<Parameter[]>([]); 16 16 const streamType = 17 - useLocation().pathname === "/firehose" ? 18 - StreamType.FIREHOSE 19 - : StreamType.JETSTREAM; 17 + useLocation().pathname === "/firehose" ? StreamType.FIREHOSE : StreamType.JETSTREAM; 20 18 21 19 const [records, setRecords] = createSignal<Array<any>>([]); 22 20 const [connected, setConnected] = createSignal(false); ··· 37 35 let url = ""; 38 36 if (streamType === StreamType.JETSTREAM) { 39 37 url = 40 - formData.get("instance")?.toString() ?? 41 - "wss://jetstream1.us-east.bsky.network/subscribe"; 38 + formData.get("instance")?.toString() ?? "wss://jetstream1.us-east.bsky.network/subscribe"; 42 39 url = url.concat("?"); 43 40 } else { 44 41 url = formData.get("instance")?.toString() ?? "wss://bsky.network"; ··· 46 43 47 44 const collections = formData.get("collections")?.toString().split(","); 48 45 collections?.forEach((collection) => { 49 - if (collection.length) 50 - url = url.concat(`wantedCollections=${collection}&`); 46 + if (collection.length) url = url.concat(`wantedCollections=${collection}&`); 51 47 }); 52 48 53 49 const dids = formData.get("dids")?.toString().split(","); ··· 131 127 132 128 onMount(async () => { 133 129 const formData = new FormData(); 134 - if (searchParams.instance) 135 - formData.append("instance", searchParams.instance.toString()); 130 + if (searchParams.instance) formData.append("instance", searchParams.instance.toString()); 136 131 if (searchParams.collections) 137 132 formData.append("collections", searchParams.collections.toString()); 138 - if (searchParams.dids) 139 - formData.append("dids", searchParams.dids.toString()); 140 - if (searchParams.cursor) 141 - formData.append("cursor", searchParams.cursor.toString()); 142 - if (searchParams.allEvents) 143 - formData.append("allEvents", searchParams.allEvents.toString()); 133 + if (searchParams.dids) formData.append("dids", searchParams.dids.toString()); 134 + if (searchParams.cursor) formData.append("cursor", searchParams.cursor.toString()); 135 + if (searchParams.allEvents) formData.append("allEvents", searchParams.allEvents.toString()); 144 136 if (searchParams.instance) connectSocket(formData); 145 137 }); 146 138 ··· 149 141 return ( 150 142 <div class="mt-4 flex flex-col items-center gap-y-3"> 151 143 <div class="flex divide-x-2 text-lg font-bold"> 152 - <A 153 - class="pr-2" 154 - inactiveClass="text-lightblue-500 hover:underline" 155 - href="/jetstream" 156 - > 144 + <A class="pr-2" inactiveClass="text-lightblue-500 hover:underline" href="/jetstream"> 157 145 Jetstream 158 146 </A> 159 - <A 160 - class="pl-2" 161 - inactiveClass="text-lightblue-500 hover:underline" 162 - href="/firehose" 163 - > 147 + <A class="pl-2" inactiveClass="text-lightblue-500 hover:underline" href="/firehose"> 164 148 Firehose 165 149 </A> 166 150 </div>