refac: citations display

This commit is contained in:
Timothy Jaeryang Baek 2025-09-09 17:27:38 +04:00
parent 0214c1e66c
commit d0b20df46c
4 changed files with 111 additions and 125 deletions

View file

@ -1,10 +1,6 @@
<script lang="ts"> <script lang="ts">
import { getContext } from 'svelte'; import { getContext } from 'svelte';
import CitationsModal from './CitationsModal.svelte'; import CitationsModal from '$lib/components/chat/Messages/Citations/CitationsModal.svelte';
import Collapsible from '$lib/components/common/Collapsible.svelte';
import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
import { mobile } from '$lib/stores';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@ -96,127 +92,39 @@
showRelevance = calculateShowRelevance(citations); showRelevance = calculateShowRelevance(citations);
showPercentage = shouldShowPercentage(citations); showPercentage = shouldShowPercentage(citations);
} }
const decodeString = (str: string) => {
try {
return decodeURIComponent(str);
} catch (e) {
return str;
}
};
</script> </script>
<CitationsModal <CitationsModal bind:show={showCitationModal} {id} {citations} {showPercentage} {showRelevance} />
bind:show={showCitationModal}
citation={selectedCitation}
{showPercentage}
{showRelevance}
/>
{#if citations.length > 0} {#if citations.length > 0}
<div class=" py-0.5 -mx-0.5 w-full flex gap-1 items-center flex-wrap"> {@const urlCitations = citations.filter((c) => c?.source?.name?.startsWith('http'))}
{#if citations.length <= 3} <div class=" py-1 -mx-0.5 w-full flex gap-1 items-center flex-wrap">
<div class="flex text-xs font-medium flex-wrap"> <button
{#each citations as citation, idx} class="text-xs font-medium text-gray-600 dark:text-gray-300 px-3.5 h-8 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition flex items-center gap-1 border border-gray-50 dark:border-gray-850"
<button on:click={() => {
id={`source-${id}-${idx + 1}`} showCitationModal = true;
class="no-toggle outline-hidden flex dark:text-gray-300 p-1 bg-white dark:bg-gray-900 rounded-xl max-w-96" }}
on:click={() => { >
showCitationModal = true; {#if urlCitations.length > 0}
selectedCitation = citation; <div class="flex -space-x-1 items-center">
}} {#each urlCitations.slice(0, 3) as citation, idx}
> <img
{#if citations.every((c) => c.distances !== undefined)} src="https://www.google.com/s2/favicons?sz=32&domain={citation.source.name}"
<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4"> alt="favicon"
{idx + 1} class="size-4 rounded-full shrink-0 border border-white dark:border-gray-850 bg-white dark:bg-gray-900"
</div> />
{/if} {/each}
<div </div>
class="flex-1 mx-1 truncate text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white transition" {/if}
> <div>
{decodeString(citation.source.name)} {#if citations.length === 1}
</div> {$i18n.t('1 Source')}
</button> {:else}
{/each} {$i18n.t('{{COUNT}} Sources', {
COUNT: citations.length
})}
{/if}
</div> </div>
{:else} </button>
<Collapsible
id={`collapsible-${id}`}
bind:open={isCollapsibleOpen}
className="w-full max-w-full "
buttonClassName="w-fit max-w-full"
>
<div
class="flex w-full overflow-auto items-center gap-2 text-gray-500 hover:text-gray-600 dark:hover:text-gray-400 transition cursor-pointer"
>
<div
class="flex-1 flex items-center gap-1 overflow-auto scrollbar-none w-full max-w-full"
>
<span class="whitespace-nowrap hidden sm:inline shrink-0"
>{$i18n.t('References from')}</span
>
<div class="flex items-center overflow-auto scrollbar-none w-full max-w-full flex-1">
<div class="flex text-xs font-medium items-center">
{#each citations.slice(0, $mobile ? 1 : 2) as citation, idx}
<button
class="no-toggle outline-hidden flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96"
on:click={() => {
showCitationModal = true;
selectedCitation = citation;
}}
on:pointerup={(e) => {
e.stopPropagation();
}}
>
{#if citations.every((c) => c.distances !== undefined)}
<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4">
{idx + 1}
</div>
{/if}
<div class="flex-1 mx-1 truncate">
{decodeString(citation.source.name)}
</div>
</button>
{/each}
</div>
</div>
<div class="flex items-center gap-1 whitespace-nowrap shrink-0">
<span class="hidden sm:inline">{$i18n.t('and')}</span>
{citations.length - ($mobile ? 1 : 2)}
<span>{$i18n.t('more')}</span>
</div>
</div>
<div class="shrink-0">
{#if isCollapsibleOpen}
<ChevronUp strokeWidth="3.5" className="size-3.5" />
{:else}
<ChevronDown strokeWidth="3.5" className="size-3.5" />
{/if}
</div>
</div>
<div slot="content">
<div class="flex text-xs font-medium flex-wrap">
{#each citations.slice($mobile ? 1 : 2) as citation, idx}
<button
class="no-toggle outline-hidden flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96"
on:click={() => {
showCitationModal = true;
selectedCitation = citation;
}}
>
{#if citations.every((c) => c.distances !== undefined)}
<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4">
{idx + 3}
</div>
{/if}
<div class="flex-1 mx-1 truncate">
{decodeString(citation.source.name)}
</div>
</button>
{/each}
</div>
</div>
</Collapsible>
{/if}
</div> </div>
{/if} {/if}

View file

@ -0,0 +1,68 @@
<script lang="ts">
import { getContext, onMount, tick } from 'svelte';
const i18n = getContext('i18n');
import Modal from '$lib/components/common/Modal.svelte';
import XMark from '$lib/components/icons/XMark.svelte';
export let id = '';
export let show = false;
export let citations = [];
export let showPercentage = false;
export let showRelevance = true;
const decodeString = (str: string) => {
try {
return decodeURIComponent(str);
} catch (e) {
return str;
}
};
</script>
<Modal size="lg" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center capitalize">
{$i18n.t('Citations')}
</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<XMark className={'size-5'} />
</button>
</div>
<div class="flex flex-col md:flex-row w-full px-6 pb-5 md:space-x-4">
<div
class="flex flex-col w-full dark:text-gray-200 overflow-y-scroll max-h-[22rem] scrollbar-hidden text-left text-sm gap-2"
>
{#each citations as citation, idx}
<button
id={`source-${id}-${idx + 1}`}
class="no-toggle outline-hidden flex dark:text-gray-300 bg-white dark:bg-gray-900 rounded-xl gap-2 items-center"
on:click={() => {
showCitationModal = true;
selectedCitation = citation;
}}
>
{#if citations.every((c) => c.distances !== undefined)}
<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-5">
{idx + 1}
</div>
{/if}
<div
class="flex-1 truncate text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white transition text-left"
>
{decodeString(citation.source.name)}
</div>
</button>
{/each}
</div>
</div>
</div>
</Modal>

View file

@ -37,6 +37,14 @@
return 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;
};
$: attributes = extractAttributes(token.text); $: attributes = extractAttributes(token.text);
</script> </script>
@ -48,9 +56,11 @@
}} }}
> >
<span class="line-clamp-1"> <span class="line-clamp-1">
{decodeURIComponent(attributes.title) {getDisplayTitle(
? formattedTitle(decodeURIComponent(attributes.title)) decodeURIComponent(attributes.title)
: ''} ? formattedTitle(decodeURIComponent(attributes.title))
: ''
)}
</span> </span>
</button> </button>
{/if} {/if}