diff --git a/src/lib/components/chat/Messages/Citations/CitationModal.svelte b/src/lib/components/chat/Messages/Citations/CitationModal.svelte index 90cea7bb5..2dd4967a1 100644 --- a/src/lib/components/chat/Messages/Citations/CitationModal.svelte +++ b/src/lib/components/chat/Messages/Citations/CitationModal.svelte @@ -53,10 +53,40 @@ const decodeString = (str: string) => { try { return decodeURIComponent(str); - } catch (e) { + } catch { return str; } }; + + const getTextFragmentUrl = (doc: any): string | null => { + const { metadata, source, document: content } = doc ?? {}; + const { file_id, page } = metadata ?? {}; + const sourceUrl = source?.url; + + const baseUrl = file_id + ? `${WEBUI_API_BASE_URL}/files/${file_id}/content${page !== undefined ? `#page=${page + 1}` : ''}` + : sourceUrl?.includes('http') + ? sourceUrl + : null; + + if (!baseUrl || !content) return baseUrl; + + // Extract first and last words for text fragment, filtering out URLs and emojis + const words = content + .trim() + .replace(/\s+/g, ' ') + .split(' ') + .filter((w: string) => w.length > 0 && !/https?:\/\/|[\u{1F300}-\u{1F9FF}]/u.test(w)); + + if (words.length === 0) return baseUrl; + + const clean = (w: string) => w.replace(/[^\w]/g, ''); + const first = clean(words[0]); + const last = clean(words.at(-1)); + const fragment = words.length === 1 ? first : `${first},${last}`; + + return fragment ? `${baseUrl}#:~:text=${fragment}` : baseUrl; + }; @@ -124,7 +154,20 @@
- {$i18n.t('Content')} + {#if document.source?.url?.includes('http')} + {@const snippetUrl = getTextFragmentUrl(document)} + {#if snippetUrl} + {$i18n.t('Content')} + {:else} + {$i18n.t('Content')} + {/if} + {:else} + {$i18n.t('Content')} + {/if} {#if showRelevance && document.distance !== undefined} {document.document .trim() .replace(/\n\n+/g, '\n\n')} - {/if} + {/if}
{/each}