1337 lines
30 KiB
Vue
1337 lines
30 KiB
Vue
|
|
<script setup lang="ts">
|
|||
|
|
import {
|
|||
|
|
ArrowLeft,
|
|||
|
|
Database,
|
|||
|
|
FileText,
|
|||
|
|
Folder,
|
|||
|
|
FolderPlus,
|
|||
|
|
Loader,
|
|||
|
|
Trash2,
|
|||
|
|
Upload,
|
|||
|
|
X,
|
|||
|
|
} from 'lucide-vue-next'
|
|||
|
|
import { useKnowledgeView } from '@/pages/knowledge/composables/useKnowledgeView'
|
|||
|
|
|
|||
|
|
const {
|
|||
|
|
documents,
|
|||
|
|
currentFolderId,
|
|||
|
|
isLoadingDocuments,
|
|||
|
|
uploadError,
|
|||
|
|
uploadInput,
|
|||
|
|
showNewFolderDialog,
|
|||
|
|
newFolderName,
|
|||
|
|
newFolderParentId,
|
|||
|
|
showRenameDialog,
|
|||
|
|
renameFolderName,
|
|||
|
|
showDeleteDialog,
|
|||
|
|
deletingFolder,
|
|||
|
|
showDocumentDialog,
|
|||
|
|
activeDocument,
|
|||
|
|
activeDocumentContent,
|
|||
|
|
isLoadingDocumentContent,
|
|||
|
|
isRoot,
|
|||
|
|
visibleFolders,
|
|||
|
|
breadcrumbs,
|
|||
|
|
explorerTitle,
|
|||
|
|
enterFolder,
|
|||
|
|
goToFolder,
|
|||
|
|
goBack,
|
|||
|
|
triggerUpload,
|
|||
|
|
handleUpload,
|
|||
|
|
handleDeleteDocument,
|
|||
|
|
openNewFolderDialog,
|
|||
|
|
createFolder,
|
|||
|
|
openRenameDialog,
|
|||
|
|
renameFolder,
|
|||
|
|
openDeleteDialog,
|
|||
|
|
deleteFolder,
|
|||
|
|
openDocument,
|
|||
|
|
closeDocumentDialog,
|
|||
|
|
getFileTypeColor,
|
|||
|
|
formatFileSize,
|
|||
|
|
formatDate,
|
|||
|
|
} = useKnowledgeView()
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div class="knowledge-view grid-bg scanlines">
|
|||
|
|
<div class="page-header">
|
|||
|
|
<div class="header-left">
|
|||
|
|
<div class="header-icon"><Database :size="20" /></div>
|
|||
|
|
<div class="header-text">
|
|||
|
|
<h1>KNOWLEDGE BASE</h1>
|
|||
|
|
<span class="header-sub">{{ explorerTitle }}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="header-actions">
|
|||
|
|
<button class="btn" @click="openNewFolderDialog(currentFolderId)">
|
|||
|
|
<FolderPlus :size="14" />
|
|||
|
|
{{ isRoot ? '新建文件夹' : '新建子文件夹' }}
|
|||
|
|
</button>
|
|||
|
|
<button v-if="!isRoot" class="btn primary" @click="triggerUpload">
|
|||
|
|
<Upload :size="14" /> 上传文件
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<input
|
|||
|
|
ref="uploadInput"
|
|||
|
|
type="file"
|
|||
|
|
class="hidden-upload"
|
|||
|
|
accept=".pdf,.md,.txt,.docx"
|
|||
|
|
@change="handleUpload"
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<div class="explorer-shell holo-card">
|
|||
|
|
<div class="toolbar">
|
|||
|
|
<div class="toolbar-left">
|
|||
|
|
<button class="nav-btn" :disabled="isRoot" @click="goBack">
|
|||
|
|
<ArrowLeft :size="14" />
|
|||
|
|
</button>
|
|||
|
|
<div class="breadcrumbs">
|
|||
|
|
<button
|
|||
|
|
v-for="(crumb, index) in breadcrumbs"
|
|||
|
|
:key="`${crumb.id ?? 'root'}-${index}`"
|
|||
|
|
class="breadcrumb-item"
|
|||
|
|
:class="{ active: crumb.id === currentFolderId || (crumb.id === null && isRoot) }"
|
|||
|
|
@click="goToFolder(crumb.id)"
|
|||
|
|
>
|
|||
|
|
{{ crumb.name }}
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="toolbar-right">
|
|||
|
|
<span class="location-tag">{{ isRoot ? 'ROOT' : 'FOLDER' }}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div v-if="uploadError" class="upload-error">
|
|||
|
|
{{ uploadError }}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div v-if="isLoadingDocuments" class="loading-strip">
|
|||
|
|
<Loader :size="14" class="spin" />
|
|||
|
|
<span>正在同步当前目录...</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="explorer-grid">
|
|||
|
|
<template v-if="visibleFolders.length || documents.length">
|
|||
|
|
<button
|
|||
|
|
v-for="folder in visibleFolders"
|
|||
|
|
:key="folder.id"
|
|||
|
|
class="explorer-tile folder-tile"
|
|||
|
|
@click="enterFolder(folder)"
|
|||
|
|
>
|
|||
|
|
<div class="folder-activate-flash"></div>
|
|||
|
|
<div class="tile-frame"></div>
|
|||
|
|
<div class="folder-tech-corners">
|
|||
|
|
<span class="corner corner-tl"></span>
|
|||
|
|
<span class="corner corner-tr"></span>
|
|||
|
|
<span class="corner corner-bl"></span>
|
|||
|
|
<span class="corner corner-br"></span>
|
|||
|
|
</div>
|
|||
|
|
<div class="folder-scan"></div>
|
|||
|
|
<div class="folder-grid-lines"></div>
|
|||
|
|
<div class="tile-actions" @click.stop>
|
|||
|
|
<button class="icon-btn" title="重命名文件夹" @click="openRenameDialog(folder)">
|
|||
|
|
<FileText :size="12" />
|
|||
|
|
</button>
|
|||
|
|
<button class="icon-btn danger" title="删除文件夹" @click="openDeleteDialog(folder)">
|
|||
|
|
<Trash2 :size="12" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="folder-glyph">
|
|||
|
|
<div class="folder-pulse-ring"></div>
|
|||
|
|
<div class="folder-core">
|
|||
|
|
<Folder :size="42" />
|
|||
|
|
</div>
|
|||
|
|
<span class="folder-beam beam-a"></span>
|
|||
|
|
<span class="folder-beam beam-b"></span>
|
|||
|
|
</div>
|
|||
|
|
<div class="folder-label-bar">
|
|||
|
|
<div class="tile-name folder-title-name">{{ folder.name }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="tile-meta folder-meta">{{ folder.children?.length ?? 0 }} 个子文件夹</div>
|
|||
|
|
</button>
|
|||
|
|
|
|||
|
|
<button
|
|||
|
|
v-for="doc in documents"
|
|||
|
|
:key="doc.id"
|
|||
|
|
class="explorer-tile file-tile"
|
|||
|
|
@click="openDocument(doc)"
|
|||
|
|
>
|
|||
|
|
<div class="tile-frame"></div>
|
|||
|
|
<div class="tile-actions" @click.stop>
|
|||
|
|
<button class="icon-btn danger" title="删除文件" @click="handleDeleteDocument(doc.id)">
|
|||
|
|
<Trash2 :size="12" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="file-badge" :style="{ color: getFileTypeColor(doc.file_type), borderColor: `${getFileTypeColor(doc.file_type)}55` }">
|
|||
|
|
{{ doc.file_type.toUpperCase() }}
|
|||
|
|
</div>
|
|||
|
|
<div class="file-icon" :style="{ color: getFileTypeColor(doc.file_type) }">
|
|||
|
|
<FileText :size="38" />
|
|||
|
|
</div>
|
|||
|
|
<div class="tile-name">{{ doc.title }}</div>
|
|||
|
|
<div class="tile-meta">{{ formatFileSize(doc.file_size) }} · {{ formatDate(doc.created_at) }}</div>
|
|||
|
|
<div class="tile-status" :class="doc.is_indexed ? 'indexed' : 'pending'">
|
|||
|
|
{{ doc.is_indexed ? 'INDEXED' : 'INDEXING...' }}
|
|||
|
|
</div>
|
|||
|
|
</button>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<div v-else class="empty-state">
|
|||
|
|
<div class="empty-core-shell">
|
|||
|
|
<div class="empty-orbit orbit-outer"></div>
|
|||
|
|
<div class="empty-orbit orbit-inner"></div>
|
|||
|
|
<div class="empty-scanline"></div>
|
|||
|
|
<div class="empty-core-glow"></div>
|
|||
|
|
<div class="empty-folder-chamber">
|
|||
|
|
<div class="empty-folder-plate"></div>
|
|||
|
|
<div class="empty-folder-pulse"></div>
|
|||
|
|
<div class="empty-folder-icon-wrap">
|
|||
|
|
<Folder :size="54" />
|
|||
|
|
</div>
|
|||
|
|
<span class="empty-beam beam-left"></span>
|
|||
|
|
<span class="empty-beam beam-right"></span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="empty-copy">
|
|||
|
|
<span class="empty-kicker">{{ isRoot ? 'ARCHIVE CORE ONLINE' : 'FOLDER CHAMBER STANDBY' }}</span>
|
|||
|
|
<span class="empty-title">{{ isRoot ? 'ROOT DIRECTORY EMPTY' : 'FOLDER IS READY FOR DEPLOYMENT' }}</span>
|
|||
|
|
<span class="empty-sub">
|
|||
|
|
{{ isRoot ? '当前知识库尚未初始化目录结构,请先创建第一个文件夹。' : '当前目录为空,可在此新建子文件夹或上传文件。' }}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="empty-actions">
|
|||
|
|
<button class="btn primary empty-cta" @click="openNewFolderDialog(currentFolderId)">
|
|||
|
|
<FolderPlus :size="14" />
|
|||
|
|
{{ isRoot ? '初始化知识仓库' : '新建子文件夹' }}
|
|||
|
|
</button>
|
|||
|
|
<button v-if="!isRoot" class="btn" @click="triggerUpload">
|
|||
|
|
<Upload :size="14" /> 上传文件
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div v-if="showNewFolderDialog" class="dialog-overlay" @click.self="showNewFolderDialog = false">
|
|||
|
|
<div class="dialog">
|
|||
|
|
<div class="dialog-header">
|
|||
|
|
<h3>{{ isRoot && newFolderParentId === null ? '新建文件夹' : '新建子文件夹' }}</h3>
|
|||
|
|
<button class="close-btn" @click="showNewFolderDialog = false">
|
|||
|
|
<X :size="16" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<input
|
|||
|
|
v-model="newFolderName"
|
|||
|
|
class="dialog-input"
|
|||
|
|
placeholder="文件夹名称"
|
|||
|
|
@keyup.enter="createFolder"
|
|||
|
|
autofocus
|
|||
|
|
/>
|
|||
|
|
<div class="dialog-actions">
|
|||
|
|
<button class="btn" @click="showNewFolderDialog = false">取消</button>
|
|||
|
|
<button class="btn primary" @click="createFolder">创建</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div v-if="showRenameDialog" class="dialog-overlay" @click.self="showRenameDialog = false">
|
|||
|
|
<div class="dialog">
|
|||
|
|
<div class="dialog-header">
|
|||
|
|
<h3>重命名文件夹</h3>
|
|||
|
|
<button class="close-btn" @click="showRenameDialog = false">
|
|||
|
|
<X :size="16" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<input
|
|||
|
|
v-model="renameFolderName"
|
|||
|
|
class="dialog-input"
|
|||
|
|
placeholder="文件夹名称"
|
|||
|
|
@keyup.enter="renameFolder"
|
|||
|
|
autofocus
|
|||
|
|
/>
|
|||
|
|
<div class="dialog-actions">
|
|||
|
|
<button class="btn" @click="showRenameDialog = false">取消</button>
|
|||
|
|
<button class="btn primary" @click="renameFolder">重命名</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div v-if="showDeleteDialog" class="dialog-overlay" @click.self="showDeleteDialog = false">
|
|||
|
|
<div class="dialog">
|
|||
|
|
<div class="dialog-header">
|
|||
|
|
<h3>删除文件夹</h3>
|
|||
|
|
<button class="close-btn" @click="showDeleteDialog = false">
|
|||
|
|
<X :size="16" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<p class="dialog-body">
|
|||
|
|
确定要删除文件夹「{{ deletingFolder?.name }}」吗?该操作不可恢复。
|
|||
|
|
</p>
|
|||
|
|
<div class="dialog-actions">
|
|||
|
|
<button class="btn" @click="showDeleteDialog = false">取消</button>
|
|||
|
|
<button class="btn danger" @click="deleteFolder">删除</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div v-if="showDocumentDialog && activeDocument" class="dialog-overlay" @click.self="closeDocumentDialog()">
|
|||
|
|
<div class="dialog document-dialog">
|
|||
|
|
<div class="dialog-header">
|
|||
|
|
<h3>{{ activeDocument.title }}</h3>
|
|||
|
|
<button class="close-btn" @click="closeDocumentDialog()">
|
|||
|
|
<X :size="16" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="document-info-row">
|
|||
|
|
<span class="file-badge" :style="{ color: getFileTypeColor(activeDocument.file_type), borderColor: `${getFileTypeColor(activeDocument.file_type)}55` }">
|
|||
|
|
{{ activeDocument.file_type.toUpperCase() }}
|
|||
|
|
</span>
|
|||
|
|
<span>{{ formatFileSize(activeDocument.file_size) }}</span>
|
|||
|
|
<span>{{ activeDocument.chunk_count }} chunks</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="document-preview">
|
|||
|
|
<div v-if="isLoadingDocumentContent" class="preview-loading">
|
|||
|
|
<Loader :size="16" class="spin" />
|
|||
|
|
<span>加载文档内容中...</span>
|
|||
|
|
</div>
|
|||
|
|
<pre v-else>{{ activeDocumentContent || '暂无可预览内容。' }}</pre>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.knowledge-view {
|
|||
|
|
height: 100%;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
padding: 24px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.page-header {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
gap: 16px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-left {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-icon {
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
filter: drop-shadow(0 0 8px var(--accent-cyan));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h1 {
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: 20px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
letter-spacing: 0.15em;
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-sub {
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 10px;
|
|||
|
|
color: var(--text-dim);
|
|||
|
|
letter-spacing: 0.1em;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-actions {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 10px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.hidden-upload {
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.explorer-shell {
|
|||
|
|
min-height: 620px;
|
|||
|
|
padding: 18px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 18px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.toolbar {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
gap: 16px;
|
|||
|
|
padding: 14px 16px;
|
|||
|
|
background: linear-gradient(180deg, rgba(0, 245, 212, 0.06), rgba(0, 245, 212, 0.02));
|
|||
|
|
border: 1px solid var(--border-dim);
|
|||
|
|
border-radius: var(--radius-lg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.toolbar-left {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 12px;
|
|||
|
|
min-width: 0;
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-btn {
|
|||
|
|
width: 34px;
|
|||
|
|
height: 34px;
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
background: rgba(0, 245, 212, 0.08);
|
|||
|
|
border: 1px solid var(--border-mid);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumbs {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-item {
|
|||
|
|
padding: 6px 10px;
|
|||
|
|
border-radius: 999px;
|
|||
|
|
background: transparent;
|
|||
|
|
border: 1px solid transparent;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.breadcrumb-item:hover,
|
|||
|
|
.breadcrumb-item.active {
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
border-color: var(--border-mid);
|
|||
|
|
background: rgba(0, 245, 212, 0.08);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.location-tag {
|
|||
|
|
padding: 4px 10px;
|
|||
|
|
border-radius: 999px;
|
|||
|
|
border: 1px solid var(--border-mid);
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
background: rgba(0, 245, 212, 0.08);
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: 10px;
|
|||
|
|
letter-spacing: 0.12em;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-error,
|
|||
|
|
.loading-strip {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
padding: 10px 14px;
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-error {
|
|||
|
|
color: var(--accent-red);
|
|||
|
|
background: rgba(255, 71, 87, 0.08);
|
|||
|
|
border: 1px solid rgba(255, 71, 87, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.loading-strip {
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
background: rgba(0, 245, 212, 0.06);
|
|||
|
|
border: 1px solid var(--border-dim);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.explorer-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
|
|||
|
|
gap: 18px;
|
|||
|
|
align-content: start;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.explorer-tile {
|
|||
|
|
position: relative;
|
|||
|
|
min-height: 220px;
|
|||
|
|
padding: 18px 16px 16px;
|
|||
|
|
border-radius: 20px;
|
|||
|
|
border: 1px solid var(--border-dim);
|
|||
|
|
background:
|
|||
|
|
radial-gradient(circle at top, rgba(0, 245, 212, 0.08), transparent 45%),
|
|||
|
|
linear-gradient(180deg, rgba(10, 15, 26, 0.92), rgba(13, 21, 37, 0.98));
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 10px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.explorer-tile:hover {
|
|||
|
|
border-color: var(--border-bright);
|
|||
|
|
box-shadow: var(--glow-cyan);
|
|||
|
|
transform: translateY(-2px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tile-frame {
|
|||
|
|
position: absolute;
|
|||
|
|
inset: 10px;
|
|||
|
|
border: 1px solid rgba(0, 245, 212, 0.08);
|
|||
|
|
border-radius: 16px;
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tile-actions {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 12px;
|
|||
|
|
right: 12px;
|
|||
|
|
display: flex;
|
|||
|
|
gap: 6px;
|
|||
|
|
opacity: 0;
|
|||
|
|
transition: opacity var(--transition-fast);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.explorer-tile:hover .tile-actions {
|
|||
|
|
opacity: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.icon-btn {
|
|||
|
|
width: 26px;
|
|||
|
|
height: 26px;
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
background: rgba(0, 9, 19, 0.78);
|
|||
|
|
border: 1px solid var(--border-dim);
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.icon-btn:hover {
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
border-color: var(--border-mid);
|
|||
|
|
background: rgba(0, 245, 212, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.icon-btn.danger:hover {
|
|||
|
|
color: var(--accent-red);
|
|||
|
|
border-color: rgba(255, 71, 87, 0.3);
|
|||
|
|
background: rgba(255, 71, 87, 0.12);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile {
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-activate-flash,
|
|||
|
|
.folder-tech-corners,
|
|||
|
|
.folder-scan,
|
|||
|
|
.folder-grid-lines {
|
|||
|
|
position: absolute;
|
|||
|
|
inset: 0;
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-activate-flash {
|
|||
|
|
opacity: 0;
|
|||
|
|
background: radial-gradient(circle at center, rgba(124, 230, 255, 0.34), rgba(0, 245, 212, 0.14) 35%, transparent 68%);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tech-corners {
|
|||
|
|
z-index: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tech-corners .corner {
|
|||
|
|
position: absolute;
|
|||
|
|
width: 26px;
|
|||
|
|
height: 26px;
|
|||
|
|
border-color: rgba(0, 245, 212, 0.22);
|
|||
|
|
border-style: solid;
|
|||
|
|
opacity: 0.75;
|
|||
|
|
transition: all var(--transition-mid);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tech-corners .corner-tl {
|
|||
|
|
top: 9px;
|
|||
|
|
left: 9px;
|
|||
|
|
border-width: 2px 0 0 2px;
|
|||
|
|
border-top-left-radius: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tech-corners .corner-tr {
|
|||
|
|
top: 9px;
|
|||
|
|
right: 9px;
|
|||
|
|
border-width: 2px 2px 0 0;
|
|||
|
|
border-top-right-radius: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tech-corners .corner-bl {
|
|||
|
|
bottom: 9px;
|
|||
|
|
left: 9px;
|
|||
|
|
border-width: 0 0 2px 2px;
|
|||
|
|
border-bottom-left-radius: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tech-corners .corner-br {
|
|||
|
|
bottom: 9px;
|
|||
|
|
right: 9px;
|
|||
|
|
border-width: 0 2px 2px 0;
|
|||
|
|
border-bottom-right-radius: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-scan,
|
|||
|
|
.folder-grid-lines {
|
|||
|
|
opacity: 0;
|
|||
|
|
transition: opacity var(--transition-mid);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-scan {
|
|||
|
|
background: linear-gradient(
|
|||
|
|
115deg,
|
|||
|
|
transparent 24%,
|
|||
|
|
rgba(0, 245, 212, 0.04) 40%,
|
|||
|
|
rgba(124, 230, 255, 0.18) 50%,
|
|||
|
|
rgba(0, 245, 212, 0.04) 60%,
|
|||
|
|
transparent 76%
|
|||
|
|
);
|
|||
|
|
transform: translateX(-130%);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-grid-lines {
|
|||
|
|
background:
|
|||
|
|
linear-gradient(rgba(0, 245, 212, 0.05) 1px, transparent 1px),
|
|||
|
|
linear-gradient(90deg, rgba(0, 245, 212, 0.05) 1px, transparent 1px);
|
|||
|
|
background-size: 18px 18px;
|
|||
|
|
mix-blend-mode: screen;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-scan,
|
|||
|
|
.folder-tile:hover .folder-grid-lines {
|
|||
|
|
opacity: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-tech-corners .corner {
|
|||
|
|
border-color: rgba(124, 230, 255, 0.7);
|
|||
|
|
box-shadow: 0 0 10px rgba(0, 245, 212, 0.18);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .corner-tl,
|
|||
|
|
.folder-tile:hover .corner-br {
|
|||
|
|
transform: translate(1px, 1px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .corner-tr,
|
|||
|
|
.folder-tile:hover .corner-bl {
|
|||
|
|
transform: translate(-1px, -1px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:active .folder-activate-flash {
|
|||
|
|
animation: activate-flash 320ms ease-out;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-scan {
|
|||
|
|
animation: hologram-scan 1.4s ease-out forwards;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-glyph {
|
|||
|
|
position: relative;
|
|||
|
|
margin-top: 22px;
|
|||
|
|
width: 100px;
|
|||
|
|
height: 94px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
animation: folder-float 4.8s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-pulse-ring {
|
|||
|
|
position: absolute;
|
|||
|
|
width: 108px;
|
|||
|
|
height: 108px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
border: 1px solid rgba(0, 245, 212, 0.16);
|
|||
|
|
box-shadow: 0 0 18px rgba(0, 245, 212, 0.08);
|
|||
|
|
animation: pulse-ring 3.2s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-core {
|
|||
|
|
width: 92px;
|
|||
|
|
height: 78px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
border-radius: 22px;
|
|||
|
|
color: #79d4ff;
|
|||
|
|
background: linear-gradient(180deg, rgba(0, 245, 212, 0.16), rgba(123, 44, 191, 0.12));
|
|||
|
|
border: 1px solid rgba(0, 245, 212, 0.22);
|
|||
|
|
box-shadow:
|
|||
|
|
inset 0 0 18px rgba(0, 245, 212, 0.08),
|
|||
|
|
0 0 18px rgba(0, 245, 212, 0.12);
|
|||
|
|
animation: core-breathe 3.4s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-core svg {
|
|||
|
|
filter: drop-shadow(0 0 6px rgba(124, 230, 255, 0.35));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-beam {
|
|||
|
|
position: absolute;
|
|||
|
|
display: block;
|
|||
|
|
width: 70px;
|
|||
|
|
height: 1px;
|
|||
|
|
background: linear-gradient(90deg, transparent, rgba(0, 245, 212, 0.85), transparent);
|
|||
|
|
animation: beam-flicker 2.6s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.beam-a {
|
|||
|
|
top: 18px;
|
|||
|
|
left: -8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.beam-b {
|
|||
|
|
right: -8px;
|
|||
|
|
bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-tile {
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-label-bar {
|
|||
|
|
width: 100%;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
min-height: 34px;
|
|||
|
|
padding: 8px 2px 0;
|
|||
|
|
border-top: 1px solid rgba(0, 245, 212, 0.1);
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-label-bar::before {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
top: -1px;
|
|||
|
|
left: 2px;
|
|||
|
|
width: 42px;
|
|||
|
|
height: 1px;
|
|||
|
|
background: linear-gradient(90deg, rgba(0, 245, 212, 0.85), transparent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-title-name {
|
|||
|
|
text-align: left;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-meta {
|
|||
|
|
color: #84c7d6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-label-bar {
|
|||
|
|
border-top-color: rgba(124, 230, 255, 0.24);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-label-bar::before {
|
|||
|
|
width: 64px;
|
|||
|
|
background: linear-gradient(90deg, rgba(124, 230, 255, 0.95), transparent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-title-name {
|
|||
|
|
color: #f2fbff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-badge {
|
|||
|
|
margin-top: 8px;
|
|||
|
|
padding: 3px 8px;
|
|||
|
|
border: 1px solid;
|
|||
|
|
border-radius: 999px;
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: 9px;
|
|||
|
|
letter-spacing: 0.12em;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-icon {
|
|||
|
|
margin-top: 22px;
|
|||
|
|
min-height: 88px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
filter: drop-shadow(0 0 10px currentColor);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tile-name {
|
|||
|
|
width: 100%;
|
|||
|
|
font-size: 13px;
|
|||
|
|
line-height: 1.45;
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
word-break: break-word;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tile-meta {
|
|||
|
|
width: 100%;
|
|||
|
|
min-height: 30px;
|
|||
|
|
color: var(--text-dim);
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 10px;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tile-status {
|
|||
|
|
margin-top: auto;
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: 9px;
|
|||
|
|
letter-spacing: 0.12em;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.indexed {
|
|||
|
|
color: var(--accent-green);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pending {
|
|||
|
|
color: var(--accent-amber);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-state {
|
|||
|
|
grid-column: 1 / -1;
|
|||
|
|
min-height: 440px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
gap: 20px;
|
|||
|
|
padding: 40px 24px;
|
|||
|
|
border: 1px solid rgba(0, 245, 212, 0.12);
|
|||
|
|
border-radius: 24px;
|
|||
|
|
background:
|
|||
|
|
radial-gradient(circle at center, rgba(0, 245, 212, 0.08), transparent 42%),
|
|||
|
|
linear-gradient(180deg, rgba(8, 13, 24, 0.96), rgba(4, 8, 16, 0.98));
|
|||
|
|
position: relative;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-state::before {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
inset: 0;
|
|||
|
|
background:
|
|||
|
|
linear-gradient(90deg, transparent, rgba(0, 245, 212, 0.05), transparent),
|
|||
|
|
radial-gradient(circle at top, rgba(123, 44, 191, 0.14), transparent 32%);
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-core-shell {
|
|||
|
|
position: relative;
|
|||
|
|
width: 250px;
|
|||
|
|
height: 250px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-orbit {
|
|||
|
|
position: absolute;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
border: 1px solid rgba(0, 245, 212, 0.12);
|
|||
|
|
box-shadow: inset 0 0 18px rgba(0, 245, 212, 0.04);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.orbit-outer {
|
|||
|
|
width: 250px;
|
|||
|
|
height: 250px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.orbit-inner {
|
|||
|
|
width: 184px;
|
|||
|
|
height: 184px;
|
|||
|
|
border-color: rgba(0, 245, 212, 0.18);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-scanline {
|
|||
|
|
position: absolute;
|
|||
|
|
width: 180px;
|
|||
|
|
height: 1px;
|
|||
|
|
background: linear-gradient(90deg, transparent, rgba(0, 245, 212, 0.9), transparent);
|
|||
|
|
box-shadow: 0 0 12px rgba(0, 245, 212, 0.5);
|
|||
|
|
animation: float 3s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-core-glow {
|
|||
|
|
position: absolute;
|
|||
|
|
width: 126px;
|
|||
|
|
height: 126px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
background: radial-gradient(circle, rgba(0, 245, 212, 0.16), rgba(123, 44, 191, 0.08), transparent 70%);
|
|||
|
|
filter: blur(6px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-folder-chamber {
|
|||
|
|
position: relative;
|
|||
|
|
width: 124px;
|
|||
|
|
height: 124px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
animation: folder-float 5.4s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-folder-plate {
|
|||
|
|
position: absolute;
|
|||
|
|
inset: 14px;
|
|||
|
|
border-radius: 28px;
|
|||
|
|
border: 1px solid rgba(0, 245, 212, 0.22);
|
|||
|
|
background: linear-gradient(180deg, rgba(0, 245, 212, 0.14), rgba(123, 44, 191, 0.14));
|
|||
|
|
box-shadow:
|
|||
|
|
inset 0 0 24px rgba(0, 245, 212, 0.08),
|
|||
|
|
0 0 32px rgba(0, 245, 212, 0.12);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-folder-pulse {
|
|||
|
|
position: absolute;
|
|||
|
|
width: 138px;
|
|||
|
|
height: 138px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
border: 1px solid rgba(0, 245, 212, 0.14);
|
|||
|
|
box-shadow: 0 0 22px rgba(0, 245, 212, 0.08);
|
|||
|
|
animation: pulse-ring 3.6s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-folder-icon-wrap {
|
|||
|
|
position: relative;
|
|||
|
|
z-index: 1;
|
|||
|
|
width: 92px;
|
|||
|
|
height: 92px;
|
|||
|
|
border-radius: 24px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
color: #7ce6ff;
|
|||
|
|
background: rgba(3, 10, 18, 0.72);
|
|||
|
|
border: 1px solid rgba(0, 245, 212, 0.16);
|
|||
|
|
box-shadow: 0 0 24px rgba(0, 245, 212, 0.16);
|
|||
|
|
animation: core-breathe 3.8s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-folder-icon-wrap svg {
|
|||
|
|
filter: drop-shadow(0 0 8px rgba(124, 230, 255, 0.45));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-beam {
|
|||
|
|
position: absolute;
|
|||
|
|
width: 78px;
|
|||
|
|
height: 1px;
|
|||
|
|
top: 50%;
|
|||
|
|
transform: translateY(-50%);
|
|||
|
|
background: linear-gradient(90deg, transparent, rgba(0, 245, 212, 0.95), transparent);
|
|||
|
|
animation: beam-flicker 2.4s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.beam-left {
|
|||
|
|
left: -34px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.beam-right {
|
|||
|
|
right: -34px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-copy {
|
|||
|
|
position: relative;
|
|||
|
|
z-index: 1;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
text-align: center;
|
|||
|
|
max-width: 560px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-kicker {
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: 10px;
|
|||
|
|
letter-spacing: 0.24em;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-title {
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: 22px;
|
|||
|
|
letter-spacing: 0.16em;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-sub {
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
font-size: 12px;
|
|||
|
|
line-height: 1.8;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-actions {
|
|||
|
|
position: relative;
|
|||
|
|
z-index: 1;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 12px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-cta {
|
|||
|
|
min-width: 188px;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes folder-float {
|
|||
|
|
0%, 100% { transform: translateY(0px); }
|
|||
|
|
50% { transform: translateY(-5px); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes core-breathe {
|
|||
|
|
0%, 100% {
|
|||
|
|
transform: scale(1);
|
|||
|
|
opacity: 0.92;
|
|||
|
|
}
|
|||
|
|
50% {
|
|||
|
|
transform: scale(1.04);
|
|||
|
|
opacity: 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes pulse-ring {
|
|||
|
|
0%, 100% {
|
|||
|
|
transform: scale(0.92);
|
|||
|
|
opacity: 0.35;
|
|||
|
|
}
|
|||
|
|
50% {
|
|||
|
|
transform: scale(1.06);
|
|||
|
|
opacity: 0.78;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes beam-flicker {
|
|||
|
|
0%, 100% {
|
|||
|
|
opacity: 0.35;
|
|||
|
|
transform: translateY(-50%) scaleX(0.92);
|
|||
|
|
}
|
|||
|
|
50% {
|
|||
|
|
opacity: 1;
|
|||
|
|
transform: translateY(-50%) scaleX(1.06);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes hologram-scan {
|
|||
|
|
0% {
|
|||
|
|
transform: translateX(-130%);
|
|||
|
|
opacity: 0;
|
|||
|
|
}
|
|||
|
|
20% {
|
|||
|
|
opacity: 0.55;
|
|||
|
|
}
|
|||
|
|
100% {
|
|||
|
|
transform: translateX(130%);
|
|||
|
|
opacity: 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes activate-flash {
|
|||
|
|
0% {
|
|||
|
|
opacity: 0;
|
|||
|
|
transform: scale(0.92);
|
|||
|
|
}
|
|||
|
|
35% {
|
|||
|
|
opacity: 0.95;
|
|||
|
|
transform: scale(1.03);
|
|||
|
|
}
|
|||
|
|
100% {
|
|||
|
|
opacity: 0;
|
|||
|
|
transform: scale(1.08);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-glyph,
|
|||
|
|
.folder-tile:hover .empty-folder-chamber {
|
|||
|
|
animation-duration: 2.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-core,
|
|||
|
|
.folder-tile:hover .folder-pulse-ring,
|
|||
|
|
.folder-tile:hover .empty-folder-icon-wrap,
|
|||
|
|
.folder-tile:hover .empty-folder-pulse {
|
|||
|
|
animation-duration: 2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-tile:hover .folder-beam,
|
|||
|
|
.folder-tile:hover .empty-beam {
|
|||
|
|
animation-duration: 1.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (prefers-reduced-motion: reduce) {
|
|||
|
|
.folder-glyph,
|
|||
|
|
.folder-pulse-ring,
|
|||
|
|
.folder-core,
|
|||
|
|
.folder-beam,
|
|||
|
|
.folder-scan,
|
|||
|
|
.folder-tech-corners .corner,
|
|||
|
|
.empty-folder-chamber,
|
|||
|
|
.empty-folder-pulse,
|
|||
|
|
.empty-folder-icon-wrap,
|
|||
|
|
.empty-beam {
|
|||
|
|
animation: none !important;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 700px) {
|
|||
|
|
.explorer-grid {
|
|||
|
|
grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
|
|||
|
|
gap: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.explorer-tile {
|
|||
|
|
min-height: 204px;
|
|||
|
|
padding: 16px 14px 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-glyph {
|
|||
|
|
width: 90px;
|
|||
|
|
height: 84px;
|
|||
|
|
margin-top: 18px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-pulse-ring {
|
|||
|
|
width: 96px;
|
|||
|
|
height: 96px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.folder-core {
|
|||
|
|
width: 84px;
|
|||
|
|
height: 72px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-core-shell {
|
|||
|
|
width: 210px;
|
|||
|
|
height: 210px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.orbit-outer {
|
|||
|
|
width: 210px;
|
|||
|
|
height: 210px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.orbit-inner {
|
|||
|
|
width: 156px;
|
|||
|
|
height: 156px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-title {
|
|||
|
|
font-size: 18px;
|
|||
|
|
letter-spacing: 0.12em;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.spin {
|
|||
|
|
animation: spin 1s linear infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-overlay {
|
|||
|
|
position: fixed;
|
|||
|
|
inset: 0;
|
|||
|
|
background: rgba(0, 0, 0, 0.6);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
z-index: 1000;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog {
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
border: 1px solid var(--border-mid);
|
|||
|
|
border-radius: var(--radius-lg);
|
|||
|
|
padding: 24px;
|
|||
|
|
width: 400px;
|
|||
|
|
max-width: 90vw;
|
|||
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.document-dialog {
|
|||
|
|
width: min(860px, 92vw);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-header {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
gap: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-header h3 {
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: 14px;
|
|||
|
|
letter-spacing: 0.1em;
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.close-btn {
|
|||
|
|
background: none;
|
|||
|
|
border: none;
|
|||
|
|
color: var(--text-dim);
|
|||
|
|
cursor: pointer;
|
|||
|
|
padding: 4px;
|
|||
|
|
border-radius: 3px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
transition: all var(--transition-fast);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.close-btn:hover {
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
background: var(--bg-panel);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-input {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 10px 14px;
|
|||
|
|
background: var(--bg-panel);
|
|||
|
|
border: 1px solid var(--border-dim);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
font-size: 13px;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-input:focus {
|
|||
|
|
border-color: var(--accent-cyan);
|
|||
|
|
outline: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-body {
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
margin: 0 0 16px 0;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dialog-actions {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 10px;
|
|||
|
|
justify-content: flex-end;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.document-info-row {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 10px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
margin-bottom: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.document-preview {
|
|||
|
|
min-height: 360px;
|
|||
|
|
max-height: 62vh;
|
|||
|
|
overflow: auto;
|
|||
|
|
background: rgba(3, 5, 10, 0.7);
|
|||
|
|
border: 1px solid var(--border-dim);
|
|||
|
|
border-radius: 14px;
|
|||
|
|
padding: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.document-preview pre {
|
|||
|
|
white-space: pre-wrap;
|
|||
|
|
word-break: break-word;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 12px;
|
|||
|
|
line-height: 1.7;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.preview-loading {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
font-family: var(--font-mono);
|
|||
|
|
font-size: 11px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 6px;
|
|||
|
|
padding: 8px 16px;
|
|||
|
|
background: var(--bg-panel);
|
|||
|
|
border: 1px solid var(--border-mid);
|
|||
|
|
border-radius: var(--radius-md);
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
font-family: var(--font-display);
|
|||
|
|
font-size: 10px;
|
|||
|
|
letter-spacing: 0.1em;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all var(--transition-fast);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn:hover {
|
|||
|
|
border-color: var(--border-mid);
|
|||
|
|
background: var(--bg-card-hover);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn.primary {
|
|||
|
|
background: var(--accent-cyan-dim);
|
|||
|
|
border-color: rgba(0, 245, 212, 0.3);
|
|||
|
|
color: var(--accent-cyan);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn.primary:hover {
|
|||
|
|
background: rgba(0, 245, 212, 0.2);
|
|||
|
|
box-shadow: var(--glow-cyan);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn.danger {
|
|||
|
|
background: rgba(255, 71, 87, 0.1);
|
|||
|
|
border-color: rgba(255, 71, 87, 0.3);
|
|||
|
|
color: var(--accent-red);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn.danger:hover {
|
|||
|
|
background: rgba(255, 71, 87, 0.2);
|
|||
|
|
box-shadow: 0 0 12px rgba(255, 71, 87, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 900px) {
|
|||
|
|
.toolbar {
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: stretch;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.toolbar-left,
|
|||
|
|
.toolbar-right {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.toolbar-right {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: flex-start;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|