+61
.tangled/workflows/build.yml
+61
.tangled/workflows/build.yml
···
1
+
when:
2
+
- event: ["push", "manual"]
3
+
branch: ["main"]
4
+
5
+
engine: "nixery"
6
+
7
+
dependencies:
8
+
nixpkgs:
9
+
- deno
10
+
- openssh
11
+
- su
12
+
13
+
steps:
14
+
- name: "Pretend we have a real local user"
15
+
command: |
16
+
echo "🪶 pretend root is a real user"
17
+
echo "root:x:0:0:System administrator:/root:/run/current-system/sw/bin/bash" >> /etc/passwd
18
+
19
+
- name: "Copy config to server"
20
+
command: |
21
+
echo "🪶 copying config from secrets"
22
+
echo "${CONFIG}" > config.ts
23
+
24
+
- name: "Set up Deno"
25
+
command: |
26
+
echo "🪶 installing deno packages"
27
+
deno install
28
+
29
+
- name: "Build static files"
30
+
command: |
31
+
echo "🪶 building static files"
32
+
deno run build
33
+
34
+
- name: "SSH setup"
35
+
command: |
36
+
echo "🪶 setting up ssh connection"
37
+
mkdir ~/.ssh
38
+
echo "${SSH_KEY}" > ~/.ssh/id_tangledsh
39
+
chmod 600 ~/.ssh/id_tangledsh
40
+
cat > /etc/ssh/ssh_config << EOF
41
+
Host deploy
42
+
HostName ${SERVER_HOST}
43
+
User ${SERVER_USER}
44
+
IdentityFile ~/.ssh/id_tangledsh
45
+
StrictHostKeyChecking no
46
+
UserKnownHostsFile /dev/null
47
+
BatchMode yes
48
+
PasswordAuthentication no
49
+
PubkeyAuthentication yes
50
+
EOF
51
+
chmod 600 /etc/ssh/ssh_config
52
+
ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
53
+
54
+
- name: "Deploy via SCP"
55
+
command: |
56
+
echo "🪶 deploying files via scp"
57
+
scp -r ./dist/* deploy:/pds/caddy/etc/caddy/static
58
+
59
+
- name: "Done!"
60
+
command: |
61
+
echo "🪶 all done!
-32
.tangled/workflows/pipeline.yml
-32
.tangled/workflows/pipeline.yml
···
1
-
when:
2
-
- event: ["push", "manual"]
3
-
branch: ["main"]
4
-
engine: "nixery"
5
-
dependencies:
6
-
nixpkgs:
7
-
- deno
8
-
steps:
9
-
- name: "Build static files"
10
-
command: "deno run build"
11
-
- name: "SSH setup"
12
-
command: |
13
-
mkdir ~/.ssh
14
-
echo "${{secrets.SSH_KEY}}" > ~/.ssh/id_tangledsh
15
-
chmod 600 ~/.ssh/id_tangledsh
16
-
cat > ~/.ssh/config << EOF
17
-
Host deploy
18
-
HostName ${{ secrets.SERVER_HOST }}
19
-
User ${{ secrets.SERVER_USER }}
20
-
IdentityFile ~/.ssh/id_tangledsh
21
-
StrictHostKeyChecking accept-new
22
-
BatchMode yes
23
-
PasswordAuthentication no
24
-
PubkeyAuthentication yes
25
-
EOF
26
-
chmod 600 ~/.ssh/config
27
-
ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
28
-
echo "🪶 deploying to shimaenaga pds"
29
-
- name: "Deploy via SCP"
30
-
run: scp -r ./dist/* deploy:/pds/caddy/webroot
31
-
- name: "Done!"
32
-
command: "echo \"🪶 all done!\""
+5
-2
README.md
+5
-2
README.md
···
8
8
* Added some stuff in the backend code to also pull the user's description and banner to make the account cards fancier
9
9
* Changed font to Recursive Variable (waffling on changing this to space mono though and maybe using recursive just for the profile cards)
10
10
* Replaced favicon
11
-
* Add guestbook functionality (see new config options)
12
-
* Wrote deploy pipeline for tangled.sh
11
+
* Ported in guestbook functionality from another PDS frontpage (see new config options)
12
+
* Secret unique easter egg :)
13
+
* Wrote deploy pipeline for tangled.sh (WORK IN PROGRESS help try #5 - changing ssh perms?)
14
+
15
+
you can see it running at [my pds!](https://shimaenaga.veryroundbird.house)
13
16
14
17
# original readme below this line
15
18
public/fonts/spacemono-bold-webfont.woff2
public/fonts/spacemono-bold-webfont.woff2
This is a binary file and will not be displayed.
public/fonts/spacemono-bolditalic-webfont.woff2
public/fonts/spacemono-bolditalic-webfont.woff2
This is a binary file and will not be displayed.
public/fonts/spacemono-italic-webfont.woff2
public/fonts/spacemono-italic-webfont.woff2
This is a binary file and will not be displayed.
public/fonts/spacemono-regular-webfont.woff2
public/fonts/spacemono-regular-webfont.woff2
This is a binary file and will not be displayed.
+71
-2
src/App.svelte
+71
-2
src/App.svelte
···
1
1
<script lang="ts">
2
2
import PostComponent from "./lib/PostComponent.svelte";
3
+
import GuestbookPostComponent from "./lib/GuestbookPostComponent.svelte";
3
4
import AccountComponent from "./lib/AccountComponent.svelte";
4
5
import InfiniteLoading from "svelte-infinite-loading";
5
6
import { getNextPosts, Post, getAllMetadataFromPds, fetchGuestbookPosts } from "./lib/pdsfetch";
···
50
51
<main>
51
52
<div id="content">
52
53
{#await accountsPromise}
53
-
<p>Loading...</p>
54
+
<div id="loadingbirds">
55
+
<div class="small">
56
+
<pre>
57
+
█▓░░░░░░░░░▒
58
+
█▓▒ ░▒
59
+
▓▒▒▒░ ▒
60
+
▓▒░░░░ ░▓
61
+
█▒ ░░ ░ ░ ▓▒ ░█
62
+
▓░ ░░ ▒▓░░▒▒░ ▒
63
+
███████ ██▓▒░▒▒░ ░░░░ ░░░ ▒
64
+
█▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▒▓▒▒▒▓░ ▓
65
+
███▓▒░░▒░░░░▓▓█▓▓▒░ ░
66
+
▓ ░
67
+
█ ░ ▒
68
+
█░ ▒█
69
+
▒ ▒
70
+
█▒ █
71
+
▓ ░█
72
+
▓▒ ░░ ░▒
73
+
▓▒▒░░▒▓░ ░░░▒▒▓▓▒▓▓
74
+
█▓█▓▓█ ███▓█
75
+
████ ████
76
+
</pre>
77
+
</div>
78
+
<div class="large">
79
+
<pre>
80
+
████▓▒░░░░░░░▒▓██
81
+
█▓░ ▒█
82
+
██▓░ ▒█
83
+
██▒ ▒█
84
+
███▓▒░ ▓█
85
+
█▓░ ░ █
86
+
██░ ░ ░█
87
+
█░ ▓
88
+
█▒ ▓
89
+
██▒ ▒▓▓ █
90
+
█▒ ▓█ ░▒ ▒▓ ▓█
91
+
██▒ ▒▓░ ░▒░░ ▓█
92
+
███▒░ ░▒▒▒▒▒░ ▓█
93
+
█████ ██▓░ ░░░░ █
94
+
█▓▒░░░░░░▒▒▒▓▓██████████▓▓▒░ ░▒▒▒▒▒▒▓▒▒░ ░▒▒░ ░█
95
+
██████▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▓▒▒▓▓▓▓▒▒▒▒▒▒▓▒░░░▒▒▒░ ▒█
96
+
███▓▓▒▒░░░░ ░░░▒▓▓▓▒▒▓▓▓▓▓▒░░▒▒▒▒░ █
97
+
███▓▓▒ ▒▓▓▓▓▓▓▓█▓▒░░ █
98
+
█▓ ░▒▒▓▒░ ▓
99
+
█▓ ▓
100
+
█ █
101
+
█ ░█
102
+
█ ▓█
103
+
█░ ▒█
104
+
▒ ▓█
105
+
▓ █
106
+
█▒ ▒█
107
+
█▒ ▒█
108
+
█▓ ▒█
109
+
██ ██
110
+
█▒ ▓█
111
+
██░ ▒█
112
+
█░ ░▓█
113
+
█▓░ ▓▓▓░ ░▓▓▒ ░██
114
+
███▓▒░ ▒▓▓▓ ▒▓▓▓▓▓▓▓████▓▓▓▓▒█
115
+
█▓██▓▓▓▓██ ████▓▓▓██
116
+
██▓▓██ ██▓▓██
117
+
███▓██ █▓▓▓██
118
+
██████ ███████
119
+
</pre>
120
+
</div>
121
+
<p id="loadingText" data-text="pds is landing..." title="pds is landing...">pds is landing...</p>
122
+
</div>
54
123
{:then accountsData}
55
124
<div id="account">
56
125
<pre id="asciiart">
···
84
153
<div id="guestbookPosts">
85
154
{#if guestbookPosts.length > 0}
86
155
{#each guestbookPosts as postObject}
87
-
<PostComponent post={postObject as Post} />
156
+
<GuestbookPostComponent post={postObject.post} />
88
157
{/each}
89
158
{:else}
90
159
<p class="noGuestbookPosts">No guestbook posts yet!</p>
+1
-1
src/lib/AccountComponent.svelte
+1
-1
src/lib/AccountComponent.svelte
···
24
24
/>
25
25
{/if}
26
26
</div>
27
-
<div class="accountTooltip" popover id="{account.handle.replace('.')}">
27
+
<div class="accountTooltip" popover id="{account.handle.replace('.', '')}">
28
28
<div class="banner">
29
29
<img class="bannerImg" src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.bannerCid}" alt="{account.displayName}'s banner" width="300" height="100" />
30
30
<img class="avatarInsetImg" src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.avatarCid}" alt="{account.displayName}'s avatar" width="50" height="50" />
+92
src/lib/GuestbookPostComponent.svelte
+92
src/lib/GuestbookPostComponent.svelte
···
1
+
<script lang="ts">
2
+
import { Post } from "./pdsfetch";
3
+
import { Config } from "../../config";
4
+
import { onMount } from "svelte";
5
+
import moment from "moment";
6
+
7
+
let { post }: { post: Post } = $props();
8
+
console.log(post);
9
+
</script>
10
+
11
+
<div class="postContainer">
12
+
<div class="postHeader">
13
+
{#if post.author.avatar}
14
+
<img
15
+
class="avatar"
16
+
src="{post.author.avatar}"
17
+
alt="avatar of {post.author.displayName}"
18
+
/>
19
+
{/if}
20
+
<div class="headerText">
21
+
<a class="displayName" href="{Config.FRONTEND_URL}/profile/{post.author.did}">{post.author.displayName}</a>
22
+
<p class="handle">
23
+
<a href="{Config.FRONTEND_URL}/profile/{post.author.handle}"
24
+
>@{post.author.handle}</a
25
+
>
26
+
<a
27
+
class="postLink" href="{Config.FRONTEND_URL}/profile/{post.author.did}/post/{post.record.id}"
28
+
>{moment(post.record.createdAt).isBefore(moment().subtract(1, "month"))
29
+
? moment(post.record.createdAt).format("MMM D, YYYY")
30
+
: moment(post.record.createdAt).fromNow()}</a>
31
+
</p>
32
+
</div>
33
+
</div>
34
+
<div class="postContent">
35
+
{#if post.record.quote}
36
+
<a
37
+
class="quotingText"
38
+
href="{Config.FRONTEND_URL}/profile/{post.record.quote.uri}/post/{post
39
+
.quotingUri.rkey}">quoting {post.quotingUri.repo}</a
40
+
>
41
+
{/if}
42
+
<div class="postText">{post.record.text}</div>
43
+
{#if post.record.imagesCid && post.record.imagesCid.length > 0}
44
+
<div id="carouselContainer">
45
+
<img
46
+
class="embedImages"
47
+
alt="Post Image {currentImageIndex + 1} of {post.record.imagesCid.length}"
48
+
src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={post.record.author.did}&cid={post.record
49
+
.imagesCid[currentImageIndex]}"
50
+
/>
51
+
52
+
{#if post.imagesCid.length > 1}
53
+
<div class="carouselControls">
54
+
<button
55
+
id="prevBtn"
56
+
onclick={prevImage}
57
+
disabled={currentImageIndex === 0}>←</button
58
+
>
59
+
<div class="carouselIndicators">
60
+
{#each post.record.imagesCid as _, i}
61
+
<div
62
+
class="indicator {i === currentImageIndex ? 'active' : ''}"
63
+
></div>
64
+
{/each}
65
+
</div>
66
+
<button
67
+
class="nextBtn"
68
+
onclick={nextImage}
69
+
disabled={currentImageIndex === post.imagesCid.length - 1}
70
+
>→</button
71
+
>
72
+
</div>
73
+
{/if}
74
+
</div>
75
+
{/if}
76
+
{#if post.record.videosLinkCid}
77
+
<!-- svelte-ignore a11y_media_has_caption -->
78
+
<video
79
+
class="embedVideo"
80
+
src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={post.authorDid}&cid={post.videosLinkCid}"
81
+
controls
82
+
></video>
83
+
{/if}
84
+
{#if post.gifLink}
85
+
<img
86
+
class="embedVideo"
87
+
src="{post.record.gifLink}"
88
+
alt="Post GIF"
89
+
/>
90
+
{/if}
91
+
</div>
92
+
</div>
+72
-4
themes/birdrights/theme.css
+72
-4
themes/birdrights/theme.css
···
1
1
@font-face {
2
2
font-family: "Recursive Variable";
3
3
font-weight: 100 700;
4
-
font-style: -5deg 5deg;
5
4
src: url(/fonts/Recursive_VF_1.085.woff2) format('woff2');
6
5
}
7
6
7
+
@font-face {
8
+
font-family: "Space Mono";
9
+
font-weight: normal;
10
+
font-style: normal;
11
+
src: url(/fonts/spacemono-regular-webfont.woff2) format('woff2');
12
+
}
13
+
14
+
@font-face {
15
+
font-family: "Space Mono";
16
+
font-weight: bold;
17
+
font-style: normal;
18
+
src: url(/fonts/spacemono-bold-webfont.woff2) format('woff2');
19
+
}
20
+
21
+
@font-face {
22
+
font-family: "Space Mono";
23
+
font-weight: normal;
24
+
font-style: italic;
25
+
src: url(/fonts/spacemono-italic-webfont.woff2) format('woff2');
26
+
}
27
+
28
+
@font-face {
29
+
font-family: "Space Mono";
30
+
font-weight: bold;
31
+
font-style: italic;
32
+
src: url(/fonts/spacemono-bolditalic-webfont.woff2) format('woff2');
33
+
}
34
+
8
35
:root {
9
36
/* Color overrides, edit to whatever you want */
10
37
--primary-h: 260; /* Hue */
···
34
61
min-width: 320px;
35
62
min-height: 100vh;
36
63
background-color: var(--background-color);
37
-
font-family: "Recursive Variable";
64
+
font-family: "Recursive Variable", monospace;
38
65
font-size: var(--base-font-size);
39
66
color: var(--text-color);
40
67
border-color: var(--border-color);
···
55
82
}
56
83
57
84
h1 {
85
+
font-family: "Space Mono", monospace;
58
86
font-size: 2em;
59
87
line-height: 1.1;
60
88
margin-bottom: 0;
···
300
328
background-color: transparent;
301
329
padding: 0;
302
330
position: relative;
331
+
cursor: pointer;
303
332
}
304
333
305
334
.accountContainer {
···
326
355
white-space: nowrap;
327
356
}
328
357
329
-
.accountTooltip {
358
+
.accountTooltip[popover] {
330
359
border: 1px var(--border-color) solid;
331
360
color: var(--text-color);
332
361
background-color: var(--content-background-color);
···
400
429
padding: 10px;
401
430
gap: 10px;
402
431
z-index: 10;
432
+
grid-template-rows: auto 1fr;
403
433
}
404
434
405
435
#guestbookContents:popover-open {
···
413
443
/* Source Link */
414
444
415
445
#footer {
416
-
position: absolute;
446
+
position: fixed;
417
447
bottom: 5px;
418
448
right: 5px;
419
449
z-index: 99;
···
428
458
429
459
#footer:hover {
430
460
opacity: 1;
461
+
}
462
+
463
+
#loadingbirds {
464
+
line-height: 1;
465
+
text-align: center;
466
+
width: 100%;
467
+
}
468
+
469
+
#loadingbirds pre {
470
+
display: inline-block;
471
+
margin: 0 auto;
472
+
text-align: left;
473
+
}
474
+
475
+
#loadingbirds .small {
476
+
display: none;
477
+
}
478
+
479
+
#loadingbirds #loadingText {
480
+
font-family: "Space Mono", monospace;
481
+
text-align: center;
482
+
font-style: oblique 5deg;
483
+
font-size: 2em;
484
+
min-height: 1em;
485
+
}
486
+
487
+
@media screen and (max-width: 780px) {
488
+
#loadingbirds .small {
489
+
display: block;
490
+
}
491
+
492
+
#loadingbirds .large {
493
+
display: none;
494
+
}
495
+
496
+
#loadingbirds #loadingText {
497
+
font-size: 1.25em;
498
+
}
431
499
}
432
500
433
501
/* Responsive Styling */