tangled
alpha
login
or
join now
flo-bit.dev
/
blento
21
fork
atom
your personal website on atproto - mirror
blento.app
21
fork
atom
overview
issues
pulls
pipelines
update link card
Florian
1 week ago
470876e6
3a62e8c9
+351
-190
4 changed files
expand all
collapse all
unified
split
src
lib
cards
core
LinkCard
EditingLinkCard.svelte
LinkCard.svelte
LinkCardSettings.svelte
social
SembleCollectionCard
index.ts
+198
-111
src/lib/cards/core/LinkCard/EditingLinkCard.svelte
···
113
onchange={handleImageChange}
114
/>
115
116
-
<div class="relative flex h-full flex-col justify-between p-4">
117
-
<div
118
-
class={[
119
-
'accent:bg-accent-500/50 absolute inset-0 z-20 bg-white/50 dark:bg-black/50',
120
-
!hasFetched ? 'animate-pulse' : 'hidden'
121
-
]}
122
-
></div>
123
-
124
-
<div class={isFetchingMetadata ? 'pointer-events-none' : ''}>
125
-
<button
126
-
type="button"
127
-
class="bg-base-100 border-base-300 accent:bg-accent-100/50 accent:border-accent-200 dark:border-base-800 dark:bg-base-900 hover:ring-accent-500 relative mb-2 inline-flex size-8 cursor-pointer items-center justify-center rounded-xl border transition-all duration-200 hover:ring-2"
128
-
onclick={() => faviconInputRef?.click()}
129
-
onmouseenter={() => (isHoveringFavicon = true)}
130
-
onmouseleave={() => (isHoveringFavicon = false)}
131
-
>
132
-
{#if hasFetched && item.cardData.favicon && !faviconHasError}
133
-
<img
134
-
class="size-6 rounded-lg object-cover"
135
-
onerror={() => (faviconHasError = true)}
136
-
src={getImage(item.cardData, did, 'favicon')}
137
-
alt=""
138
-
/>
139
-
{:else}
140
-
<svg
141
-
xmlns="http://www.w3.org/2000/svg"
142
-
fill="none"
143
-
viewBox="0 0 24 24"
144
-
stroke-width="1.5"
145
-
stroke="currentColor"
146
-
class="size-4"
147
-
>
148
-
<path
149
-
stroke-linecap="round"
150
-
stroke-linejoin="round"
151
-
d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 1 1.242 7.244"
152
-
/>
153
-
</svg>
154
-
{/if}
155
-
<!-- Hover overlay -->
156
-
<div
157
-
class={[
158
-
'absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 transition-opacity duration-200',
159
-
isHoveringFavicon ? 'opacity-100' : 'opacity-0'
160
-
]}
161
-
>
162
-
<svg
163
-
xmlns="http://www.w3.org/2000/svg"
164
-
fill="none"
165
-
viewBox="0 0 24 24"
166
-
stroke-width="2"
167
-
stroke="currentColor"
168
-
class="size-4 text-white"
169
-
>
170
-
<path
171
-
stroke-linecap="round"
172
-
stroke-linejoin="round"
173
-
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Z"
174
-
/>
175
-
</svg>
176
-
</div>
177
-
</button>
178
-
179
<div
180
class={[
181
-
'-m-1 rounded-md p-1 transition-colors duration-200',
182
-
hasFetched
183
-
? 'hover:bg-base-200/70 dark:hover:bg-base-800/70 accent:hover:bg-accent-200/30'
184
-
: ''
185
]}
186
-
>
187
-
{#if hasFetched}
188
-
<PlainTextEditor
189
-
class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold"
190
-
key="title"
191
-
bind:contentDict={item.cardData}
192
-
placeholder="Title here"
193
-
/>
194
-
{:else}
195
-
<span class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold">
196
-
Loading data...
197
-
</span>
198
-
{/if}
199
-
</div>
200
-
<!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> -->
201
<div
202
-
class="text-accent-600 accent:text-accent-950 dark:text-accent-400 mt-2 text-xs font-semibold"
203
-
>
204
-
{item.cardData.domain}
205
-
</div>
206
-
</div>
207
208
-
{#if hasFetched && browser && ((isMobile() && item.mobileH >= 8) || (!isMobile() && item.h >= 4))}
209
<button
210
type="button"
211
-
class="hover:ring-accent-500 relative mb-2 aspect-2/1 w-full cursor-pointer overflow-hidden rounded-xl transition-all duration-200 hover:ring-2"
212
onclick={() => imageInputRef?.click()}
213
onmouseenter={() => (isHoveringImage = true)}
214
onmouseleave={() => (isHoveringImage = false)}
215
>
216
-
{#if item.cardData.image}
217
-
<img
218
-
class="h-full w-full object-cover opacity-100 transition-opacity duration-100 starting:opacity-0"
219
-
src={getImage(item.cardData, did)}
220
-
alt=""
221
-
/>
222
-
{:else}
223
-
<div class="bg-base-200 dark:bg-base-800 flex h-full w-full items-center justify-center">
224
<svg
225
xmlns="http://www.w3.org/2000/svg"
226
fill="none"
227
viewBox="0 0 24 24"
228
stroke-width="1.5"
229
stroke="currentColor"
230
-
class="text-base-400 dark:text-base-600 size-8"
231
>
232
<path
233
stroke-linecap="round"
234
stroke-linejoin="round"
235
-
d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
0
0
0
0
0
236
/>
237
</svg>
0
238
</div>
239
-
{/if}
240
-
<!-- Hover overlay -->
0
0
0
0
0
0
241
<div
242
class={[
243
-
'absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 transition-opacity duration-200',
244
-
isHoveringImage ? 'opacity-100' : 'opacity-0'
245
]}
246
>
247
-
<div class="text-center text-sm text-white">
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
248
<svg
249
xmlns="http://www.w3.org/2000/svg"
250
fill="none"
251
viewBox="0 0 24 24"
252
stroke-width="1.5"
253
stroke="currentColor"
254
-
class="mx-auto mb-1 size-6"
255
>
256
<path
257
stroke-linecap="round"
258
stroke-linejoin="round"
259
-
d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z"
260
/>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
261
<path
262
stroke-linecap="round"
263
stroke-linejoin="round"
264
-
d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z"
265
/>
266
</svg>
267
-
<span class="font-medium">{item.cardData.image ? 'Change' : 'Add image'}</span>
268
</div>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
269
</div>
270
-
</button>
271
-
{/if}
272
-
</div>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
113
onchange={handleImageChange}
114
/>
115
116
+
{#if item.cardData.showBackgroundImage}
117
+
<div class="relative flex h-full flex-col justify-end p-4">
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
118
<div
119
class={[
120
+
'accent:bg-accent-500/50 absolute inset-0 z-30 bg-white/50 dark:bg-black/50',
121
+
!hasFetched ? 'animate-pulse' : 'hidden'
0
0
122
]}
123
+
></div>
124
+
125
+
{#if item.cardData.image}
126
+
<img
127
+
class="absolute inset-0 -z-10 size-full object-cover"
128
+
src={getImage(item.cardData, did)}
129
+
alt=""
130
+
/>
131
+
{/if}
0
0
0
0
0
0
132
<div
133
+
class="from-base-50/90 via-base-50/40 dark:from-base-950/90 dark:via-base-950/40 absolute inset-0 -z-10 bg-linear-to-t to-transparent"
134
+
></div>
0
0
0
135
136
+
<!-- Full card click to change image -->
137
<button
138
type="button"
139
+
class="absolute inset-0 z-10 cursor-pointer"
140
onclick={() => imageInputRef?.click()}
141
onmouseenter={() => (isHoveringImage = true)}
142
onmouseleave={() => (isHoveringImage = false)}
143
>
144
+
<div
145
+
class={[
146
+
'absolute inset-0 flex items-center justify-center bg-black/50 transition-opacity duration-200',
147
+
isHoveringImage ? 'opacity-100' : 'opacity-0'
148
+
]}
149
+
>
150
+
<div class="text-center text-sm text-white">
0
151
<svg
152
xmlns="http://www.w3.org/2000/svg"
153
fill="none"
154
viewBox="0 0 24 24"
155
stroke-width="1.5"
156
stroke="currentColor"
157
+
class="mx-auto mb-1 size-6"
158
>
159
<path
160
stroke-linecap="round"
161
stroke-linejoin="round"
162
+
d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z"
163
+
/>
164
+
<path
165
+
stroke-linecap="round"
166
+
stroke-linejoin="round"
167
+
d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z"
168
/>
169
</svg>
170
+
<span class="font-medium">{item.cardData.image ? 'Change image' : 'Add image'}</span>
171
</div>
172
+
</div>
173
+
</button>
174
+
175
+
<!-- Domain and title at the bottom, above the image button -->
176
+
<div class="relative z-20">
177
+
<div class="text-accent-600 dark:text-accent-400 text-xs font-semibold">
178
+
{item.cardData.domain}
179
+
</div>
180
<div
181
class={[
182
+
'-m-1 rounded-md p-1 transition-colors duration-200',
183
+
hasFetched ? 'hover:bg-base-200/70 dark:hover:bg-base-800/70' : ''
184
]}
185
>
186
+
{#if hasFetched}
187
+
<PlainTextEditor
188
+
class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold"
189
+
key="title"
190
+
bind:contentDict={item.cardData}
191
+
placeholder="Title here"
192
+
/>
193
+
{:else}
194
+
<span class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold">
195
+
Loading data...
196
+
</span>
197
+
{/if}
198
+
</div>
199
+
</div>
200
+
</div>
201
+
{:else}
202
+
<div class="relative flex h-full flex-col justify-between p-4">
203
+
<div
204
+
class={[
205
+
'accent:bg-accent-500/50 absolute inset-0 z-20 bg-white/50 dark:bg-black/50',
206
+
!hasFetched ? 'animate-pulse' : 'hidden'
207
+
]}
208
+
></div>
209
+
210
+
<div class={isFetchingMetadata ? 'pointer-events-none' : ''}>
211
+
<button
212
+
type="button"
213
+
class="bg-base-100 border-base-300 accent:bg-accent-100/50 accent:border-accent-200 dark:border-base-800 dark:bg-base-900 hover:ring-accent-500 relative mb-2 inline-flex size-8 cursor-pointer items-center justify-center rounded-xl border transition-all duration-200 hover:ring-2"
214
+
onclick={() => faviconInputRef?.click()}
215
+
onmouseenter={() => (isHoveringFavicon = true)}
216
+
onmouseleave={() => (isHoveringFavicon = false)}
217
+
>
218
+
{#if hasFetched && item.cardData.favicon && !faviconHasError}
219
+
<img
220
+
class="size-6 rounded-lg object-cover"
221
+
onerror={() => (faviconHasError = true)}
222
+
src={getImage(item.cardData, did, 'favicon')}
223
+
alt=""
224
+
/>
225
+
{:else}
226
<svg
227
xmlns="http://www.w3.org/2000/svg"
228
fill="none"
229
viewBox="0 0 24 24"
230
stroke-width="1.5"
231
stroke="currentColor"
232
+
class="size-4"
233
>
234
<path
235
stroke-linecap="round"
236
stroke-linejoin="round"
237
+
d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 1 1.242 7.244"
238
/>
239
+
</svg>
240
+
{/if}
241
+
<!-- Hover overlay -->
242
+
<div
243
+
class={[
244
+
'absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 transition-opacity duration-200',
245
+
isHoveringFavicon ? 'opacity-100' : 'opacity-0'
246
+
]}
247
+
>
248
+
<svg
249
+
xmlns="http://www.w3.org/2000/svg"
250
+
fill="none"
251
+
viewBox="0 0 24 24"
252
+
stroke-width="2"
253
+
stroke="currentColor"
254
+
class="size-4 text-white"
255
+
>
256
<path
257
stroke-linecap="round"
258
stroke-linejoin="round"
259
+
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Z"
260
/>
261
</svg>
0
262
</div>
263
+
</button>
264
+
265
+
<div
266
+
class={[
267
+
'-m-1 rounded-md p-1 transition-colors duration-200',
268
+
hasFetched
269
+
? 'hover:bg-base-200/70 dark:hover:bg-base-800/70 accent:hover:bg-accent-200/30'
270
+
: ''
271
+
]}
272
+
>
273
+
{#if hasFetched}
274
+
<PlainTextEditor
275
+
class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold"
276
+
key="title"
277
+
bind:contentDict={item.cardData}
278
+
placeholder="Title here"
279
+
/>
280
+
{:else}
281
+
<span class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold">
282
+
Loading data...
283
+
</span>
284
+
{/if}
285
</div>
286
+
<!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> -->
287
+
<div
288
+
class="text-accent-600 accent:text-accent-950 dark:text-accent-400 mt-2 text-xs font-semibold"
289
+
>
290
+
{item.cardData.domain}
291
+
</div>
292
+
</div>
293
+
294
+
{#if hasFetched && browser && ((isMobile() && item.mobileH >= 8) || (!isMobile() && item.h >= 4))}
295
+
<button
296
+
type="button"
297
+
class="hover:ring-accent-500 relative mb-2 aspect-2/1 w-full cursor-pointer overflow-hidden rounded-xl transition-all duration-200 hover:ring-2"
298
+
onclick={() => imageInputRef?.click()}
299
+
onmouseenter={() => (isHoveringImage = true)}
300
+
onmouseleave={() => (isHoveringImage = false)}
301
+
>
302
+
{#if item.cardData.image}
303
+
<img
304
+
class="h-full w-full object-cover opacity-100 transition-opacity duration-100 starting:opacity-0"
305
+
src={getImage(item.cardData, did)}
306
+
alt=""
307
+
/>
308
+
{:else}
309
+
<div class="bg-base-200 dark:bg-base-800 flex h-full w-full items-center justify-center">
310
+
<svg
311
+
xmlns="http://www.w3.org/2000/svg"
312
+
fill="none"
313
+
viewBox="0 0 24 24"
314
+
stroke-width="1.5"
315
+
stroke="currentColor"
316
+
class="text-base-400 dark:text-base-600 size-8"
317
+
>
318
+
<path
319
+
stroke-linecap="round"
320
+
stroke-linejoin="round"
321
+
d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
322
+
/>
323
+
</svg>
324
+
</div>
325
+
{/if}
326
+
<!-- Hover overlay -->
327
+
<div
328
+
class={[
329
+
'absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 transition-opacity duration-200',
330
+
isHoveringImage ? 'opacity-100' : 'opacity-0'
331
+
]}
332
+
>
333
+
<div class="text-center text-sm text-white">
334
+
<svg
335
+
xmlns="http://www.w3.org/2000/svg"
336
+
fill="none"
337
+
viewBox="0 0 24 24"
338
+
stroke-width="1.5"
339
+
stroke="currentColor"
340
+
class="mx-auto mb-1 size-6"
341
+
>
342
+
<path
343
+
stroke-linecap="round"
344
+
stroke-linejoin="round"
345
+
d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z"
346
+
/>
347
+
<path
348
+
stroke-linecap="round"
349
+
stroke-linejoin="round"
350
+
d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z"
351
+
/>
352
+
</svg>
353
+
<span class="font-medium">{item.cardData.image ? 'Change' : 'Add image'}</span>
354
+
</div>
355
+
</div>
356
+
</button>
357
+
{/if}
358
+
</div>
359
+
{/if}
+134
-74
src/lib/cards/core/LinkCard/LinkCard.svelte
···
14
let did = getDidContext();
15
</script>
16
17
-
<div class="flex h-full flex-col justify-between p-4">
18
-
<div>
0
0
0
0
0
19
<div
20
-
class="bg-base-100 border-base-300 accent:bg-accent-100/50 accent:border-accent-200 dark:border-base-800 dark:bg-base-900 mb-2 inline-flex size-8 items-center justify-center rounded-xl border"
21
-
>
22
-
{#if item.cardData.favicon && !faviconHasError}
23
-
<img
24
-
class="size-6 rounded-lg object-cover"
25
-
onerror={() => (faviconHasError = true)}
26
-
src={getImage(item.cardData, did, 'favicon')}
27
-
alt=""
28
-
/>
29
-
{:else}
30
-
<svg
31
-
xmlns="http://www.w3.org/2000/svg"
32
-
fill="none"
33
-
viewBox="0 0 24 24"
34
-
stroke-width="1.5"
35
-
stroke="currentColor"
36
-
class="size-4"
37
-
>
38
-
<path
39
-
stroke-linecap="round"
40
-
stroke-linejoin="round"
41
-
d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
42
-
/>
43
-
</svg>
44
-
{/if}
45
</div>
46
<div
47
class={[
···
51
>
52
{item.cardData.title}
53
</div>
54
-
<!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> -->
55
-
<div
56
-
class="text-accent-600 accent:text-accent-950 dark:text-accent-400 mt-2 text-xs font-semibold"
57
-
>
58
-
{item.cardData.domain}
59
-
</div>
60
-
</div>
61
-
62
-
{#if browser && ((isMobile() && item.mobileH >= 8) || (!isMobile() && item.h >= 4)) && item.cardData.image}
63
-
<img
64
-
class="mb-2 aspect-2/1 w-full rounded-xl object-cover opacity-100 transition-opacity duration-100 starting:opacity-0"
65
-
src={getImage(item.cardData, did)}
66
-
alt=""
67
-
/>
68
-
{/if}
69
-
{#if item.cardData.href && !isEditing}
70
-
<a
71
-
href={item.cardData.href}
72
-
class="absolute inset-0 h-full w-full"
73
-
target="_blank"
74
-
rel="noopener noreferrer"
75
-
use:qrOverlay={{
76
-
context: {
77
-
title: item.cardData.title
78
-
}
79
-
}}
80
-
>
81
-
<span class="sr-only">
82
-
{item.cardData.hrefText ?? 'Learn more'}
83
-
</span>
84
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
85
<div
86
-
class="bg-base-800/30 border-base-900/30 absolute top-2 right-2 rounded-full border p-1 text-white opacity-50 backdrop-blur-lg group-focus-within:opacity-100 group-hover:opacity-100"
87
>
88
-
<svg
89
-
xmlns="http://www.w3.org/2000/svg"
90
-
fill="none"
91
-
viewBox="0 0 24 24"
92
-
stroke-width="2.5"
93
-
stroke="currentColor"
94
-
class="size-4"
95
-
>
96
-
<path
97
-
stroke-linecap="round"
98
-
stroke-linejoin="round"
99
-
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"
100
/>
101
-
</svg>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
102
</div>
103
-
</a>
104
-
{/if}
105
-
</div>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
14
let did = getDidContext();
15
</script>
16
17
+
{#if item.cardData.showBackgroundImage && item.cardData.image}
18
+
<div class="relative flex h-full flex-col justify-end p-4">
19
+
<img
20
+
class="absolute inset-0 -z-10 size-full object-cover"
21
+
src={getImage(item.cardData, did)}
22
+
alt=""
23
+
/>
24
<div
25
+
class="from-base-50/90 via-base-50/40 dark:from-base-950/90 dark:via-base-950/40 absolute inset-0 -z-10 bg-linear-to-t to-transparent"
26
+
></div>
27
+
<div class="text-accent-600 dark:text-accent-400 text-xs font-semibold">
28
+
{item.cardData.domain}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
29
</div>
30
<div
31
class={[
···
35
>
36
{item.cardData.title}
37
</div>
38
+
{#if item.cardData.href && !isEditing}
39
+
<a
40
+
href={item.cardData.href}
41
+
class="absolute inset-0 h-full w-full"
42
+
target="_blank"
43
+
rel="noopener noreferrer"
44
+
use:qrOverlay={{
45
+
context: {
46
+
title: item.cardData.title
47
+
}
48
+
}}
49
+
>
50
+
<span class="sr-only">
51
+
{item.cardData.hrefText ?? 'Learn more'}
52
+
</span>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
53
54
+
<div
55
+
class="bg-base-800/30 border-base-900/30 absolute top-2 right-2 rounded-full border p-1 text-white opacity-50 backdrop-blur-lg group-focus-within:opacity-100 group-hover:opacity-100"
56
+
>
57
+
<svg
58
+
xmlns="http://www.w3.org/2000/svg"
59
+
fill="none"
60
+
viewBox="0 0 24 24"
61
+
stroke-width="2.5"
62
+
stroke="currentColor"
63
+
class="size-4"
64
+
>
65
+
<path
66
+
stroke-linecap="round"
67
+
stroke-linejoin="round"
68
+
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"
69
+
/>
70
+
</svg>
71
+
</div>
72
+
</a>
73
+
{/if}
74
+
</div>
75
+
{:else}
76
+
<div class="flex h-full flex-col justify-between p-4">
77
+
<div>
78
<div
79
+
class="bg-base-100 border-base-300 accent:bg-accent-100/50 accent:border-accent-200 dark:border-base-800 dark:bg-base-900 mb-2 inline-flex size-8 items-center justify-center rounded-xl border"
80
>
81
+
{#if item.cardData.favicon && !faviconHasError}
82
+
<img
83
+
class="size-6 rounded-lg object-cover"
84
+
onerror={() => (faviconHasError = true)}
85
+
src={getImage(item.cardData, did, 'favicon')}
86
+
alt=""
0
0
0
0
0
0
87
/>
88
+
{:else}
89
+
<svg
90
+
xmlns="http://www.w3.org/2000/svg"
91
+
fill="none"
92
+
viewBox="0 0 24 24"
93
+
stroke-width="1.5"
94
+
stroke="currentColor"
95
+
class="size-4"
96
+
>
97
+
<path
98
+
stroke-linecap="round"
99
+
stroke-linejoin="round"
100
+
d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
101
+
/>
102
+
</svg>
103
+
{/if}
104
</div>
105
+
<div
106
+
class={[
107
+
'text-base-900 dark:text-base-50 text-lg font-bold',
108
+
(isMobile() && item.mobileH < 8) || (!isMobile() && item.h < 4) ? 'line-clamp-2' : ''
109
+
]}
110
+
>
111
+
{item.cardData.title}
112
+
</div>
113
+
<!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> -->
114
+
<div
115
+
class="text-accent-600 accent:text-accent-950 dark:text-accent-400 mt-2 text-xs font-semibold"
116
+
>
117
+
{item.cardData.domain}
118
+
</div>
119
+
</div>
120
+
121
+
{#if browser && ((isMobile() && item.mobileH >= 8) || (!isMobile() && item.h >= 4)) && item.cardData.image}
122
+
<img
123
+
class="mb-2 aspect-2/1 w-full rounded-xl object-cover opacity-100 transition-opacity duration-100 starting:opacity-0"
124
+
src={getImage(item.cardData, did)}
125
+
alt=""
126
+
/>
127
+
{/if}
128
+
{#if item.cardData.href && !isEditing}
129
+
<a
130
+
href={item.cardData.href}
131
+
class="absolute inset-0 h-full w-full"
132
+
target="_blank"
133
+
rel="noopener noreferrer"
134
+
use:qrOverlay={{
135
+
context: {
136
+
title: item.cardData.title
137
+
}
138
+
}}
139
+
>
140
+
<span class="sr-only">
141
+
{item.cardData.hrefText ?? 'Learn more'}
142
+
</span>
143
+
144
+
<div
145
+
class="bg-base-800/30 border-base-900/30 absolute top-2 right-2 rounded-full border p-1 text-white opacity-50 backdrop-blur-lg group-focus-within:opacity-100 group-hover:opacity-100"
146
+
>
147
+
<svg
148
+
xmlns="http://www.w3.org/2000/svg"
149
+
fill="none"
150
+
viewBox="0 0 24 24"
151
+
stroke-width="2.5"
152
+
stroke="currentColor"
153
+
class="size-4"
154
+
>
155
+
<path
156
+
stroke-linecap="round"
157
+
stroke-linejoin="round"
158
+
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"
159
+
/>
160
+
</svg>
161
+
</div>
162
+
</a>
163
+
{/if}
164
+
</div>
165
+
{/if}
+19
-1
src/lib/cards/core/LinkCard/LinkCardSettings.svelte
···
1
<script lang="ts">
2
import { validateLink } from '$lib/helper';
3
import type { Item } from '$lib/types';
4
-
import { Button, Input, toast } from '@foxui/core';
5
6
let { item, onclose }: { item: Item; onclose: () => void } = $props();
7
···
48
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
49
</svg>
50
</Button>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
<script lang="ts">
2
import { validateLink } from '$lib/helper';
3
import type { Item } from '$lib/types';
4
+
import { Button, Checkbox, Input, Label, toast } from '@foxui/core';
5
6
let { item, onclose }: { item: Item; onclose: () => void } = $props();
7
···
48
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
49
</svg>
50
</Button>
51
+
<div class="flex items-center space-x- mt-4">
52
+
<Checkbox
53
+
bind:checked={
54
+
() => Boolean(item.cardData.showBackgroundImage),
55
+
(val) => (item.cardData.showBackgroundImage = val)
56
+
}
57
+
id="show-bg-image"
58
+
aria-labelledby="show-bg-image-label"
59
+
variant="secondary"
60
+
/>
61
+
<Label
62
+
id="show-bg-image-label"
63
+
for="show-bg-image"
64
+
class="text-sm leading-none ml-2 font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
65
+
>
66
+
Show background image
67
+
</Label>
68
+
</div>
-4
src/lib/cards/social/SembleCollectionCard/index.ts
···
46
listRecords({ did, collection: 'network.cosmik.card', limit: 0 }).catch(() => [])
47
]);
48
49
-
console.log(allLinks);
50
-
51
if (!collection) return undefined;
52
53
const linkedCardUris = new Set(
···
55
.filter((link: any) => link.value.collection?.uri === collectionUri)
56
.map((link: any) => link.value.card?.uri)
57
);
58
-
59
-
console.log(linkedCardUris);
60
61
const cards: SembleCard[] = allCards
62
.filter((card: any) => linkedCardUris.has(card.uri))
···
46
listRecords({ did, collection: 'network.cosmik.card', limit: 0 }).catch(() => [])
47
]);
48
0
0
49
if (!collection) return undefined;
50
51
const linkedCardUris = new Set(
···
53
.filter((link: any) => link.value.collection?.uri === collectionUri)
54
.map((link: any) => link.value.card?.uri)
55
);
0
0
56
57
const cards: SembleCard[] = allCards
58
.filter((card: any) => linkedCardUris.has(card.uri))