728 lines
15 KiB
Vue
728 lines
15 KiB
Vue
|
|
<template>
|
||
|
|
<article class="knowledge-ingest-panel panel">
|
||
|
|
<header class="ingest-head">
|
||
|
|
<div>
|
||
|
|
<span class="eyebrow">LightRAG 知识归集</span>
|
||
|
|
<h3>{{ model.folder || '未指定目录' }}</h3>
|
||
|
|
<p>{{ model.phaseLabel }} · {{ model.statusLabel }}</p>
|
||
|
|
</div>
|
||
|
|
<div class="progress-ring" :aria-label="`归集进度 ${model.progress.percent}%`">
|
||
|
|
<strong>{{ model.progress.percent }}%</strong>
|
||
|
|
<span>进度</span>
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<div class="progress-bar" aria-hidden="true">
|
||
|
|
<span :style="{ width: `${model.progress.percent}%` }"></span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="metric-strip">
|
||
|
|
<div v-for="metric in model.metrics" :key="metric.label" class="metric-tile">
|
||
|
|
<span>{{ metric.label }}</span>
|
||
|
|
<strong>{{ metric.value }}</strong>
|
||
|
|
<small>{{ metric.hint }}</small>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="ingest-workspace">
|
||
|
|
<aside class="file-rail">
|
||
|
|
<button
|
||
|
|
v-for="document in model.documents"
|
||
|
|
:key="document.documentId"
|
||
|
|
type="button"
|
||
|
|
class="file-item"
|
||
|
|
:class="{ active: selectedDocumentId === document.documentId }"
|
||
|
|
@click="selectDocument(document.documentId)"
|
||
|
|
>
|
||
|
|
<i :class="documentIcon(document)"></i>
|
||
|
|
<span class="file-copy">
|
||
|
|
<strong>{{ document.name }}</strong>
|
||
|
|
<small>
|
||
|
|
{{ document.phaseLabel }} · {{ document.chunkCount }} chunk
|
||
|
|
</small>
|
||
|
|
</span>
|
||
|
|
<span class="mini-status" :class="document.statusTone">
|
||
|
|
{{ document.statusLabel }}
|
||
|
|
</span>
|
||
|
|
</button>
|
||
|
|
</aside>
|
||
|
|
|
||
|
|
<section v-if="selectedDocument" class="file-detail">
|
||
|
|
<div class="detail-topline">
|
||
|
|
<div>
|
||
|
|
<h4>{{ selectedDocument.name }}</h4>
|
||
|
|
<p>
|
||
|
|
{{ selectedDocument.folder || '根目录' }}
|
||
|
|
<span v-if="selectedDocument.extension"> · {{ selectedDocument.extension }}</span>
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<span class="status-chip" :class="selectedDocument.statusTone">
|
||
|
|
{{ selectedDocument.statusLabel }}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="detail-stats">
|
||
|
|
<div>
|
||
|
|
<span>原文字符</span>
|
||
|
|
<strong>{{ formatKnowledgeMetric(selectedDocument.textChars) }}</strong>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<span>索引字符</span>
|
||
|
|
<strong>{{ formatKnowledgeMetric(selectedDocument.indexedTextChars) }}</strong>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<span>Chunk</span>
|
||
|
|
<strong>{{ formatKnowledgeMetric(selectedDocument.chunkCount) }}</strong>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<span>实体 / 关系</span>
|
||
|
|
<strong>
|
||
|
|
{{ formatKnowledgeMetric(selectedDocument.entityCount) }}
|
||
|
|
/
|
||
|
|
{{ formatKnowledgeMetric(selectedDocument.relationCount) }}
|
||
|
|
</strong>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<p v-if="selectedDocument.error" class="error-note">
|
||
|
|
{{ selectedDocument.error }}
|
||
|
|
</p>
|
||
|
|
|
||
|
|
<div class="detail-section-grid">
|
||
|
|
<section class="detail-section">
|
||
|
|
<div class="section-head">
|
||
|
|
<h5>Chunk 信息</h5>
|
||
|
|
<span>{{ selectedDocument.chunks.length }} 条</span>
|
||
|
|
</div>
|
||
|
|
<div v-if="selectedDocument.chunks.length" class="chunk-list">
|
||
|
|
<div v-for="chunk in selectedDocument.chunks" :key="chunk.id" class="chunk-row">
|
||
|
|
<span class="chunk-index">#{{ chunk.order + 1 }}</span>
|
||
|
|
<div>
|
||
|
|
<strong>{{ compactId(chunk.id) }}</strong>
|
||
|
|
<p>{{ chunk.summary || '暂无摘要' }}</p>
|
||
|
|
</div>
|
||
|
|
<small>{{ chunk.tokens }} tokens</small>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div v-else class="compact-empty">暂无 chunk 明细</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section class="detail-section">
|
||
|
|
<div class="section-head">
|
||
|
|
<h5>章节提取</h5>
|
||
|
|
<span>{{ selectedDocument.sectionCount }} 条</span>
|
||
|
|
</div>
|
||
|
|
<div v-if="selectedDocument.sections.length" class="section-list">
|
||
|
|
<div
|
||
|
|
v-for="section in selectedDocument.sections"
|
||
|
|
:key="section.title"
|
||
|
|
class="section-row"
|
||
|
|
>
|
||
|
|
<strong>{{ section.title }}</strong>
|
||
|
|
<p>{{ section.excerpt || '暂无章节摘要' }}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div v-else class="compact-empty">暂无章节信息</div>
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<section class="detail-section">
|
||
|
|
<div class="section-head">
|
||
|
|
<h5>处理事件</h5>
|
||
|
|
<span>{{ selectedDocument.events.length }} 条</span>
|
||
|
|
</div>
|
||
|
|
<div v-if="selectedDocument.events.length" class="event-list">
|
||
|
|
<div
|
||
|
|
v-for="event in selectedDocument.events"
|
||
|
|
:key="`${event.at}-${event.message}`"
|
||
|
|
class="event-row"
|
||
|
|
:class="event.level"
|
||
|
|
>
|
||
|
|
<span></span>
|
||
|
|
<div>
|
||
|
|
<strong>{{ formatEventTime(event.at) }}</strong>
|
||
|
|
<p>{{ event.message }}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div v-else class="compact-empty">暂无处理事件</div>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<section class="graph-section">
|
||
|
|
<div class="section-head">
|
||
|
|
<h4>图谱形成</h4>
|
||
|
|
<span>
|
||
|
|
{{ formatKnowledgeMetric(model.graph.entityCount) }} 实体 ·
|
||
|
|
{{ formatKnowledgeMetric(model.graph.relationCount) }} 关系
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div class="graph-grid">
|
||
|
|
<div class="graph-pane">
|
||
|
|
<h5>实体</h5>
|
||
|
|
<div v-if="model.graph.entities.length" class="entity-cloud">
|
||
|
|
<span v-for="entity in model.graph.entities" :key="entity">{{ entity }}</span>
|
||
|
|
</div>
|
||
|
|
<div v-else class="compact-empty">暂无实体</div>
|
||
|
|
</div>
|
||
|
|
<div class="graph-pane">
|
||
|
|
<h5>关系</h5>
|
||
|
|
<div v-if="model.graph.relations.length" class="relation-list">
|
||
|
|
<div
|
||
|
|
v-for="relation in model.graph.relations"
|
||
|
|
:key="`${relation.source}-${relation.target}-${relation.type}`"
|
||
|
|
class="relation-row"
|
||
|
|
>
|
||
|
|
<strong>{{ relation.source }}</strong>
|
||
|
|
<i class="mdi mdi-arrow-right-thin"></i>
|
||
|
|
<strong>{{ relation.target }}</strong>
|
||
|
|
<span>{{ relation.type }}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div v-else class="compact-empty">暂无关系</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
</article>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup>
|
||
|
|
import { computed, ref, watch } from 'vue'
|
||
|
|
|
||
|
|
import {
|
||
|
|
buildKnowledgeIngestLogModel,
|
||
|
|
formatKnowledgeMetric
|
||
|
|
} from '../../utils/knowledgeIngestLogModel.js'
|
||
|
|
|
||
|
|
const props = defineProps({
|
||
|
|
run: {
|
||
|
|
type: Object,
|
||
|
|
required: true
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
const selectedDocumentId = ref('')
|
||
|
|
const model = computed(() => buildKnowledgeIngestLogModel(props.run))
|
||
|
|
const selectedDocument = computed(
|
||
|
|
() => model.value.documents.find((item) => item.documentId === selectedDocumentId.value) || null
|
||
|
|
)
|
||
|
|
|
||
|
|
watch(
|
||
|
|
() => model.value.selectedDocumentId,
|
||
|
|
(nextDocumentId) => {
|
||
|
|
if (!nextDocumentId) {
|
||
|
|
selectedDocumentId.value = ''
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if (!selectedDocumentId.value || !model.value.documents.some((item) => item.documentId === selectedDocumentId.value)) {
|
||
|
|
selectedDocumentId.value = nextDocumentId
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{ immediate: true }
|
||
|
|
)
|
||
|
|
|
||
|
|
function selectDocument(documentId) {
|
||
|
|
selectedDocumentId.value = documentId
|
||
|
|
}
|
||
|
|
|
||
|
|
function documentIcon(document) {
|
||
|
|
const extension = String(document?.extension || '').toLowerCase()
|
||
|
|
if (extension === 'pdf') return 'mdi mdi-file-pdf-box'
|
||
|
|
if (['doc', 'docx'].includes(extension)) return 'mdi mdi-file-word-box'
|
||
|
|
if (['xls', 'xlsx', 'csv'].includes(extension)) return 'mdi mdi-file-excel-box'
|
||
|
|
if (['ppt', 'pptx'].includes(extension)) return 'mdi mdi-file-powerpoint-box'
|
||
|
|
return 'mdi mdi-file-document-outline'
|
||
|
|
}
|
||
|
|
|
||
|
|
function compactId(value) {
|
||
|
|
const text = String(value || '').trim()
|
||
|
|
if (text.length <= 18) return text || 'chunk'
|
||
|
|
return `${text.slice(0, 8)}...${text.slice(-6)}`
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatEventTime(value) {
|
||
|
|
if (!value) return '刚刚'
|
||
|
|
const date = new Date(value)
|
||
|
|
if (Number.isNaN(date.getTime())) return String(value)
|
||
|
|
return date.toLocaleTimeString('zh-CN', {
|
||
|
|
hour: '2-digit',
|
||
|
|
minute: '2-digit',
|
||
|
|
second: '2-digit',
|
||
|
|
hour12: false
|
||
|
|
})
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.knowledge-ingest-panel {
|
||
|
|
display: grid;
|
||
|
|
gap: 14px;
|
||
|
|
padding: 18px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ingest-head {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
gap: 18px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.eyebrow {
|
||
|
|
color: #0f766e;
|
||
|
|
font-size: 12px;
|
||
|
|
font-weight: 800;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ingest-head h3 {
|
||
|
|
margin: 5px 0 0;
|
||
|
|
color: #0f172a;
|
||
|
|
font-size: 18px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ingest-head p {
|
||
|
|
margin: 6px 0 0;
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 13px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progress-ring {
|
||
|
|
width: 72px;
|
||
|
|
height: 72px;
|
||
|
|
flex: 0 0 auto;
|
||
|
|
display: grid;
|
||
|
|
place-items: center;
|
||
|
|
border: 1px solid #d7e0ea;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: #f8fafc;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progress-ring strong,
|
||
|
|
.progress-ring span {
|
||
|
|
grid-area: 1 / 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progress-ring strong {
|
||
|
|
margin-top: -12px;
|
||
|
|
color: #0f172a;
|
||
|
|
font-size: 17px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progress-ring span {
|
||
|
|
margin-top: 26px;
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 11px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progress-bar {
|
||
|
|
height: 7px;
|
||
|
|
overflow: hidden;
|
||
|
|
border-radius: 999px;
|
||
|
|
background: #e5eaf0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progress-bar span {
|
||
|
|
display: block;
|
||
|
|
height: 100%;
|
||
|
|
border-radius: inherit;
|
||
|
|
background: linear-gradient(90deg, #0f766e, #2563eb);
|
||
|
|
transition: width 0.24s ease;
|
||
|
|
}
|
||
|
|
|
||
|
|
.metric-strip {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
|
|
gap: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.metric-tile {
|
||
|
|
min-width: 0;
|
||
|
|
display: grid;
|
||
|
|
gap: 4px;
|
||
|
|
padding: 11px 12px;
|
||
|
|
border: 1px solid #e5edf5;
|
||
|
|
border-radius: 8px;
|
||
|
|
background: #fff;
|
||
|
|
}
|
||
|
|
|
||
|
|
.metric-tile span,
|
||
|
|
.metric-tile small {
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.metric-tile strong {
|
||
|
|
color: #0f172a;
|
||
|
|
font-size: 18px;
|
||
|
|
line-height: 1.2;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ingest-workspace {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: minmax(230px, 0.85fr) minmax(0, 2fr);
|
||
|
|
gap: 14px;
|
||
|
|
min-height: 360px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-rail {
|
||
|
|
min-width: 0;
|
||
|
|
display: grid;
|
||
|
|
align-content: start;
|
||
|
|
gap: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-item {
|
||
|
|
width: 100%;
|
||
|
|
min-height: 58px;
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||
|
|
align-items: center;
|
||
|
|
gap: 10px;
|
||
|
|
padding: 10px;
|
||
|
|
border: 1px solid #e5edf5;
|
||
|
|
border-radius: 8px;
|
||
|
|
background: #fff;
|
||
|
|
text-align: left;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-item.active {
|
||
|
|
border-color: rgba(15, 118, 110, 0.38);
|
||
|
|
background: #f0fdfa;
|
||
|
|
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.08);
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-item > i {
|
||
|
|
color: #334155;
|
||
|
|
font-size: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-copy {
|
||
|
|
min-width: 0;
|
||
|
|
display: grid;
|
||
|
|
gap: 3px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-copy strong,
|
||
|
|
.file-copy small {
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
white-space: nowrap;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-copy strong {
|
||
|
|
color: #0f172a;
|
||
|
|
font-size: 13px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-copy small {
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.mini-status,
|
||
|
|
.status-chip {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
min-height: 24px;
|
||
|
|
padding: 0 8px;
|
||
|
|
border-radius: 999px;
|
||
|
|
font-size: 12px;
|
||
|
|
font-weight: 800;
|
||
|
|
white-space: nowrap;
|
||
|
|
}
|
||
|
|
|
||
|
|
.mini-status.success,
|
||
|
|
.status-chip.success {
|
||
|
|
background: #dcfce7;
|
||
|
|
color: #166534;
|
||
|
|
}
|
||
|
|
|
||
|
|
.mini-status.warning,
|
||
|
|
.status-chip.warning {
|
||
|
|
background: #fef3c7;
|
||
|
|
color: #92400e;
|
||
|
|
}
|
||
|
|
|
||
|
|
.mini-status.danger,
|
||
|
|
.status-chip.danger {
|
||
|
|
background: #fee2e2;
|
||
|
|
color: #991b1b;
|
||
|
|
}
|
||
|
|
|
||
|
|
.mini-status.muted,
|
||
|
|
.status-chip.muted {
|
||
|
|
background: #eef2f7;
|
||
|
|
color: #475569;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-detail {
|
||
|
|
min-width: 0;
|
||
|
|
display: grid;
|
||
|
|
align-content: start;
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-topline {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
gap: 14px;
|
||
|
|
padding-bottom: 10px;
|
||
|
|
border-bottom: 1px solid #e5edf5;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-topline h4 {
|
||
|
|
margin: 0;
|
||
|
|
color: #0f172a;
|
||
|
|
font-size: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-topline p {
|
||
|
|
margin: 5px 0 0;
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-stats {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
|
|
gap: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-stats div {
|
||
|
|
min-width: 0;
|
||
|
|
display: grid;
|
||
|
|
gap: 4px;
|
||
|
|
padding: 10px;
|
||
|
|
border-radius: 8px;
|
||
|
|
background: #f8fafc;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-stats span {
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-stats strong {
|
||
|
|
color: #0f172a;
|
||
|
|
font-size: 13px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.error-note {
|
||
|
|
margin: 0;
|
||
|
|
padding: 10px 12px;
|
||
|
|
border: 1px solid #fecaca;
|
||
|
|
border-radius: 8px;
|
||
|
|
background: #fff1f2;
|
||
|
|
color: #991b1b;
|
||
|
|
font-size: 13px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-section-grid,
|
||
|
|
.graph-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detail-section,
|
||
|
|
.graph-section,
|
||
|
|
.graph-pane {
|
||
|
|
min-width: 0;
|
||
|
|
display: grid;
|
||
|
|
gap: 10px;
|
||
|
|
align-content: start;
|
||
|
|
}
|
||
|
|
|
||
|
|
.section-head {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
gap: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.section-head h4,
|
||
|
|
.section-head h5,
|
||
|
|
.graph-pane h5 {
|
||
|
|
margin: 0;
|
||
|
|
color: #0f172a;
|
||
|
|
font-size: 14px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.section-head span {
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 12px;
|
||
|
|
font-weight: 700;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chunk-list,
|
||
|
|
.section-list,
|
||
|
|
.event-list,
|
||
|
|
.relation-list {
|
||
|
|
display: grid;
|
||
|
|
gap: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chunk-row,
|
||
|
|
.section-row,
|
||
|
|
.event-row,
|
||
|
|
.relation-row {
|
||
|
|
min-width: 0;
|
||
|
|
border: 1px solid #e5edf5;
|
||
|
|
border-radius: 8px;
|
||
|
|
background: #fff;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chunk-row {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||
|
|
gap: 10px;
|
||
|
|
padding: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chunk-index {
|
||
|
|
color: #2563eb;
|
||
|
|
font-size: 12px;
|
||
|
|
font-weight: 850;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chunk-row strong,
|
||
|
|
.section-row strong,
|
||
|
|
.event-row strong,
|
||
|
|
.relation-row strong {
|
||
|
|
color: #0f172a;
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chunk-row p,
|
||
|
|
.section-row p,
|
||
|
|
.event-row p {
|
||
|
|
margin: 4px 0 0;
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 12px;
|
||
|
|
line-height: 1.55;
|
||
|
|
}
|
||
|
|
|
||
|
|
.chunk-row small {
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 11px;
|
||
|
|
white-space: nowrap;
|
||
|
|
}
|
||
|
|
|
||
|
|
.section-row {
|
||
|
|
padding: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.event-row {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: auto minmax(0, 1fr);
|
||
|
|
gap: 10px;
|
||
|
|
padding: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.event-row > span {
|
||
|
|
width: 8px;
|
||
|
|
height: 8px;
|
||
|
|
margin-top: 5px;
|
||
|
|
border-radius: 999px;
|
||
|
|
background: #2563eb;
|
||
|
|
}
|
||
|
|
|
||
|
|
.event-row.error > span {
|
||
|
|
background: #dc2626;
|
||
|
|
}
|
||
|
|
|
||
|
|
.entity-cloud {
|
||
|
|
display: flex;
|
||
|
|
flex-wrap: wrap;
|
||
|
|
gap: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.entity-cloud span {
|
||
|
|
max-width: 100%;
|
||
|
|
padding: 5px 9px;
|
||
|
|
border: 1px solid #bfdbfe;
|
||
|
|
border-radius: 999px;
|
||
|
|
background: #eff6ff;
|
||
|
|
color: #1d4ed8;
|
||
|
|
font-size: 12px;
|
||
|
|
font-weight: 750;
|
||
|
|
}
|
||
|
|
|
||
|
|
.relation-row {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto;
|
||
|
|
align-items: center;
|
||
|
|
gap: 8px;
|
||
|
|
padding: 9px 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.relation-row strong {
|
||
|
|
overflow: hidden;
|
||
|
|
text-overflow: ellipsis;
|
||
|
|
white-space: nowrap;
|
||
|
|
}
|
||
|
|
|
||
|
|
.relation-row i {
|
||
|
|
color: #0f766e;
|
||
|
|
font-size: 18px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.relation-row span {
|
||
|
|
padding: 3px 7px;
|
||
|
|
border-radius: 999px;
|
||
|
|
background: #f1f5f9;
|
||
|
|
color: #475569;
|
||
|
|
font-size: 11px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.compact-empty {
|
||
|
|
min-height: 42px;
|
||
|
|
display: grid;
|
||
|
|
place-items: center;
|
||
|
|
border: 1px dashed #cbd5e1;
|
||
|
|
border-radius: 8px;
|
||
|
|
color: #64748b;
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (max-width: 980px) {
|
||
|
|
.metric-strip,
|
||
|
|
.detail-stats,
|
||
|
|
.detail-section-grid,
|
||
|
|
.graph-grid,
|
||
|
|
.ingest-workspace {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-rail {
|
||
|
|
max-height: 260px;
|
||
|
|
overflow: auto;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (max-width: 620px) {
|
||
|
|
.ingest-head,
|
||
|
|
.detail-topline {
|
||
|
|
flex-direction: column;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progress-ring {
|
||
|
|
width: 64px;
|
||
|
|
height: 64px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.file-item {
|
||
|
|
grid-template-columns: auto minmax(0, 1fr);
|
||
|
|
}
|
||
|
|
|
||
|
|
.mini-status {
|
||
|
|
grid-column: 2;
|
||
|
|
justify-self: start;
|
||
|
|
}
|
||
|
|
|
||
|
|
.relation-row {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|