소스 검색

refac: citations display

Timothy Jaeryang Baek 1 개월 전
부모
커밋
d0b20df46c

+ 30 - 122
src/lib/components/chat/Messages/Citations.svelte

@@ -1,10 +1,6 @@
 <script lang="ts">
 	import { getContext } from 'svelte';
-	import CitationsModal from './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';
+	import CitationsModal from '$lib/components/chat/Messages/Citations/CitationsModal.svelte';
 
 	const i18n = getContext('i18n');
 
@@ -96,127 +92,39 @@
 		showRelevance = calculateShowRelevance(citations);
 		showPercentage = shouldShowPercentage(citations);
 	}
-
-	const decodeString = (str: string) => {
-		try {
-			return decodeURIComponent(str);
-		} catch (e) {
-			return str;
-		}
-	};
 </script>
 
-<CitationsModal
-	bind:show={showCitationModal}
-	citation={selectedCitation}
-	{showPercentage}
-	{showRelevance}
-/>
+<CitationsModal bind:show={showCitationModal} {id} {citations} {showPercentage} {showRelevance} />
 
 {#if citations.length > 0}
-	<div class=" py-0.5 -mx-0.5 w-full flex gap-1 items-center flex-wrap">
-		{#if citations.length <= 3}
-			<div class="flex text-xs font-medium flex-wrap">
-				{#each citations as citation, idx}
-					<button
-						id={`source-${id}-${idx + 1}`}
-						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;
-							selectedCitation = citation;
-						}}
-					>
-						{#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 text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white transition"
-						>
-							{decodeString(citation.source.name)}
-						</div>
-					</button>
-				{/each}
-			</div>
-		{:else}
-			<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>
+	{@const urlCitations = citations.filter((c) => c?.source?.name?.startsWith('http'))}
+	<div class=" py-1 -mx-0.5 w-full flex gap-1 items-center flex-wrap">
+		<button
+			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"
+			on:click={() => {
+				showCitationModal = true;
+			}}
+		>
+			{#if urlCitations.length > 0}
+				<div class="flex -space-x-1 items-center">
+					{#each urlCitations.slice(0, 3) as citation, idx}
+						<img
+							src="https://www.google.com/s2/favicons?sz=32&domain={citation.source.name}"
+							alt="favicon"
+							class="size-4 rounded-full shrink-0 border border-white dark:border-gray-850 bg-white dark:bg-gray-900"
+						/>
+					{/each}
 				</div>
-			</Collapsible>
-		{/if}
+			{/if}
+			<div>
+				{#if citations.length === 1}
+					{$i18n.t('1 Source')}
+				{:else}
+					{$i18n.t('{{COUNT}} Sources', {
+						COUNT: citations.length
+					})}
+				{/if}
+			</div>
+		</button>
 	</div>
 {/if}

+ 0 - 0
src/lib/components/chat/Messages/CitationsModal.svelte → src/lib/components/chat/Messages/Citations/CitationModal.svelte


+ 68 - 0
src/lib/components/chat/Messages/Citations/CitationsModal.svelte

@@ -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>

+ 13 - 3
src/lib/components/chat/Messages/Markdown/Source.svelte

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