| 1 | --- |
| 2 | type Link = { |
| 3 | id: number; |
| 4 | short_id: string; |
| 5 | title: string; |
| 6 | url: string; |
| 7 | category_id: number; |
| 8 | created_at: number; |
| 9 | favicon_url?: string | null; |
| 10 | }; |
| 11 | |
| 12 | const { link } = Astro.props as { link: Link }; |
| 13 | const domain = new URL(link.url).hostname.replace(/^www\./, ""); |
| 14 | --- |
| 15 | |
| 16 | <article class="flex flex-col py-3 border-b border-[#333]"> |
| 17 | <a |
| 18 | href={link.url} |
| 19 | target="_blank" |
| 20 | rel="noopener noreferrer" |
| 21 | class="inline-flex items-start gap-2 text-base hover:opacity-70 transition-opacity" |
| 22 | > |
| 23 | {link.favicon_url && ( |
| 24 | <img |
| 25 | src={link.favicon_url} |
| 26 | alt={`Favicon for ${domain}`} |
| 27 | width="16" |
| 28 | height="16" |
| 29 | loading="lazy" |
| 30 | class="w-4 h-4 rounded-sm shrink-0 mt-1" |
| 31 | onerror="this.style.display='none';this.nextElementSibling.style.display='inline-block'" |
| 32 | /> |
| 33 | )} |
| 34 | <svg |
| 35 | xmlns="http://www.w3.org/2000/svg" |
| 36 | width="16" |
| 37 | height="16" |
| 38 | viewBox="0 0 256 256" |
| 39 | class="w-4 h-4 shrink-0 mt-1" |
| 40 | style={link.favicon_url ? "display:none" : ""} |
| 41 | aria-hidden="true" |
| 42 | > |
| 43 | <path |
| 44 | fill="currentColor" |
| 45 | d="M216 40H40a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h176a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16m0 16v32H40V56Zm0 144H40v-96h176z" |
| 46 | /> |
| 47 | </svg> |
| 48 | <span>{link.title}</span> |
| 49 | </a> |
| 50 | <span class="text-xs opacity-50 mt-0.5">{domain}</span> |
| 51 | </article> |