atagen

malice

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