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
113
onchange={handleImageChange}
114
114
/>
115
115
116
116
-
<div class="relative flex h-full flex-col justify-between p-4">
117
117
-
<div
118
118
-
class={[
119
119
-
'accent:bg-accent-500/50 absolute inset-0 z-20 bg-white/50 dark:bg-black/50',
120
120
-
!hasFetched ? 'animate-pulse' : 'hidden'
121
121
-
]}
122
122
-
></div>
123
123
-
124
124
-
<div class={isFetchingMetadata ? 'pointer-events-none' : ''}>
125
125
-
<button
126
126
-
type="button"
127
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
128
-
onclick={() => faviconInputRef?.click()}
129
129
-
onmouseenter={() => (isHoveringFavicon = true)}
130
130
-
onmouseleave={() => (isHoveringFavicon = false)}
131
131
-
>
132
132
-
{#if hasFetched && item.cardData.favicon && !faviconHasError}
133
133
-
<img
134
134
-
class="size-6 rounded-lg object-cover"
135
135
-
onerror={() => (faviconHasError = true)}
136
136
-
src={getImage(item.cardData, did, 'favicon')}
137
137
-
alt=""
138
138
-
/>
139
139
-
{:else}
140
140
-
<svg
141
141
-
xmlns="http://www.w3.org/2000/svg"
142
142
-
fill="none"
143
143
-
viewBox="0 0 24 24"
144
144
-
stroke-width="1.5"
145
145
-
stroke="currentColor"
146
146
-
class="size-4"
147
147
-
>
148
148
-
<path
149
149
-
stroke-linecap="round"
150
150
-
stroke-linejoin="round"
151
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
152
-
/>
153
153
-
</svg>
154
154
-
{/if}
155
155
-
<!-- Hover overlay -->
156
156
-
<div
157
157
-
class={[
158
158
-
'absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 transition-opacity duration-200',
159
159
-
isHoveringFavicon ? 'opacity-100' : 'opacity-0'
160
160
-
]}
161
161
-
>
162
162
-
<svg
163
163
-
xmlns="http://www.w3.org/2000/svg"
164
164
-
fill="none"
165
165
-
viewBox="0 0 24 24"
166
166
-
stroke-width="2"
167
167
-
stroke="currentColor"
168
168
-
class="size-4 text-white"
169
169
-
>
170
170
-
<path
171
171
-
stroke-linecap="round"
172
172
-
stroke-linejoin="round"
173
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
174
-
/>
175
175
-
</svg>
176
176
-
</div>
177
177
-
</button>
178
178
-
116
116
+
{#if item.cardData.showBackgroundImage}
117
117
+
<div class="relative flex h-full flex-col justify-end p-4">
179
118
<div
180
119
class={[
181
181
-
'-m-1 rounded-md p-1 transition-colors duration-200',
182
182
-
hasFetched
183
183
-
? 'hover:bg-base-200/70 dark:hover:bg-base-800/70 accent:hover:bg-accent-200/30'
184
184
-
: ''
120
120
+
'accent:bg-accent-500/50 absolute inset-0 z-30 bg-white/50 dark:bg-black/50',
121
121
+
!hasFetched ? 'animate-pulse' : 'hidden'
185
122
]}
186
186
-
>
187
187
-
{#if hasFetched}
188
188
-
<PlainTextEditor
189
189
-
class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold"
190
190
-
key="title"
191
191
-
bind:contentDict={item.cardData}
192
192
-
placeholder="Title here"
193
193
-
/>
194
194
-
{:else}
195
195
-
<span class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold">
196
196
-
Loading data...
197
197
-
</span>
198
198
-
{/if}
199
199
-
</div>
200
200
-
<!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> -->
123
123
+
></div>
124
124
+
125
125
+
{#if item.cardData.image}
126
126
+
<img
127
127
+
class="absolute inset-0 -z-10 size-full object-cover"
128
128
+
src={getImage(item.cardData, did)}
129
129
+
alt=""
130
130
+
/>
131
131
+
{/if}
201
132
<div
202
202
-
class="text-accent-600 accent:text-accent-950 dark:text-accent-400 mt-2 text-xs font-semibold"
203
203
-
>
204
204
-
{item.cardData.domain}
205
205
-
</div>
206
206
-
</div>
133
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
134
+
></div>
207
135
208
208
-
{#if hasFetched && browser && ((isMobile() && item.mobileH >= 8) || (!isMobile() && item.h >= 4))}
136
136
+
<!-- Full card click to change image -->
209
137
<button
210
138
type="button"
211
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"
139
139
+
class="absolute inset-0 z-10 cursor-pointer"
212
140
onclick={() => imageInputRef?.click()}
213
141
onmouseenter={() => (isHoveringImage = true)}
214
142
onmouseleave={() => (isHoveringImage = false)}
215
143
>
216
216
-
{#if item.cardData.image}
217
217
-
<img
218
218
-
class="h-full w-full object-cover opacity-100 transition-opacity duration-100 starting:opacity-0"
219
219
-
src={getImage(item.cardData, did)}
220
220
-
alt=""
221
221
-
/>
222
222
-
{:else}
223
223
-
<div class="bg-base-200 dark:bg-base-800 flex h-full w-full items-center justify-center">
144
144
+
<div
145
145
+
class={[
146
146
+
'absolute inset-0 flex items-center justify-center bg-black/50 transition-opacity duration-200',
147
147
+
isHoveringImage ? 'opacity-100' : 'opacity-0'
148
148
+
]}
149
149
+
>
150
150
+
<div class="text-center text-sm text-white">
224
151
<svg
225
152
xmlns="http://www.w3.org/2000/svg"
226
153
fill="none"
227
154
viewBox="0 0 24 24"
228
155
stroke-width="1.5"
229
156
stroke="currentColor"
230
230
-
class="text-base-400 dark:text-base-600 size-8"
157
157
+
class="mx-auto mb-1 size-6"
231
158
>
232
159
<path
233
160
stroke-linecap="round"
234
161
stroke-linejoin="round"
235
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"
162
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
163
+
/>
164
164
+
<path
165
165
+
stroke-linecap="round"
166
166
+
stroke-linejoin="round"
167
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"
236
168
/>
237
169
</svg>
170
170
+
<span class="font-medium">{item.cardData.image ? 'Change image' : 'Add image'}</span>
238
171
</div>
239
239
-
{/if}
240
240
-
<!-- Hover overlay -->
172
172
+
</div>
173
173
+
</button>
174
174
+
175
175
+
<!-- Domain and title at the bottom, above the image button -->
176
176
+
<div class="relative z-20">
177
177
+
<div class="text-accent-600 dark:text-accent-400 text-xs font-semibold">
178
178
+
{item.cardData.domain}
179
179
+
</div>
241
180
<div
242
181
class={[
243
243
-
'absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 transition-opacity duration-200',
244
244
-
isHoveringImage ? 'opacity-100' : 'opacity-0'
182
182
+
'-m-1 rounded-md p-1 transition-colors duration-200',
183
183
+
hasFetched ? 'hover:bg-base-200/70 dark:hover:bg-base-800/70' : ''
245
184
]}
246
185
>
247
247
-
<div class="text-center text-sm text-white">
186
186
+
{#if hasFetched}
187
187
+
<PlainTextEditor
188
188
+
class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold"
189
189
+
key="title"
190
190
+
bind:contentDict={item.cardData}
191
191
+
placeholder="Title here"
192
192
+
/>
193
193
+
{:else}
194
194
+
<span class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold">
195
195
+
Loading data...
196
196
+
</span>
197
197
+
{/if}
198
198
+
</div>
199
199
+
</div>
200
200
+
</div>
201
201
+
{:else}
202
202
+
<div class="relative flex h-full flex-col justify-between p-4">
203
203
+
<div
204
204
+
class={[
205
205
+
'accent:bg-accent-500/50 absolute inset-0 z-20 bg-white/50 dark:bg-black/50',
206
206
+
!hasFetched ? 'animate-pulse' : 'hidden'
207
207
+
]}
208
208
+
></div>
209
209
+
210
210
+
<div class={isFetchingMetadata ? 'pointer-events-none' : ''}>
211
211
+
<button
212
212
+
type="button"
213
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
214
+
onclick={() => faviconInputRef?.click()}
215
215
+
onmouseenter={() => (isHoveringFavicon = true)}
216
216
+
onmouseleave={() => (isHoveringFavicon = false)}
217
217
+
>
218
218
+
{#if hasFetched && item.cardData.favicon && !faviconHasError}
219
219
+
<img
220
220
+
class="size-6 rounded-lg object-cover"
221
221
+
onerror={() => (faviconHasError = true)}
222
222
+
src={getImage(item.cardData, did, 'favicon')}
223
223
+
alt=""
224
224
+
/>
225
225
+
{:else}
248
226
<svg
249
227
xmlns="http://www.w3.org/2000/svg"
250
228
fill="none"
251
229
viewBox="0 0 24 24"
252
230
stroke-width="1.5"
253
231
stroke="currentColor"
254
254
-
class="mx-auto mb-1 size-6"
232
232
+
class="size-4"
255
233
>
256
234
<path
257
235
stroke-linecap="round"
258
236
stroke-linejoin="round"
259
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"
237
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"
260
238
/>
239
239
+
</svg>
240
240
+
{/if}
241
241
+
<!-- Hover overlay -->
242
242
+
<div
243
243
+
class={[
244
244
+
'absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 transition-opacity duration-200',
245
245
+
isHoveringFavicon ? 'opacity-100' : 'opacity-0'
246
246
+
]}
247
247
+
>
248
248
+
<svg
249
249
+
xmlns="http://www.w3.org/2000/svg"
250
250
+
fill="none"
251
251
+
viewBox="0 0 24 24"
252
252
+
stroke-width="2"
253
253
+
stroke="currentColor"
254
254
+
class="size-4 text-white"
255
255
+
>
261
256
<path
262
257
stroke-linecap="round"
263
258
stroke-linejoin="round"
264
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"
259
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"
265
260
/>
266
261
</svg>
267
267
-
<span class="font-medium">{item.cardData.image ? 'Change' : 'Add image'}</span>
268
262
</div>
263
263
+
</button>
264
264
+
265
265
+
<div
266
266
+
class={[
267
267
+
'-m-1 rounded-md p-1 transition-colors duration-200',
268
268
+
hasFetched
269
269
+
? 'hover:bg-base-200/70 dark:hover:bg-base-800/70 accent:hover:bg-accent-200/30'
270
270
+
: ''
271
271
+
]}
272
272
+
>
273
273
+
{#if hasFetched}
274
274
+
<PlainTextEditor
275
275
+
class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold"
276
276
+
key="title"
277
277
+
bind:contentDict={item.cardData}
278
278
+
placeholder="Title here"
279
279
+
/>
280
280
+
{:else}
281
281
+
<span class="text-base-900 dark:text-base-50 line-clamp-2 text-lg font-bold">
282
282
+
Loading data...
283
283
+
</span>
284
284
+
{/if}
269
285
</div>
270
270
-
</button>
271
271
-
{/if}
272
272
-
</div>
286
286
+
<!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> -->
287
287
+
<div
288
288
+
class="text-accent-600 accent:text-accent-950 dark:text-accent-400 mt-2 text-xs font-semibold"
289
289
+
>
290
290
+
{item.cardData.domain}
291
291
+
</div>
292
292
+
</div>
293
293
+
294
294
+
{#if hasFetched && browser && ((isMobile() && item.mobileH >= 8) || (!isMobile() && item.h >= 4))}
295
295
+
<button
296
296
+
type="button"
297
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
298
+
onclick={() => imageInputRef?.click()}
299
299
+
onmouseenter={() => (isHoveringImage = true)}
300
300
+
onmouseleave={() => (isHoveringImage = false)}
301
301
+
>
302
302
+
{#if item.cardData.image}
303
303
+
<img
304
304
+
class="h-full w-full object-cover opacity-100 transition-opacity duration-100 starting:opacity-0"
305
305
+
src={getImage(item.cardData, did)}
306
306
+
alt=""
307
307
+
/>
308
308
+
{:else}
309
309
+
<div class="bg-base-200 dark:bg-base-800 flex h-full w-full items-center justify-center">
310
310
+
<svg
311
311
+
xmlns="http://www.w3.org/2000/svg"
312
312
+
fill="none"
313
313
+
viewBox="0 0 24 24"
314
314
+
stroke-width="1.5"
315
315
+
stroke="currentColor"
316
316
+
class="text-base-400 dark:text-base-600 size-8"
317
317
+
>
318
318
+
<path
319
319
+
stroke-linecap="round"
320
320
+
stroke-linejoin="round"
321
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
322
+
/>
323
323
+
</svg>
324
324
+
</div>
325
325
+
{/if}
326
326
+
<!-- Hover overlay -->
327
327
+
<div
328
328
+
class={[
329
329
+
'absolute inset-0 flex items-center justify-center rounded-xl bg-black/50 transition-opacity duration-200',
330
330
+
isHoveringImage ? 'opacity-100' : 'opacity-0'
331
331
+
]}
332
332
+
>
333
333
+
<div class="text-center text-sm text-white">
334
334
+
<svg
335
335
+
xmlns="http://www.w3.org/2000/svg"
336
336
+
fill="none"
337
337
+
viewBox="0 0 24 24"
338
338
+
stroke-width="1.5"
339
339
+
stroke="currentColor"
340
340
+
class="mx-auto mb-1 size-6"
341
341
+
>
342
342
+
<path
343
343
+
stroke-linecap="round"
344
344
+
stroke-linejoin="round"
345
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
346
+
/>
347
347
+
<path
348
348
+
stroke-linecap="round"
349
349
+
stroke-linejoin="round"
350
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
351
+
/>
352
352
+
</svg>
353
353
+
<span class="font-medium">{item.cardData.image ? 'Change' : 'Add image'}</span>
354
354
+
</div>
355
355
+
</div>
356
356
+
</button>
357
357
+
{/if}
358
358
+
</div>
359
359
+
{/if}
+134
-74
src/lib/cards/core/LinkCard/LinkCard.svelte
···
14
14
let did = getDidContext();
15
15
</script>
16
16
17
17
-
<div class="flex h-full flex-col justify-between p-4">
18
18
-
<div>
17
17
+
{#if item.cardData.showBackgroundImage && item.cardData.image}
18
18
+
<div class="relative flex h-full flex-col justify-end p-4">
19
19
+
<img
20
20
+
class="absolute inset-0 -z-10 size-full object-cover"
21
21
+
src={getImage(item.cardData, did)}
22
22
+
alt=""
23
23
+
/>
19
24
<div
20
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
21
-
>
22
22
-
{#if item.cardData.favicon && !faviconHasError}
23
23
-
<img
24
24
-
class="size-6 rounded-lg object-cover"
25
25
-
onerror={() => (faviconHasError = true)}
26
26
-
src={getImage(item.cardData, did, 'favicon')}
27
27
-
alt=""
28
28
-
/>
29
29
-
{:else}
30
30
-
<svg
31
31
-
xmlns="http://www.w3.org/2000/svg"
32
32
-
fill="none"
33
33
-
viewBox="0 0 24 24"
34
34
-
stroke-width="1.5"
35
35
-
stroke="currentColor"
36
36
-
class="size-4"
37
37
-
>
38
38
-
<path
39
39
-
stroke-linecap="round"
40
40
-
stroke-linejoin="round"
41
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
42
-
/>
43
43
-
</svg>
44
44
-
{/if}
25
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
26
+
></div>
27
27
+
<div class="text-accent-600 dark:text-accent-400 text-xs font-semibold">
28
28
+
{item.cardData.domain}
45
29
</div>
46
30
<div
47
31
class={[
···
51
35
>
52
36
{item.cardData.title}
53
37
</div>
54
54
-
<!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> -->
55
55
-
<div
56
56
-
class="text-accent-600 accent:text-accent-950 dark:text-accent-400 mt-2 text-xs font-semibold"
57
57
-
>
58
58
-
{item.cardData.domain}
59
59
-
</div>
60
60
-
</div>
61
61
-
62
62
-
{#if browser && ((isMobile() && item.mobileH >= 8) || (!isMobile() && item.h >= 4)) && item.cardData.image}
63
63
-
<img
64
64
-
class="mb-2 aspect-2/1 w-full rounded-xl object-cover opacity-100 transition-opacity duration-100 starting:opacity-0"
65
65
-
src={getImage(item.cardData, did)}
66
66
-
alt=""
67
67
-
/>
68
68
-
{/if}
69
69
-
{#if item.cardData.href && !isEditing}
70
70
-
<a
71
71
-
href={item.cardData.href}
72
72
-
class="absolute inset-0 h-full w-full"
73
73
-
target="_blank"
74
74
-
rel="noopener noreferrer"
75
75
-
use:qrOverlay={{
76
76
-
context: {
77
77
-
title: item.cardData.title
78
78
-
}
79
79
-
}}
80
80
-
>
81
81
-
<span class="sr-only">
82
82
-
{item.cardData.hrefText ?? 'Learn more'}
83
83
-
</span>
38
38
+
{#if item.cardData.href && !isEditing}
39
39
+
<a
40
40
+
href={item.cardData.href}
41
41
+
class="absolute inset-0 h-full w-full"
42
42
+
target="_blank"
43
43
+
rel="noopener noreferrer"
44
44
+
use:qrOverlay={{
45
45
+
context: {
46
46
+
title: item.cardData.title
47
47
+
}
48
48
+
}}
49
49
+
>
50
50
+
<span class="sr-only">
51
51
+
{item.cardData.hrefText ?? 'Learn more'}
52
52
+
</span>
84
53
54
54
+
<div
55
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
56
+
>
57
57
+
<svg
58
58
+
xmlns="http://www.w3.org/2000/svg"
59
59
+
fill="none"
60
60
+
viewBox="0 0 24 24"
61
61
+
stroke-width="2.5"
62
62
+
stroke="currentColor"
63
63
+
class="size-4"
64
64
+
>
65
65
+
<path
66
66
+
stroke-linecap="round"
67
67
+
stroke-linejoin="round"
68
68
+
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"
69
69
+
/>
70
70
+
</svg>
71
71
+
</div>
72
72
+
</a>
73
73
+
{/if}
74
74
+
</div>
75
75
+
{:else}
76
76
+
<div class="flex h-full flex-col justify-between p-4">
77
77
+
<div>
85
78
<div
86
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"
79
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"
87
80
>
88
88
-
<svg
89
89
-
xmlns="http://www.w3.org/2000/svg"
90
90
-
fill="none"
91
91
-
viewBox="0 0 24 24"
92
92
-
stroke-width="2.5"
93
93
-
stroke="currentColor"
94
94
-
class="size-4"
95
95
-
>
96
96
-
<path
97
97
-
stroke-linecap="round"
98
98
-
stroke-linejoin="round"
99
99
-
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"
81
81
+
{#if item.cardData.favicon && !faviconHasError}
82
82
+
<img
83
83
+
class="size-6 rounded-lg object-cover"
84
84
+
onerror={() => (faviconHasError = true)}
85
85
+
src={getImage(item.cardData, did, 'favicon')}
86
86
+
alt=""
100
87
/>
101
101
-
</svg>
88
88
+
{:else}
89
89
+
<svg
90
90
+
xmlns="http://www.w3.org/2000/svg"
91
91
+
fill="none"
92
92
+
viewBox="0 0 24 24"
93
93
+
stroke-width="1.5"
94
94
+
stroke="currentColor"
95
95
+
class="size-4"
96
96
+
>
97
97
+
<path
98
98
+
stroke-linecap="round"
99
99
+
stroke-linejoin="round"
100
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
101
+
/>
102
102
+
</svg>
103
103
+
{/if}
102
104
</div>
103
103
-
</a>
104
104
-
{/if}
105
105
-
</div>
105
105
+
<div
106
106
+
class={[
107
107
+
'text-base-900 dark:text-base-50 text-lg font-bold',
108
108
+
(isMobile() && item.mobileH < 8) || (!isMobile() && item.h < 4) ? 'line-clamp-2' : ''
109
109
+
]}
110
110
+
>
111
111
+
{item.cardData.title}
112
112
+
</div>
113
113
+
<!-- <div class="text-base-800 dark:text-base-100 mt-2 text-xs">{item.cardData.description}</div> -->
114
114
+
<div
115
115
+
class="text-accent-600 accent:text-accent-950 dark:text-accent-400 mt-2 text-xs font-semibold"
116
116
+
>
117
117
+
{item.cardData.domain}
118
118
+
</div>
119
119
+
</div>
120
120
+
121
121
+
{#if browser && ((isMobile() && item.mobileH >= 8) || (!isMobile() && item.h >= 4)) && item.cardData.image}
122
122
+
<img
123
123
+
class="mb-2 aspect-2/1 w-full rounded-xl object-cover opacity-100 transition-opacity duration-100 starting:opacity-0"
124
124
+
src={getImage(item.cardData, did)}
125
125
+
alt=""
126
126
+
/>
127
127
+
{/if}
128
128
+
{#if item.cardData.href && !isEditing}
129
129
+
<a
130
130
+
href={item.cardData.href}
131
131
+
class="absolute inset-0 h-full w-full"
132
132
+
target="_blank"
133
133
+
rel="noopener noreferrer"
134
134
+
use:qrOverlay={{
135
135
+
context: {
136
136
+
title: item.cardData.title
137
137
+
}
138
138
+
}}
139
139
+
>
140
140
+
<span class="sr-only">
141
141
+
{item.cardData.hrefText ?? 'Learn more'}
142
142
+
</span>
143
143
+
144
144
+
<div
145
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
146
+
>
147
147
+
<svg
148
148
+
xmlns="http://www.w3.org/2000/svg"
149
149
+
fill="none"
150
150
+
viewBox="0 0 24 24"
151
151
+
stroke-width="2.5"
152
152
+
stroke="currentColor"
153
153
+
class="size-4"
154
154
+
>
155
155
+
<path
156
156
+
stroke-linecap="round"
157
157
+
stroke-linejoin="round"
158
158
+
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"
159
159
+
/>
160
160
+
</svg>
161
161
+
</div>
162
162
+
</a>
163
163
+
{/if}
164
164
+
</div>
165
165
+
{/if}
+19
-1
src/lib/cards/core/LinkCard/LinkCardSettings.svelte
···
1
1
<script lang="ts">
2
2
import { validateLink } from '$lib/helper';
3
3
import type { Item } from '$lib/types';
4
4
-
import { Button, Input, toast } from '@foxui/core';
4
4
+
import { Button, Checkbox, Input, Label, toast } from '@foxui/core';
5
5
6
6
let { item, onclose }: { item: Item; onclose: () => void } = $props();
7
7
···
48
48
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
49
49
</svg>
50
50
</Button>
51
51
+
<div class="flex items-center space-x- mt-4">
52
52
+
<Checkbox
53
53
+
bind:checked={
54
54
+
() => Boolean(item.cardData.showBackgroundImage),
55
55
+
(val) => (item.cardData.showBackgroundImage = val)
56
56
+
}
57
57
+
id="show-bg-image"
58
58
+
aria-labelledby="show-bg-image-label"
59
59
+
variant="secondary"
60
60
+
/>
61
61
+
<Label
62
62
+
id="show-bg-image-label"
63
63
+
for="show-bg-image"
64
64
+
class="text-sm leading-none ml-2 font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
65
65
+
>
66
66
+
Show background image
67
67
+
</Label>
68
68
+
</div>
-4
src/lib/cards/social/SembleCollectionCard/index.ts
···
46
46
listRecords({ did, collection: 'network.cosmik.card', limit: 0 }).catch(() => [])
47
47
]);
48
48
49
49
-
console.log(allLinks);
50
50
-
51
49
if (!collection) return undefined;
52
50
53
51
const linkedCardUris = new Set(
···
55
53
.filter((link: any) => link.value.collection?.uri === collectionUri)
56
54
.map((link: any) => link.value.card?.uri)
57
55
);
58
58
-
59
59
-
console.log(linkedCardUris);
60
56
61
57
const cards: SembleCard[] = allCards
62
58
.filter((card: any) => linkedCardUris.has(card.uri))