b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 1 | <script lang="ts"> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 2 | import { messageMediaUrl } from './api'; |
445bf199a9cf | atagen | 2026-10-25 02:56 UTC | 3 | import EmojiText from './EmojiText.svelte'; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 4 | import type { LinkPreview } from './types'; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 5 | import type { UrlPreview } from './urls'; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 6 | |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 7 | export let link: UrlPreview; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 8 | export let preview: LinkPreview | null = null; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 9 | export let imageUrl: (mediaId: string) => string = (mediaId) => messageMediaUrl(mediaId, 'Thumbnail'); |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 10 | |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 11 | const BANNER_MIN_WIDTH = 320; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 12 | const BANNER_MIN_ASPECT = 1.3; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 13 | |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 14 | let imageFailed = false; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 15 | |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 16 | $: image = preview?.image && !imageFailed ? preview.image : null; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 17 | $: imageSrc = image ? imageUrl(image.media_id) : null; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 18 | $: banner = |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 19 | image !== null && |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 20 | image.width !== null && |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 21 | image.height !== null && |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 22 | image.width >= BANNER_MIN_WIDTH && |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 23 | image.width / image.height >= BANNER_MIN_ASPECT; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 24 | $: rich = Boolean(preview && (preview.title || preview.description)); |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 25 | $: if (preview) imageFailed = false; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 26 | </script> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 27 | |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 28 | <a class="link-card" class:rich class:banner href={link.href} target="_blank" rel="noopener noreferrer"> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 29 | {#if rich && preview} |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 30 | <span class="body"> |
445bf199a9cf | atagen | 2026-10-25 02:56 UTC | 31 | <span class="link-host"><EmojiText text={preview.site_name ?? link.hostname} /></span> |
445bf199a9cf | atagen | 2026-10-25 02:56 UTC | 32 | {#if preview.title}<span class="link-title"><EmojiText text={preview.title} /></span>{/if} |
445bf199a9cf | atagen | 2026-10-25 02:56 UTC | 33 | {#if preview.description}<span class="link-desc"><EmojiText text={preview.description} /></span>{/if} |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 34 | </span> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 35 | {:else} |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 36 | <span class="body"> |
445bf199a9cf | atagen | 2026-10-25 02:56 UTC | 37 | <span class="link-host"><EmojiText text={link.hostname} /></span> |
445bf199a9cf | atagen | 2026-10-25 02:56 UTC | 38 | {#if link.path}<span class="link-path"><EmojiText text={link.path} /></span>{/if} |
445bf199a9cf | atagen | 2026-10-25 02:56 UTC | 39 | <span class="link-url"><EmojiText text={link.display} /></span> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 40 | </span> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 41 | {/if} |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 42 | {#if imageSrc} |
85334efe0e3a | atagen | 2026-10-27 05:55 UTC | 43 | <img class="link-image" src={imageSrc} alt="" width={image?.width ?? undefined} height={image?.height ?? undefined} loading="lazy" on:error={() => (imageFailed = true)} /> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 44 | {/if} |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 45 | </a> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 46 | |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 47 | <style> |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 48 | .link-card { |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 49 | display: flex; gap: 10px; |
05a48e9e7942 | atagen | 2026-10-24 09:32 UTC | 50 | border: 1px solid var(--border); border-radius: var(--r-md); |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 51 | background: var(--bg-soft); padding: 8px 10px; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 52 | text-decoration: none; color: var(--text); |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 53 | overflow: hidden; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 54 | } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 55 | .link-card.rich { border-left: 3px solid var(--accent); } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 56 | .link-card.banner { flex-direction: column; } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 57 | .link-card:hover { border-color: var(--accent); } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 58 | .body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 59 | .link-host { font-size: 12.5px; font-weight: 650; } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 60 | .rich .link-host { font-size: 11.5px; font-weight: 600; color: var(--muted); } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 61 | .link-title { font-size: 14px; font-weight: 650; color: var(--accent); overflow-wrap: anywhere; } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 62 | .link-desc { |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 63 | font-size: 12.5px; color: var(--muted); overflow-wrap: anywhere; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 64 | display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 65 | } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 66 | .link-path { font-size: 12px; color: var(--muted); overflow-wrap: anywhere; } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 67 | .link-url { font-size: 11.5px; color: var(--accent); overflow-wrap: anywhere; } |
05a48e9e7942 | atagen | 2026-10-24 09:32 UTC | 68 | .link-image { flex: none; width: 72px; height: 72px; object-fit: cover; border-radius: var(--r-sm); } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 69 | .banner .link-image { width: 100%; height: auto; max-height: 220px; } |
b8ab00377c20 | atagen | 2026-10-22 05:13 UTC | 70 | </style> |