tangled
alpha
login
or
join now
ptr.pet
/
nsid-tracker
3
fork
atom
tracks lexicons and how many times they appeared on the jetstream
3
fork
atom
overview
issues
pulls
pipelines
feat(client): add dark mode
ptr.pet
6 months ago
b7d77d22
e8cda421
verified
This commit was signed with the committer's
known signature
.
ptr.pet
SSH Key Fingerprint:
SHA256:Abmvag+juovVufZTxyWY8KcVgrznxvBjQpJesv071Aw=
+80
-59
10 changed files
expand all
collapse all
unified
split
client
src
app.html
lib
components
BskyToggle.svelte
EventCard.svelte
FilterControls.svelte
RefreshControl.svelte
ShowControls.svelte
SortControls.svelte
StatsCard.svelte
StatusBadge.svelte
routes
+page.svelte
+9
-9
client/src/app.html
···
1
1
<!doctype html>
2
2
<html lang="en">
3
3
-
<head>
4
4
-
<meta charset="utf-8" />
5
5
-
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
6
6
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
7
7
-
%sveltekit.head%
8
8
-
</head>
9
9
-
<body data-sveltekit-preload-data="hover">
10
10
-
<div style="display: contents">%sveltekit.body%</div>
11
11
-
</body>
3
3
+
<head>
4
4
+
<meta charset="utf-8" />
5
5
+
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
6
6
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7
7
+
%sveltekit.head%
8
8
+
</head>
9
9
+
<body class="bg-white dark:bg-gray-900" data-sveltekit-preload-data="hover">
10
10
+
<div style="display: contents">%sveltekit.body%</div>
11
11
+
</body>
12
12
</html>
+2
-2
client/src/lib/components/BskyToggle.svelte
···
11
11
<!-- svelte-ignore a11y_no_static_element_interactions -->
12
12
<button
13
13
onclick={onBskyToggle}
14
14
-
class="wsbadge !mt-0 !font-normal bg-blue-100 hover:bg-blue-200 border-blue-300"
14
14
+
class="wsbadge !mt-0 !font-normal bg-blue-100 dark:bg-blue-900 hover:bg-blue-200 dark:hover:bg-blue-800 border-blue-300 dark:border-blue-700"
15
15
>
16
16
<input checked={dontShowBsky} type="checkbox" />
17
17
-
<span class="ml-0.5"> hide app.bsky.* </span>
17
17
+
<span class="ml-0.5 text-black dark:text-gray-200"> hide app.bsky.* </span>
18
18
</button>
+8
-5
client/src/lib/components/EventCard.svelte
···
104
104
</script>
105
105
106
106
<div
107
107
-
class="group flex flex-col gap-2 p-1.5 md:p-3 min-h-64 bg-white border border-gray-200 rounded-lg hover:shadow-lg md:hover:-translate-y-1 transition-all duration-200 transform"
107
107
+
class="group flex flex-col gap-2 p-1.5 md:p-3 min-h-64 bg-white dark:bg-gray-800/50 border border-gray-200 dark:border-gray-950 rounded-lg hover:shadow-lg md:hover:-translate-y-1 transition-all duration-200 transform"
108
108
class:has-activity={isAnimating}
109
109
style="--border-thickness: {borderThickness}px"
110
110
>
111
111
<div class="flex items-start gap-2">
112
112
<div
113
113
-
class="text-sm font-bold text-blue-600 bg-blue-100 px-3 py-1 rounded-full"
113
113
+
class="text-sm font-bold text-blue-600 bg-blue-100 dark:bg-indigo-950 px-3 py-1 rounded-full"
114
114
>
115
115
#{index + 1}
116
116
</div>
117
117
<div
118
118
title={event.nsid}
119
119
-
class="font-mono text-sm text-gray-700 mt-0.5 leading-relaxed rounded-full text-nowrap text-ellipsis overflow-hidden group-hover:overflow-visible group-hover:bg-gray-50 border-gray-100 group-hover:border transition-all px-1"
119
119
+
class="font-mono text-sm text-gray-700 dark:text-gray-300 mt-0.5 leading-relaxed rounded-full text-nowrap text-ellipsis overflow-hidden group-hover:overflow-visible group-hover:bg-gray-50 dark:group-hover:bg-gray-700 border-gray-100 dark:border-gray-900 group-hover:border transition-all px-1"
120
120
>
121
121
{event.nsid}
122
122
</div>
···
136
136
</div>
137
137
</div>
138
138
139
139
-
<style>
139
139
+
<style lang="postcss">
140
140
.has-activity {
141
141
position: relative;
142
142
transition: all 0.2s ease-out;
143
143
}
144
144
145
145
.has-activity::before {
146
146
+
@reference "../../app.css";
147
147
+
@apply border-blue-500 dark:border-blue-800;
146
148
content: "";
147
149
position: absolute;
148
150
top: calc(-1 * var(--border-thickness));
149
151
left: calc(-1 * var(--border-thickness));
150
152
right: calc(-1 * var(--border-thickness));
151
153
bottom: calc(-1 * var(--border-thickness));
152
152
-
border: var(--border-thickness) solid rgba(59, 130, 246, 0.8);
154
154
+
border-width: var(--border-thickness);
155
155
+
border-style: solid;
153
156
border-radius: calc(0.5rem + var(--border-thickness));
154
157
pointer-events: none;
155
158
transition: all 0.3s ease-out;
+5
-3
client/src/lib/components/FilterControls.svelte
···
8
8
</script>
9
9
10
10
<div
11
11
-
class="wsbadge !pl-2 !px-1 !mt-0 !font-normal bg-blue-100 hover:bg-blue-200 border-blue-300"
11
11
+
class="wsbadge !pl-2 !px-1 !mt-0 !font-normal bg-blue-100 dark:bg-blue-900 hover:bg-blue-200 dark:hover:bg-blue-800 border-blue-300 dark:border-blue-700"
12
12
>
13
13
-
<label for="filter-regex" class="text-blue-800 mr-1"> filter: </label>
13
13
+
<label for="filter-regex" class="text-blue-800 dark:text-gray-200 mr-1">
14
14
+
filter:
15
15
+
</label>
14
16
<input
15
17
id="filter-regex"
16
18
value={filterRegex}
17
19
oninput={(e) => onFilterChange((e.target as HTMLInputElement).value)}
18
20
type="text"
19
21
placeholder="regex..."
20
20
-
class="bg-blue-50 text-blue-900 placeholder-blue-400 border border-blue-200 rounded-full px-1 outline-none focus:bg-white focus:border-blue-400 min-w-0 w-24"
22
22
+
class="bg-blue-50 dark:bg-blue-950 text-blue-900 dark:text-gray-400 placeholder-blue-400 dark:placeholder-blue-700 border border-blue-200 dark:border-blue-700 rounded-full px-1 outline-none focus:border-blue-400 min-w-0 w-24"
21
23
/>
22
24
</div>
+6
-4
client/src/lib/components/RefreshControl.svelte
···
8
8
</script>
9
9
10
10
<div
11
11
-
class="wsbadge !pl-2 !px-1 !mt-0 !font-normal bg-lime-100 hover:bg-lime-200 border-lime-300"
11
11
+
class="wsbadge !pl-2 !px-1 !mt-0 !font-normal bg-lime-100 dark:bg-lime-900 dark:hover:bg-lime-800 hover:bg-lime-200 border-lime-300 dark:border-lime-700"
12
12
>
13
13
-
<label for="refresh-rate" class="text-lime-800 mr-1">refresh:</label>
13
13
+
<label for="refresh-rate" class="text-lime-800 dark:text-lime-200 mr-1"
14
14
+
>refresh:</label
15
15
+
>
14
16
<input
15
17
id="refresh-rate"
16
18
value={refreshRate}
···
24
26
pattern="[0-9]*"
25
27
min="0"
26
28
placeholder="real-time"
27
27
-
class="bg-green-50 text-lime-900 placeholder-lime-600 border border-lime-200 rounded-full px-1 outline-none focus:bg-white focus:border-lime-400 min-w-0 w-20"
29
29
+
class="bg-green-50 dark:bg-green-900 text-lime-900 dark:text-lime-200 placeholder-lime-600 dark:placeholder-lime-400 border border-lime-200 dark:border-lime-700 rounded-full px-1 outline-none focus:border-lime-400 min-w-0 w-20"
28
30
/>
29
29
-
<span class="text-lime-700">s</span>
31
31
+
<span class="text-lime-800 dark:text-lime-200">s</span>
30
32
</div>
+5
-3
client/src/lib/components/ShowControls.svelte
···
12
12
</script>
13
13
14
14
<div
15
15
-
class="wsbadge !pl-2 !px-1 !mt-0 !font-normal bg-pink-100 hover:bg-pink-200 border-pink-300"
15
15
+
class="wsbadge !pl-2 !px-1 !mt-0 !font-normal bg-pink-100 dark:bg-pink-800 hover:bg-pink-200 dark:hover:bg-pink-700 border-pink-300 dark:border-pink-700"
16
16
>
17
17
-
<label for="show" class="text-pink-800 mr-1"> show since: </label>
17
17
+
<label for="show" class="text-pink-800 dark:text-pink-100 mr-1">
18
18
+
show since:
19
19
+
</label>
18
20
<select
19
21
id="show"
20
22
value={show}
21
23
onchange={(e) =>
22
24
onShowChange((e.target as HTMLSelectElement).value as ShowOption)}
23
23
-
class="bg-pink-50 text-pink-900 border border-pink-200 rounded-full px-1 outline-none focus:bg-white focus:border-pink-400 min-w-0"
25
25
+
class="bg-pink-50 dark:bg-pink-900 text-pink-900 dark:text-pink-100 border border-pink-200 dark:border-pink-700 rounded-full px-1 outline-none focus:border-pink-400 min-w-0"
24
26
>
25
27
{#each showOptions as option}
26
28
<option value={option}>{option}</option>
+5
-3
client/src/lib/components/SortControls.svelte
···
17
17
</script>
18
18
19
19
<div
20
20
-
class="wsbadge !pl-2 !px-1 !mt-0 !font-normal bg-purple-100 hover:bg-purple-200 border-purple-300"
20
20
+
class="wsbadge !pl-2 !px-1 !mt-0 !font-normal bg-purple-100 dark:bg-purple-800 hover:bg-purple-200 dark:hover:bg-purple-700 border-purple-300 dark:border-purple-700"
21
21
>
22
22
-
<label for="sort-by" class="text-purple-800 mr-1"> sort by: </label>
22
22
+
<label for="sort-by" class="text-purple-800 dark:text-purple-300 mr-1">
23
23
+
sort by:
24
24
+
</label>
23
25
<select
24
26
id="sort-by"
25
27
value={sortBy}
26
28
onchange={(e) =>
27
29
onSortChange((e.target as HTMLSelectElement).value as SortOption)}
28
28
-
class="bg-purple-50 text-purple-900 border border-purple-200 rounded-full px-1 outline-none focus:bg-white focus:border-purple-400 min-w-0"
30
30
+
class="bg-purple-50 dark:bg-purple-900 text-purple-900 dark:text-purple-300 border border-purple-200 dark:border-purple-700 rounded-full px-1 outline-none focus:border-purple-400 min-w-0"
29
31
>
30
32
{#each sortOptions as option}
31
33
<option value={option.value}>{option.label}</option>
+16
-16
client/src/lib/components/StatsCard.svelte
···
3
3
4
4
const colorClasses = {
5
5
green: {
6
6
-
bg: "from-green-50 to-green-100",
7
7
-
border: "border-green-200",
8
8
-
titleText: "text-green-700",
9
9
-
valueText: "text-green-900",
6
6
+
bg: "from-green-50 to-green-100 dark:from-green-900 dark:to-green-800",
7
7
+
border: "border-green-200 dark:border-green-800",
8
8
+
titleText: "text-green-700 dark:text-green-400",
9
9
+
valueText: "text-green-900 dark:text-green-200",
10
10
},
11
11
red: {
12
12
-
bg: "from-red-50 to-red-100",
13
13
-
border: "border-red-200",
14
14
-
titleText: "text-red-700",
15
15
-
valueText: "text-red-900",
12
12
+
bg: "from-red-50 to-red-100 dark:from-red-900 dark:to-red-800",
13
13
+
border: "border-red-200 dark:border-red-800",
14
14
+
titleText: "text-red-700 dark:text-red-400",
15
15
+
valueText: "text-red-900 dark:text-red-200",
16
16
},
17
17
turqoise: {
18
18
-
bg: "from-teal-50 to-teal-100",
19
19
-
border: "border-teal-200",
20
20
-
titleText: "text-teal-700",
21
21
-
valueText: "text-teal-900",
18
18
+
bg: "from-teal-50 to-teal-100 dark:from-teal-900 dark:to-teal-800",
19
19
+
border: "border-teal-200 dark:border-teal-800",
20
20
+
titleText: "text-teal-700 dark:text-teal-400",
21
21
+
valueText: "text-teal-900 dark:text-teal-200",
22
22
},
23
23
orange: {
24
24
-
bg: "from-orange-50 to-orange-100",
25
25
-
border: "border-orange-200",
26
26
-
titleText: "text-orange-700",
27
27
-
valueText: "text-orange-900",
24
24
+
bg: "from-orange-50 to-orange-100 dark:from-orange-900 dark:to-orange-800",
25
25
+
border: "border-orange-200 dark:border-orange-800",
26
26
+
titleText: "text-orange-700 dark:text-orange-400",
27
27
+
valueText: "text-orange-900 dark:text-orange-200",
28
28
},
29
29
};
30
30
+9
-5
client/src/lib/components/StatusBadge.svelte
···
8
8
const statusConfig = {
9
9
connected: {
10
10
text: "stream live",
11
11
-
classes: "bg-green-100 text-green-800 border-green-200",
11
11
+
classes:
12
12
+
"bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 border-green-200 dark:border-green-800",
12
13
},
13
14
connecting: {
14
15
text: "stream connecting",
15
15
-
classes: "bg-yellow-100 text-yellow-800 border-yellow-200",
16
16
+
classes:
17
17
+
"bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 border-yellow-200 dark:border-yellow-800",
16
18
},
17
19
error: {
18
20
text: "stream errored",
19
19
-
classes: "bg-red-100 text-red-800 border-red-200",
21
21
+
classes:
22
22
+
"bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 border-red-200 dark:border-red-800",
20
23
},
21
24
disconnected: {
22
25
text: "stream offline",
23
23
-
classes: "bg-gray-100 text-gray-800 border-gray-200",
26
26
+
classes:
27
27
+
"bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 border-gray-200 dark:border-gray-800",
24
28
},
25
29
};
26
30
···
31
35
<!-- connecting spinner -->
32
36
{#if status === "connecting"}
33
37
<div
34
34
-
class="animate-spin rounded-full h-4 w-4 border-b-2 border-yellow-800"
38
38
+
class="animate-spin rounded-full h-4 w-4 border-b-2 border-yellow-800 dark:border-yellow-200"
35
39
></div>
36
40
{/if}
37
41
<!-- status text -->
+15
-9
client/src/routes/+page.svelte
···
267
267
/>
268
268
</svelte:head>
269
269
270
270
-
<header class="border-gray-300 border-b mb-4 pb-2">
270
270
+
<header
271
271
+
class="bg-white dark:bg-gray-900 border-gray-300 dark:border-gray-950 border-b mb-4 pb-2"
272
272
+
>
271
273
<div
272
274
class="px-2 md:ml-[19vw] mx-auto flex flex-wrap items-center text-center"
273
275
>
274
274
-
<h1 class="text-4xl font-bold mr-4 text-gray-900">lexicon tracker</h1>
275
275
-
<p class="text-lg mt-1 text-gray-600">
276
276
+
<h1 class="text-4xl font-bold mr-4 text-gray-900 dark:text-gray-200">
277
277
+
lexicon tracker
278
278
+
</h1>
279
279
+
<p class="text-lg mt-1 text-gray-600 dark:text-gray-300">
276
280
tracks lexicons seen on the jetstream {tracking_since === 0
277
281
? ""
278
282
: `(since: ${formatTimestamp(tracking_since)})`}
279
283
</p>
280
284
</div>
281
285
</header>
282
282
-
<div class="md:max-w-[61vw] mx-auto p-2">
286
286
+
<div class="bg-white dark:bg-gray-900 md:max-w-[61vw] mx-auto p-2">
283
287
<div class="min-w-fit grid grid-cols-2 xl:grid-cols-4 gap-2 2xl:gap-6 mb-8">
284
288
<StatsCard
285
289
title="total creation"
···
305
309
306
310
{#if error}
307
311
<div
308
308
-
class="bg-red-100 border border-red-300 text-red-700 px-4 py-3 rounded-lg mb-6"
312
312
+
class="bg-red-100 dark:bg-red-900 border border-red-300 dark:border-red-700 text-red-700 dark:text-red-200 px-4 py-3 rounded-lg mb-6"
309
313
>
310
314
<p>Error: {error}</p>
311
315
</div>
···
314
318
{#if eventsList.length > 0}
315
319
<div class="mb-8">
316
320
<div class="flex flex-wrap items-center gap-3 mb-3">
317
317
-
<h2 class="text-2xl font-bold text-gray-900">seen lexicons</h2>
321
321
+
<h2 class="text-2xl font-bold text-gray-900 dark:text-gray-200">
322
322
+
seen lexicons
323
323
+
</h2>
318
324
<StatusBadge status={websocketStatus} />
319
325
</div>
320
326
<div class="flex flex-wrap items-center gap-1.5 mb-6">
···
367
373
{/if}
368
374
</div>
369
375
370
370
-
<footer class="py-2 border-t border-gray-200 text-center">
371
371
-
<p class="text-gray-600 text-sm">
376
376
+
<footer class="py-2 border-t border-gray-200 dark:border-gray-800 text-center">
377
377
+
<p class="text-gray-600 dark:text-gray-200 text-sm">
372
378
source code <a
373
379
href="https://tangled.sh/@poor.dog/nsid-tracker"
374
380
target="_blank"
375
381
rel="noopener noreferrer"
376
376
-
class="text-blue-600 hover:text-blue-800 underline"
382
382
+
class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-600 underline"
377
383
>@poor.dog/nsid-tracker</a
378
384
>
379
385
</p>