enh: renderMarkdownInPreviews
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
import { getContext, onMount, tick } from 'svelte';
|
import { getContext, onMount, tick } from 'svelte';
|
||||||
import Modal from '$lib/components/common/Modal.svelte';
|
import Modal from '$lib/components/common/Modal.svelte';
|
||||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||||
|
import Markdown from '$lib/components/chat/Messages/Markdown.svelte';
|
||||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||||
import { settings } from '$lib/stores';
|
import { settings } from '$lib/stores';
|
||||||
|
|
||||||
@@ -10,6 +11,9 @@
|
|||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
const CONTENT_PREVIEW_LIMIT = 10000;
|
||||||
|
let expandedDocs: Set<number> = new Set();
|
||||||
|
|
||||||
export let show = false;
|
export let show = false;
|
||||||
export let citation;
|
export let citation;
|
||||||
export let showPercentage = false;
|
export let showPercentage = false;
|
||||||
@@ -35,6 +39,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: if (citation) {
|
$: if (citation) {
|
||||||
|
expandedDocs = new Set();
|
||||||
mergedDocuments = citation.document?.map((c, i) => {
|
mergedDocuments = citation.document?.map((c, i) => {
|
||||||
return {
|
return {
|
||||||
source: citation.source,
|
source: citation.source,
|
||||||
@@ -206,20 +211,37 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if document.metadata?.html}
|
{#if document.metadata?.html}
|
||||||
<iframe
|
<iframe
|
||||||
class="w-full border-0 h-auto rounded-none"
|
class="w-full border-0 h-auto rounded-none"
|
||||||
sandbox="allow-scripts allow-forms{($settings?.iframeSandboxAllowSameOrigin ??
|
sandbox="allow-scripts allow-forms{($settings?.iframeSandboxAllowSameOrigin ??
|
||||||
false)
|
false)
|
||||||
? ' allow-same-origin'
|
? ' allow-same-origin'
|
||||||
: ''}"
|
: ''}"
|
||||||
srcdoc={document.document}
|
srcdoc={document.document}
|
||||||
title={$i18n.t('Content')}
|
title={$i18n.t('Content')}
|
||||||
></iframe>
|
></iframe>
|
||||||
{:else}
|
{:else}
|
||||||
<pre class="text-sm dark:text-gray-400 whitespace-pre-line">{document.document
|
{@const rawContent = document.document.trim().replace(/\n\n+/g, '\n\n')}
|
||||||
.trim()
|
{@const isTruncated = ($settings?.renderMarkdownInPreviews ?? true) && rawContent.length > CONTENT_PREVIEW_LIMIT && !expandedDocs.has(documentIdx)}
|
||||||
.replace(/\n\n+/g, '\n\n')}</pre>
|
{#if $settings?.renderMarkdownInPreviews ?? true}
|
||||||
|
<div class="text-sm prose dark:prose-invert max-w-full">
|
||||||
|
<Markdown content={isTruncated ? rawContent.slice(0, CONTENT_PREVIEW_LIMIT) : rawContent} id="citation-{documentIdx}" />
|
||||||
|
</div>
|
||||||
|
{#if isTruncated}
|
||||||
|
<button
|
||||||
|
class="mt-1 text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
|
||||||
|
on:click={() => {
|
||||||
|
expandedDocs.add(documentIdx);
|
||||||
|
expandedDocs = expandedDocs;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{$i18n.t('Show all ({{COUNT}} characters)', { COUNT: rawContent.length.toLocaleString() })}
|
||||||
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<pre class="text-sm dark:text-gray-400 whitespace-pre-line">{rawContent}</pre>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
let chatFadeStreamingText = true;
|
let chatFadeStreamingText = true;
|
||||||
let collapseCodeBlocks = false;
|
let collapseCodeBlocks = false;
|
||||||
let expandDetails = false;
|
let expandDetails = false;
|
||||||
|
let renderMarkdownInPreviews = true;
|
||||||
let showChatTitleInTab = true;
|
let showChatTitleInTab = true;
|
||||||
|
|
||||||
let showFloatingActionButtons = true;
|
let showFloatingActionButtons = true;
|
||||||
@@ -232,6 +233,7 @@
|
|||||||
|
|
||||||
collapseCodeBlocks = $settings?.collapseCodeBlocks ?? false;
|
collapseCodeBlocks = $settings?.collapseCodeBlocks ?? false;
|
||||||
expandDetails = $settings?.expandDetails ?? false;
|
expandDetails = $settings?.expandDetails ?? false;
|
||||||
|
renderMarkdownInPreviews = $settings?.renderMarkdownInPreviews ?? true;
|
||||||
|
|
||||||
landingPageMode = $settings?.landingPageMode ?? '';
|
landingPageMode = $settings?.landingPageMode ?? '';
|
||||||
chatBubble = $settings?.chatBubble ?? true;
|
chatBubble = $settings?.chatBubble ?? true;
|
||||||
@@ -964,6 +966,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class=" py-0.5 flex w-full justify-between">
|
||||||
|
<div id="render-markdown-in-previews-label" class=" self-center text-xs">
|
||||||
|
{$i18n.t('Render Markdown in Previews')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2 p-1">
|
||||||
|
<Switch
|
||||||
|
ariaLabelledbyId="render-markdown-in-previews-label"
|
||||||
|
tooltip={true}
|
||||||
|
bind:state={renderMarkdownInPreviews}
|
||||||
|
on:change={() => {
|
||||||
|
saveSettings({ renderMarkdownInPreviews });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class=" py-0.5 flex w-full justify-between">
|
<div class=" py-0.5 flex w-full justify-between">
|
||||||
<div id="keep-followup-prompts-label" class=" self-center text-xs">
|
<div id="keep-followup-prompts-label" class=" self-center text-xs">
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
import { formatFileSize, getLineCount } from '$lib/utils';
|
import { formatFileSize, getLineCount } from '$lib/utils';
|
||||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||||
|
import { settings } from '$lib/stores';
|
||||||
import { getKnowledgeById } from '$lib/apis/knowledge';
|
import { getKnowledgeById } from '$lib/apis/knowledge';
|
||||||
import { getFileById, getFileContentById } from '$lib/apis/files';
|
import { getFileById, getFileContentById } from '$lib/apis/files';
|
||||||
|
|
||||||
@@ -14,6 +15,9 @@
|
|||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
const CONTENT_PREVIEW_LIMIT = 10000;
|
||||||
|
let expandedContent = false;
|
||||||
|
|
||||||
import Modal from './Modal.svelte';
|
import Modal from './Modal.svelte';
|
||||||
import XMark from '../icons/XMark.svelte';
|
import XMark from '../icons/XMark.svelte';
|
||||||
import Switch from './Switch.svelte';
|
import Switch from './Switch.svelte';
|
||||||
@@ -147,6 +151,7 @@
|
|||||||
|
|
||||||
const loadContent = async () => {
|
const loadContent = async () => {
|
||||||
selectedTab = '';
|
selectedTab = '';
|
||||||
|
expandedContent = false;
|
||||||
if (item?.type === 'collection') {
|
if (item?.type === 'collection') {
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
@@ -365,13 +370,45 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else if selectedTab === ''}
|
{:else if selectedTab === ''}
|
||||||
{#if item?.file?.data}
|
{#if item?.file?.data}
|
||||||
<div class="max-h-96 overflow-scroll scrollbar-hidden text-xs whitespace-pre-wrap">
|
{@const rawContent = (item?.file?.data?.content ?? '').trim() || 'No content'}
|
||||||
{(item?.file?.data?.content ?? '').trim() || 'No content'}
|
{@const isTruncated = ($settings?.renderMarkdownInPreviews ?? true) && rawContent.length > CONTENT_PREVIEW_LIMIT && !expandedContent}
|
||||||
</div>
|
{#if $settings?.renderMarkdownInPreviews ?? true}
|
||||||
|
<div class="max-h-96 overflow-scroll scrollbar-hidden text-sm prose dark:prose-invert max-w-full">
|
||||||
|
<Markdown content={isTruncated ? rawContent.slice(0, CONTENT_PREVIEW_LIMIT) : rawContent} id="file-preview" />
|
||||||
|
</div>
|
||||||
|
{#if isTruncated}
|
||||||
|
<button
|
||||||
|
class="mt-1 text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
|
||||||
|
on:click={() => { expandedContent = true; }}
|
||||||
|
>
|
||||||
|
{$i18n.t('Show all ({{COUNT}} characters)', { COUNT: rawContent.length.toLocaleString() })}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<div class="max-h-96 overflow-scroll scrollbar-hidden text-xs whitespace-pre-wrap">
|
||||||
|
{rawContent}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{:else if item?.content}
|
{:else if item?.content}
|
||||||
<div class="max-h-96 overflow-scroll scrollbar-hidden text-xs whitespace-pre-wrap">
|
{@const rawContent = (item?.content ?? '').trim() || 'No content'}
|
||||||
{(item?.content ?? '').trim() || 'No content'}
|
{@const isTruncated = ($settings?.renderMarkdownInPreviews ?? true) && rawContent.length > CONTENT_PREVIEW_LIMIT && !expandedContent}
|
||||||
</div>
|
{#if $settings?.renderMarkdownInPreviews ?? true}
|
||||||
|
<div class="max-h-96 overflow-scroll scrollbar-hidden text-sm prose dark:prose-invert max-w-full">
|
||||||
|
<Markdown content={isTruncated ? rawContent.slice(0, CONTENT_PREVIEW_LIMIT) : rawContent} id="file-preview-content" />
|
||||||
|
</div>
|
||||||
|
{#if isTruncated}
|
||||||
|
<button
|
||||||
|
class="mt-1 text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
|
||||||
|
on:click={() => { expandedContent = true; }}
|
||||||
|
>
|
||||||
|
{$i18n.t('Show all ({{COUNT}} characters)', { COUNT: rawContent.length.toLocaleString() })}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<div class="max-h-96 overflow-scroll scrollbar-hidden text-xs whitespace-pre-wrap">
|
||||||
|
{rawContent}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{:else if selectedTab === 'preview'}
|
{:else if selectedTab === 'preview'}
|
||||||
{#if isAudio}
|
{#if isAudio}
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ type Settings = {
|
|||||||
splitLargeDeltas?: boolean;
|
splitLargeDeltas?: boolean;
|
||||||
chatDirection?: 'LTR' | 'RTL' | 'auto';
|
chatDirection?: 'LTR' | 'RTL' | 'auto';
|
||||||
ctrlEnterToSend?: boolean;
|
ctrlEnterToSend?: boolean;
|
||||||
|
renderMarkdownInPreviews?: boolean;
|
||||||
|
|
||||||
system?: string;
|
system?: string;
|
||||||
seed?: number;
|
seed?: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user