+1
-1
src/components/backlinks.tsx
+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
+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
+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
+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
+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
+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
+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
+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
+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
() => (