enh: summary tag support

This commit is contained in:
Timothy J. Baek 2024-09-30 12:50:53 +02:00
parent d255251e5f
commit ffd598c5d7
3 changed files with 82 additions and 2 deletions

View File

@ -1,9 +1,11 @@
<script>
import { marked } from 'marked';
import markedKatex from '$lib/utils/marked/katex-extension';
import { replaceTokens, processResponseContent } from '$lib/utils';
import { user } from '$lib/stores';
import markedExtension from '$lib/utils/marked/extension';
import markedKatexExtension from '$lib/utils/marked/katex-extension';
import MarkdownTokens from './Markdown/MarkdownTokens.svelte';
export let id;
@ -16,7 +18,8 @@
throwOnError: false
};
marked.use(markedKatex(options));
marked.use(markedKatexExtension(options));
marked.use(markedExtension(options));
$: (async () => {
if (content) {

View File

@ -8,6 +8,7 @@
import MarkdownInlineTokens from '$lib/components/chat/Messages/Markdown/MarkdownInlineTokens.svelte';
import KatexRenderer from './KatexRenderer.svelte';
import { WEBUI_BASE_URL } from '$lib/constants';
import { stringify } from 'postcss';
export let id: string;
export let tokens: Token[];
@ -94,6 +95,12 @@
{/each}
</ul>
{/if}
{:else if token.type === 'details'}
<details>
<summary>{token.summary}</summary>
<svelte:self id={`${id}-${tokenIdx}-d`} tokens={marked.lexer(token.text)} />
</details>
{:else if token.type === 'html'}
{@const html = DOMPurify.sanitize(token.text)}
{#if html && html.includes('<video')}

View File

@ -0,0 +1,70 @@
// Helper function to find matching closing tag
function findMatchingClosingTag(src, openTag, closeTag) {
let depth = 1;
let index = openTag.length;
while (depth > 0 && index < src.length) {
if (src.startsWith(openTag, index)) {
depth++;
} else if (src.startsWith(closeTag, index)) {
depth--;
}
if (depth > 0) {
index++;
}
}
return depth === 0 ? index + closeTag.length : -1;
}
function detailsTokenizer(src) {
const detailsRegex = /^<details>\n/;
const summaryRegex = /^<summary>(.*?)<\/summary>\n/;
if (detailsRegex.test(src)) {
const endIndex = findMatchingClosingTag(src, '<details>', '</details>');
if (endIndex === -1) return;
const fullMatch = src.slice(0, endIndex);
let content = fullMatch.slice(10, -10).trim(); // Remove <details> and </details>
let summary = '';
const summaryMatch = summaryRegex.exec(content);
if (summaryMatch) {
summary = summaryMatch[1].trim();
content = content.slice(summaryMatch[0].length).trim();
}
return {
type: 'details',
raw: fullMatch,
summary: summary,
text: content
};
}
}
function detailsStart(src) {
return src.match(/^<details>/) ? 0 : -1;
}
function detailsRenderer(token) {
return `<details>
${token.summary ? `<summary>${token.summary}</summary>` : ''}
${token.text}
</details>`;
}
function detailsExtension() {
return {
name: 'details',
level: 'block',
start: detailsStart,
tokenizer: detailsTokenizer,
renderer: detailsRenderer
};
}
export default function (options = {}) {
return {
extensions: [detailsExtension(options)]
};
}