tangled
alpha
login
or
join now
pds.ls
/
pdsls
398
fork
atom
atmosphere explorer
pds.ls
tool
typescript
atproto
398
fork
atom
overview
issues
1
pulls
pipelines
new homepage
handle.invalid
2 weeks ago
687eb3ea
0658d7cc
verified
This commit was signed with the committer's
known signature
.
handle.invalid
SSH Key Fingerprint:
SHA256:mBrT4x0JdzLpbVR95g1hjI1aaErfC02kmLRkPXwsYCk=
+193
-231
4 changed files
expand all
collapse all
unified
split
src
components
dropdown.tsx
search.tsx
layout.tsx
views
home.tsx
+9
-3
src/components/dropdown.tsx
···
37
37
class="flex items-center gap-2 rounded-md p-1.5 whitespace-nowrap hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"
38
38
>
39
39
<Show when={props.icon}>
40
40
-
<span class={"iconify shrink-0 " + props.icon}></span>
40
40
+
<span
41
41
+
class={"iconify shrink-0 text-neutral-500 dark:text-neutral-400 " + props.icon}
42
42
+
></span>
41
43
</Show>
42
44
<span class="whitespace-nowrap">{props.label}</span>
43
45
</button>
···
64
66
>
65
67
<div class="flex items-center gap-2">
66
68
<Show when={props.icon}>
67
67
-
<span class={"iconify shrink-0 " + props.icon}></span>
69
69
+
<span
70
70
+
class={"iconify shrink-0 text-neutral-500 dark:text-neutral-400 " + props.icon}
71
71
+
></span>
68
72
</Show>
69
73
<span class="whitespace-nowrap">{props.label}</span>
70
74
</div>
···
92
96
class="flex items-center gap-2 rounded-md p-1.5 whitespace-nowrap hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"
93
97
>
94
98
<Show when={props.icon}>
95
95
-
<span class={"iconify shrink-0 " + props.icon}></span>
99
99
+
<span
100
100
+
class={"iconify shrink-0 text-neutral-500 dark:text-neutral-400 " + props.icon}
101
101
+
></span>
96
102
</Show>
97
103
<span class="whitespace-nowrap">{props.label}</span>
98
104
</button>
+59
-22
src/components/search.tsx
···
50
50
51
51
export const [showSearch, setShowSearch] = createSignal(false);
52
52
53
53
+
const EXAMPLES: (RecentSearch & { prefix: string })[] = [
54
54
+
{
55
55
+
path: "/at://did:plc:vwzwgnygau7ed7b7wt5ux7y2",
56
56
+
label: "retr0.id",
57
57
+
type: "handle",
58
58
+
prefix: "@",
59
59
+
},
60
60
+
{
61
61
+
path: "/at://did:plc:ia76kvnndjutgedggx2ibrem/app.bsky.actor.profile/self",
62
62
+
label: "mary.my.id/app.bsky.actor.profile/self",
63
63
+
type: "at-uri",
64
64
+
prefix: "at://",
65
65
+
},
66
66
+
{ path: "/npmx.social", label: "npmx.social", type: "pds", prefix: "pds:" },
67
67
+
{
68
68
+
path: "/at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.post#schema",
69
69
+
label: "app.bsky.feed.post",
70
70
+
type: "lexicon",
71
71
+
prefix: "lex:",
72
72
+
},
73
73
+
{
74
74
+
path: "/at://did:plc:oisofpd7lj26yvgiivf3lxsi/app.bsky.feed.post/3mfflamxxvk2t",
75
75
+
label: "bsky.app/profile/hailey.at/post/3mfflamxxvk2t",
76
76
+
type: "at-uri",
77
77
+
prefix: "https://",
78
78
+
},
79
79
+
];
80
80
+
53
81
const SEARCH_PREFIXES: { prefix: string; description: string }[] = [
54
82
{ prefix: "@", description: "example.com" },
55
83
{ prefix: "did:", description: "web:example.com" },
···
343
371
/>
344
372
</div>
345
373
346
346
-
<Show when={getRecentSuggestions().length > 0 || search()?.length}>
374
374
+
<Show
375
375
+
when={
376
376
+
getRecentSuggestions().length > 0 ||
377
377
+
search()?.length ||
378
378
+
(!input() && recentSearches().length === 0)
379
379
+
}
380
380
+
>
347
381
<div
348
348
-
class={`flex w-full flex-col overflow-hidden border-t border-neutral-200 dark:border-neutral-700 ${input() ? "rounded-b-md" : ""}`}
382
382
+
class="flex w-full flex-col overflow-hidden rounded-b-md border-t border-neutral-200 dark:border-neutral-700"
349
383
onMouseDown={(e) => e.preventDefault()}
350
384
>
385
385
+
{/* Suggestions (shown when no recents and no input) */}
386
386
+
<Show when={!input() && recentSearches().length === 0}>
387
387
+
<div class="mt-2 mb-1 flex px-3">
388
388
+
<span class="text-xs font-medium text-neutral-500 dark:text-neutral-400">
389
389
+
Examples
390
390
+
</span>
391
391
+
</div>
392
392
+
<For each={EXAMPLES}>
393
393
+
{(example) => (
394
394
+
<A
395
395
+
href={example.path}
396
396
+
class="dark:hover:bg-dark-100 flex items-center gap-2 px-3 py-2 text-sm hover:bg-neutral-100"
397
397
+
onClick={() => setShowSearch(false)}
398
398
+
>
399
399
+
<span class="truncate">
400
400
+
<span class="text-neutral-500 dark:text-neutral-400">{example.prefix}</span>
401
401
+
{example.label}
402
402
+
</span>
403
403
+
</A>
404
404
+
)}
405
405
+
</For>
406
406
+
</Show>
407
407
+
351
408
{/* Recent searches */}
352
409
<Show when={getRecentSuggestions().length > 0}>
353
410
<div class="mt-2 mb-1 flex items-center justify-between px-3">
···
446
503
);
447
504
}}
448
505
</For>
449
449
-
</div>
450
450
-
</Show>
451
451
-
<Show when={!input()}>
452
452
-
<div class="flex flex-col gap-1 border-t border-neutral-200 px-3 py-2 text-xs text-neutral-500 dark:border-neutral-700 dark:text-neutral-400">
453
453
-
<div class="flex flex-wrap gap-1.5">
454
454
-
<div>
455
455
-
@<span class="text-neutral-400 dark:text-neutral-500">retr0.id</span>
456
456
-
</div>
457
457
-
<div>did:</div>
458
458
-
<div>at://</div>
459
459
-
<div>
460
460
-
lex:
461
461
-
<span class="text-neutral-400 dark:text-neutral-500">app.bsky.feed.post</span>
462
462
-
</div>
463
463
-
<div>
464
464
-
pds:
465
465
-
<span class="text-neutral-400 dark:text-neutral-500">tngl.sh</span>
466
466
-
</div>
467
467
-
</div>
468
468
-
<span>Bluesky, Tangled, Pinksea, Popfeed, or Blento links</span>
469
506
</div>
470
507
</Show>
471
508
</form>
+1
-1
src/layout.tsx
···
159
159
<DropdownMenu icon="lucide--menu text-lg" buttonClass="rounded-md p-1.5">
160
160
<NavMenu href="/jetstream" label="Jetstream" icon="lucide--radio-tower" />
161
161
<NavMenu href="/firehose" label="Firehose" icon="lucide--rss" />
162
162
-
<NavMenu href="/spacedust" label="Spacedust" icon="lucide--orbit" />
162
162
+
<NavMenu href="/spacedust" label="Spacedust" icon="lucide--sparkles" />
163
163
<MenuSeparator />
164
164
<NavMenu href="/labels" label="Labels" icon="lucide--tag" />
165
165
<NavMenu href="/car" label="Archive tools" icon="lucide--folder-archive" />
+124
-205
src/views/home.tsx
···
1
1
import { A } from "@solidjs/router";
2
2
-
import { createSignal, For, JSX, onCleanup, onMount } from "solid-js";
2
2
+
import { JSX } from "solid-js";
3
3
import { setOpenManager, setShowAddAccount } from "../auth/state";
4
4
-
import { Button } from "../components/button";
5
5
-
import { Favicon } from "../components/favicon";
6
6
-
import { JSONValue } from "../components/json";
7
7
-
import { SearchButton } from "../components/search";
4
4
+
import { setShowSearch } from "../components/search";
8
5
9
9
-
const SLIDES = ["Repository", "Record", "PDS"] as const;
6
6
+
const baseCardClass =
7
7
+
"group flex flex-col gap-1 rounded-lg border border-neutral-200 bg-neutral-50 p-3 text-neutral-700 transition-colors dark:border-neutral-700 dark:bg-neutral-800/50 dark:text-neutral-300 hover:bg-neutral-50/50 dark:hover:bg-neutral-800";
10
8
11
11
-
const slideLinks = [
12
12
-
"/at://did:plc:vwzwgnygau7ed7b7wt5ux7y2",
13
13
-
"/at://did:plc:ia76kvnndjutgedggx2ibrem/app.bsky.feed.post/3kenlltlvus2u",
14
14
-
"/npmx.social",
15
15
-
] as const;
9
9
+
const accentCard = {
10
10
+
blue: `${baseCardClass} hover:border-blue-500 dark:hover:border-blue-400`,
11
11
+
orange: `${baseCardClass} hover:border-red-500 dark:hover:border-red-400`,
12
12
+
violet: `${baseCardClass} hover:border-emerald-500 dark:hover:border-emerald-400`,
13
13
+
};
16
14
17
17
-
const exampleRecord = {
18
18
-
text: "ma'am do you know where the petard is, i'd like to hoist myself with it",
19
19
-
$type: "app.bsky.feed.post",
20
20
-
langs: ["en"],
21
21
-
createdAt: "2023-11-20T21:44:21.000Z",
15
15
+
const accentIcon = {
16
16
+
blue: "text-neutral-400 dark:text-neutral-500 group-hover:text-blue-500 dark:group-hover:text-blue-400",
17
17
+
orange:
18
18
+
"text-neutral-400 dark:text-neutral-500 group-hover:text-red-500 dark:group-hover:text-red-400",
19
19
+
violet:
20
20
+
"text-neutral-400 dark:text-neutral-500 group-hover:text-emerald-500 dark:group-hover:text-emerald-400",
22
21
};
23
22
24
24
-
const exampleCollections = [
25
25
-
{ authority: "app.bsky", nsids: ["actor.profile", "feed.post", "feed.like", "graph.follow"] },
26
26
-
{ authority: "sh.tangled", nsids: ["actor.profile", "repo.pull"] },
27
27
-
{ authority: "place.stream", nsids: ["chat.message"] },
28
28
-
];
23
23
+
type Accent = "blue" | "orange" | "violet";
29
24
30
30
-
const exampleRepos = [
31
31
-
"did:plc:ty2jdjtqqq4jn7kk7p3mpwae",
32
32
-
"did:plc:byfvayavc7z2ldyu6bu5myz2",
33
33
-
"did:plc:n34gdj7o3o6ktuxp5qfbgllu",
34
34
-
"did:plc:vh7y4mqklsu2uui5tlwl42dy",
35
35
-
"did:plc:uz76j2yr2ps7apdxtlgqljwk",
36
36
-
];
25
25
+
const CardContent = (props: {
26
26
+
icon: string;
27
27
+
title: string;
28
28
+
description: string;
29
29
+
accent: Accent;
30
30
+
}) => (
31
31
+
<>
32
32
+
<span class="flex items-center gap-1.5 text-xs sm:text-sm">
33
33
+
<span class={`${props.icon} iconify shrink-0 ${accentIcon[props.accent]}`} />
34
34
+
<span class="font-medium">{props.title}</span>
35
35
+
</span>
36
36
+
<span class="text-xs text-neutral-500 dark:text-neutral-400">{props.description}</span>
37
37
+
</>
38
38
+
);
37
39
38
38
-
const ExplorerShowcase = () => {
39
39
-
const [slide, setSlide] = createSignal(0);
40
40
+
const ButtonCard = (props: {
41
41
+
onClick: () => void;
42
42
+
icon: string;
43
43
+
title: string;
44
44
+
description: string;
45
45
+
accent: Accent;
46
46
+
}) => (
47
47
+
<button onClick={props.onClick} class={`${accentCard[props.accent]} text-left`}>
48
48
+
<CardContent
49
49
+
icon={props.icon}
50
50
+
title={props.title}
51
51
+
description={props.description}
52
52
+
accent={props.accent}
53
53
+
/>
54
54
+
</button>
55
55
+
);
40
56
41
41
-
onMount(() => {
42
42
-
const id = setInterval(() => setSlide((s) => (s + 1) % SLIDES.length), 5000);
43
43
-
onCleanup(() => clearInterval(id));
44
44
-
});
45
45
-
46
46
-
return (
47
47
-
<div class="flex flex-col gap-1.5">
48
48
-
<A
49
49
-
href={slideLinks[slide()]}
50
50
-
class="relative block h-42 overflow-hidden rounded-lg border border-neutral-200 bg-neutral-50 transition-colors hover:border-neutral-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600"
51
51
-
>
52
52
-
{/* Collections slide */}
53
53
-
<div
54
54
-
class="pointer-events-none absolute inset-0 flex flex-col gap-1 overflow-hidden px-3 py-2 text-sm wrap-anywhere transition-opacity duration-700"
55
55
-
classList={{ "opacity-0": slide() !== 0 }}
56
56
-
>
57
57
-
<For each={exampleCollections}>
58
58
-
{({ authority, nsids }) => (
59
59
-
<div class="flex items-start gap-2">
60
60
-
<Favicon authority={authority} />
61
61
-
<div class="flex flex-col">
62
62
-
<For each={nsids}>
63
63
-
{(nsid) => (
64
64
-
<span>
65
65
-
<span class="text-neutral-500 dark:text-neutral-400">{authority}.</span>
66
66
-
<span>{nsid}</span>
67
67
-
</span>
68
68
-
)}
69
69
-
</For>
70
70
-
</div>
71
71
-
</div>
72
72
-
)}
73
73
-
</For>
74
74
-
</div>
75
75
-
76
76
-
{/* Record slide */}
77
77
-
<div
78
78
-
class="pointer-events-none absolute inset-0 overflow-hidden px-3 py-2 font-mono text-xs wrap-anywhere whitespace-pre-wrap transition-opacity duration-700 sm:text-sm"
79
79
-
classList={{ "opacity-0": slide() !== 1 }}
80
80
-
>
81
81
-
<JSONValue data={exampleRecord as any} repo="did:plc:ia76kvnndjutgedggx2ibrem" />
82
82
-
</div>
83
83
-
84
84
-
{/* Repos slide */}
85
85
-
<div
86
86
-
class="pointer-events-none absolute inset-0 overflow-hidden py-0.5 transition-opacity duration-700"
87
87
-
classList={{ "opacity-0": slide() !== 2 }}
88
88
-
>
89
89
-
<For each={exampleRepos}>
90
90
-
{(did) => (
91
91
-
<div class="flex min-w-0 items-center gap-2 p-1.5 font-mono text-sm">
92
92
-
<span class="flex shrink-0 items-center text-neutral-400 dark:text-neutral-500">
93
93
-
<span class="iconify lucide--chevron-right" />
94
94
-
</span>
95
95
-
<span class="truncate text-blue-500 dark:text-blue-400">{did}</span>
96
96
-
</div>
97
97
-
)}
98
98
-
</For>
99
99
-
</div>
100
100
-
</A>
101
101
-
102
102
-
{/* Slide indicator */}
103
103
-
<div class="flex items-center justify-between px-0.5">
104
104
-
<span class="text-xs text-neutral-400 dark:text-neutral-500">{SLIDES[slide()]}</span>
105
105
-
<div class="flex gap-1">
106
106
-
<For each={SLIDES}>
107
107
-
{(_, i) => (
108
108
-
<button
109
109
-
onClick={() => setSlide(i())}
110
110
-
class="h-1 rounded-full transition-all duration-300"
111
111
-
classList={{
112
112
-
"w-4 bg-neutral-400 dark:bg-neutral-500": slide() === i(),
113
113
-
"w-1.5 bg-neutral-300 dark:bg-neutral-600": slide() !== i(),
114
114
-
}}
115
115
-
/>
116
116
-
)}
117
117
-
</For>
118
118
-
</div>
119
119
-
</div>
120
120
-
</div>
121
121
-
);
122
122
-
};
57
57
+
const LinkCard = (props: {
58
58
+
href: string;
59
59
+
icon: string;
60
60
+
title: string;
61
61
+
description: string;
62
62
+
accent: Accent;
63
63
+
}) => (
64
64
+
<A href={props.href} class={accentCard[props.accent]}>
65
65
+
<CardContent
66
66
+
icon={props.icon}
67
67
+
title={props.title}
68
68
+
description={props.description}
69
69
+
accent={props.accent}
70
70
+
/>
71
71
+
</A>
72
72
+
);
123
73
124
74
export const Home = () => {
125
75
const FooterLink = (props: {
···
138
88
);
139
89
140
90
return (
141
141
-
<div class="flex w-full flex-col gap-5 px-2 wrap-break-word">
91
91
+
<div class="flex w-full flex-col gap-6 px-2 wrap-break-word">
142
92
{/* Welcome Section */}
143
143
-
<div class="flex flex-col gap-4">
144
144
-
<div class="flex flex-col gap-1">
145
145
-
<h1 class="text-lg font-medium">Atmosphere Explorer</h1>
146
146
-
<div class="text-sm text-neutral-600 dark:text-neutral-300">
147
147
-
<p>
148
148
-
Browse the public data on the{" "}
149
149
-
<a
150
150
-
href="https://atproto.com"
151
151
-
target="_blank"
152
152
-
class="underline decoration-neutral-400 transition-colors hover:text-blue-500 hover:decoration-blue-500 dark:decoration-neutral-500 dark:hover:text-blue-400"
153
153
-
>
154
154
-
AT Protocol
155
155
-
</a>
156
156
-
</p>
157
157
-
</div>
93
93
+
<div class="flex flex-col gap-1">
94
94
+
<h1 class="text-lg font-medium">Atmosphere Explorer</h1>
95
95
+
<div class="text-sm text-neutral-600 dark:text-neutral-300/80">
96
96
+
<p>
97
97
+
Browse the public data on the{" "}
98
98
+
<a
99
99
+
href="https://atproto.com"
100
100
+
target="_blank"
101
101
+
class="underline decoration-neutral-400 transition-colors hover:text-blue-500 hover:decoration-blue-500 dark:decoration-neutral-500 dark:hover:text-blue-400"
102
102
+
>
103
103
+
AT Protocol
104
104
+
</a>
105
105
+
</p>
158
106
</div>
159
159
-
160
160
-
<ExplorerShowcase />
107
107
+
</div>
161
108
162
162
-
<div class="flex items-center gap-1.5 text-xs text-neutral-500 dark:text-neutral-400">
163
163
-
<SearchButton />
164
164
-
<span>to find any account</span>
165
165
-
</div>
166
166
-
<div class="flex items-center gap-1.5 text-xs text-neutral-500 dark:text-neutral-400">
167
167
-
<Button
109
109
+
<div class="flex flex-col gap-3 text-sm">
110
110
+
<div class="grid grid-cols-2 gap-2 text-sm">
111
111
+
<ButtonCard
112
112
+
onClick={() => setShowSearch(true)}
113
113
+
icon="lucide--search"
114
114
+
title="Search"
115
115
+
description="Find any user or record"
116
116
+
accent="blue"
117
117
+
/>
118
118
+
<ButtonCard
168
119
onClick={() => {
169
120
setOpenManager(true);
170
121
setShowAddAccount(true);
171
122
}}
172
172
-
>
173
173
-
<span class="iconify lucide--user-round"></span>
174
174
-
Sign in
175
175
-
</Button>
176
176
-
<span>to manage records</span>
123
123
+
icon="lucide--user-round"
124
124
+
title="Sign in"
125
125
+
description="Manage records"
126
126
+
accent="blue"
127
127
+
/>
177
128
</div>
178
178
-
</div>
179
129
180
180
-
<div class="flex flex-col gap-4 text-sm">
181
181
-
<div class="flex flex-col gap-2">
182
182
-
<A
130
130
+
<div class="grid grid-cols-3 gap-2">
131
131
+
<LinkCard
183
132
href="/jetstream"
184
184
-
class="group grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-0.5 text-neutral-700 transition-colors hover:text-blue-500 dark:text-neutral-300 dark:hover:text-blue-400"
185
185
-
>
186
186
-
<div class="iconify lucide--radio-tower" />
187
187
-
<span class="underline decoration-transparent group-hover:decoration-current">
188
188
-
Jetstream
189
189
-
</span>
190
190
-
<div />
191
191
-
<span class="text-xs text-neutral-500 dark:text-neutral-400">
192
192
-
Event stream with filtering
193
193
-
</span>
194
194
-
</A>
195
195
-
<A
133
133
+
icon="lucide--radio-tower"
134
134
+
title="Jetstream"
135
135
+
description="Simplified stream"
136
136
+
accent="orange"
137
137
+
/>
138
138
+
<LinkCard
196
139
href="/firehose"
197
197
-
class="group grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-0.5 text-neutral-700 transition-colors hover:text-blue-500 dark:text-neutral-300 dark:hover:text-blue-400"
198
198
-
>
199
199
-
<div class="iconify lucide--rss" />
200
200
-
<span class="underline decoration-transparent group-hover:decoration-current">
201
201
-
Firehose
202
202
-
</span>
203
203
-
<div />
204
204
-
<span class="text-xs text-neutral-500 dark:text-neutral-400">
205
205
-
Raw relay event stream
206
206
-
</span>
207
207
-
</A>
208
208
-
<A
140
140
+
icon="lucide--rss"
141
141
+
title="Firehose"
142
142
+
description="Raw event stream"
143
143
+
accent="orange"
144
144
+
/>
145
145
+
<LinkCard
209
146
href="/spacedust"
210
210
-
class="group grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-0.5 text-neutral-700 transition-colors hover:text-blue-500 dark:text-neutral-300 dark:hover:text-blue-400"
211
211
-
>
212
212
-
<div class="iconify lucide--orbit" />
213
213
-
<span class="underline decoration-transparent group-hover:decoration-current">
214
214
-
Spacedust
215
215
-
</span>
216
216
-
<div />
217
217
-
<span class="text-xs text-neutral-500 dark:text-neutral-400">
218
218
-
Interaction links stream
219
219
-
</span>
220
220
-
</A>
147
147
+
icon="lucide--sparkles"
148
148
+
title="Spacedust"
149
149
+
description="Backlinks stream"
150
150
+
accent="orange"
151
151
+
/>
221
152
</div>
222
153
223
223
-
<div class="flex flex-col gap-2">
224
224
-
<A
154
154
+
<div class="grid grid-cols-2 gap-2">
155
155
+
<LinkCard
225
156
href="/labels"
226
226
-
class="group grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-0.5 text-neutral-700 transition-colors hover:text-blue-500 dark:text-neutral-300 dark:hover:text-blue-400"
227
227
-
>
228
228
-
<div class="iconify lucide--tag" />
229
229
-
<span class="underline decoration-transparent group-hover:decoration-current">
230
230
-
Labels
231
231
-
</span>
232
232
-
<div />
233
233
-
<span class="text-xs text-neutral-500 dark:text-neutral-400">
234
234
-
Query labeler services
235
235
-
</span>
236
236
-
</A>
237
237
-
<A
157
157
+
icon="lucide--tag"
158
158
+
title="Labels"
159
159
+
description="Query labeler services"
160
160
+
accent="violet"
161
161
+
/>
162
162
+
<LinkCard
238
163
href="/car"
239
239
-
class="group grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-0.5 text-neutral-700 transition-colors hover:text-blue-500 dark:text-neutral-300 dark:hover:text-blue-400"
240
240
-
>
241
241
-
<div class="iconify lucide--folder-archive" />
242
242
-
<span class="underline decoration-transparent group-hover:decoration-current">
243
243
-
Archive
244
244
-
</span>
245
245
-
<div />
246
246
-
<span class="text-xs text-neutral-500 dark:text-neutral-400">
247
247
-
Explore and unpack CAR files
248
248
-
</span>
249
249
-
</A>
164
164
+
icon="lucide--folder-archive"
165
165
+
title="Archive"
166
166
+
description="Explore CAR files"
167
167
+
accent="violet"
168
168
+
/>
250
169
</div>
251
170
</div>
252
171