94 lines
4.0 KiB
Vue
94 lines
4.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { marked } from 'marked'
|
|
import SplitPane from '@framework/components/SplitPane.vue'
|
|
import NativeViewer from '@/components/NativeViewer.vue'
|
|
import { store, humanBytes } from '@/store'
|
|
|
|
const d = computed(() => store.detail)
|
|
const node = computed(() => d.value?.node)
|
|
|
|
// Left pane (extracted search text) only exists for extracted heavy formats,
|
|
// and not for pptx (whose viewer already shows the per-slide text).
|
|
const hasExtracted = computed(() =>
|
|
node.value?.mode === 'extracted' && node.value.ext !== 'pptx' && d.value?.content != null)
|
|
|
|
const extractedHtml = computed(() => (d.value?.content ? marked.parse(d.value.content) : ''))
|
|
|
|
const metaRows = computed(() => {
|
|
const m = d.value?.meta as Record<string, any> | null
|
|
if (!m) return []
|
|
const out: Array<[string, string]> = []
|
|
const push = (k: string, v: unknown) => {
|
|
if (v == null || v === '') return
|
|
out.push([k, Array.isArray(v) ? v.join(', ') : typeof v === 'object' ? JSON.stringify(v) : String(v)])
|
|
}
|
|
push('title', m.title); push('author', m.author); push('created', m.created)
|
|
push('pages', m.pages); push('slides', m.slides); push('sheets', m.sheets)
|
|
push('words', m.word_count); push('extractor', m.extractor)
|
|
return out
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="!node" class="empty">Select a file to view it.</div>
|
|
<div v-else class="detail">
|
|
<!-- meetings get no selection header — the viewer is the meeting itself -->
|
|
<header v-if="node.mode !== 'meeting'" class="head">
|
|
<span class="badge" :data-mode="node.mode">{{ node.mode }}</span>
|
|
<span class="name">{{ node.path }}</span>
|
|
<span class="dim">{{ node.ext }} · {{ humanBytes(node.bytes) }}</span>
|
|
</header>
|
|
|
|
<!-- extracted: search text (left, secondary) | native original (right) -->
|
|
<SplitPane v-if="hasExtracted" class="body" :initial-size="1" :min="0.25" :max="3">
|
|
<template #first>
|
|
<div class="pane">
|
|
<div class="pane-label">Extracted text · search index (not the document)</div>
|
|
<div class="md" v-html="extractedHtml" />
|
|
</div>
|
|
</template>
|
|
<template #second>
|
|
<NativeViewer :node="node" :original-url="d!.originalUrl" :content="d?.content" :text="d?.text" />
|
|
</template>
|
|
</SplitPane>
|
|
|
|
<!-- link / pptx / meeting: single native viewer -->
|
|
<div v-else class="body single">
|
|
<NativeViewer :node="node" :original-url="d!.originalUrl" :content="d?.content" :text="d?.text" />
|
|
</div>
|
|
|
|
<footer v-if="metaRows.length" class="meta">
|
|
<span v-for="[k, v] in metaRows" :key="k" class="chip"><b>{{ k }}</b> {{ v }}</span>
|
|
</footer>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.empty { padding: var(--space-4); color: var(--text-secondary); }
|
|
.detail { display: flex; flex-direction: column; height: 100%; }
|
|
.head {
|
|
display: flex; align-items: center; gap: var(--space-3);
|
|
padding: var(--space-2) var(--space-3); border-bottom: var(--panel-border); flex-shrink: 0;
|
|
}
|
|
.head .name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
|
|
.head .dim, .dim { color: var(--text-secondary); font-size: var(--font-size-sm); }
|
|
.badge {
|
|
font-size: 10px; text-transform: uppercase; padding: 1px 5px; border-radius: 3px;
|
|
background: var(--surface-3); color: var(--text-secondary);
|
|
}
|
|
.badge[data-mode='extracted'] { background: var(--status-live, #2a6); color: #fff; }
|
|
.badge[data-mode='meeting'] { background: var(--status-processing, #a60); color: #fff; }
|
|
.body { flex: 1; min-height: 0; }
|
|
.body.single { overflow: hidden; }
|
|
.pane { height: 100%; display: flex; flex-direction: column; overflow: hidden; }
|
|
.pane-label {
|
|
font-size: 11px; color: var(--text-secondary); padding: var(--space-1) var(--space-3);
|
|
border-bottom: 1px solid var(--surface-2); flex-shrink: 0;
|
|
}
|
|
.md { padding: var(--space-3); overflow: auto; }
|
|
.meta { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-top: var(--panel-border); flex-shrink: 0; }
|
|
.chip { font-size: var(--font-size-sm); color: var(--text-secondary); }
|
|
.chip b { color: var(--text-primary); }
|
|
</style>
|