2025-11-23 23:27:57 +00:00
|
|
|
<script lang="ts">
|
|
|
|
|
import { LinkPreview } from 'bits-ui';
|
2025-11-28 07:44:36 +00:00
|
|
|
import { decodeString } from '$lib/utils';
|
2025-11-23 23:27:57 +00:00
|
|
|
import Source from './Source.svelte';
|
|
|
|
|
|
|
|
|
|
export let id;
|
|
|
|
|
export let token;
|
|
|
|
|
export let sourceIds = [];
|
|
|
|
|
export let onClick: Function = () => {};
|
|
|
|
|
|
|
|
|
|
let containerElement;
|
2025-11-23 23:31:59 +00:00
|
|
|
let openPreview = false;
|
2025-11-23 23:27:57 +00:00
|
|
|
|
|
|
|
|
// Helper function to return only the domain from a URL
|
|
|
|
|
function getDomain(url: string): string {
|
|
|
|
|
const domain = url.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
|
|
|
|
|
|
|
|
|
|
if (domain.startsWith('www.')) {
|
|
|
|
|
return domain.slice(4);
|
|
|
|
|
}
|
|
|
|
|
return domain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper function to check if text is a URL and return the domain
|
|
|
|
|
function formattedTitle(title: string): string {
|
|
|
|
|
if (title.startsWith('http')) {
|
|
|
|
|
return getDomain(title);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return title;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getDisplayTitle = (title: string) => {
|
|
|
|
|
if (!title) return 'N/A';
|
|
|
|
|
if (title.length > 30) {
|
|
|
|
|
return title.slice(0, 15) + '...' + title.slice(-10);
|
|
|
|
|
}
|
|
|
|
|
return title;
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
{#if (token?.ids ?? []).length == 1}
|
|
|
|
|
<Source id={token.ids[0] - 1} title={sourceIds[token.ids[0] - 1]} {onClick} />
|
|
|
|
|
{:else}
|
2025-11-23 23:31:59 +00:00
|
|
|
<LinkPreview.Root openDelay={0} bind:open={openPreview}>
|
2025-11-23 23:27:57 +00:00
|
|
|
<LinkPreview.Trigger>
|
|
|
|
|
<button
|
|
|
|
|
class="text-[10px] w-fit translate-y-[2px] px-2 py-0.5 dark:bg-white/5 dark:text-white/80 dark:hover:text-white bg-gray-50 text-black/80 hover:text-black transition rounded-xl"
|
2025-11-23 23:31:59 +00:00
|
|
|
on:click={() => {
|
|
|
|
|
openPreview = !openPreview;
|
|
|
|
|
}}
|
2025-11-23 23:27:57 +00:00
|
|
|
>
|
|
|
|
|
<span class="line-clamp-1">
|
2025-11-28 07:44:36 +00:00
|
|
|
{getDisplayTitle(formattedTitle(decodeString(sourceIds[token.ids[0] - 1])))}
|
2025-11-23 23:27:57 +00:00
|
|
|
<span class="dark:text-white/50 text-black/50">+{(token?.ids ?? []).length - 1}</span>
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
</LinkPreview.Trigger>
|
|
|
|
|
<LinkPreview.Content
|
|
|
|
|
class="z-[999]"
|
|
|
|
|
align="start"
|
|
|
|
|
strategy="fixed"
|
|
|
|
|
sideOffset={6}
|
|
|
|
|
el={containerElement}
|
|
|
|
|
>
|
|
|
|
|
<div class="bg-gray-50 dark:bg-gray-850 rounded-xl p-1 cursor-pointer">
|
|
|
|
|
{#each token.ids as sourceId}
|
|
|
|
|
<div class="">
|
|
|
|
|
<Source id={sourceId - 1} title={sourceIds[sourceId - 1]} {onClick} />
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</LinkPreview.Content>
|
|
|
|
|
</LinkPreview.Root>
|
|
|
|
|
{/if}
|