feat: improve chat retrieval, message versions and Markdown rendering
This commit is contained in:
@@ -103,6 +103,26 @@ function widthOf(svg: SVGSVGElement) {
|
||||
}
|
||||
async function interact(event: MouseEvent) {
|
||||
if (!(event.target instanceof Element)) return
|
||||
const codeButton = event.target.closest<HTMLButtonElement>('[data-code-action]')
|
||||
if (codeButton) {
|
||||
const block = codeButton.closest<HTMLElement>('.markdown-code-block, .markdown-mermaid')
|
||||
const source = block?.querySelector<HTMLElement>('.markdown-code-source')
|
||||
if (!block || !source) return
|
||||
event.preventDefault(); event.stopPropagation()
|
||||
if (codeButton.dataset.codeAction === 'copy') {
|
||||
try { await navigator.clipboard.writeText(source.textContent ?? ''); codeButton.textContent = '已复制' }
|
||||
catch { codeButton.textContent = '复制失败,请选择源码复制' }
|
||||
} else {
|
||||
disarm()
|
||||
source.hidden = !source.hidden
|
||||
const svg = block.querySelector<SVGSVGElement>(':scope > svg')
|
||||
if (svg) svg.style.display = source.hidden ? '' : 'none'
|
||||
block.dataset.sourceView = String(!source.hidden)
|
||||
codeButton.setAttribute('aria-pressed', String(!source.hidden))
|
||||
codeButton.textContent = source.hidden ? '查看源码' : '查看预览'
|
||||
}
|
||||
return
|
||||
}
|
||||
const button = event.target.closest<HTMLElement>('[data-diagram-action]')
|
||||
const diagram = button?.closest<HTMLElement>('.editor-mermaid-preview, .markdown-mermaid')
|
||||
const svg = diagram?.querySelector<SVGSVGElement>('svg')
|
||||
@@ -167,6 +187,12 @@ function close() { disarm(); viewer.value?.close(); svgHtml.value = ''; opener?.
|
||||
|
||||
<style>
|
||||
.diagram-interactions { min-width: 0; }
|
||||
.markdown-code-toolbar { display: flex; align-items: center; gap: var(--space-sm); padding: var(--space-sm); color: var(--color-code-muted); font: 12px/1.4 var(--font-editor-mono); }
|
||||
.markdown-code-toolbar > span { margin-right: auto; }
|
||||
.markdown-code-toolbar button { font: inherit; }
|
||||
.markdown-code-source { text-align: left; white-space: pre; overflow: auto; padding: var(--space-md); background: var(--color-code-background); color: var(--color-code-text); font-family: var(--font-editor-mono); }
|
||||
.markdown-code-source[hidden] { display: none !important; }
|
||||
.markdown-mermaid[data-source-view='true'] > .diagram-controls { display: none; }
|
||||
.diagram-controls { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
|
||||
.diagram-controls button { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border: 1px solid var(--color-border-default); border-radius: var(--radius-sm); color: var(--color-text-primary); background: var(--color-surface-primary); cursor: pointer; font: inherit; font-size: 12px; }
|
||||
.diagram-controls button:hover { border-color: var(--color-accent-primary); }
|
||||
|
||||
@@ -8,7 +8,13 @@ const headingAppearance = useHeadingAppearanceStore()
|
||||
import { useMarkdownPreferencesStore } from '@/stores/markdownPreferences'
|
||||
const markdownPreferences = useMarkdownPreferencesStore()
|
||||
|
||||
const props = defineProps<{ source: string }>()
|
||||
const props = defineProps<{ source: string; citationNumbers?: number[]; citationAliases?: Record<string, number> }>()
|
||||
const emit = defineEmits<{ citation: [number: number] }>()
|
||||
function citationClick(event: MouseEvent) {
|
||||
if (!(event.target instanceof Element)) return
|
||||
const number = Number(event.target.closest('[data-citation-number]')?.getAttribute('data-citation-number'))
|
||||
if (props.citationNumbers?.includes(number)) { event.preventDefault(); emit('citation', number) }
|
||||
}
|
||||
const themeStore = useThemeStore()
|
||||
const html = ref('')
|
||||
let renderVersion = 0
|
||||
@@ -16,19 +22,21 @@ let renderVersion = 0
|
||||
const diagramTheme = computed<'light' | 'dark'>(() => (themeStore.isDark ? 'dark' : 'light'))
|
||||
|
||||
// 主题切换需要重渲染:Mermaid SVG 的配色在渲染时烘焙,无法靠 CSS 变量事后调整。
|
||||
watch([() => props.source, diagramTheme, () => themeStore.currentThemeId, () => JSON.stringify(markdownPreferences.normalized)], async ([source, theme]) => {
|
||||
watch([() => props.source, diagramTheme, () => themeStore.currentThemeId, () => JSON.stringify(markdownPreferences.normalized), () => JSON.stringify([props.citationNumbers, props.citationAliases])], async ([source, theme]) => {
|
||||
const version = ++renderVersion
|
||||
const result = await renderMarkdown(source, { theme, preferences: markdownPreferences.normalized })
|
||||
const result = await renderMarkdown(source, { theme, preferences: markdownPreferences.normalized, citationNumbers: props.citationNumbers, citationAliases: props.citationAliases })
|
||||
if (version === renderVersion) html.value = result
|
||||
}, { immediate: true, flush: 'post' })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DiagramInteractions :data-heading-style="headingAppearance.preferences.custom ? 'custom' : undefined" :style="headingAppearance.cssVariables"><div class="markdown-content" :data-code-wrap="markdownPreferences.normalized.wrapCode" :data-line-numbers="markdownPreferences.normalized.lineNumbers" :style="{ '--markdown-code-indent': markdownPreferences.normalized.indent }" v-html="html" /></DiagramInteractions>
|
||||
<DiagramInteractions :data-heading-style="headingAppearance.preferences.custom ? 'custom' : undefined" :style="headingAppearance.cssVariables"><div class="markdown-content" @click="citationClick" :data-code-wrap="markdownPreferences.normalized.wrapCode" :data-line-numbers="markdownPreferences.normalized.lineNumbers" :style="{ '--markdown-code-indent': markdownPreferences.normalized.indent }" v-html="html" /></DiagramInteractions>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.markdown-content { white-space: normal; user-select: text; }
|
||||
.inline-citation { display: inline; padding: 0 .15em; border: 0; background: var(--color-accent-soft); color: var(--color-text-link); border-radius: var(--radius-sm); cursor: pointer; font: inherit; }
|
||||
.inline-citation:focus-visible { outline: 2px solid var(--color-border-focus); }
|
||||
.markdown-content p, .markdown-content ul, .markdown-content ol, .markdown-content pre, .markdown-content blockquote { margin: .65em 0; }
|
||||
.markdown-content h1, .markdown-content h2, .markdown-content h3 { margin: 1em 0 .5em; line-height: var(--line-height-tight); }
|
||||
.markdown-content ul { padding-left: 1.5em; list-style: disc; }
|
||||
@@ -36,6 +44,7 @@ watch([() => props.source, diagramTheme, () => themeStore.currentThemeId, () =>
|
||||
.markdown-content li::marker { color: var(--color-markdown-marker); font-weight: 700; }
|
||||
.markdown-content .shiki { overflow: auto; margin: .85em 0; padding: 16px; border: 1px solid var(--color-code-border); border-radius: 6px; background: var(--color-code-background) !important; color: var(--color-code-text); font-family: var(--font-ui-mono); font-size: .875em; line-height: 1.45; tab-size: 4; }
|
||||
.markdown-content code { padding: .1em .3em; border-radius: var(--radius-sm); background: var(--color-background-tertiary); font-family: var(--font-ui-mono); }
|
||||
.markdown-content .shiki { font-family: var(--font-editor-mono); font-size: var(--font-editor-size); line-height: var(--font-editor-line-height); }
|
||||
.markdown-content :not(pre) > code { background: var(--color-code-background); color: var(--color-code-text); border: 1px solid var(--color-code-border); }
|
||||
.markdown-content div.markdown-math { overflow-x: auto; padding-block: .5em; }
|
||||
.markdown-content h4, .markdown-content h5, .markdown-content h6 { margin: 1em 0 .5em; font-weight: 600; }
|
||||
|
||||
Reference in New Issue
Block a user