mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-06-04 13:30:32 +08:00
feat: Enhance image preview functionality in WikiBrowser component
- Updated the `picture-preview.vue` component to conditionally render images based on the `reviewUrl` prop, improving performance and usability. - Integrated image preview functionality into the `WikiBrowser` component, allowing users to view images in a modal when clicked. - Added references for `drawerBodyRef` and `readerBodyRef` to facilitate image loading and hydration, ensuring images are displayed correctly. - Enhanced the `prompts_wiki.go` file to include new image handling rules for summaries, ensuring images are contextually relevant in generated content. These changes improve the user experience by providing a seamless image viewing option within the WikiBrowser interface.
This commit is contained in:
@@ -8,11 +8,8 @@ const close = () => {
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<t-image-viewer :visible="reviewImg" closeOnOverlay closeOnEscKeydown @close="close"
|
||||
:images="[{
|
||||
mainImage: reviewUrl,
|
||||
download: false
|
||||
}]">
|
||||
<t-image-viewer :visible="reviewImg" closeOnOverlay closeOnEscKeydown @close="close"
|
||||
:images="reviewUrl ? [reviewUrl] : []">
|
||||
</t-image-viewer>
|
||||
</template>
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
:attach="false"
|
||||
:show-overlay="false"
|
||||
:close-btn="true"
|
||||
destroy-on-close
|
||||
class="wiki-graph-drawer"
|
||||
>
|
||||
<template v-if="graphDrawerPage">
|
||||
@@ -79,7 +80,7 @@
|
||||
</t-tag>
|
||||
<span class="wiki-reader-meta-text">{{ $t('knowledgeEditor.wikiBrowser.version', { ver: graphDrawerPage.version }) }}</span>
|
||||
</div>
|
||||
<div class="wiki-reader-body" v-html="graphDrawerContent" @click="handleGraphDrawerClick"></div>
|
||||
<div ref="drawerBodyRef" class="wiki-reader-body" v-html="graphDrawerContent" @click="handleGraphDrawerClick"></div>
|
||||
</template>
|
||||
</t-drawer>
|
||||
</div>
|
||||
@@ -206,7 +207,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="wiki-reader-body" v-html="renderedContent" @click="handleContentClick"></div>
|
||||
<div ref="readerBodyRef" class="wiki-reader-body" v-html="renderedContent" @click="handleContentClick"></div>
|
||||
|
||||
<!-- Source refs -->
|
||||
<div v-if="parsedSourceRefs.length" class="wiki-reader-sources">
|
||||
@@ -239,6 +240,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Image Preview -->
|
||||
<Teleport to="body">
|
||||
<picturePreview v-if="imagePreviewVisible" :reviewImg="imagePreviewVisible" :reviewUrl="imagePreviewUrl" @closePreImg="closeImagePreview" />
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -246,6 +252,8 @@
|
||||
import { ref, computed, onMounted, watch, nextTick, reactive } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { marked } from 'marked'
|
||||
import { hydrateProtectedFileImages } from '@/utils/security'
|
||||
import picturePreview from '@/components/picture-preview.vue'
|
||||
import {
|
||||
listWikiPages,
|
||||
getWikiPage,
|
||||
@@ -274,6 +282,8 @@ const graphData = ref<WikiGraphData | null>(null)
|
||||
const searchQuery = ref('')
|
||||
const graphSearchValue = ref('')
|
||||
const graphRef = ref<HTMLElement | null>(null)
|
||||
const readerBodyRef = ref<HTMLElement | null>(null)
|
||||
const drawerBodyRef = ref<HTMLElement | null>(null)
|
||||
const loading = ref(false)
|
||||
const graphLoading = ref(false)
|
||||
const graphReady = ref(false)
|
||||
@@ -340,6 +350,21 @@ const graphDrawerContent = computed(() => {
|
||||
return renderMarkdown(graphDrawerPage.value.content)
|
||||
})
|
||||
|
||||
const imagePreviewVisible = ref(false)
|
||||
const imagePreviewUrl = ref('')
|
||||
|
||||
function closeImagePreview() {
|
||||
imagePreviewVisible.value = false
|
||||
imagePreviewUrl.value = ''
|
||||
}
|
||||
|
||||
watch(graphDrawerContent, async () => {
|
||||
await nextTick()
|
||||
if (drawerBodyRef.value) {
|
||||
await hydrateProtectedFileImages(drawerBodyRef.value)
|
||||
}
|
||||
})
|
||||
|
||||
function renderMarkdown(content: string): string {
|
||||
// Pre-process wiki links [[slug|name]] to custom HTML tags
|
||||
let preprocessed = content.replace(/\[\[([^\]]+)\]\]/g, (_, inner: string) => {
|
||||
@@ -369,6 +394,12 @@ function handleGraphDrawerClick(e: MouseEvent) {
|
||||
e.preventDefault()
|
||||
const slug = target.getAttribute('data-slug')
|
||||
if (slug) handleGraphSearchSelect(slug)
|
||||
} else if (target.tagName.toLowerCase() === 'img') {
|
||||
e.preventDefault()
|
||||
imagePreviewUrl.value = target.getAttribute('src') || ''
|
||||
if (imagePreviewUrl.value) {
|
||||
imagePreviewVisible.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,12 +433,25 @@ const renderedContent = computed(() => {
|
||||
return renderMarkdown(selectedPage.value.content)
|
||||
})
|
||||
|
||||
watch(renderedContent, async () => {
|
||||
await nextTick()
|
||||
if (readerBodyRef.value) {
|
||||
await hydrateProtectedFileImages(readerBodyRef.value)
|
||||
}
|
||||
})
|
||||
|
||||
function handleContentClick(e: MouseEvent) {
|
||||
const target = e.target as HTMLElement
|
||||
if (target.classList.contains('wiki-content-link')) {
|
||||
e.preventDefault()
|
||||
const slug = target.getAttribute('data-slug')
|
||||
if (slug) navigateToSlug(slug)
|
||||
} else if (target.tagName.toLowerCase() === 'img') {
|
||||
e.preventDefault()
|
||||
imagePreviewUrl.value = target.getAttribute('src') || ''
|
||||
if (imagePreviewUrl.value) {
|
||||
imagePreviewVisible.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1234,7 +1278,15 @@ watch(searchQuery, (val) => {
|
||||
})
|
||||
|
||||
watch(() => props.view, (v) => {
|
||||
if (v === 'graph') loadGraph()
|
||||
if (v === 'graph') {
|
||||
loadGraph()
|
||||
} else if (v === 'browser') {
|
||||
nextTick(async () => {
|
||||
if (readerBodyRef.value && renderedContent.value) {
|
||||
await hydrateProtectedFileImages(readerBodyRef.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@@ -1563,6 +1615,29 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
:deep(p:has(img)) {
|
||||
text-align: center;
|
||||
color: var(--td-text-color-secondary);
|
||||
font-size: 13px;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
object-fit: contain;
|
||||
border-radius: 6px;
|
||||
display: block;
|
||||
margin: 0 auto 8px;
|
||||
cursor: zoom-in;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.wiki-content-link) {
|
||||
color: var(--td-brand-color);
|
||||
text-decoration: none;
|
||||
|
||||
@@ -26,9 +26,10 @@ const WikiSummaryPrompt = `You are a wiki editor. Given the following document c
|
||||
3. Include the key facts, arguments, and conclusions.
|
||||
4. Use proper heading hierarchy (## for sections, ### for subsections).
|
||||
5. **Wiki-link rule**: The available_wiki_pages list above maps slugs to display names (format: "[[slug]] = display name"). Whenever you mention a name that matches a listed entry, you MUST write it as [[slug|display name]] (e.g. [[entity/zhong-guo|中国]]), NOT as bold (**name**) or bare [[slug]]. Use the EXACT slugs provided — do NOT invent new slugs.
|
||||
6. At the end, include a "## Key Takeaways" section with bullet points.
|
||||
7. Write in {{.Language}}.
|
||||
8. Keep the summary concise but thorough (500-1500 words depending on document length).
|
||||
6. **Image rule**: If the document contains <images> tags with <image> elements, you SHOULD include the relevant images in your summary using the Markdown syntax: . Place the images where they are contextually relevant to the text.
|
||||
7. At the end, include a "## Key Takeaways" section with bullet points.
|
||||
8. Write in {{.Language}}.
|
||||
9. Keep the summary concise but thorough (500-1500 words depending on document length).
|
||||
</instructions>
|
||||
|
||||
Output the SUMMARY line first, then the Markdown content. Do not include any other preamble.`
|
||||
@@ -51,7 +52,7 @@ const WikiKnowledgeExtractPrompt = `You are a knowledge extraction system. Analy
|
||||
|
||||
<instructions>
|
||||
Return a JSON object with two arrays: "entities" and "concepts".
|
||||
**IMPORTANT: Write ALL names, descriptions, and details in {{.Language}}.**
|
||||
**IMPORTANT: Write ALL names, descriptions, and details in {{.Language}}**.
|
||||
|
||||
### Slug Continuity Rules
|
||||
If previous slugs are provided above, you MUST follow these rules:
|
||||
@@ -65,7 +66,7 @@ Each entity should have:
|
||||
- "name": The entity name in {{.Language}} (human-readable)
|
||||
- "slug": URL-friendly slug, format "entity/<lowercase-hyphenated-name>" (use romanized/pinyin form for non-Latin names). **Reuse previous slug if the entity was extracted before.**
|
||||
- "description": **Index listing summary** — one sentence, 15-40 words, in {{.Language}}. Describes WHAT this entity IS and its role in the document. Must be self-contained (understandable without reading the full page). This will be displayed in the wiki index.
|
||||
- "details": A 2-5 sentence summary in {{.Language}} of key facts from the document
|
||||
- "details": A 2-5 sentence summary in {{.Language}} of key facts from the document. **Image rule**: If the document contains relevant <image> elements in an <images> tag, include them in the details using Markdown syntax: .
|
||||
|
||||
Only include entities that are substantively discussed (mentioned at least twice or described in detail). Do NOT include generic terms.
|
||||
|
||||
@@ -74,7 +75,7 @@ Each concept should have:
|
||||
- "name": The concept name in {{.Language}} (human-readable)
|
||||
- "slug": URL-friendly slug, format "concept/<lowercase-hyphenated-name>" (use romanized/pinyin form for non-Latin names). **Reuse previous slug if the concept was extracted before.**
|
||||
- "description": **Index listing summary** — one sentence, 15-40 words, in {{.Language}}. Defines WHAT this concept IS. Must be self-contained (understandable without reading the full page). This will be displayed in the wiki index.
|
||||
- "details": A 2-5 sentence explanation in {{.Language}} as discussed in the document
|
||||
- "details": A 2-5 sentence explanation in {{.Language}} as discussed in the document. **Image rule**: If the document contains relevant <image> elements in an <images> tag, include them in the details using Markdown syntax: .
|
||||
|
||||
Only include concepts that are substantively discussed. Skip trivial or overly generic concepts.
|
||||
|
||||
@@ -127,7 +128,8 @@ const WikiPageUpdatePrompt = `You are a wiki editor tasked with updating an exis
|
||||
6. Preserve any existing [[slug|name]] wiki-link references in the content. Do NOT invent new wiki-link slugs.
|
||||
7. Maintain the existing page structure and formatting style.
|
||||
8. Add a source reference to the new document at the bottom.
|
||||
9. Write in {{.Language}}.
|
||||
9. **Image rule**: If the new document contains <images> tags with <image> elements, you SHOULD include the relevant images in your updated page using the Markdown syntax: . Place the images where they are contextually relevant to the text.
|
||||
10. Write in {{.Language}}.
|
||||
</instructions>
|
||||
|
||||
Output the SUMMARY line first, then the updated Markdown content. Do not include any other preamble.`
|
||||
|
||||
Reference in New Issue
Block a user