tangled
alpha
login
or
join now
vt3e.cat
/
www
2
fork
atom
this repo has no descr,ription
vt3e.cat
2
fork
atom
overview
issues
pulls
pipelines
feat: projects page
vt3e.cat
2 months ago
1afc898b
0430f7a6
verified
This commit was signed with the committer's
known signature
.
vt3e.cat
SSH Key Fingerprint:
SHA256:bC12nO0d6wKnJ426YBbLO7LVxmZlwJ1l2X0eqOroDV0=
+277
-6
5 changed files
expand all
collapse all
unified
split
bun.lock
package.json
pkgs
web
src
components
Card
CardLayout.vue
views
ProjectsView.vue
tsconfig.app.json
+3
bun.lock
···
7
7
"@atcute/atproto": "^3.1.10",
8
8
"@atcute/client": "^4.2.0",
9
9
"@atcute/lexicons": "^1.2.6",
10
10
+
"@atcute/tangled": "^1.0.13",
10
11
"@iconify-prerendered/vue-material-symbols": "^0.28.1755063979",
11
12
"blurhash": "^2.0.5",
12
13
},
···
92
93
"@atcute/lexicon-doc": ["@atcute/lexicon-doc@1.1.3", "", { "dependencies": { "@badrap/valita": "^0.4.6" } }, "sha512-HlQBmB4NCZPzREyVzr7lzjRxSiRHook2xfa7DgA3dk3oYZ+KnnPEtS6M1sAmAAddtUdrOrJ+0xJPQHkfElZmpQ=="],
93
94
94
95
"@atcute/lexicons": ["@atcute/lexicons@1.2.6", "", { "dependencies": { "@atcute/uint8array": "^1.0.6", "@atcute/util-text": "^0.0.1", "@standard-schema/spec": "^1.1.0", "esm-env": "^1.2.2" } }, "sha512-s76UQd8D+XmHIzrjD9CJ9SOOeeLPHc+sMmcj7UFakAW/dDFXc579fcRdRfuUKvXBL5v1Gs2VgDdlh/IvvQZAwA=="],
96
96
+
97
97
+
"@atcute/tangled": ["@atcute/tangled@1.0.13", "", { "dependencies": { "@atcute/atproto": "^3.1.9", "@atcute/lexicons": "^1.2.5" } }, "sha512-K95jmjDXl/f1FFzOJkk07ibNbFsPmn64sdrMACxQmUibO9WcfSjzjZLPXuH6WHFnCNtIBG3x1FQ7ndQgLoZAmw=="],
95
98
96
99
"@atcute/uint8array": ["@atcute/uint8array@1.0.6", "", {}, "sha512-ucfRBQc7BFT8n9eCyGOzDHEMKF/nZwhS2pPao4Xtab1ML3HdFYcX2DM1tadCzas85QTGxHe5urnUAAcNKGRi9A=="],
97
100
+1
package.json
···
26
26
"@atcute/atproto": "^3.1.10",
27
27
"@atcute/client": "^4.2.0",
28
28
"@atcute/lexicons": "^1.2.6",
29
29
+
"@atcute/tangled": "^1.0.13",
29
30
"@iconify-prerendered/vue-material-symbols": "^0.28.1755063979",
30
31
"blurhash": "^2.0.5"
31
32
}
+5
-3
pkgs/web/src/components/Card/CardLayout.vue
···
264
264
padding: 1rem;
265
265
266
266
.card-intro {
267
267
-
margin-bottom: 1.5rem;
268
268
-
padding-bottom: 1rem;
269
269
-
border-bottom: 1px solid hsla(var(--overlay1) / 0.25);
267
267
+
&:has(> *) {
268
268
+
margin-bottom: 1.5rem;
269
269
+
padding-bottom: 1rem;
270
270
+
border-bottom: 1px solid hsla(var(--overlay1) / 0.25);
271
271
+
}
270
272
271
273
:deep(h2) {
272
274
margin-top: 0;
+267
-2
pkgs/web/src/views/ProjectsView.vue
···
1
1
<script setup lang="ts">
2
2
+
import { onMounted, ref } from 'vue'
3
3
+
import { IconWeb } from '@iconify-prerendered/vue-material-symbols'
4
4
+
import { Client, simpleFetchHandler, ok } from '@atcute/client'
5
5
+
import type { ShTangledRepo, ShTangledRepoLanguages } from '@atcute/tangled'
6
6
+
7
7
+
import TangledLogo from '@/assets/icons/tangled.svg?raw'
8
8
+
import SvgComponent from '@/components/SvgComponent.vue'
9
9
+
2
10
import CardLayout from '@/components/Card/CardLayout.vue'
11
11
+
import { DID } from '@/utils/links'
12
12
+
import type { ResourceUri } from '@atcute/lexicons'
13
13
+
14
14
+
const repoUrl = (repo: ShTangledRepo.Main) => {
15
15
+
return `https://tangled.org/${DID}/${repo.name}`
16
16
+
}
17
17
+
18
18
+
const repositories = ref<{ uri: ResourceUri; value: ShTangledRepo.Main }[]>([])
19
19
+
const repoLanguages = ref<Record<ResourceUri, ShTangledRepoLanguages.Language[]>>({})
20
20
+
const loading = ref(true)
21
21
+
const error = ref<string | null>(null)
22
22
+
23
23
+
const formatDate = (d?: string) => {
24
24
+
if (!d) return ''
25
25
+
try {
26
26
+
return new Date(d).toLocaleDateString(undefined, { year: 'numeric', month: 'long' })
27
27
+
} catch {
28
28
+
return d
29
29
+
}
30
30
+
}
31
31
+
32
32
+
onMounted(async () => {
33
33
+
const manager = simpleFetchHandler({ service: 'https://pds.wlo.moe' })
34
34
+
const client = new Client({ handler: manager })
35
35
+
36
36
+
const reposRes = await client.get('com.atproto.repo.listRecords', {
37
37
+
params: {
38
38
+
repo: DID,
39
39
+
collection: 'sh.tangled.repo',
40
40
+
},
41
41
+
})
42
42
+
43
43
+
if (!reposRes.ok) {
44
44
+
error.value =
45
45
+
reposRes.data.error || 'An unknown error occurred while fetching repositories from tangled.'
46
46
+
loading.value = false
47
47
+
return
48
48
+
}
49
49
+
50
50
+
repositories.value = reposRes.data.records.map((record) => {
51
51
+
return { uri: record.uri, value: record.value as ShTangledRepo.Main }
52
52
+
})
53
53
+
loading.value = false
54
54
+
55
55
+
await Promise.all(
56
56
+
repositories.value.map(async (repo) => {
57
57
+
const knotManager = simpleFetchHandler({ service: `https://${repo.value.knot}` })
58
58
+
const knotRpc = new Client({ handler: knotManager })
59
59
+
60
60
+
const defaultBranch = ok(
61
61
+
await knotRpc.get('sh.tangled.repo.getDefaultBranch', {
62
62
+
params: {
63
63
+
repo: `${DID}/${repo.value.name}`,
64
64
+
},
65
65
+
}),
66
66
+
)
67
67
+
68
68
+
const languages = ok(
69
69
+
await knotRpc.get('sh.tangled.repo.languages', {
70
70
+
params: {
71
71
+
repo: `${DID}/${repo.value.name}`,
72
72
+
ref: defaultBranch.name,
73
73
+
},
74
74
+
}),
75
75
+
)
76
76
+
77
77
+
repoLanguages.value[repo.uri] = languages.languages
78
78
+
.filter((lang) => lang.name !== '')
79
79
+
.slice(0, 3)
80
80
+
}),
81
81
+
)
82
82
+
})
3
83
</script>
4
84
5
85
<template>
6
6
-
<CardLayout title="placeholder!"> i love place holding :D </CardLayout>
86
86
+
<CardLayout title="projects">
87
87
+
<section class="main">
88
88
+
<h2></h2>
89
89
+
</section>
90
90
+
91
91
+
<section class="repositories">
92
92
+
<h2>all repositories</h2>
93
93
+
<p>these are all the repositories i've published on tangled.</p>
94
94
+
95
95
+
<div class="repositories-list">
96
96
+
<article v-for="repo in repositories" :key="repo.uri" class="repository">
97
97
+
<div class="repo-header">
98
98
+
<div class="repo-meta">
99
99
+
<time
100
100
+
class="repo-tag repo-created"
101
101
+
:datetime="repo.value.createdAt"
102
102
+
:aria-label="`created on ${formatDate(repo.value.createdAt)}`"
103
103
+
:title="`created on ${formatDate(repo.value.createdAt)}`"
104
104
+
>
105
105
+
{{ formatDate(repo.value.createdAt) }}
106
106
+
</time>
107
107
+
<div class="tags" v-if="repo.value.topics && repo.value.topics.length">
108
108
+
<span v-for="tag in repo.value.topics" :key="tag" class="repo-tag">
109
109
+
{{ tag }}
110
110
+
</span>
111
111
+
</div>
112
112
+
<div class="tags" v-if="repoLanguages[repo.uri] && repoLanguages[repo.uri]?.length">
113
113
+
<span v-for="lang in repoLanguages[repo.uri]" :key="lang.name" class="repo-tag">{{
114
114
+
lang.name
115
115
+
}}</span>
116
116
+
</div>
117
117
+
</div>
118
118
+
<h3 class="repo-name">{{ repo.value.name }}</h3>
119
119
+
<p class="repo-description">{{ repo.value.description }}</p>
120
120
+
</div>
121
121
+
<div class="repo-links">
122
122
+
<a :href="repoUrl(repo.value)" target="_blank" rel="noopener noreferrer">
123
123
+
<SvgComponent :icon="TangledLogo" :decorative="true" />
124
124
+
repository
125
125
+
</a>
126
126
+
<a
127
127
+
v-if="repo.value.website"
128
128
+
:href="repo.value.website"
129
129
+
target="_blank"
130
130
+
rel="noopener noreferrer"
131
131
+
>
132
132
+
<IconWeb role="decorative" aria-hidden="true" />
133
133
+
website
134
134
+
</a>
135
135
+
</div>
136
136
+
</article>
137
137
+
</div>
138
138
+
</section>
139
139
+
</CardLayout>
7
140
</template>
8
141
9
9
-
<style scoped></style>
142
142
+
<style scoped lang="scss">
143
143
+
.repositories {
144
144
+
h2 {
145
145
+
margin-bottom: 0;
146
146
+
font-size: 1.5rem;
147
147
+
font-weight: 800;
148
148
+
}
149
149
+
p {
150
150
+
margin-top: 0;
151
151
+
margin-bottom: 1rem;
152
152
+
color: var(--text-secondary);
153
153
+
}
154
154
+
}
155
155
+
156
156
+
.repositories-list {
157
157
+
display: flex;
158
158
+
flex-direction: column;
159
159
+
gap: 0.25rem;
160
160
+
161
161
+
.repository {
162
162
+
background-color: hsla(var(--surface0) / 1);
163
163
+
padding: 0.5rem;
164
164
+
border-radius: 0.5rem;
165
165
+
166
166
+
&:first-child {
167
167
+
border-radius: 1rem 1rem 0.5rem 0.5rem;
168
168
+
}
169
169
+
&:last-child {
170
170
+
border-radius: 0.5rem 0.5rem 1rem 1rem;
171
171
+
}
172
172
+
173
173
+
.repo-header {
174
174
+
margin-bottom: 0.75rem;
175
175
+
176
176
+
.repo-meta {
177
177
+
display: inline-flex;
178
178
+
padding: 0.25rem;
179
179
+
border-radius: 5rem;
180
180
+
flex-direction: row;
181
181
+
align-items: center;
182
182
+
gap: 0.25rem;
183
183
+
184
184
+
background-color: hsla(var(--accent) / 0.075);
185
185
+
186
186
+
.repo-tag {
187
187
+
font-size: 0.75rem;
188
188
+
color: hsl(var(--subtext1));
189
189
+
background-color: hsla(var(--page-accent) / 0.1);
190
190
+
user-select: none;
191
191
+
padding: 0.15rem 0.5rem;
192
192
+
border-radius: 2rem;
193
193
+
font-weight: 700;
194
194
+
text-transform: lowercase;
195
195
+
196
196
+
&.repo-created {
197
197
+
border-radius: 2rem;
198
198
+
background-color: hsla(var(--page-accent) / 0.2);
199
199
+
}
200
200
+
&:hover {
201
201
+
background-color: hsla(var(--page-accent) / 0.15);
202
202
+
}
203
203
+
}
204
204
+
205
205
+
.tags {
206
206
+
display: inline-flex;
207
207
+
flex-wrap: wrap;
208
208
+
gap: 0.2rem;
209
209
+
210
210
+
.repo-tag {
211
211
+
border-radius: 0.25rem;
212
212
+
213
213
+
&:first-child {
214
214
+
border-top-left-radius: 1rem;
215
215
+
border-bottom-left-radius: 1rem;
216
216
+
}
217
217
+
&:last-child {
218
218
+
border-top-right-radius: 1rem;
219
219
+
border-bottom-right-radius: 1rem;
220
220
+
}
221
221
+
}
222
222
+
}
223
223
+
}
224
224
+
225
225
+
.repo-name {
226
226
+
margin: 0;
227
227
+
font-size: 1.25rem;
228
228
+
font-weight: 600;
229
229
+
margin-left: 0.65rem;
230
230
+
}
231
231
+
232
232
+
.repo-description {
233
233
+
color: var(--text-secondary);
234
234
+
margin-left: 0.65rem;
235
235
+
}
236
236
+
}
237
237
+
238
238
+
.repo-links {
239
239
+
display: flex;
240
240
+
gap: 0.5rem;
241
241
+
242
242
+
a {
243
243
+
padding: 0.5rem 0.85rem;
244
244
+
background-color: hsla(var(--page-accent) / 0.05);
245
245
+
border: 1px solid var(--border-color);
246
246
+
border-radius: 0.5rem;
247
247
+
font-size: 0.875rem;
248
248
+
display: inline-flex;
249
249
+
align-items: center;
250
250
+
gap: 0.5rem;
251
251
+
color: hsl(var(--subtext0));
252
252
+
text-decoration: none;
253
253
+
font-weight: 500;
254
254
+
255
255
+
:deep(svg) {
256
256
+
fill: currentColor;
257
257
+
height: 1.5em;
258
258
+
width: 1.5em;
259
259
+
}
260
260
+
261
261
+
&:hover,
262
262
+
&:focus-visible {
263
263
+
background-color: hsla(var(--page-accent) / 0.1);
264
264
+
color: hsl(var(--page-accent));
265
265
+
}
266
266
+
&:active,
267
267
+
&.active {
268
268
+
background-color: hsla(var(--page-accent) / 0.04);
269
269
+
}
270
270
+
}
271
271
+
}
272
272
+
}
273
273
+
}
274
274
+
</style>
+1
-1
pkgs/web/tsconfig.app.json
···
4
4
"exclude": ["src/**/__tests__/*"],
5
5
"compilerOptions": {
6
6
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
7
7
-
"types": ["@atcute/atproto", "@sillowww/gallery"],
7
7
+
"types": ["@atcute/atproto", "@atcute/tangled", "@sillowww/gallery"],
8
8
"paths": {
9
9
"@/*": ["./src/*"],
10
10
},