tangled
alpha
login
or
join now
aylac.top
/
pdsls
forked from
pds.ls/pdsls
0
fork
atom
atproto explorer
0
fork
atom
overview
issues
pulls
pipelines
new repo tabs
handle.invalid
5 months ago
785a22bf
143a70c2
verified
This commit was signed with the committer's
known signature
.
handle.invalid
SSH Key Fingerprint:
SHA256:mBrT4x0JdzLpbVR95g1hjI1aaErfC02kmLRkPXwsYCk=
+327
-335
3 changed files
expand all
collapse all
unified
split
src
components
dropdown.tsx
views
logs.tsx
repo.tsx
+18
src/components/dropdown.tsx
···
59
59
);
60
60
};
61
61
62
62
+
export const ActionMenu = (props: {
63
63
+
label: string;
64
64
+
icon: string;
65
65
+
onClick: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent>;
66
66
+
}) => {
67
67
+
return (
68
68
+
<button
69
69
+
onClick={props.onClick}
70
70
+
class="flex items-center gap-1.5 rounded-lg p-1 whitespace-nowrap hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"
71
71
+
>
72
72
+
<Show when={props.icon}>
73
73
+
<span class={"iconify shrink-0 " + props.icon}></span>
74
74
+
</Show>
75
75
+
<span class="whitespace-nowrap">{props.label}</span>
76
76
+
</button>
77
77
+
);
78
78
+
};
79
79
+
62
80
export const DropdownMenu = (props: {
63
81
icon: string;
64
82
buttonClass?: string;
+155
src/views/logs.tsx
···
1
1
+
import {
2
2
+
CompatibleOperationOrTombstone,
3
3
+
defs,
4
4
+
IndexedEntry,
5
5
+
processIndexedEntryLog,
6
6
+
} from "@atcute/did-plc";
7
7
+
import { createResource, createSignal, For, Show } from "solid-js";
8
8
+
import Tooltip from "../components/tooltip.jsx";
9
9
+
import { localDateFromTimestamp } from "../utils/date.js";
10
10
+
import { createOperationHistory, DiffEntry, groupBy } from "../utils/plc-logs.js";
11
11
+
12
12
+
type PlcEvent = "handle" | "rotation_key" | "service" | "verification_method";
13
13
+
14
14
+
export const PlcLogView = (props: { did: string }) => {
15
15
+
const [activePlcEvent, setActivePlcEvent] = createSignal<PlcEvent | undefined>();
16
16
+
17
17
+
const fetchPlcLogs = async () => {
18
18
+
const res = await fetch(
19
19
+
`${localStorage.plcDirectory ?? "https://plc.directory"}/${props.did}/log/audit`,
20
20
+
);
21
21
+
const json = await res.json();
22
22
+
const logs = defs.indexedEntryLog.parse(json);
23
23
+
try {
24
24
+
await processIndexedEntryLog(props.did as any, logs);
25
25
+
} catch (e) {
26
26
+
console.error(e);
27
27
+
}
28
28
+
const opHistory = createOperationHistory(logs).reverse();
29
29
+
return Array.from(groupBy(opHistory, (item) => item.orig));
30
30
+
};
31
31
+
32
32
+
const [plcOps] =
33
33
+
createResource<[IndexedEntry<CompatibleOperationOrTombstone>, DiffEntry[]][]>(fetchPlcLogs);
34
34
+
35
35
+
const FilterButton = (props: { icon: string; event: PlcEvent }) => (
36
36
+
<button
37
37
+
classList={{
38
38
+
"flex items-center rounded-full p-1.5": true,
39
39
+
"bg-neutral-700 dark:bg-neutral-200": activePlcEvent() === props.event,
40
40
+
}}
41
41
+
onclick={() => setActivePlcEvent(activePlcEvent() === props.event ? undefined : props.event)}
42
42
+
>
43
43
+
<span
44
44
+
class={`${props.icon} ${activePlcEvent() === props.event ? "text-neutral-200 dark:text-neutral-900" : ""}`}
45
45
+
></span>
46
46
+
</button>
47
47
+
);
48
48
+
49
49
+
const DiffItem = (props: { diff: DiffEntry }) => {
50
50
+
const diff = props.diff;
51
51
+
let title = "Unknown log entry";
52
52
+
let icon = "lucide--circle-help";
53
53
+
let value = "";
54
54
+
55
55
+
if (diff.type === "identity_created") {
56
56
+
icon = "lucide--bell";
57
57
+
title = `Identity created`;
58
58
+
} else if (diff.type === "identity_tombstoned") {
59
59
+
icon = "lucide--skull";
60
60
+
title = `Identity tombstoned`;
61
61
+
} else if (diff.type === "handle_added" || diff.type === "handle_removed") {
62
62
+
icon = "lucide--at-sign";
63
63
+
title = diff.type === "handle_added" ? "Alias added" : "Alias removed";
64
64
+
value = diff.handle;
65
65
+
} else if (diff.type === "handle_changed") {
66
66
+
icon = "lucide--at-sign";
67
67
+
title = "Alias updated";
68
68
+
value = `${diff.prev_handle} → ${diff.next_handle}`;
69
69
+
} else if (diff.type === "rotation_key_added" || diff.type === "rotation_key_removed") {
70
70
+
icon = "lucide--key-round";
71
71
+
title = diff.type === "rotation_key_added" ? "Rotation key added" : "Rotation key removed";
72
72
+
value = diff.rotation_key;
73
73
+
} else if (diff.type === "service_added" || diff.type === "service_removed") {
74
74
+
icon = "lucide--hard-drive";
75
75
+
title = `Service ${diff.service_id} ${diff.type === "service_added" ? "added" : "removed"}`;
76
76
+
value = `${diff.service_endpoint}`;
77
77
+
} else if (diff.type === "service_changed") {
78
78
+
icon = "lucide--hard-drive";
79
79
+
title = `Service ${diff.service_id} updated`;
80
80
+
value = `${diff.prev_service_endpoint} → ${diff.next_service_endpoint}`;
81
81
+
} else if (
82
82
+
diff.type === "verification_method_added" ||
83
83
+
diff.type === "verification_method_removed"
84
84
+
) {
85
85
+
icon = "lucide--shield-check";
86
86
+
title = `Verification method ${diff.method_id} ${diff.type === "verification_method_added" ? "added" : "removed"}`;
87
87
+
value = `${diff.method_key}`;
88
88
+
} else if (diff.type === "verification_method_changed") {
89
89
+
icon = "lucide--shield-check";
90
90
+
title = `Verification method ${diff.method_id} updated`;
91
91
+
value = `${diff.prev_method_key} → ${diff.next_method_key}`;
92
92
+
}
93
93
+
94
94
+
return (
95
95
+
<div class="grid grid-cols-[min-content_1fr] items-center gap-x-1">
96
96
+
<div class={icon + ` iconify shrink-0`} />
97
97
+
<p
98
98
+
classList={{
99
99
+
"font-semibold": true,
100
100
+
"text-neutral-400 line-through dark:text-neutral-600": diff.orig.nullified,
101
101
+
}}
102
102
+
>
103
103
+
{title}
104
104
+
</p>
105
105
+
<div></div>
106
106
+
{value}
107
107
+
</div>
108
108
+
);
109
109
+
};
110
110
+
111
111
+
return (
112
112
+
<div class="flex w-full flex-col gap-2 wrap-anywhere">
113
113
+
<div class="flex items-center justify-between">
114
114
+
<div class="flex items-center gap-1">
115
115
+
<div class="iconify lucide--filter" />
116
116
+
<div class="dark:shadow-dark-800 dark:bg-dark-300 flex w-fit items-center rounded-full border-[0.5px] border-neutral-300 bg-neutral-50 shadow-xs dark:border-neutral-700">
117
117
+
<FilterButton icon="iconify lucide--at-sign" event="handle" />
118
118
+
<FilterButton icon="iconify lucide--key-round" event="rotation_key" />
119
119
+
<FilterButton icon="iconify lucide--hard-drive" event="service" />
120
120
+
<FilterButton icon="iconify lucide--shield-check" event="verification_method" />
121
121
+
</div>
122
122
+
</div>
123
123
+
<Tooltip text="Audit log">
124
124
+
<a
125
125
+
href={`${localStorage.plcDirectory ?? "https://plc.directory"}/${props.did}/log/audit`}
126
126
+
target="_blank"
127
127
+
class="-mr-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"
128
128
+
>
129
129
+
<span class="iconify lucide--external-link"></span>
130
130
+
</a>
131
131
+
</Tooltip>
132
132
+
</div>
133
133
+
<div class="flex flex-col gap-1 text-sm">
134
134
+
<For each={plcOps()}>
135
135
+
{([entry, diffs]) => (
136
136
+
<Show
137
137
+
when={!activePlcEvent() || diffs.find((d) => d.type.startsWith(activePlcEvent()!))}
138
138
+
>
139
139
+
<div class="flex flex-col">
140
140
+
<span class="text-neutral-500 dark:text-neutral-400">
141
141
+
{localDateFromTimestamp(new Date(entry.createdAt).getTime())}
142
142
+
</span>
143
143
+
{diffs.map((diff) => (
144
144
+
<Show when={!activePlcEvent() || diff.type.startsWith(activePlcEvent()!)}>
145
145
+
<DiffItem diff={diff} />
146
146
+
</Show>
147
147
+
))}
148
148
+
</div>
149
149
+
</Show>
150
150
+
)}
151
151
+
</For>
152
152
+
</div>
153
153
+
</div>
154
154
+
);
155
155
+
};
+154
-335
src/views/repo.tsx
···
1
1
import { Client, CredentialManager } from "@atcute/client";
2
2
import { parsePublicMultikey } from "@atcute/crypto";
3
3
-
import {
4
4
-
CompatibleOperationOrTombstone,
5
5
-
defs,
6
6
-
IndexedEntry,
7
7
-
processIndexedEntryLog,
8
8
-
} from "@atcute/did-plc";
9
3
import { DidDocument } from "@atcute/identity";
10
4
import { ActorIdentifier, Did, Handle } from "@atcute/lexicons";
11
5
import { A, useLocation, useNavigate, useParams } from "@solidjs/router";
···
20
14
} from "solid-js";
21
15
import { createStore } from "solid-js/store";
22
16
import { Backlinks } from "../components/backlinks.jsx";
23
23
-
import { Button } from "../components/button.jsx";
17
17
+
import { ActionMenu, DropdownMenu, MenuProvider, NavMenu } from "../components/dropdown.jsx";
24
18
import { TextInput } from "../components/text-input.jsx";
25
19
import Tooltip from "../components/tooltip.jsx";
26
20
import { didDocCache, resolveHandle, resolvePDS, validateHandle } from "../utils/api.js";
27
27
-
import { localDateFromTimestamp } from "../utils/date.js";
28
28
-
import { createOperationHistory, DiffEntry, groupBy } from "../utils/plc-logs.js";
29
21
import { BlobView } from "./blob.jsx";
30
30
-
31
31
-
type Tab = "collections" | "backlinks" | "identity" | "blobs";
32
32
-
type PlcEvent = "handle" | "rotation_key" | "service" | "verification_method";
33
33
-
34
34
-
const PlcLogView = (props: {
35
35
-
did: string;
36
36
-
plcOps: [IndexedEntry<CompatibleOperationOrTombstone>, DiffEntry[]][];
37
37
-
}) => {
38
38
-
const [activePlcEvent, setActivePlcEvent] = createSignal<PlcEvent | undefined>();
39
39
-
40
40
-
const FilterButton = (props: { icon: string; event: PlcEvent }) => (
41
41
-
<button
42
42
-
classList={{
43
43
-
"flex items-center rounded-full p-1.5": true,
44
44
-
"bg-neutral-700 dark:bg-neutral-200": activePlcEvent() === props.event,
45
45
-
}}
46
46
-
onclick={() => setActivePlcEvent(activePlcEvent() === props.event ? undefined : props.event)}
47
47
-
>
48
48
-
<span
49
49
-
class={`${props.icon} ${activePlcEvent() === props.event ? "text-neutral-200 dark:text-neutral-900" : ""}`}
50
50
-
></span>
51
51
-
</button>
52
52
-
);
53
53
-
54
54
-
const DiffItem = (props: { diff: DiffEntry }) => {
55
55
-
const diff = props.diff;
56
56
-
let title = "Unknown log entry";
57
57
-
let icon = "lucide--circle-help";
58
58
-
let value = "";
22
22
+
import { PlcLogView } from "./logs.jsx";
59
23
60
60
-
if (diff.type === "identity_created") {
61
61
-
icon = "lucide--bell";
62
62
-
title = `Identity created`;
63
63
-
} else if (diff.type === "identity_tombstoned") {
64
64
-
icon = "lucide--skull";
65
65
-
title = `Identity tombstoned`;
66
66
-
} else if (diff.type === "handle_added" || diff.type === "handle_removed") {
67
67
-
icon = "lucide--at-sign";
68
68
-
title = diff.type === "handle_added" ? "Alias added" : "Alias removed";
69
69
-
value = diff.handle;
70
70
-
} else if (diff.type === "handle_changed") {
71
71
-
icon = "lucide--at-sign";
72
72
-
title = "Alias updated";
73
73
-
value = `${diff.prev_handle} → ${diff.next_handle}`;
74
74
-
} else if (diff.type === "rotation_key_added" || diff.type === "rotation_key_removed") {
75
75
-
icon = "lucide--key-round";
76
76
-
title = diff.type === "rotation_key_added" ? "Rotation key added" : "Rotation key removed";
77
77
-
value = diff.rotation_key;
78
78
-
} else if (diff.type === "service_added" || diff.type === "service_removed") {
79
79
-
icon = "lucide--hard-drive";
80
80
-
title = `Service ${diff.service_id} ${diff.type === "service_added" ? "added" : "removed"}`;
81
81
-
value = `${diff.service_endpoint}`;
82
82
-
} else if (diff.type === "service_changed") {
83
83
-
icon = "lucide--hard-drive";
84
84
-
title = `Service ${diff.service_id} updated`;
85
85
-
value = `${diff.prev_service_endpoint} → ${diff.next_service_endpoint}`;
86
86
-
} else if (
87
87
-
diff.type === "verification_method_added" ||
88
88
-
diff.type === "verification_method_removed"
89
89
-
) {
90
90
-
icon = "lucide--shield-check";
91
91
-
title = `Verification method ${diff.method_id} ${diff.type === "verification_method_added" ? "added" : "removed"}`;
92
92
-
value = `${diff.method_key}`;
93
93
-
} else if (diff.type === "verification_method_changed") {
94
94
-
icon = "lucide--shield-check";
95
95
-
title = `Verification method ${diff.method_id} updated`;
96
96
-
value = `${diff.prev_method_key} → ${diff.next_method_key}`;
97
97
-
}
98
98
-
99
99
-
return (
100
100
-
<div class="grid grid-cols-[min-content_1fr] items-center gap-x-1">
101
101
-
<div class={icon + ` iconify shrink-0`} />
102
102
-
<p
103
103
-
classList={{
104
104
-
"font-semibold": true,
105
105
-
"text-neutral-400 line-through dark:text-neutral-600": diff.orig.nullified,
106
106
-
}}
107
107
-
>
108
108
-
{title}
109
109
-
</p>
110
110
-
<div></div>
111
111
-
{value}
112
112
-
</div>
113
113
-
);
114
114
-
};
115
115
-
116
116
-
return (
117
117
-
<>
118
118
-
<div class="flex items-center justify-between">
119
119
-
<div class="flex items-center gap-1">
120
120
-
<div class="iconify lucide--filter" />
121
121
-
<div class="dark:shadow-dark-800 dark:bg-dark-300 flex w-fit items-center rounded-full border-[0.5px] border-neutral-300 bg-neutral-50 shadow-xs dark:border-neutral-700">
122
122
-
<FilterButton icon="iconify lucide--at-sign" event="handle" />
123
123
-
<FilterButton icon="iconify lucide--key-round" event="rotation_key" />
124
124
-
<FilterButton icon="iconify lucide--hard-drive" event="service" />
125
125
-
<FilterButton icon="iconify lucide--shield-check" event="verification_method" />
126
126
-
</div>
127
127
-
</div>
128
128
-
<Tooltip text="Audit log">
129
129
-
<a
130
130
-
href={`${localStorage.plcDirectory ?? "https://plc.directory"}/${props.did}/log/audit`}
131
131
-
target="_blank"
132
132
-
class="-mr-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"
133
133
-
>
134
134
-
<span class="iconify lucide--external-link"></span>
135
135
-
</a>
136
136
-
</Tooltip>
137
137
-
</div>
138
138
-
<div class="flex flex-col gap-1 text-sm">
139
139
-
<For each={props.plcOps}>
140
140
-
{([entry, diffs]) => (
141
141
-
<Show
142
142
-
when={!activePlcEvent() || diffs.find((d) => d.type.startsWith(activePlcEvent()!))}
143
143
-
>
144
144
-
<div class="flex flex-col">
145
145
-
<span class="text-neutral-500 dark:text-neutral-400">
146
146
-
{localDateFromTimestamp(new Date(entry.createdAt).getTime())}
147
147
-
</span>
148
148
-
{diffs.map((diff) => (
149
149
-
<Show when={!activePlcEvent() || diff.type.startsWith(activePlcEvent()!)}>
150
150
-
<DiffItem diff={diff} />
151
151
-
</Show>
152
152
-
))}
153
153
-
</div>
154
154
-
</Show>
155
155
-
)}
156
156
-
</For>
157
157
-
</div>
158
158
-
</>
159
159
-
);
160
160
-
};
161
161
-
24
24
+
type Tab = "collections" | "backlinks" | "identity" | "blobs" | "logs";
162
25
const RepoView = () => {
163
26
const params = useParams();
164
27
const location = useLocation();
···
168
31
const [didDoc, setDidDoc] = createSignal<DidDocument>();
169
32
const [nsids, setNsids] = createSignal<Record<string, { hidden: boolean; nsids: string[] }>>();
170
33
const [filter, setFilter] = createSignal<string>();
171
171
-
const [plcOps, setPlcOps] =
172
172
-
createSignal<[IndexedEntry<CompatibleOperationOrTombstone>, DiffEntry[]][]>();
173
173
-
const [showPlcLogs, setShowPlcLogs] = createSignal(false);
174
174
-
const [loading, setLoading] = createSignal(false);
175
175
-
const [notice, setNotice] = createSignal<string>();
176
34
const [validHandles, setValidHandles] = createStore<Record<string, boolean>>({});
177
35
let rpc: Client;
178
36
let pds: string;
179
37
const did = params.repo;
180
38
181
181
-
const RepoTab = (props: { tab: Tab; label: string; icon: string }) => (
182
182
-
<A class="group flex flex-1 justify-center" href={`/at://${params.repo}#${props.tab}`}>
39
39
+
const RepoTab = (props: { tab: Tab; label: string }) => (
40
40
+
<A class="group flex justify-center" href={`/at://${params.repo}#${props.tab}`}>
183
41
<span
184
42
classList={{
185
185
-
"flex gap-1 items-center border-b-2": true,
43
43
+
"flex flex-1 border-b-2": true,
186
44
"border-transparent group-hover:border-neutral-400 dark:group-hover:border-neutral-600":
187
45
(location.hash !== `#${props.tab}` && !!location.hash) ||
188
46
(!location.hash && props.tab !== "collections"),
189
47
}}
190
48
>
191
191
-
<span class={"iconify " + props.icon}></span>
192
49
{props.label}
193
50
</span>
194
51
</A>
···
294
151
</div>
295
152
</Show>
296
153
<div
297
297
-
class={`dark:shadow-dark-800 dark:bg-dark-300 flex ${error() ? "justify-around" : "justify-between"} rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700`}
154
154
+
class={`dark:shadow-dark-800 dark:bg-dark-300 flex justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700`}
298
155
>
299
299
-
<Show when={!error()}>
300
300
-
<RepoTab tab="collections" label="Collections" icon="lucide--folder-open" />
301
301
-
</Show>
302
302
-
<RepoTab tab="identity" label="Identity" icon="lucide--id-card" />
303
303
-
<Show when={!error()}>
304
304
-
<RepoTab tab="blobs" label="Blobs" icon="lucide--file-digit" />
305
305
-
</Show>
306
306
-
<RepoTab tab="backlinks" label="Backlinks" icon="lucide--send-to-back" />
156
156
+
<div class="flex gap-2 sm:gap-4">
157
157
+
<Show when={!error()}>
158
158
+
<RepoTab tab="collections" label="Collections" />
159
159
+
</Show>
160
160
+
<RepoTab tab="identity" label="Identity" />
161
161
+
<Show when={did.startsWith("did:plc")}>
162
162
+
<RepoTab tab="logs" label="Logs" />
163
163
+
</Show>
164
164
+
<Show when={!error()}>
165
165
+
<RepoTab tab="blobs" label="Blobs" />
166
166
+
</Show>
167
167
+
<RepoTab tab="backlinks" label="Backlinks" />
168
168
+
</div>
169
169
+
<MenuProvider>
170
170
+
<DropdownMenu
171
171
+
icon="lucide--ellipsis-vertical"
172
172
+
buttonClass="rounded-sm p-1"
173
173
+
menuClass="top-8 p-2 text-sm"
174
174
+
>
175
175
+
<NavMenu
176
176
+
href={`/jetstream?dids=${params.repo}`}
177
177
+
label="Jetstream"
178
178
+
icon="lucide--radio-tower"
179
179
+
/>
180
180
+
<Show when={error()?.length === 0 || error() === undefined}>
181
181
+
<ActionMenu
182
182
+
label="Export Repo"
183
183
+
icon={downloading() ? "lucide--loader-circle animate-spin" : "lucide--download"}
184
184
+
onClick={() => downloadRepo()}
185
185
+
/>
186
186
+
</Show>
187
187
+
</DropdownMenu>
188
188
+
</MenuProvider>
307
189
</div>
190
190
+
<Show when={location.hash === "#logs"}>
191
191
+
<ErrorBoundary fallback={(err) => <div class="break-words">Error: {err.message}</div>}>
192
192
+
<Suspense
193
193
+
fallback={
194
194
+
<div class="iconify lucide--loader-circle animate-spin self-center text-xl" />
195
195
+
}
196
196
+
>
197
197
+
<PlcLogView did={did} />
198
198
+
</Suspense>
199
199
+
</ErrorBoundary>
200
200
+
</Show>
308
201
<Show when={location.hash === "#backlinks"}>
309
202
<ErrorBoundary fallback={(err) => <div class="break-words">Error: {err.message}</div>}>
310
203
<Suspense
···
328
221
</ErrorBoundary>
329
222
</Show>
330
223
<Show when={nsids() && (!location.hash || location.hash === "#collections")}>
331
331
-
<div class="flex items-center gap-1">
332
332
-
<Tooltip text="Jetstream">
333
333
-
<A
334
334
-
href={`/jetstream?dids=${params.repo}`}
335
335
-
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"
336
336
-
>
337
337
-
<span class="iconify lucide--radio-tower text-lg"></span>
338
338
-
</A>
339
339
-
</Tooltip>
340
340
-
<TextInput
341
341
-
name="filter"
342
342
-
placeholder="Filter collections"
343
343
-
onInput={(e) => setFilter(e.currentTarget.value)}
344
344
-
class="grow"
345
345
-
/>
346
346
-
</div>
224
224
+
<TextInput
225
225
+
name="filter"
226
226
+
placeholder="Filter collections"
227
227
+
onInput={(e) => setFilter(e.currentTarget.value)}
228
228
+
class="grow"
229
229
+
/>
347
230
<div class="flex flex-col font-mono">
348
231
<div class="grid grid-cols-[min-content_1fr] items-center gap-x-2 overflow-hidden text-sm">
349
232
<For
···
399
282
<Show when={location.hash === "#identity"}>
400
283
<Show when={didDoc()}>
401
284
{(didDocument) => (
402
402
-
<div class="flex flex-col gap-y-2 wrap-anywhere">
403
403
-
<div class="flex flex-col gap-y-1">
404
404
-
<div class="flex items-baseline justify-between gap-2">
405
405
-
<div>
406
406
-
<div class="flex items-center gap-1">
407
407
-
<div class="iconify lucide--id-card" />
408
408
-
<p class="font-semibold">ID</p>
409
409
-
</div>
410
410
-
<div class="text-sm">{didDocument().id}</div>
411
411
-
</div>
412
412
-
<Tooltip text="DID document">
413
413
-
<a
414
414
-
href={
415
415
-
did.startsWith("did:plc") ?
416
416
-
`${localStorage.plcDirectory ?? "https://plc.directory"}/${did}`
417
417
-
: `https://${did.split("did:web:")[1]}/.well-known/did.json`
418
418
-
}
419
419
-
target="_blank"
420
420
-
class="-mr-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"
421
421
-
>
422
422
-
<span class="iconify lucide--external-link"></span>
423
423
-
</a>
424
424
-
</Tooltip>
425
425
-
</div>
285
285
+
<div class="flex flex-col gap-y-1 wrap-anywhere">
286
286
+
<div class="flex items-baseline justify-between gap-2">
426
287
<div>
427
288
<div class="flex items-center gap-1">
428
428
-
<div class="iconify lucide--at-sign" />
429
429
-
<p class="font-semibold">Aliases</p>
289
289
+
<div class="iconify lucide--id-card" />
290
290
+
<p class="font-semibold">ID</p>
430
291
</div>
431
431
-
<ul>
432
432
-
<For each={didDocument().alsoKnownAs}>
433
433
-
{(alias) => (
434
434
-
<li class="flex items-center gap-1 text-sm">
435
435
-
<span>{alias}</span>
436
436
-
<Show when={alias.startsWith("at://")}>
437
437
-
<Tooltip
438
438
-
text={
439
439
-
validHandles[alias] === true ? "Valid handle"
440
440
-
: validHandles[alias] === undefined ?
441
441
-
"Validating"
442
442
-
: "Invalid handle"
443
443
-
}
444
444
-
>
445
445
-
<span
446
446
-
classList={{
447
447
-
"iconify lucide--circle-check": validHandles[alias] === true,
448
448
-
"iconify lucide--circle-x text-red-500 dark:text-red-400":
449
449
-
validHandles[alias] === false,
450
450
-
"iconify lucide--loader-circle animate-spin":
451
451
-
validHandles[alias] === undefined,
452
452
-
}}
453
453
-
></span>
454
454
-
</Tooltip>
455
455
-
</Show>
456
456
-
</li>
457
457
-
)}
458
458
-
</For>
459
459
-
</ul>
292
292
+
<div class="text-sm">{didDocument().id}</div>
460
293
</div>
461
461
-
<div>
462
462
-
<div class="flex items-center gap-1">
463
463
-
<div class="iconify lucide--hard-drive" />
464
464
-
<p class="font-semibold">Services</p>
465
465
-
</div>
466
466
-
<ul>
467
467
-
<For each={didDocument().service}>
468
468
-
{(service) => (
469
469
-
<li class="flex flex-col text-sm">
470
470
-
<span>#{service.id.split("#")[1]}</span>
471
471
-
<a
472
472
-
class="w-fit text-blue-400 hover:underline active:underline"
473
473
-
href={service.serviceEndpoint.toString()}
474
474
-
target="_blank"
475
475
-
>
476
476
-
{service.serviceEndpoint.toString()}
477
477
-
</a>
478
478
-
</li>
479
479
-
)}
480
480
-
</For>
481
481
-
</ul>
294
294
+
<Tooltip text="DID document">
295
295
+
<a
296
296
+
href={
297
297
+
did.startsWith("did:plc") ?
298
298
+
`${localStorage.plcDirectory ?? "https://plc.directory"}/${did}`
299
299
+
: `https://${did.split("did:web:")[1]}/.well-known/did.json`
300
300
+
}
301
301
+
target="_blank"
302
302
+
class="-mr-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"
303
303
+
>
304
304
+
<span class="iconify lucide--external-link"></span>
305
305
+
</a>
306
306
+
</Tooltip>
307
307
+
</div>
308
308
+
<div>
309
309
+
<div class="flex items-center gap-1">
310
310
+
<div class="iconify lucide--at-sign" />
311
311
+
<p class="font-semibold">Aliases</p>
482
312
</div>
483
483
-
<div>
484
484
-
<div class="flex items-center gap-1">
485
485
-
<div class="iconify lucide--shield-check" />
486
486
-
<p class="font-semibold">Verification methods</p>
487
487
-
</div>
488
488
-
<ul>
489
489
-
<For each={didDocument().verificationMethod}>
490
490
-
{(verif) => (
491
491
-
<Show when={verif.publicKeyMultibase}>
492
492
-
{(key) => (
493
493
-
<li class="flex flex-col text-sm">
494
494
-
<span>#{verif.id.split("#")[1]}</span>
495
495
-
<span class="flex items-center gap-0.5">
496
496
-
<div class="iconify lucide--key-round" />
497
497
-
<ErrorBoundary fallback={<>unknown</>}>
498
498
-
{parsePublicMultikey(key()).type}
499
499
-
</ErrorBoundary>
500
500
-
</span>
501
501
-
<span class="truncate">{key()}</span>
502
502
-
</li>
503
503
-
)}
313
313
+
<ul>
314
314
+
<For each={didDocument().alsoKnownAs}>
315
315
+
{(alias) => (
316
316
+
<li class="flex items-center gap-1 text-sm">
317
317
+
<span>{alias}</span>
318
318
+
<Show when={alias.startsWith("at://")}>
319
319
+
<Tooltip
320
320
+
text={
321
321
+
validHandles[alias] === true ? "Valid handle"
322
322
+
: validHandles[alias] === undefined ?
323
323
+
"Validating"
324
324
+
: "Invalid handle"
325
325
+
}
326
326
+
>
327
327
+
<span
328
328
+
classList={{
329
329
+
"iconify lucide--circle-check": validHandles[alias] === true,
330
330
+
"iconify lucide--circle-x text-red-500 dark:text-red-400":
331
331
+
validHandles[alias] === false,
332
332
+
"iconify lucide--loader-circle animate-spin":
333
333
+
validHandles[alias] === undefined,
334
334
+
}}
335
335
+
></span>
336
336
+
</Tooltip>
504
337
</Show>
505
505
-
)}
506
506
-
</For>
507
507
-
</ul>
338
338
+
</li>
339
339
+
)}
340
340
+
</For>
341
341
+
</ul>
342
342
+
</div>
343
343
+
<div>
344
344
+
<div class="flex items-center gap-1">
345
345
+
<div class="iconify lucide--hard-drive" />
346
346
+
<p class="font-semibold">Services</p>
508
347
</div>
348
348
+
<ul>
349
349
+
<For each={didDocument().service}>
350
350
+
{(service) => (
351
351
+
<li class="flex flex-col text-sm">
352
352
+
<span>#{service.id.split("#")[1]}</span>
353
353
+
<a
354
354
+
class="w-fit text-blue-400 hover:underline active:underline"
355
355
+
href={service.serviceEndpoint.toString()}
356
356
+
target="_blank"
357
357
+
>
358
358
+
{service.serviceEndpoint.toString()}
359
359
+
</a>
360
360
+
</li>
361
361
+
)}
362
362
+
</For>
363
363
+
</ul>
509
364
</div>
510
510
-
<div class="flex justify-between">
511
511
-
<Show when={did.startsWith("did:plc")}>
512
512
-
<div class="flex items-center gap-1">
513
513
-
<Button
514
514
-
onClick={async () => {
515
515
-
if (!plcOps()) {
516
516
-
setLoading(true);
517
517
-
const response = await fetch(
518
518
-
`${localStorage.plcDirectory ?? "https://plc.directory"}/${did}/log/audit`,
519
519
-
);
520
520
-
const json = await response.json();
521
521
-
try {
522
522
-
const logs = defs.indexedEntryLog.parse(json);
523
523
-
try {
524
524
-
await processIndexedEntryLog(did as any, logs);
525
525
-
} catch (e) {
526
526
-
console.error(e);
527
527
-
}
528
528
-
const opHistory = createOperationHistory(logs).reverse();
529
529
-
setPlcOps(Array.from(groupBy(opHistory, (item) => item.orig)));
530
530
-
setLoading(false);
531
531
-
} catch (e: any) {
532
532
-
setNotice(e);
533
533
-
console.error(e);
534
534
-
setLoading(false);
535
535
-
}
536
536
-
}
537
537
-
538
538
-
setShowPlcLogs(!showPlcLogs());
539
539
-
}}
540
540
-
>
541
541
-
<span class="iconify lucide--logs text-sm"></span>
542
542
-
{showPlcLogs() ? "Hide" : "Show"} PLC Logs
543
543
-
</Button>
544
544
-
<Show when={loading()}>
545
545
-
<div class="iconify lucide--loader-circle animate-spin text-xl" />
546
546
-
</Show>
547
547
-
</div>
548
548
-
</Show>
549
549
-
<Show when={error()?.length === 0 || error() === undefined}>
550
550
-
<div
551
551
-
classList={{
552
552
-
"flex items-center gap-1": true,
553
553
-
"flex-row-reverse": did.startsWith("did:web"),
554
554
-
}}
555
555
-
>
556
556
-
<Show when={downloading()}>
557
557
-
<div class="iconify lucide--loader-circle animate-spin text-xl" />
558
558
-
</Show>
559
559
-
<Button onClick={() => downloadRepo()}>
560
560
-
<span class="iconify lucide--download text-sm"></span>
561
561
-
Export Repo
562
562
-
</Button>
563
563
-
</div>
564
564
-
</Show>
365
365
+
<div>
366
366
+
<div class="flex items-center gap-1">
367
367
+
<div class="iconify lucide--shield-check" />
368
368
+
<p class="font-semibold">Verification methods</p>
369
369
+
</div>
370
370
+
<ul>
371
371
+
<For each={didDocument().verificationMethod}>
372
372
+
{(verif) => (
373
373
+
<Show when={verif.publicKeyMultibase}>
374
374
+
{(key) => (
375
375
+
<li class="flex flex-col text-sm">
376
376
+
<span>#{verif.id.split("#")[1]}</span>
377
377
+
<span class="flex items-center gap-0.5">
378
378
+
<div class="iconify lucide--key-round" />
379
379
+
<ErrorBoundary fallback={<>unknown</>}>
380
380
+
{parsePublicMultikey(key()).type}
381
381
+
</ErrorBoundary>
382
382
+
</span>
383
383
+
<span class="truncate">{key()}</span>
384
384
+
</li>
385
385
+
)}
386
386
+
</Show>
387
387
+
)}
388
388
+
</For>
389
389
+
</ul>
565
390
</div>
566
566
-
<Show when={showPlcLogs()}>
567
567
-
<Show when={notice()}>
568
568
-
<div>{notice()}</div>
569
569
-
</Show>
570
570
-
<PlcLogView plcOps={plcOps() ?? []} did={did} />
571
571
-
</Show>
572
391
</div>
573
392
)}
574
393
</Show>