2083 lines
130 KiB
HTML
2083 lines
130 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="description" content="Paper Burner X - 您的一站式AI文献阅读与智能分析平台,支持OCR、翻译、深度解析与高级管理。">
|
||
<title>Paper Burner X - AI文献阅读与智能分析平台</title>
|
||
<link rel="icon" type="image/svg+xml" href="public/pure.svg">
|
||
<!-- CDN 性能优化:DNS 预连接 - 提前建立连接,节省 100-300ms --> <link rel="dns-prefetch" href="https://gcore.jsdelivr.net"> <link rel="preconnect" href="https://gcore.jsdelivr.net" crossorigin> <link rel="dns-prefetch" href="https://cdnjs.cloudflare.com"> <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin> <link rel="dns-prefetch" href="https://cdn.tailwindcss.com"> <link rel="preconnect" href="https://cdn.tailwindcss.com" crossorigin>
|
||
|
||
<!-- XSS 防护:DOMPurify - 用于清理 AI 生成的 HTML 内容 -->
|
||
<script src="https://gcore.jsdelivr.net/npm/dompurify@3.0.6/dist/purify.min.js"></script>
|
||
|
||
<!-- Tailwind CSS & 依赖库 -->
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/iconify-icon@2.0.0/dist/iconify-icon.min.js"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/js-base64@3.7.5/base64.min.js"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>
|
||
<!-- dagre.js for graph layout (层次化图布局算法) -->
|
||
<script src="https://gcore.jsdelivr.net/npm/graphlib@2.1.8/dist/graphlib.min.js"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/dagre@0.8.5/dist/dagre.min.js"></script>
|
||
<!-- docx-preview.js for Word preview -->
|
||
<script src="https://gcore.jsdelivr.net/npm/docx-preview@0.3.7/dist/docx-preview.min.js"></script>
|
||
<link rel="stylesheet" href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
||
<link rel="stylesheet" href="css/history_detail/01-foundation/variables.css">
|
||
<link rel="stylesheet" href="css/history_detail/05-utilities/buttons.css">
|
||
<link rel="stylesheet" href="css/history_detail/04-features/markdown-enhancements.css">
|
||
<link rel="stylesheet" href="css/history_detail/04-features/math-display.css">
|
||
<link rel="stylesheet" href="css/_deprecated/chatbot-model-config-modal.css">
|
||
<script src="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||
<!-- Markdown-it for AST-based processing (新架构) -->
|
||
<script src="https://gcore.jsdelivr.net/npm/markdown-it@14.0.0/dist/markdown-it.min.js"></script>
|
||
<script>
|
||
// failback: 如果 CDN 加载失败,自动加载本地 jszip
|
||
if (typeof JSZip === 'undefined') {
|
||
var script = document.createElement('script');
|
||
script.src = 'js/lib/jszip.min.js';
|
||
document.head.appendChild(script);
|
||
}
|
||
</script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||
<style>
|
||
/* 谷歌字体 Inter */
|
||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||
/* 基础字体设置 */
|
||
body {
|
||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
background-color: #f8fafc; /* 更中性的应用背景色 */
|
||
}
|
||
/* App Shell Layout */
|
||
.app-shell {
|
||
display: flex;
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
}
|
||
.app-sidebar {
|
||
width: var(--sidebar-width);
|
||
background-color: var(--color-bg-base);
|
||
border-right: 1px solid var(--color-border-light);
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex-shrink: 0;
|
||
/* Mobile defaults: fixed off-screen */
|
||
position: fixed;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
z-index: 50;
|
||
transform: translateX(-100%);
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
@media (min-width: 768px) {
|
||
.app-sidebar {
|
||
position: static;
|
||
transform: none;
|
||
border-top-right-radius: 1.5rem;
|
||
border-bottom-right-radius: 1.5rem;
|
||
}
|
||
/* 侧边栏收起状态 */
|
||
.app-sidebar.collapsed {
|
||
width: 76px;
|
||
}
|
||
.app-sidebar.collapsed .px-6 {
|
||
padding-left: 0;
|
||
padding-right: 0;
|
||
}
|
||
/* 收起时头部垂直排列并居中 */
|
||
.app-sidebar.collapsed #sidebarHeader {
|
||
flex-direction: column;
|
||
align-items: center; /* 关键:水平居中 */
|
||
gap: 1.5rem;
|
||
padding-top: 1.5rem;
|
||
padding-bottom: 1rem;
|
||
}
|
||
.app-sidebar.collapsed #sidebarLogo {
|
||
width: 32px;
|
||
height: 32px;
|
||
object-fit: contain;
|
||
}
|
||
.app-sidebar.collapsed .nav-text,
|
||
.app-sidebar.collapsed .nav-section-title,
|
||
.app-sidebar.collapsed #sidebarHistoryToggleBtn,
|
||
.app-sidebar.collapsed #sidebarHistoryQuickList {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
display: none;
|
||
}
|
||
.app-sidebar.collapsed .nav-item,
|
||
.app-sidebar.collapsed #sidebarHistoryMainBtn {
|
||
justify-content: center;
|
||
padding-left: 0;
|
||
padding-right: 0;
|
||
}
|
||
.app-sidebar.collapsed .nav-icon {
|
||
margin-right: 0;
|
||
}
|
||
.app-sidebar.collapsed #sidebarHistoryMainBtn {
|
||
justify-content: center; /* 确保历史记录图标居中 */
|
||
border-radius: 0.5rem;
|
||
}
|
||
.app-sidebar.collapsed #sidebarFooter {
|
||
display: none;
|
||
}
|
||
.app-sidebar.collapsed #sidebarFooterCollapsed {
|
||
display: flex;
|
||
}
|
||
.app-sidebar.collapsed #sidebarDivider {
|
||
margin-left: 1rem;
|
||
margin-right: 1rem;
|
||
}
|
||
}
|
||
.app-sidebar.mobile-open {
|
||
transform: translateX(0);
|
||
box-shadow: 4px 0 24px rgba(0,0,0,0.1);
|
||
}
|
||
.sidebar-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
background-color: rgba(0,0,0,0.4);
|
||
z-index: 40;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.3s linear;
|
||
backdrop-filter: blur(2px);
|
||
}
|
||
.sidebar-overlay.show {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
}
|
||
.app-main {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
position: relative;
|
||
width: 100%; /* Ensure it takes full width on mobile when sidebar is hidden */
|
||
}
|
||
.nav-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: var(--sidebar-nav-item-padding);
|
||
color: var(--slate-600);
|
||
border-radius: var(--radius-lg);
|
||
transition: all 0.2s ease;
|
||
font-weight: 500;
|
||
font-size: var(--sidebar-nav-item-font-size);
|
||
margin-bottom: 0.25rem;
|
||
user-select: none;
|
||
}
|
||
.nav-item:hover {
|
||
background-color: var(--slate-100);
|
||
color: var(--slate-900);
|
||
}
|
||
.nav-item.active {
|
||
background-color: var(--indigo-50);
|
||
color: var(--indigo-600);
|
||
font-weight: 600;
|
||
}
|
||
.nav-item .nav-icon {
|
||
margin-right: 0.75rem;
|
||
font-size: 1.25rem; /* 20px */
|
||
color: #94a3b8; /* slate-400 */
|
||
transition: color 0.2s ease;
|
||
}
|
||
.nav-item:hover .nav-icon {
|
||
color: #64748b; /* slate-500 */
|
||
}
|
||
.nav-item.active .nav-icon {
|
||
color: var(--color-primary);
|
||
}
|
||
/* 侧边栏子菜单项 */
|
||
.nav-sub-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0.5rem 0.75rem 0.5rem 2.75rem; /* 左侧留出空间对齐文字 */
|
||
font-size: 0.875rem; /* 14px */
|
||
color: #64748b;
|
||
border-radius: 0.5rem;
|
||
transition: all 0.15s;
|
||
text-decoration: none;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.nav-sub-item:hover {
|
||
color: #0f172a;
|
||
background-color: #f8fafc;
|
||
}
|
||
|
||
/* Workspace Header */
|
||
.workspace-header {
|
||
background: linear-gradient(135deg, var(--indigo-50) 0%, var(--slate-50) 100%);
|
||
border-radius: 1.5rem;
|
||
padding: 2.5rem;
|
||
margin-bottom: 2.5rem;
|
||
border: 1px solid var(--slate-200);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.workspace-header::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -30%;
|
||
right: -10%;
|
||
width: 400px;
|
||
height: 400px;
|
||
background: radial-gradient(circle, rgba(99, 102, 241, 0.06) 0%, rgba(255,255,255,0) 70%);
|
||
border-radius: 50%;
|
||
pointer-events: none;
|
||
}
|
||
/* 新增:右侧大 Logo 背景装饰 */
|
||
/* .workspace-header::after {
|
||
content: '';
|
||
position: absolute;
|
||
right: -20px;
|
||
bottom: -40px;
|
||
width: 300px;
|
||
height: 300px;
|
||
background-image: url('public/pure.svg');
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: contain;
|
||
极低透明度,仅作纹理
|
||
opacity: 0.07;
|
||
transform: rotate(-10deg);
|
||
pointer-events: none;
|
||
} */
|
||
.workspace-header-content {
|
||
position: relative;
|
||
z-index: 1;
|
||
max-width: 600px;
|
||
}
|
||
|
||
/* 背景渐变 (保留用于特定区域,如果需要) */
|
||
.gradient-bg {
|
||
background-color: #F3F6FA;
|
||
}
|
||
/* 卡片渐变 */
|
||
.gradient-card {
|
||
background: linear-gradient(135deg, #ffffff 0%, #f9fbff 100%);
|
||
}
|
||
/* 通用过渡效果 */
|
||
.transition-all {
|
||
transition: all 0.3s ease;
|
||
}
|
||
/* 文件列表项样式 */
|
||
.file-list-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0.5rem 0.75rem;
|
||
border-radius: 0.5rem;
|
||
background-color: #f8f9fa;
|
||
margin-bottom: 0.5rem;
|
||
border: 1px solid #e9ecef;
|
||
}
|
||
.file-list-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
/* API Key 输入框样式 */
|
||
textarea.api-key-input {
|
||
min-height: 60px; /* 最小高度 */
|
||
resize: vertical; /* 允许垂直方向调整大小 */
|
||
}
|
||
/* 闪烁提示动画基类 */
|
||
.flash-tip-anim {
|
||
opacity: 0;
|
||
transition: opacity 0.5s;
|
||
will-change: opacity;
|
||
}
|
||
/* 闪烁提示显示状态 */
|
||
.flash-tip-anim.show {
|
||
opacity: 1;
|
||
}
|
||
/* 闪烁提示隐藏状态 */
|
||
.flash-tip-anim.hide {
|
||
opacity: 0;
|
||
}
|
||
/* Skeleton Character Rows */
|
||
.skeleton-bg .skeleton-row {
|
||
border-radius: 8px;
|
||
background: linear-gradient(90deg, #e5e7eb 25%, #f3f6fa 50%, #e5e7eb 75%);
|
||
background-size: 200% 100%;
|
||
animation: shimmer 1.8s infinite linear;
|
||
}
|
||
.skeleton-fake-text {
|
||
color: #d1d5db;
|
||
font-size: 1rem;
|
||
font-family: 'Fira Mono', 'Consolas', monospace;
|
||
font-weight: bold;
|
||
opacity: 0.5;
|
||
letter-spacing: 0.1em;
|
||
animation: shimmer 1.8s infinite linear;
|
||
user-select: none;
|
||
}
|
||
.skeleton-fake-text.text-lg {
|
||
font-size: 1.25rem;
|
||
}
|
||
|
||
/* New Top-Blue to Bottom-White Gradient for Landing Page Background */
|
||
.ocean-gradient-bg {
|
||
background: linear-gradient(to bottom, #93C5FD 0%, #FFFFFF 100%); /* Tailwind blue-300 to white */
|
||
}
|
||
|
||
/* ===== 版权声明弹窗美化与动画 ===== */
|
||
#copyrightModal {
|
||
backdrop-filter: blur(6px) saturate(120%);
|
||
background: rgba(30, 41, 59, 0.35) !important; /* 深色毛玻璃 */
|
||
animation: copyrightModalFadeIn 0.4s cubic-bezier(.4,2,.6,1) both;
|
||
}
|
||
@keyframes copyrightModalFadeIn {
|
||
0% { opacity: 0; }
|
||
100% { opacity: 1; }
|
||
}
|
||
.copyright-card-ani {
|
||
background: linear-gradient(135deg, #f8fafc 60%, #e0e7ef 100%);
|
||
box-shadow: 0 8px 32px 0 rgba(31, 41, 55, 0.18), 0 1.5px 6px 0 rgba(59,130,246,0.08);
|
||
border-radius: 1.5rem;
|
||
border: 1.5px solid #e0e7ef;
|
||
animation: copyrightCardPopIn 0.5s cubic-bezier(.4,2,.6,1) both;
|
||
max-width: 900px;
|
||
transform: scale(0.9);
|
||
transform-origin: top center;
|
||
}
|
||
@keyframes copyrightCardPopIn {
|
||
0% { transform: scale(0.92) translateY(40px); opacity: 0; }
|
||
100% { transform: scale(1) translateY(0); opacity: 1; }
|
||
}
|
||
.copyright-card-ani h2 {
|
||
letter-spacing: 0.02em;
|
||
background: linear-gradient(90deg, var(--indigo-600) 30%, var(--indigo-400) 100%);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
}
|
||
.copyright-card-ani .main-btn {
|
||
background: var(--color-primary);
|
||
color: white;
|
||
font-weight: 600;
|
||
box-shadow: var(--shadow-sm);
|
||
border-radius: var(--radius-lg);
|
||
transition: all 0.2s ease;
|
||
}
|
||
.copyright-card-ani .main-btn:hover {
|
||
background: var(--color-primary-hover);
|
||
box-shadow: var(--shadow-md);
|
||
}
|
||
.copyright-card-ani .iconify-icon {
|
||
vertical-align: middle;
|
||
}
|
||
.feature-card-modern {
|
||
position: relative;
|
||
overflow: hidden;
|
||
border-radius: 20px;
|
||
padding: clamp(16px, 2vw, 20px);
|
||
background: linear-gradient(130deg, rgba(99, 102, 241, 0.08), rgba(255,255,255,0.98));
|
||
border: 1px solid rgba(99, 102, 241, 0.14);
|
||
box-shadow: var(--shadow-lg);
|
||
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||
}
|
||
.feature-card-modern::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background: linear-gradient(135deg, rgba(255,255,255,0.32), rgba(99, 102, 241, 0.18));
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
}
|
||
.feature-card-modern:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: var(--shadow-xl);
|
||
}
|
||
.feature-card-modern:hover::before {
|
||
opacity: 1;
|
||
}
|
||
.feature-card-modern > * {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
.feature-icon-ring {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 42px;
|
||
height: 42px;
|
||
border-radius: 14px;
|
||
background: linear-gradient(135deg, rgba(255,255,255,0.9), rgba(99, 102, 241, 0.16));
|
||
box-shadow: inset 0 1px 0 rgba(255,255,255,0.5);
|
||
}
|
||
.feature-meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.3rem;
|
||
margin-top: 0.6rem;
|
||
}
|
||
.feature-meta span {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 0.28rem 0.65rem;
|
||
border-radius: 999px;
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.01em;
|
||
backdrop-filter: blur(8px);
|
||
}
|
||
.feature-card-inner {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 14px;
|
||
}
|
||
.feature-card-inner h4 {
|
||
margin-bottom: 0.35rem;
|
||
}
|
||
|
||
/* 文件预览模态框动画 */
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
@keyframes fadeOut {
|
||
from { opacity: 1; }
|
||
to { opacity: 0; }
|
||
}
|
||
@keyframes slideUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(20px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
/* Phase 2.3: Core Workflow Enhancements */
|
||
.modern-card {
|
||
background: var(--color-bg-base);
|
||
border: 1px solid var(--color-border-light);
|
||
box-shadow: var(--shadow-sm);
|
||
border-radius: var(--radius-xl);
|
||
transition: box-shadow 0.3s ease, border-color 0.3s ease;
|
||
}
|
||
.modern-card:hover {
|
||
box-shadow: var(--shadow-md);
|
||
border-color: var(--color-border-medium);
|
||
}
|
||
.section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 1.5rem;
|
||
padding-bottom: 1rem;
|
||
border-bottom: 1px solid var(--color-border-light);
|
||
}
|
||
.section-title {
|
||
font-size: var(--font-size-md);
|
||
font-weight: var(--font-weight-semibold);
|
||
color: var(--slate-800);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
.upload-zone-modern {
|
||
border: 2px dashed var(--slate-300);
|
||
border-radius: var(--radius-xl);
|
||
background-color: var(--slate-50);
|
||
transition: all 0.2s ease;
|
||
}
|
||
.upload-zone-modern:hover, .upload-zone-modern.drag-over {
|
||
border-color: var(--color-primary);
|
||
background-color: var(--indigo-50);
|
||
}
|
||
.btn-primary-large {
|
||
background: var(--color-primary);
|
||
color: white;
|
||
border-radius: var(--radius-lg);
|
||
transition: all 0.2s ease;
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
.btn-primary-large:hover {
|
||
background: var(--color-primary-hover);
|
||
box-shadow: var(--shadow-md);
|
||
transform: translateY(-1px);
|
||
}
|
||
.btn-primary-large:active {
|
||
background: var(--color-primary-active);
|
||
transform: translateY(0);
|
||
}
|
||
/* 全局入场动画 */
|
||
@keyframes fadeInUp {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.animate-fade-in-up {
|
||
animation: fadeInUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||
}
|
||
/* 悬浮历史记录按钮 (极简风格) */
|
||
.floating-history-btn {
|
||
position: fixed;
|
||
bottom: 2rem;
|
||
right: 2rem;
|
||
z-index: 40;
|
||
/* 使用 buttons.css 中的样式,这里只保留定位 */
|
||
}
|
||
@media (max-width: 768px) {
|
||
.floating-history-btn {
|
||
bottom: 1.5rem;
|
||
right: 1.5rem;
|
||
opacity: 0.8; /* 移动端稍微不那么透明,因为没有 hover */
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="bg-slate-50 min-h-screen">
|
||
|
||
|
||
<!-- Mobile Sidebar Overlay -->
|
||
<div id="sidebarOverlay" class="sidebar-overlay md:hidden"></div>
|
||
|
||
<div class="app-shell">
|
||
<!-- ===================== -->
|
||
<!-- 侧边栏 (Responsive) -->
|
||
<!-- ===================== -->
|
||
<aside id="appSidebar" class="app-sidebar">
|
||
<nav class="flex-1 px-3 py-4 space-y-1 overflow-y-auto custom-scrollbar overflow-x-hidden">
|
||
|
||
<!-- 可折叠的历史记录菜单 (Split Action) -->
|
||
<div id="historyMenuContainer" class="mb-4">
|
||
<div
|
||
class="flex items-stretch select-none group bg-white border border-slate-200 overflow-hidden">
|
||
<div id="sidebarHistoryMainBtn"
|
||
class="flex-1 flex items-center px-4 py-3 text-sm font-medium text-slate-600 hover:bg-[#000f33] hover:text-[#ffffff] cursor-pointer transition-all"
|
||
title="打开完整历史记录面板">
|
||
<!-- <iconify-icon icon="carbon:time"
|
||
class="mr-3 text-lg text-[#000f33] group-hover:text-[#ffffff] transition-colors"></iconify-icon> -->
|
||
<span>历史记录</span>
|
||
</div>
|
||
<button id="sidebarHistoryToggleBtn"
|
||
class="flex items-center justify-center px-3 hover:bg-[#000f33] text-slate-300 hover:text-slate-500 cursor-pointer transition-colors"
|
||
title="展开/收起最近记录">
|
||
<iconify-icon icon="carbon:chevron-right" class="transition-transform duration-200"
|
||
id="sidebarHistoryChevron" width="16"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
<div id="sidebarHistoryQuickList"
|
||
class="hidden py-1 space-y-0.5 pl-2 transition-all bg-white border border-slate-200">
|
||
<!-- JS 将在此处填充最近记录 -->
|
||
<div class="px-3 py-2 text-xs text-slate-400 text-center">加载中...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="my-4 border-t border-slate-100 mx-3 transition-all" id="sidebarDivider"></div>
|
||
</nav>
|
||
|
||
</aside>
|
||
|
||
<!-- ===================== -->
|
||
<!-- 主内容区 -->
|
||
<!-- ===================== -->
|
||
<main class="app-main flex flex-col">
|
||
<!-- 移动端 Header
|
||
<header class="md:hidden bg-white/80 backdrop-blur-md border-b border-slate-200 px-4 py-3 flex items-center justify-between sticky top-0 z-10">
|
||
<a href="views/landing/landing-page.html" class="text-lg font-bold flex items-center gap-2 text-slate-800">
|
||
<img src="public/pure.svg" class="w-7 h-7" alt="PBX Logo">
|
||
PBX
|
||
</a>
|
||
<div class="flex items-center gap-3">
|
||
<button id="mobileHistoryBtn" class="text-slate-600 p-1">
|
||
<iconify-icon icon="carbon:time" width="20"></iconify-icon>
|
||
</button>
|
||
<button id="mobileMenuBtn" class="text-slate-600 p-1">
|
||
<iconify-icon icon="carbon:menu" width="24"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
</header> -->
|
||
|
||
<!-- 滚动内容容器 -->
|
||
<div class="flex-1 overflow-y-auto custom-scrollbar">
|
||
<div id="mainAppContainer" class="container h-full flex flex-col justify-center mx-auto px-4 py-6 md:py-8 max-w-6xl animate-fade-in-up">
|
||
|
||
<!-- ===================== -->
|
||
<!-- 顶部欢迎区 (Modern Dashboard Style) -->
|
||
<!-- ===================== -->
|
||
<!-- <div class="workspace-header">
|
||
<div class="workspace-header-content">
|
||
<h1 class="text-3xl md:text-4xl font-bold text-slate-900 mb-4 tracking-tight" style="color: var(--slate-900);">
|
||
可学可学可学可学可学,测试提交后,vercel更新是否成功
|
||
</h1>
|
||
<p class="text-lg text-slate-600 leading-relaxed" style="color: var(--slate-600);">
|
||
配置多种OCR 引擎与 AI 翻译模型。上传文档后,Paper Burner X将给您流畅的阅读与处理体验。
|
||
</p>
|
||
</div>
|
||
</div> -->
|
||
|
||
<!-- ===================== -->
|
||
<!-- 主功能区 -->
|
||
<!-- ===================== -->
|
||
<div>
|
||
|
||
<div class="space-y-6">
|
||
|
||
<!-- ===================== -->
|
||
<!-- 卡片 1: OCR 文档解析 -->
|
||
<!-- ===================== -->
|
||
<div class="modern-card p-4 md:p-6 hidden">
|
||
|
||
<div class="mb-6">
|
||
<h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
|
||
<iconify-icon icon="carbon:api" class="mr-2 text-indigo-500" width="24"></iconify-icon>
|
||
API 密钥设置
|
||
</h2>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div>
|
||
<label for="mistralApiKeys" class="block text-sm font-medium text-gray-700 mb-1">Mistral API Keys <span class="text-red-500">*</span> (每行一个)</label>
|
||
<div class="relative">
|
||
<textarea id="mistralApiKeys" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all api-key-input" placeholder="输入您的 Mistral API Key(s),每行一个"></textarea>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label for="translationApiKeys" class="block text-sm font-medium text-gray-700 mb-1">翻译 API Keys (可选, 每行一个)</label>
|
||
<div class="relative">
|
||
<textarea id="translationApiKeys" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all api-key-input" placeholder="输入翻译服务 API Key(s),每行一个 (可选)"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ===================== -->
|
||
<!-- OCR 设置 -->
|
||
<!-- ===================== -->
|
||
<div>
|
||
<div class="section-header justify-between">
|
||
<h2 class="section-title">
|
||
<div class="w-9 h-9 rounded-xl bg-gradient-to-br from-indigo-50 to-indigo-100/80 border border-indigo-100 text-indigo-600 flex items-center justify-center mr-3 shadow-sm" style="background: var(--indigo-50); color: var(--color-primary); border-color: var(--indigo-100);">
|
||
<iconify-icon icon="carbon:scan" width="20"></iconify-icon>
|
||
</div>
|
||
OCR 文档解析
|
||
</h2>
|
||
<div class="flex items-center">
|
||
<span id="flashConfigTip" class="flash-tip-anim text-sm font-medium mr-2" style="color: var(--color-primary);">配置模型与Key</span>
|
||
<button id="modelKeyManagerBtn" class="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-slate-600 bg-slate-100 hover:bg-slate-200 rounded-lg transition-colors" title="模型与Key管理">
|
||
<iconify-icon icon="carbon:settings" width="18"></iconify-icon>
|
||
<span>设置</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- OCR 引擎选择(简化版) -->
|
||
<div>
|
||
<label for="ocrEngine" class="block text-sm font-medium text-gray-700 mb-1">OCR 引擎</label>
|
||
<select id="ocrEngine" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
<option value="none">不需要 OCR(仅文本)</option>
|
||
<option value="local">本地解析(仅文本 PDF)</option>
|
||
<option value="mistral">Mistral OCR</option>
|
||
<option value="mineru">MinerU</option>
|
||
<option value="doc2x">Doc2X</option>
|
||
</select>
|
||
<div id="localOcrHint" class="text-xs text-gray-500 mt-1 hidden">
|
||
<strong>本地解析</strong>:适用于文字型 PDF(非扫描件),免费且快速,但不支持图片和复杂排版的识别
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MinerU 翻译模式配置(仅当选择 MinerU 时显示) -->
|
||
<div id="mineruTranslationModeConfig" class="hidden mt-4">
|
||
<div class="border border-gray-200 rounded-lg bg-gray-50 p-4">
|
||
<label class="block text-sm font-medium text-gray-700 mb-3">MinerU 翻译模式</label>
|
||
<div class="space-y-2">
|
||
<label class="flex items-center gap-3 cursor-pointer">
|
||
<input type="radio" name="mineruTranslationMode" value="standard" checked class="w-4 h-4">
|
||
<div>
|
||
<div class="text-sm text-gray-800">标准翻译模式</div>
|
||
<div class="text-xs text-gray-500">将 MinerU 输出的 Markdown 按段落翻译</div>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-3 cursor-pointer">
|
||
<input type="radio" name="mineruTranslationMode" value="structured" class="w-4 h-4">
|
||
<div>
|
||
<div class="text-sm text-gray-800">结构化翻译模式</div>
|
||
<div class="text-xs text-gray-500">基于 JSON 进行翻译,支持 PDF 对照显示</div>
|
||
</div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===================== -->
|
||
<!-- 卡片 2: 翻译与分析 -->
|
||
<!-- ===================== -->
|
||
<div class="modern-card p-4 md:p-6 hidden">
|
||
<div class="section-header">
|
||
<h2 class="section-title">
|
||
<div class="w-9 h-9 rounded-xl border flex items-center justify-center mr-3 shadow-sm" style="background: var(--indigo-50); color: var(--color-primary); border-color: var(--indigo-100);">
|
||
<iconify-icon icon="carbon:language" width="20"></iconify-icon>
|
||
</div>
|
||
翻译与分析
|
||
</h2>
|
||
</div>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<!-- 翻译模型选择 -->
|
||
<div>
|
||
<label for="translationModel" class="block text-sm font-medium text-gray-700 mb-1">翻译和分析模型</label>
|
||
<select id="translationModel" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
<option value="none">不需要翻译</option>
|
||
<option value="mistral">Mistral Large</option>
|
||
<option value="deepseek">deepseek</option>
|
||
<option value="gemini">gemini</option>
|
||
<option value="deeplx">DeepLX (DeepL 接口)</option>
|
||
<option value="tongyi">通义百炼</option>
|
||
<option value="volcano">火山引擎</option>
|
||
<option value="custom">自定义模型</option>
|
||
</select>
|
||
<div id="translationModelDefaultHint" class="text-xs text-gray-500 mt-1"></div>
|
||
</div>
|
||
<!-- 目标语言选择 -->
|
||
<div>
|
||
<label for="targetLanguage" class="block text-sm font-medium text-gray-700 mb-1">目标语言</label>
|
||
<select id="targetLanguage" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
<option value="chinese">中文</option>
|
||
<option value="english">English</option>
|
||
<option value="japanese">日本語</option>
|
||
<option value="korean">한국어</option>
|
||
<option value="french">Français</option>
|
||
<option value="custom">自定义</option>
|
||
</select>
|
||
<!-- 自定义目标语言名称输入区域 -->
|
||
<div id="customTargetLanguageContainer" class="hidden mt-2">
|
||
<label for="customTargetLanguageInput" class="block text-sm font-medium text-gray-700 mb-1">自定义目标语言名称</label>
|
||
<input type="text" id="customTargetLanguageInput" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all" placeholder="例如: Spanish" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
</div>
|
||
</div>
|
||
<!-- Gemini 模型信息(选择 Gemini 时显示) -->
|
||
<div id="geminiModelInfo" class="md:col-span-2 hidden">
|
||
<div class="mt-2 border border-gray-200 rounded-lg bg-gray-50 p-3">
|
||
<div class="flex items-center justify-between">
|
||
<div class="text-sm text-gray-700 font-medium">Gemini 默认模型</div>
|
||
<div class="flex items-center gap-2">
|
||
<span id="geminiKeysCountHint" class="text-xs text-gray-500"></span>
|
||
<button id="geminiDetectBtn" type="button" class="px-2 py-1 text-xs border rounded hover:bg-white">检测可用模型</button>
|
||
</div>
|
||
</div>
|
||
<div class="mt-2 space-y-2">
|
||
<div class="flex flex-col sm:flex-row sm:items-center sm:gap-2">
|
||
<select id="geminiModelSelect" class="flex-1 px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 transition-colors" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
<!-- 动态填充,可为空则仅显示占位提示 -->
|
||
</select>
|
||
<button id="geminiModelSearchBtn" type="button" class="mt-2 sm:mt-0 px-3 py-1.5 text-sm border border-gray-300 rounded-md text-gray-600 hover:text-indigo-600 hover:border-indigo-400 transition-colors flex items-center gap-1 disabled:opacity-60 disabled:cursor-not-allowed" style="border-color: var(--color-border-medium); color: var(--slate-600);">
|
||
<iconify-icon icon="carbon:search" width="16"></iconify-icon>
|
||
搜索模型
|
||
</button>
|
||
</div>
|
||
<p id="geminiModelHint" class="text-xs text-gray-500">若列表为空,请先点击上方“检测可用模型”。</p>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<!-- DeepLX 模型信息(选择 DeepLX 时显示) -->
|
||
<div id="deeplxModelInfo" class="md:col-span-2 hidden">
|
||
<div class="mt-2 border border-gray-200 rounded-lg bg-gray-50 p-3">
|
||
<div class="flex items-center justify-between">
|
||
<div class="text-sm text-gray-700 font-medium">DeepLX 接口设置</div>
|
||
<span id="deeplxKeysCountHint" class="text-xs text-gray-500"></span>
|
||
</div>
|
||
<div class="mt-2 space-y-2">
|
||
<label for="deeplxEndpointTemplateInput" class="text-xs text-gray-500 block">接口模板(使用 <api-key> 或 {API_KEY} 占位符)</label>
|
||
<div class="flex items-center gap-2">
|
||
<input id="deeplxEndpointTemplateInput" type="text" class="flex-1 px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors" placeholder="https://api.deeplx.org/<api-key>/translate">
|
||
<button id="deeplxEndpointResetBtn" type="button" class="px-2 py-1 text-xs border rounded hover:bg-white">恢复默认</button>
|
||
</div>
|
||
<p class="text-xs text-gray-500 leading-5">默认模板会在占位符位置插入当前使用的 API Key,例如 <code>https://api.deeplx.org/<api-key>/translate</code>。如需自建代理,可在此输入自定义地址。</p>
|
||
<p id="deeplxTargetLangHint" class="text-xs leading-5" style="color: var(--indigo-700);"></p>
|
||
<div id="deeplxLangTable" class="mt-2 text-xs text-gray-600 leading-5"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DeepSeek 模型信息(选择 DeepSeek 时显示) -->
|
||
<div id="deepseekModelInfo" class="md:col-span-2 hidden">
|
||
<div class="mt-2 border border-gray-200 rounded-lg bg-gray-50 p-3">
|
||
<div class="flex items-center justify-between">
|
||
<div class="text-sm text-gray-700 font-medium">DeepSeek 默认模型</div>
|
||
<div class="flex items-center gap-2">
|
||
<span id="deepseekKeysCountHint" class="text-xs text-gray-500"></span>
|
||
<button id="deepseekDetectBtn" type="button" class="px-2 py-1 text-xs border rounded hover:bg-white">检测可用模型</button>
|
||
</div>
|
||
</div>
|
||
<div class="mt-2 space-y-2">
|
||
<div class="flex flex-col sm:flex-row sm:items-center sm:gap-2">
|
||
<select id="deepseekModelSelect" class="flex-1 px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 transition-colors" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);"></select>
|
||
<button id="deepseekModelSearchBtn" type="button" class="mt-2 sm:mt-0 px-3 py-1.5 text-sm border border-gray-300 rounded-md text-gray-600 hover:text-indigo-600 hover:border-indigo-400 transition-colors flex items-center gap-1 disabled:opacity-60 disabled:cursor-not-allowed" style="border-color: var(--color-border-medium); color: var(--slate-600);">
|
||
<iconify-icon icon="carbon:search" width="16"></iconify-icon>
|
||
搜索模型
|
||
</button>
|
||
</div>
|
||
<p id="deepseekModelHint" class="text-xs text-gray-500">若列表为空,请先点击上方“检测可用模型”。</p>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- 通义 模型信息(选择通义时显示) -->
|
||
<div id="tongyiModelInfo" class="md:col-span-2 hidden">
|
||
<div class="mt-2 border border-gray-200 rounded-lg bg-gray-50 p-3">
|
||
<div class="flex items-center justify-between">
|
||
<div class="text-sm text-gray-700 font-medium">通义 默认模型</div>
|
||
<div class="flex items-center gap-2">
|
||
<span id="tongyiKeysCountHint" class="text-xs text-gray-500"></span>
|
||
<button id="tongyiDetectBtn" type="button" class="px-2 py-1 text-xs border rounded hover:bg-white">检测可用模型</button>
|
||
</div>
|
||
</div>
|
||
<div class="mt-2 space-y-2">
|
||
<div class="flex flex-col sm:flex-row sm:items-center sm:gap-2">
|
||
<select id="tongyiModelSelect" class="flex-1 px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 transition-colors" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);"></select>
|
||
<button id="tongyiModelSearchBtn" type="button" class="mt-2 sm:mt-0 px-3 py-1.5 text-sm border border-gray-300 rounded-md text-gray-600 hover:text-indigo-600 hover:border-indigo-400 transition-colors flex items-center gap-1 disabled:opacity-60 disabled:cursor-not-allowed" style="border-color: var(--color-border-medium); color: var(--slate-600);">
|
||
<iconify-icon icon="carbon:search" width="16"></iconify-icon>
|
||
搜索模型
|
||
</button>
|
||
</div>
|
||
<p id="tongyiModelHint" class="text-xs text-gray-500">若列表为空,请先点击上方“检测可用模型”。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 火山 模型信息(选择火山时显示) -->
|
||
<div id="volcanoModelInfo" class="md:col-span-2 hidden">
|
||
<div class="mt-2 border border-gray-200 rounded-lg bg-gray-50 p-3">
|
||
<div class="flex items-center justify-between">
|
||
<div class="text-sm text-gray-700 font-medium">火山 默认模型</div>
|
||
<div class="flex items-center gap-2">
|
||
<span id="volcanoKeysCountHint" class="text-xs text-gray-500"></span>
|
||
<button id="volcanoDetectBtn" type="button" class="px-2 py-1 text-xs border rounded hover:bg-white">检测可用模型</button>
|
||
</div>
|
||
</div>
|
||
<div class="mt-2">
|
||
<select id="volcanoModelSelect" class="w-full px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 transition-colors" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);"></select>
|
||
<p id="volcanoModelHint" class="mt-1 text-xs text-gray-500">若列表为空,请手动输入或在设置中选择。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 自定义源站点设置区域 (选择自定义模型时显示) -->
|
||
<div id="customSourceSiteContainer" class="md:col-span-2 hidden">
|
||
<div class="pt-4">
|
||
<!-- 展开/收起自定义源站点设置的按钮 -->
|
||
<button id="customSourceSiteToggle" type="button" class="text-sm text-gray-500 hover:text-gray-700 flex items-center">
|
||
<iconify-icon icon="carbon:settings-adjust" class="mr-1" width="16"></iconify-icon>
|
||
<span>自定义源站点设置</span>
|
||
<iconify-icon id="customSourceSiteToggleIcon" icon="carbon:chevron-down" class="ml-1" width="16"></iconify-icon>
|
||
</button>
|
||
<!-- 自定义源站点详细配置区 -->
|
||
<div id="customSourceSite" class="hidden mt-2 border-2 border-dashed border-gray-300 rounded-lg p-4">
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<!-- 选择已保存的自定义源站点 -->
|
||
<div>
|
||
<label for="customSourceSiteSelect" class="block text-sm font-medium text-gray-700 mb-1">选择自定义源站点</label>
|
||
<select id="customSourceSiteSelect" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
<!-- Options will be populated by JavaScript -->
|
||
</select>
|
||
</div>
|
||
<!-- 显示选定源站点的基本信息 -->
|
||
<div id="customSourceSiteInfo" class="mt-3 w-full border border-gray-200 rounded-lg bg-gray-50 p-0 hidden md:col-span-2">
|
||
<!-- 站点信息将由JS动态填充 -->
|
||
</div>
|
||
<!-- 管理API Key及检测模型的按钮(通常根据选择的源站点动态显示/隐藏) -->
|
||
<div class="mt-2 flex justify-end w-full md:col-span-2">
|
||
<button id="manageSourceSiteKeyBtn" class="px-3 py-1.5 text-sm text-white rounded transition-colors flex items-center hidden" style="background-color: var(--color-primary);">
|
||
<iconify-icon icon="carbon:api" class="mr-1" width="16"></iconify-icon>
|
||
管理该站点 API Key
|
||
</button>
|
||
<button id="detectModelsBtn" type="button" class="hidden px-3 py-1.5 text-sm text-white rounded transition-colors flex items-center" style="background-color: var(--color-primary);">
|
||
<iconify-icon icon="carbon:model-alt" class="mr-1" width="16"></iconify-icon>
|
||
检测可用模型
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== 翻译备择库管理(多术语库) ===== -->
|
||
<div id="glossaryManagerSection" class="md:col-span-2 hidden">
|
||
<div class="bg-white/90 backdrop-blur-sm border border-dashed rounded-2xl shadow-sm p-5 md:p-6 space-y-5" style="border-color: var(--color-border-light);">
|
||
<div class="flex flex-col md:flex-row md:items-start md:justify-between gap-4">
|
||
<div>
|
||
<div class="flex items-center gap-2 text-lg font-semibold text-slate-800">
|
||
<iconify-icon icon="carbon:dictionary" width="22" style="color: var(--color-primary);"></iconify-icon>
|
||
<span>翻译备择库管理</span>
|
||
<span id="glossarySetsCountHint" class="ml-1 inline-flex items-center text-xs font-medium rounded-full px-2 py-0.5" style="color: var(--color-primary); background-color: var(--indigo-50); border-color: var(--indigo-100);">启用 0 / 0</span>
|
||
</div>
|
||
<p class="mt-2 text-sm text-slate-500 leading-relaxed">
|
||
支持维护多套术语库,命中时自动注入翻译提示,帮助保持术语一致性。可按项目导入导出,灵活协同。
|
||
</p>
|
||
</div>
|
||
<label class="inline-flex items-center gap-2 bg-slate-50 border border-slate-200 rounded-full px-4 py-2 text-sm text-slate-600 select-none">
|
||
<input id="enableGlossaryToggle" type="checkbox" class="h-4 w-4 border-slate-300 rounded" style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span>启用翻译备择库</span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="flex flex-wrap items-center gap-2 md:gap-3">
|
||
<button id="addGlossarySetBtn" class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-slate-700 bg-white border border-slate-200 rounded-lg hover:text-indigo-600 hover:shadow-sm transition-all" style="border-color: var(--color-border-medium);">
|
||
<iconify-icon icon="carbon:add-alt" width="18"></iconify-icon>新增术语库
|
||
</button>
|
||
<button id="importGlossarySetBtn" class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-slate-700 bg-white border border-slate-200 rounded-lg hover:text-indigo-600 hover:shadow-sm transition-all" style="border-color: var(--color-border-medium);">
|
||
<iconify-icon icon="carbon:import" width="18"></iconify-icon>导入术语库
|
||
</button>
|
||
<input id="importGlossarySetFile" type="file" accept="application/json" class="hidden">
|
||
<button id="exportAllGlossarySetsBtn" class="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-slate-700 bg-white border border-slate-200 rounded-lg hover:text-indigo-600 hover:shadow-sm transition-all" style="border-color: var(--color-border-medium);">
|
||
<iconify-icon icon="carbon:export" width="18"></iconify-icon>导出全部术语库
|
||
</button>
|
||
</div>
|
||
|
||
<div id="glossarySetsTable" class="space-y-3"></div>
|
||
|
||
<!-- 编辑面板 -->
|
||
<div id="glossaryEditorPanel" class="hidden border border-slate-200 rounded-xl bg-slate-50/70 p-4 md:p-5" data-editing-id="">
|
||
<!-- 工具栏容器(搜索、批量操作等) -->
|
||
<div id="glossaryEditorToolbar"></div>
|
||
|
||
<!-- 条目列表容器 -->
|
||
<div id="glossaryEntriesTable"></div>
|
||
|
||
<!-- 分页容器 -->
|
||
<div id="glossaryEditorPagination"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===================== -->
|
||
<!-- 卡片 3: 高级设置 -->
|
||
<!-- ===================== -->
|
||
<div class="modern-card p-4 md:p-6 hidden">
|
||
<!-- 展开/收起高级设置的头部 -->
|
||
<div id="advancedSettingsToggle" class="section-header justify-between cursor-pointer select-none hover:opacity-80 transition-opacity -mb-4 pb-4">
|
||
<h2 class="section-title">
|
||
<div class="w-8 h-8 rounded-lg bg-slate-100 text-slate-600 flex items-center justify-center mr-3">
|
||
<iconify-icon icon="carbon:settings-adjust" width="20"></iconify-icon>
|
||
</div>
|
||
高级设置
|
||
</h2>
|
||
<iconify-icon icon="carbon:chevron-down" class="text-slate-400 transition-transform duration-300" width="24" id="advancedSettingsIcon"></iconify-icon>
|
||
</div>
|
||
|
||
<!-- 高级设置详细内容区 -->
|
||
<div id="advancedSettings" class="hidden pt-4">
|
||
<div class="pt-2">
|
||
<h3 class="text-base font-semibold text-slate-800 mb-4 flex items-center">
|
||
<iconify-icon icon="carbon:settings-check" class="mr-2" style="color: var(--color-primary);"></iconify-icon>
|
||
性能与流程
|
||
</h3>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
||
<!-- 文件处理并发数 -->
|
||
<div>
|
||
<label for="concurrencyLevel" class="block text-sm font-medium text-gray-700 mb-1">文件处理并发数</label>
|
||
<input type="number" id="concurrencyLevel" min="1" max="50" value="1"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 transition-all text-sm" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
<p class="text-xs text-gray-500 mt-1">同时处理的文件数量。默认 1。</p>
|
||
</div>
|
||
<!-- 翻译任务并发数 -->
|
||
<div>
|
||
<label for="translationConcurrencyLevel" class="block text-sm font-medium text-gray-700 mb-1">翻译任务并发数</label>
|
||
<input type="number" id="translationConcurrencyLevel" min="1" max="150" value="15"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 transition-all text-sm" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
<p class="text-xs text-gray-500 mt-1">同时进行的翻译 API 调用数量。默认 15。</p>
|
||
</div>
|
||
<!-- 翻译分段最大Token数 -->
|
||
<div class="md:col-span-2">
|
||
<label for="maxTokensPerChunk" class="block text-sm font-medium text-gray-700 mb-1">翻译分段最大Token数 (<span id="maxTokensPerChunkValue">2000</span>)</label>
|
||
<input type="range" id="maxTokensPerChunk" min="500" max="10000" step="100" value="1000"
|
||
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" style="accent-color: var(--color-primary);">
|
||
<p class="text-xs text-gray-500 mt-1">长文档翻译时,按此限制分段。默认 2000。</p>
|
||
</div>
|
||
<!-- 跳过已处理文件选项 -->
|
||
<div class="md:col-span-2 flex items-center mt-2">
|
||
<input type="checkbox" id="skipProcessedFiles" class="rounded focus:ring-indigo-500 h-4 w-4 mr-2 flex-shrink-0" style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<label for="skipProcessedFiles" class="text-sm text-gray-600">跳过已处理过的文件 (基于文件名和大小)</label>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 class="text-lg font-semibold text-gray-800 mb-4 border-b pb-2 flex items-center"><iconify-icon icon="carbon:edit" class="mr-2 text-gray-600"></iconify-icon>翻译提示词管理</h3>
|
||
|
||
<!-- 提示词模式选择(单选,三选一) -->
|
||
<div class="mb-4">
|
||
<div class="flex items-center space-x-6 mb-3">
|
||
<label class="flex items-center">
|
||
<input type="radio" name="promptMode" value="pool" id="promptModePool" class="rounded focus:ring-indigo-500 h-4 w-4 mr-2" style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span class="text-sm font-medium text-gray-700">提示词池</span>
|
||
</label>
|
||
<label class="flex items-center">
|
||
<input type="radio" name="promptMode" value="builtin" id="promptModeBuiltin" class="rounded focus:ring-indigo-500 h-4 w-4 mr-2" checked style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span class="text-sm font-medium text-gray-700">内置提示词</span>
|
||
</label>
|
||
<label class="flex items-center">
|
||
<input type="radio" name="promptMode" value="custom" id="promptModeCustom" class="rounded focus:ring-indigo-500 h-4 w-4 mr-2" style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span class="text-sm font-medium text-gray-700">单个自定义</span>
|
||
</label>
|
||
</div>
|
||
<p class="text-xs text-gray-500">选择翻译提示词的使用方式。提示词池提供多样表达且保持风格一致。</p>
|
||
</div>
|
||
|
||
<!-- 单个自定义提示区域 -->
|
||
<div id="customPromptsContainer" class="hidden space-y-4 mb-6">
|
||
<div>
|
||
<label for="defaultSystemPrompt" class="block text-sm font-medium text-gray-700 mb-1">自定义系统提示</label>
|
||
<textarea id="defaultSystemPrompt" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 transition-all api-key-input text-sm" placeholder="输入系统提示词..." style="border-color: var(--color-border-medium);"></textarea>
|
||
</div>
|
||
<div>
|
||
<label for="defaultUserPromptTemplate" class="block text-sm font-medium text-gray-700 mb-1">自定义用户提示模板</label>
|
||
<textarea id="defaultUserPromptTemplate" rows="6" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 transition-all api-key-input text-sm" placeholder="请将以下内容翻译为${targetLangName}: ${content}" style="border-color: var(--color-border-medium);"></textarea>
|
||
<p class="text-xs text-gray-500 mt-1">可用占位符:<code>${targetLangName}</code>, <code>${content}</code></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 提示词池管理区域 -->
|
||
<div id="promptPoolContainer" class="hidden">
|
||
|
||
<!-- 参考提示词输入(去除橙色,改为中性) -->
|
||
<div id="ppRefSection" class="bg-gray-50 rounded-lg p-4 mb-4 border border-gray-200">
|
||
<h4 class="font-medium text-gray-800 mb-3 flex items-center">
|
||
<iconify-icon icon="carbon:template" class="mr-2" width="18" style="color: var(--color-primary);"></iconify-icon>
|
||
参考提示词 (AI生成基础)
|
||
</h4>
|
||
<div class="space-y-3">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">参考系统提示词</label>
|
||
<textarea id="referenceSystemPrompt" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-1 focus:ring-indigo-500 text-sm" placeholder="你是专业的翻译助手,请准确翻译用户提供的内容。" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);"></textarea>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">参考用户提示模板</label>
|
||
<textarea id="referenceUserPrompt" rows="4" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-1 focus:ring-indigo-500 text-sm" placeholder="请将以下内容翻译为${targetLangName}: ${content}" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);"></textarea>
|
||
<p class="text-xs text-gray-500 mt-1">必须包含 <code>${targetLangName}</code> 和 <code>${content}</code> 占位符</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 提示词池控制面板 -->
|
||
<div class="rounded-lg p-4 mb-4" style="background-color: var(--indigo-50);">
|
||
<div id="ppControlHeader" class="flex justify-between items-center mb-3">
|
||
<div class="flex items-center">
|
||
<iconify-icon icon="carbon:ai-status-running" class="mr-2" width="20" style="color: var(--color-primary);"></iconify-icon>
|
||
<span class="font-medium" style="color: var(--indigo-800);">AI生成提示词池</span>
|
||
<span id="poolStatsText" class="ml-2 text-sm" style="color: var(--color-primary);">(0 个已选)</span>
|
||
</div>
|
||
<div class="flex items-center space-x-2">
|
||
<select id="promptPoolMode" class="text-sm border rounded px-2 py-1 bg-white" style="border-color: var(--indigo-100);">
|
||
<option value="random">智能随机</option>
|
||
<option value="rotation">健康轮换</option>
|
||
</select>
|
||
<button id="toggleGeneratorPromptsBtn" class="text-xs px-2 py-1 border rounded hover:bg-gray-50">生成器提示词</button>
|
||
<button id="resetGeneratorPromptsBtn" class="text-xs px-2 py-1 border rounded hover:bg-gray-50">重置为默认</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 健康状态概览 -->
|
||
<div id="healthOverview" class="bg-white rounded-md p-3 mb-3 border border-indigo-200" style="border-color: var(--indigo-100);">
|
||
<div class="flex items-center justify-between mb-2">
|
||
<span class="text-sm font-medium text-gray-700">健康状态概览</span>
|
||
<button id="healthSettingsBtn" class="text-xs text-indigo-600 hover:text-indigo-800" style="color: var(--color-primary);">
|
||
<iconify-icon icon="carbon:settings" width="14"></iconify-icon> 设置
|
||
</button>
|
||
</div>
|
||
<div class="grid grid-cols-4 gap-3 text-center">
|
||
<div class="text-xs">
|
||
<div class="text-green-600 font-medium" id="healthyCount">0</div>
|
||
<div class="text-gray-500">健康</div>
|
||
</div>
|
||
<div class="text-xs">
|
||
<div class="text-yellow-600 font-medium" id="degradedCount">0</div>
|
||
<div class="text-gray-500">降级</div>
|
||
</div>
|
||
<div class="text-xs">
|
||
<div class="text-red-600 font-medium" id="deactivatedCount">0</div>
|
||
<div class="text-gray-500">失活</div>
|
||
</div>
|
||
<div class="text-xs">
|
||
<div class="text-indigo-600 font-medium" id="successRate" style="color: var(--color-primary);">0%</div>
|
||
<div class="text-gray-500">成功率</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 生成器元提示词(可选) -->
|
||
<div id="generatorPromptsPanel" class="hidden mb-3">
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||
<div>
|
||
<label class="block text-xs text-gray-600 mb-1">生成器系统提示(可选)</label>
|
||
<textarea id="generatorSystemPrompt" rows="3" class="w-full text-sm border border-gray-300 rounded px-2 py-1" placeholder="自定义用于生成变体的系统提示(不填用默认)"></textarea>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs text-gray-600 mb-1">生成器用户提示(可选)</label>
|
||
<textarea id="generatorUserPrompt" rows="3" class="w-full text-sm border border-gray-300 rounded px-2 py-1" placeholder="自定义用于生成变体的用户提示(不填用默认)"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 生成参数控制 -->
|
||
<div id="ppGenSection">
|
||
<!-- 生成参数控制 -->
|
||
<div class="grid grid-cols-1 md:grid-cols-5 gap-3 mb-3">
|
||
<div>
|
||
<label class="block text-xs text-gray-600 mb-1">生成数量</label>
|
||
<input type="number" id="variationCount" value="10" min="5" max="20" class="w-full text-sm border border-gray-300 rounded px-2 py-1">
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs text-gray-600 mb-1">并发数</label>
|
||
<input type="number" id="generationConcurrency" value="1" min="1" max="10" class="w-full text-sm border border-gray-300 rounded px-2 py-1">
|
||
<p class="text-xs text-gray-500 mt-1">最多 10;总生成量 = 生成数量 × 并发数</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs text-gray-600 mb-1">生成语言</label>
|
||
<input type="text" id="generatorLanguage" value="中文" placeholder="如:中文、English、日本語" class="w-full text-sm border border-gray-300 rounded px-2 py-1">
|
||
<p class="text-xs text-gray-500 mt-1">用于替换生成器提示词中的 ${genlanguage}</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs text-gray-600 mb-1">相似度</label>
|
||
<select id="similarityControl" class="w-full text-sm border border-gray-300 rounded px-2 py-1">
|
||
<option value="0.2">差异很大</option>
|
||
<option value="0.4">中等差异</option>
|
||
<option value="0.6" selected>适度相似</option>
|
||
<option value="0.8">高度相似</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs text-gray-600 mb-1">AI模型</label>
|
||
<select id="generationModel" class="w-full text-sm border border-gray-300 rounded px-2 py-1">
|
||
<option value="">加载中...</option>
|
||
</select>
|
||
<p class="text-xs text-gray-500 mt-1">自动使用已配置的API密钥</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 生成控制按钮 -->
|
||
<div class="flex items-center justify-between">
|
||
<div class="flex items-center space-x-3">
|
||
<button id="generateVariationsBtn" class="px-4 py-2 text-white rounded-md transition-colors text-sm flex items-center disabled:opacity-50 disabled:cursor-not-allowed" style="background-color: var(--color-primary);">
|
||
<iconify-icon icon="carbon:ai-status" class="mr-2" width="16"></iconify-icon>
|
||
AI生成变体
|
||
</button>
|
||
<div id="generateStatus" class="text-xs text-gray-600 hidden">
|
||
<iconify-icon icon="carbon:loading" class="animate-spin mr-1" width="14"></iconify-icon>
|
||
正在生成...
|
||
</div>
|
||
</div>
|
||
<div class="flex items-center space-x-2">
|
||
<button id="clearPoolBtn" class="px-3 py-1.5 bg-gray-400 text-white rounded text-xs hover:bg-gray-500 transition-colors">
|
||
清空
|
||
</button>
|
||
<button id="importPoolBtn" class="px-3 py-1.5 bg-green-600 text-white rounded text-xs hover:bg-green-700 transition-colors">
|
||
导入
|
||
</button>
|
||
<button id="exportPoolBtn" class="px-3 py-1.5 bg-green-600 text-white rounded text-xs hover:bg-green-700 transition-colors">
|
||
导出
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div> <!-- /#ppGenSection -->
|
||
</div>
|
||
|
||
<!-- 提示词列表容器 -->
|
||
<div id="promptPoolList" class="space-y-2 max-h-96 overflow-y-auto border border-gray-200 rounded-lg p-3 bg-gray-50">
|
||
<div class="text-center text-gray-500 text-sm py-8">
|
||
<iconify-icon icon="carbon:ai-status-queued" class="text-gray-400 mb-2" width="32"></iconify-icon>
|
||
<p>暂无提示词</p>
|
||
<p class="text-xs mt-1">请先填写参考提示词,然后点击"AI生成变体"</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div><!-- /.modern-card (Advanced) -->
|
||
|
||
<!-- ===================== -->
|
||
<!-- 卡片 4: 文件上传 -->
|
||
<!-- ===================== -->
|
||
<div class="modern-card p-4 md:p-6">
|
||
<div class="section-header justify-between">
|
||
<h2 class="section-title">
|
||
<div class="w-9 h-9 rounded-xl text-white flex items-center justify-center mr-3 shadow-md" style="background: var(--color-primary); box-shadow: var(--shadow-sm);">
|
||
<iconify-icon icon="mdi:upload" width="20"></iconify-icon>
|
||
</div>
|
||
文件上传
|
||
</h2>
|
||
<label id="batchModeToggleWrapper" class="hidden flex items-center space-x-2 text-sm text-slate-600 bg-slate-100 px-3 py-1.5 rounded-full cursor-pointer hover:bg-slate-200 transition-colors">
|
||
<input type="checkbox" id="batchModeToggle" class="rounded border-slate-300 focus:ring-indigo-500 h-4 w-4" style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span class="select-none font-medium">批量模式</span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 拖拽区域 -->
|
||
<div id="dropZone" class="upload-zone-modern p-8 text-center mb-6 group cursor-pointer">
|
||
<input type="file" id="pdfFileInput" accept=".pdf,.md,.txt,.docx,.pptx,.html,.htm,.epub,.yaml,.yml,.zip" class="hidden" multiple>
|
||
<input type="file" id="folderInput" class="hidden" webkitdirectory mozdirectory multiple>
|
||
|
||
<div class="mx-auto mb-4 w-20 h-20 flex items-center justify-center rounded-full transition-all duration-300" style="background-color: var(--indigo-50); color: var(--color-primary);">
|
||
<iconify-icon icon="mdi:upload" width="40"></iconify-icon>
|
||
</div>
|
||
<h3 class="text-lg font-semibold text-slate-700 mb-2">点击或拖拽文件到此处</h3>
|
||
<p class="text-sm text-slate-500 mb-6 max-w-md mx-auto">
|
||
支持 PDF, Markdown, Word, PowerPoint, HTML, EPUB 等常见文档格式
|
||
</p>
|
||
|
||
<div class="flex flex-wrap justify-center gap-3">
|
||
<button id="browseFilesBtn" class="px-5 py-2.5 bg-white border border-slate-200 text-slate-700 rounded-xl hover:text-indigo-600 transition-all flex items-center gap-2 font-medium shadow-sm" style="border-color: var(--color-border-medium); color: var(--slate-700);">
|
||
<iconify-icon icon="carbon:document-add" width="18"></iconify-icon>
|
||
选择文件
|
||
</button>
|
||
<button id="browseFolderBtn" class="px-5 py-2.5 bg-white border border-slate-200 text-slate-700 rounded-xl hover:text-indigo-600 transition-all flex items-center gap-2 font-medium shadow-sm" style="border-color: var(--color-border-medium); color: var(--slate-700);">
|
||
<iconify-icon icon="carbon:folder-add" width="18"></iconify-icon>
|
||
选择文件夹
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 已选文件列表 -->
|
||
<div id="fileListContainer" class="hidden">
|
||
<div class="bg-slate-50 rounded-xl border border-slate-200 overflow-hidden">
|
||
<div class="px-4 py-3 border-b border-slate-200 flex justify-between items-center bg-slate-100/50">
|
||
<span class="text-sm font-semibold text-slate-700 flex items-center gap-2">
|
||
<iconify-icon icon="carbon:document-tasks" class="text-slate-500"></iconify-icon>
|
||
待处理文件
|
||
</span>
|
||
<button id="clearFilesBtn" class="text-xs text-red-500 hover:text-red-600 hover:bg-red-50 px-2 py-1 rounded transition-colors flex items-center gap-1">
|
||
<iconify-icon icon="carbon:trash-can" width="14"></iconify-icon>
|
||
清空
|
||
</button>
|
||
</div>
|
||
<div class="p-3">
|
||
<div id="fileFormatFilters" class="flex flex-wrap gap-2 mb-3"></div>
|
||
<div id="fileList" class="max-h-[300px] overflow-y-auto pr-1 space-y-2">
|
||
<!-- JS 填充 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 批量配置 (保持原有逻辑,仅微调样式) -->
|
||
<div id="batchModeConfig" class="hidden mt-4 border rounded-xl overflow-hidden" style="border-color: var(--indigo-100); background-color: var(--indigo-50);">
|
||
<div class="flex items-center justify-between px-4 py-3 border-b" style="border-color: var(--indigo-100);">
|
||
<span class="text-sm font-semibold" style="color: var(--indigo-700);">批量导出配置</span>
|
||
<button type="button" id="batchModeConfigToggle" class="text-xs hover:opacity-80 flex items-center space-x-1" style="color: var(--color-primary);">
|
||
<iconify-icon id="batchModeConfigToggleIcon" icon="carbon:chevron-down" width="14"></iconify-icon>
|
||
<span id="batchModeConfigToggleLabel">展开设置</span>
|
||
</button>
|
||
</div>
|
||
<div id="batchModeConfigBody" class="hidden px-4 py-4">
|
||
<div class="flex flex-col md:flex-row md:items-start md:space-x-6 space-y-4 md:space-y-0">
|
||
<div class="flex-1 min-w-0">
|
||
<label for="batchModeTemplate" class="block text-sm font-medium text-gray-700 mb-1">命名模板</label>
|
||
<input type="text" id="batchModeTemplate" class="w-full rounded-md border bg-white px-3 py-2 text-sm text-gray-800 focus:ring-2" placeholder="例如 {original_name}_{output_language}_{processing_time:YYYYMMDD-HHmmss}.{original_type}" style="border-color: var(--indigo-200); outline-color: var(--color-primary);">
|
||
<p class="text-xs text-gray-600 mt-2 leading-4">
|
||
可用变量:<span class="font-mono text-[11px]">{original_name}</span>、<span class="font-mono text-[11px]">{original_type}</span>、<span class="font-mono text-[11px]">{output_language}</span>、<span class="font-mono text-[11px]">{processing_time}</span> (支持 <span class="font-mono text-[11px]">{processing_time:YYYYMMDD-HHmmss}</span> 等格式)。
|
||
</p>
|
||
</div>
|
||
<div class="md:w-64">
|
||
<span class="block text-sm font-medium text-gray-700 mb-1">批量导出格式</span>
|
||
<div class="flex flex-wrap items-center gap-3 text-sm text-gray-700">
|
||
<label class="inline-flex items-center space-x-2">
|
||
<input type="checkbox" data-batch-format="original" class="rounded border-gray-300 focus:ring-indigo-500" checked style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span>原格式</span>
|
||
</label>
|
||
<label class="inline-flex items-center space-x-2">
|
||
<input type="checkbox" data-batch-format="markdown" class="rounded border-gray-300 focus:ring-indigo-500" checked style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span>Markdown</span>
|
||
</label>
|
||
<label class="inline-flex items-center space-x-2">
|
||
<input type="checkbox" data-batch-format="html" class="rounded border-gray-300 focus:ring-indigo-500" style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span>HTML</span>
|
||
</label>
|
||
<label class="inline-flex items-center space-x-2">
|
||
<input type="checkbox" data-batch-format="docx" class="rounded border-gray-300 focus:ring-indigo-500" style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span>DOCX</span>
|
||
</label>
|
||
<span class="hidden w-px h-4 md:block" style="background-color: var(--indigo-100);"></span>
|
||
<label class="inline-flex items-center space-x-2">
|
||
<input type="checkbox" id="batchModeZipToggle" data-batch-zip class="rounded border-gray-300 focus:ring-indigo-500" style="color: var(--color-primary); accent-color: var(--color-primary);">
|
||
<span>导出时打包为 ZIP</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 验证状态提示区域 -->
|
||
<div id="validationAlert" class="hidden mt-6 mb-4 p-3.5 rounded-lg border transition-all shadow-sm">
|
||
<div class="flex items-start gap-3">
|
||
<iconify-icon id="validationIcon" icon="carbon:warning" class="flex-shrink-0 mt-0.5" width="18"></iconify-icon>
|
||
<div class="flex-1 min-w-0">
|
||
<div class="flex items-center justify-between mb-1">
|
||
<h3 id="validationTitle" class="font-medium text-sm"></h3>
|
||
<button id="validationRefreshBtn" class="text-xs opacity-60 hover:opacity-100 transition-opacity" title="重新检查">
|
||
<iconify-icon icon="carbon:renew" width="16"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
<p id="validationMessage" class="text-sm opacity-90"></p>
|
||
<div id="validationActions" class="mt-2.5 flex flex-wrap gap-2"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 处理按钮 -->
|
||
<div class="flex justify-center mt-8 mb-12">
|
||
<button id="processBtn" class="btn-primary-large px-10 hidden py-4 text-lg font-semibold text-white rounded-2xl transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-3 hover:scale-[1.02] active:scale-[0.98]" style="background: var(--color-primary);">
|
||
<iconify-icon icon="carbon:rocket" width="24"></iconify-icon>
|
||
<span>开始处理</span>
|
||
</button>
|
||
<button id="onlyReadBtn" class="btn-primary-large px-10 py-4 text-lg font-semibold text-white rounded-2xl transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-3 hover:scale-[1.02] active:scale-[0.98]" style="background: var(--color-primary);">
|
||
<iconify-icon icon="carbon:rocket" width="24"></iconify-icon>
|
||
<span>仅阅读</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===================== -->
|
||
<!-- 进度与结果区域 (恢复为单栏垂直排列) -->
|
||
<!-- ===================== -->
|
||
<div class="mt-12 space-y-6">
|
||
|
||
<!-- ===================== -->
|
||
<!-- 进度与结果区域 -->
|
||
<!-- ===================== -->
|
||
<!-- 处理进度显示区 -->
|
||
<div id="progressSection" class="modern-card p-4 md:p-6 hidden">
|
||
<div class="section-header">
|
||
<h2 class="section-title">
|
||
<div class="w-8 h-8 rounded-lg flex items-center justify-center mr-3" style="background-color: var(--indigo-100); color: var(--color-primary);">
|
||
<iconify-icon icon="carbon:progress-bar-round" width="20"></iconify-icon>
|
||
</div>
|
||
处理进度
|
||
</h2>
|
||
</div>
|
||
|
||
<div class="space-y-4">
|
||
<!-- 进度概览 -->
|
||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
|
||
<span id="batchProgressText" class="text-sm font-semibold text-slate-700"></span>
|
||
<span id="concurrentProgressText" class="text-xs font-medium text-slate-500 bg-slate-100 px-2 py-1 rounded-full"></span>
|
||
</div>
|
||
|
||
<!-- 主进度条 -->
|
||
<div>
|
||
<div class="flex justify-between mb-2">
|
||
<span id="progressStep" class="text-sm font-medium text-slate-700">等待开始...</span>
|
||
<span id="progressPercentage" class="text-sm font-bold" style="color: var(--color-primary);">0%</span>
|
||
</div>
|
||
<div class="w-full bg-slate-100 rounded-full h-3 overflow-hidden">
|
||
<div id="progressBar" class="h-full rounded-full transition-all duration-300" style="width: 0%; background: var(--color-primary);"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 处理日志 -->
|
||
<div id="progressLog" class="bg-slate-900 rounded-xl p-4 h-48 overflow-auto text-xs text-slate-300 font-mono leading-relaxed shadow-inner"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 处理结果显示与下载区 -->
|
||
<div id="resultsSection" class="modern-card p-4 md:p-6 hidden">
|
||
<div class="section-header">
|
||
<h2 class="section-title">
|
||
<div class="w-8 h-8 rounded-lg bg-green-100 text-green-600 flex items-center justify-center mr-3">
|
||
<iconify-icon icon="carbon:result" width="20"></iconify-icon>
|
||
</div>
|
||
处理完成
|
||
</h2>
|
||
</div>
|
||
|
||
<div class="bg-green-50 border border-green-100 rounded-xl p-4 mb-6 flex items-start gap-3">
|
||
<iconify-icon icon="carbon:checkmark-filled" class="text-green-500 mt-0.5" width="20"></iconify-icon>
|
||
<div>
|
||
<h4 class="text-sm font-semibold text-green-800 mb-1">所有任务已完成</h4>
|
||
<p class="text-sm text-green-700">您可以下载包含所有结果的 ZIP 文件,或在历史记录中查看详情。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 结果统计摘要 -->
|
||
<div id="resultsSummary" class="mb-6 text-sm text-slate-600">
|
||
<!-- Summary will be added here -->
|
||
</div>
|
||
|
||
<!-- 下载全部结果按钮 -->
|
||
<button id="downloadAllBtn" class="btn-primary-large px-6 py-3 text-white rounded-xl transition-all flex items-center justify-center w-full sm:w-auto gap-2 font-medium hover:scale-[1.02] active:scale-[0.98] bg-gradient-to-r from-green-500 to-emerald-600 shadow-green-500/20">
|
||
<iconify-icon icon="carbon:download" width="20"></iconify-icon>
|
||
<span>下载全部结果 (ZIP)</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div><!-- /#mainAppContainer -->
|
||
</div><!-- /.flex-1.overflow-y-auto -->
|
||
</main><!-- /.app-main -->
|
||
|
||
</div>
|
||
|
||
<!-- ===================== -->
|
||
<!-- 通知系统浮层 -->
|
||
<!-- (用于显示操作反馈,如成功、错误、警告信息) -->
|
||
<!-- ===================== -->
|
||
<div id="notification-container" class="fixed top-4 right-4 z-50 flex flex-col items-end space-y-2 pointer-events-none max-w-lg"></div>
|
||
|
||
<!-- 悬浮历史记录按钮 -->
|
||
<button id="floatingHistoryBtn" class="floating-history-btn tiny-round-btn" title="查看历史记录">
|
||
<iconify-icon icon="carbon:time"></iconify-icon>
|
||
</button>
|
||
|
||
<!-- ===================== -->
|
||
<!-- 脚本引入区(顺序不能乱) -->
|
||
<!-- ===================== -->
|
||
<script src="https://unpkg.com/iconify-icon@1.0.7/dist/iconify-icon.min.js"></script>
|
||
<!-- GitHub Stars 统一获取模块 -->
|
||
<script src="js/utils/github-stars.js"></script>
|
||
<!-- 自动探测后端:若 /api/健康检查通过将切换为 backend;也可用 ?mode=backend 强制 -->
|
||
<script>window.ENV_DEPLOYMENT_MODE = 'auto';</script>
|
||
<!-- 代理服务器地址统一配置(必须在其他 API 相关脚本之前加载) -->
|
||
<script src="js/config/proxy-config.js"></script>
|
||
<script src="js/api/api.js?v=2"></script>
|
||
<script src="js/storage/storage.js"></script>
|
||
<!-- 先初始化存储适配器(提供 isFrontendMode 标记) -->
|
||
<script src="js/storage/storage-adapter.js"></script>
|
||
<script src="js/boot/backend-gate.js"></script>
|
||
<!-- 后端模式访问信任门禁:后端模式且未登录时,跳转到登录页 -->
|
||
<script>
|
||
(function() {
|
||
function gateIfBackend() {
|
||
try {
|
||
if (window.DEPLOYMENT_MODE === 'backend') {
|
||
var token = localStorage.getItem('auth_token');
|
||
if (!token) {
|
||
// 允许 ?mode=frontend 显式绕过(用于演示/调试)
|
||
var m = (new URLSearchParams(window.location.search).get('mode') || '').toLowerCase();
|
||
if (m === 'frontend') return;
|
||
var redirect = encodeURIComponent(window.location.href);
|
||
window.location.replace('/login.html?redirect=' + redirect);
|
||
}
|
||
}
|
||
} catch(e) { /* ignore */ }
|
||
}
|
||
|
||
// 若显式强制 backend,则立即校验;否则等待自动探测事件
|
||
try {
|
||
var forced = (new URLSearchParams(window.location.search).get('mode') || '').toLowerCase();
|
||
var envMode = (window.ENV_DEPLOYMENT_MODE || '').toLowerCase();
|
||
if (forced === 'backend' || envMode === 'backend') {
|
||
gateIfBackend();
|
||
}
|
||
} catch(_) {}
|
||
|
||
// 监听存储模式自动切换事件
|
||
window.addEventListener('pb:storage-mode-changed', function(evt) {
|
||
if (evt && evt.detail && evt.detail.mode === 'backend') {
|
||
gateIfBackend();
|
||
}
|
||
});
|
||
})();
|
||
</script>
|
||
<!-- 再加载术语库存储(会依据适配器模式决定是否探测后端) -->
|
||
<script src="js/storage/glossary-storage.js"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/mammoth@1.4.21/mammoth.browser.min.js"></script>
|
||
<script src="https://gcore.jsdelivr.net/npm/turndown@7.1.2/dist/turndown.min.js"></script>
|
||
<script src="js/ui/ui-helpers.js"></script>
|
||
<script src="js/ui/ui-notifications.js"></script>
|
||
<script src="js/ui/ui-model-search.js"></script>
|
||
<script src="js/ui/ui-model-panels.js"></script>
|
||
<script src="js/ui/ui-processing.js"></script>
|
||
<script src="js/ui/ui-key-manager-modal.js"></script>
|
||
<script src="js/ui/key-manager-ui.js"></script>
|
||
<!-- Embedding 向量搜索与重排模块 -->
|
||
<script src="js/boot/ensure-embedding.js"></script>
|
||
<script src="js/chatbot/agents/embedding-client.js"></script>
|
||
<script>
|
||
(function(){
|
||
try {
|
||
if (!window.EmbeddingClient || typeof window.EmbeddingClient.saveConfig !== 'function') {
|
||
var s = document.createElement('script');
|
||
s.src = 'js/chatbot/agents/embedding-client.js?v=' + Date.now();
|
||
s.async = true;
|
||
s.onload = function(){ console.log('[Boot] EmbeddingClient loaded via cache-bust'); };
|
||
s.onerror = function(){ console.error('[Boot] Failed to load EmbeddingClient'); };
|
||
document.head.appendChild(s);
|
||
}
|
||
} catch (e) {
|
||
console.warn('[Boot] EmbeddingClient ensure failed:', e.message);
|
||
}
|
||
})();
|
||
</script>
|
||
<script src="js/chatbot/agents/rerank-client.js"></script>
|
||
<script src="js/chatbot/agents/vector-store.js"></script>
|
||
<script src="js/chatbot/agents/semantic-vector-search.js"></script>
|
||
<!-- 高级搜索工具模块 -->
|
||
<script src="js/chatbot/agents/advanced-search-tools.js"></script>
|
||
|
||
<!-- ReAct 模块 v2.0 - 模块化重构版本 -->
|
||
<!-- 注意:必须按此顺序加载,engine.js 依赖其他所有模块 -->
|
||
<script src="js/chatbot/react/token-budget.js"></script>
|
||
<script src="js/chatbot/react/tool-registry.js"></script>
|
||
<script src="js/chatbot/react/json-parser.js"></script>
|
||
<script src="js/chatbot/react/system-prompt.js"></script>
|
||
<script src="js/chatbot/react/context-builder.js"></script>
|
||
<script src="js/chatbot/react/engine.js"></script>
|
||
<script src="js/chatbot/react/index.js"></script>
|
||
|
||
<script src="js/chatbot/core/llm-caller.js"></script>
|
||
<!-- Chatbot 配置管理模块 -->
|
||
<script src="js/chatbot/core/chatbot-config-manager.js?v=20251107c"></script>
|
||
<script src="js/chatbot/ui/chatbot-model-config-modal.js?v=20251107f"></script>
|
||
<script src="js/chatbot/ui/chatbot-floating-options.js"></script>
|
||
<script src="js/chatbot/ui/chatbot-tooltrace-ui.js"></script>
|
||
<!-- UI 模块化组件 (新增) -->
|
||
<script src="js/ui/ui_dom_elements.js"></script>
|
||
<script src="js/ui/ui_model_manager_core.js"></script>
|
||
<script src="js/ui/ui_model_ocr_config.js"></script>
|
||
<script src="js/ui/ui_embedding_config.js"></script>
|
||
<!-- UI 主文件 -->
|
||
<script src="js/ui/ui.js"></script>
|
||
<script src="js/ui/ocr-settings.js?v=2"></script> <!-- OCR 配置管理 -->
|
||
<!-- PDF.js 库(用于本地解析 PDF) -->
|
||
<script src="https://gcore.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.min.js"></script>
|
||
<script>
|
||
// 配置 PDF.js worker
|
||
if (typeof pdfjsLib !== 'undefined') {
|
||
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://gcore.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.worker.min.js';
|
||
}
|
||
</script>
|
||
<!-- OCR 处理模块 -->
|
||
<script src="js/process/ocr-adapters/base-adapter.js"></script> <!-- 基类 -->
|
||
<script src="js/process/ocr-adapters/mistral-adapter.js?v=2"></script>
|
||
<script src="js/process/ocr-adapters/mineru-adapter.js"></script>
|
||
<script src="js/process/ocr-adapters/doc2x-adapter.js"></script>
|
||
<script src="js/process/ocr-adapters/local-adapter.js"></script> <!-- 本地 PDF 解析 -->
|
||
<script src="js/process/ocr-manager.js"></script> <!-- OCR 管理器 -->
|
||
<script src="js/process/glossary-matcher.js"></script> <!-- 高效术语匹配器 -->
|
||
<!-- glossary-core.js 由 index.js 动态加载,避免重复 -->
|
||
<script src="js/process/glossary-config.js"></script> <!-- 术语库性能配置 -->
|
||
<script src="js/ui/glossary-progress.js"></script> <!-- 术语库进度条 -->
|
||
<script src="js/ui/glossary-editor-enhanced.js"></script> <!-- 增强版术语库编辑器 -->
|
||
<script src="js/ui/glossary-ui.js"></script>
|
||
<!-- 子块分割器(支持中英文标点分割),用于高亮与批注的精确定位 -->
|
||
<script src="js/processing/sub_block_segmenter.js"></script>
|
||
<!-- 修改为普通脚本标签,不使用模块 -->
|
||
<script src="js/process/index.js"></script>
|
||
<script src="js/process/prompt-pool-api.js"></script>
|
||
<script src="js/process/prompt-pool.js"></script>
|
||
<script src="js/process/mineru-structured-translation.js"></script> <!-- MinerU 结构化翻译 -->
|
||
<script src="js/ui/prompt-pool-ui.js"></script>
|
||
<script src="js/api/model-detector.js"></script>
|
||
<script src="js/app.js"></script>
|
||
<script src="js/processing/markdown_processor_enhanced.js"></script>
|
||
<!-- AST-based processor (新架构) -->
|
||
<script src="js/processing/annotation_plugin_ast.js"></script> <!-- 注释插件 -->
|
||
<script src="js/processing/markdown_processor_ast.js"></script>
|
||
<script src="js/processing/markdown_processor_integration.js"></script> <!-- 集成层 -->
|
||
<script src="js/processing/markdown_text_fix.js"></script>
|
||
<script src="js/processing/markdown_processor.js"></script>
|
||
|
||
<!-- AST 架构状态显示 -->
|
||
<script>
|
||
(function() {
|
||
if (window.MarkdownProcessorAST && window.MarkdownProcessor === window.MarkdownProcessorAST) {
|
||
console.log(
|
||
'%c🚀 Markdown 渲染引擎升级成功!%c\n' +
|
||
'当前版本: ' + window.MarkdownProcessorAST.version + '\n' +
|
||
'架构: AST (markdown-it)\n' +
|
||
'特性: 上下文感知 | 精准匹配 | 插件系统',
|
||
'color: #10b981; font-size: 14px; font-weight: bold; padding: 4px 0;',
|
||
'color: #64748b; font-size: 12px;'
|
||
);
|
||
}
|
||
})();
|
||
</script>
|
||
|
||
<!-- 文本自适应算法 (PDF 保留格式翻译) - 必须在 history 脚本之前加载 -->
|
||
<script src="js/utils/text-fitting.js"></script>
|
||
<script src="js/utils/text-fitting-integration.js"></script>
|
||
|
||
<!-- DOCX 导出 - MathML → OMML 转换器 -->
|
||
<script src="js/history/exporter/mathml2omml.browser.js"></script> <!-- 专业转换库(优先) -->
|
||
<script src="js/history/exporter/docx_mathml_converter_enhanced.js"></script> <!-- 增强转换器(备用) -->
|
||
<script src="js/history/exporter/history_exporter_docx.js"></script>
|
||
<script src="js/history/history_exporter.js"></script>
|
||
<script src="js/history/history.js"></script>
|
||
|
||
<script src="https://gcore.jsdelivr.net/npm/html2pdf.js@0.10.1/dist/html2pdf.bundle.min.js"></script>
|
||
|
||
|
||
|
||
<!-- ===================== -->
|
||
<!-- 历史记录面板 (模态框) -->
|
||
<div id="historyPanel" class="fixed inset-0 z-50 bg-slate-900/40 backdrop-blur-sm flex items-center justify-center hidden p-4 md:p-8">
|
||
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-6xl max-h-[85vh] flex flex-col overflow-hidden border border-slate-100">
|
||
<div class="flex justify-between items-center p-5 border-b border-slate-100 bg-slate-50/50">
|
||
<div class="flex items-center gap-3">
|
||
<div class="w-10 h-10 rounded-xl flex items-center justify-center" style="background-color: var(--indigo-100); color: var(--color-primary);">
|
||
<iconify-icon icon="carbon:time" width="24"></iconify-icon>
|
||
</div>
|
||
<div>
|
||
<h3 class="text-lg font-bold text-slate-800">历史记录</h3>
|
||
<p class="text-xs text-slate-500">查看和管理您处理过的文档</p>
|
||
</div>
|
||
</div>
|
||
<button id="closeHistoryPanel" class="p-2 text-slate-400 hover:text-slate-700 hover:bg-slate-100 rounded-lg transition-colors">
|
||
<iconify-icon icon="carbon:close" width="24"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
<div class="flex flex-1 min-h-0">
|
||
<aside class="hidden md:flex w-64 shrink-0 border-r border-slate-100 bg-slate-50 p-4 flex-col" id="historySidebar">
|
||
<div class="flex items-center justify-between mb-4">
|
||
<span class="text-xs font-bold text-slate-500 uppercase tracking-wider">文件夹</span>
|
||
<button id="historyAddFolderBtn" class="hover:bg-slate-100 p-1.5 rounded-md transition-colors" title="新建文件夹" style="color: var(--color-primary);">
|
||
<iconify-icon icon="carbon:add" width="16"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
<div class="flex-1 overflow-y-auto -mx-2 px-2" id="historyFolderList"></div>
|
||
</aside>
|
||
<div class="flex-1 flex flex-col min-h-0 p-5">
|
||
<!-- Mobile Folder Controls -->
|
||
<div class="md:hidden mb-4 flex gap-2">
|
||
<select id="historyFolderSelectMobile" class="flex-1 rounded-xl border-slate-200 text-sm py-2 pl-3 pr-8"></select>
|
||
<button id="historyAddFolderBtnMobile" class="p-2 border border-slate-200 rounded-xl" style="color: var(--color-primary);"><iconify-icon icon="carbon:add" width="20"></iconify-icon></button>
|
||
<button id="historyRenameFolderBtnMobile" class="p-2 border border-slate-200 rounded-xl text-slate-600"><iconify-icon icon="carbon:edit" width="20"></iconify-icon></button>
|
||
<button id="historyDeleteFolderBtnMobile" class="p-2 border border-slate-200 rounded-xl text-red-500"><iconify-icon icon="carbon:trash-can" width="20"></iconify-icon></button>
|
||
</div>
|
||
|
||
<div class="mb-4 relative">
|
||
<iconify-icon icon="carbon:search" class="absolute left-3.5 top-1/2 -translate-y-1/2 text-slate-400" width="20"></iconify-icon>
|
||
<input id="historySearchInput" type="search" placeholder="搜索文档标题、内容或 OCR 结果..."
|
||
class="w-full pl-11 pr-4 py-3 bg-slate-50 border-transparent rounded-xl focus:bg-white focus:ring-2 transition-all text-sm" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
</div>
|
||
|
||
<div id="historyList" class="flex-1 overflow-y-auto space-y-3 pr-1"></div>
|
||
|
||
<div class="mt-5 pt-4 border-t border-slate-100">
|
||
<button id="clearHistoryBtn" class="w-full flex items-center justify-center gap-2 py-3 px-4 bg-red-50 text-red-600 font-medium rounded-xl hover:bg-red-100 transition-colors">
|
||
<iconify-icon icon="carbon:trash-can" width="20"></iconify-icon>
|
||
<span>清空所有历史记录</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 历史记录详情弹窗 (已废弃,详情在新页面打开) -->
|
||
<div id="historyDetailModal" class="fixed inset-0 z-[99999] bg-black bg-opacity-30 flex items-center justify-center hidden">
|
||
<div class="bg-white rounded-xl border-2 border-dashed border-gray-300 p-6 w-[90vw] max-w-2xl max-h-[90vh] overflow-y-auto relative">
|
||
<button id="closeHistoryDetail" class="absolute top-2 right-2 text-gray-400 hover:text-red-500"><iconify-icon icon="carbon:close" width="20"></iconify-icon></button>
|
||
<div id="historyDetailContent"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 历史记录清空三步确认弹窗 -->
|
||
<div id="historyClearConfirmModal" class="fixed inset-0 z-[100000] bg-black/50 backdrop-blur-sm hidden items-center justify-center px-4">
|
||
<div class="bg-white rounded-2xl shadow-2xl max-w-md w-full overflow-hidden">
|
||
<div class="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
|
||
<div class="flex items-center space-x-3">
|
||
<span class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-red-100 text-red-600 font-semibold">!</span>
|
||
<div>
|
||
<h3 class="text-lg font-semibold text-gray-800">清空历史记录</h3>
|
||
<p class="text-xs text-gray-500">清除操作不可恢复,请谨慎确认</p>
|
||
</div>
|
||
</div>
|
||
<button id="historyClearCloseBtn" class="text-gray-400 hover:text-red-500 transition-colors">
|
||
<iconify-icon icon="carbon:close" width="22"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="px-6 py-5 space-y-2">
|
||
<div id="historyClearStep1" class="space-y-3" aria-hidden="false">
|
||
<p id="historyClearStep1Message" class="text-sm text-gray-700 leading-relaxed">
|
||
即将永久删除所有历史记录(包括所有批次、译文、文件夹分配)。请确认是否继续。
|
||
</p>
|
||
<div class="flex justify-end space-x-3">
|
||
<button id="historyClearCancelBtn" class="px-3 py-2 text-sm rounded-md border border-gray-200 text-gray-500 hover:bg-gray-100">取消</button>
|
||
<button id="historyClearStep1Next" class="px-4 py-2 text-sm rounded-md bg-red-500 text-white hover:bg-red-600">继续</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="historyClearStep2" class="space-y-2" hidden aria-hidden="true">
|
||
<p class="text-sm text-gray-700 leading-relaxed -mt-1">
|
||
请在下方输入 <span class="px-2 py-0.5 bg-red-50 text-red-600 rounded-md font-semibold">确定删除</span> 以确认操作。
|
||
</p>
|
||
<label class="sr-only" for="historyClearPhraseInput">输入 “确定删除” 以继续</label>
|
||
<input id="historyClearPhraseInput" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-red-300 focus:border-red-400" placeholder="请输入 确定删除" autocomplete="off" aria-describedby="historyClearPhraseHint">
|
||
<p id="historyClearPhraseHint" class="text-xs text-gray-500">必须准确输入 “确定删除” 才能继续。</p>
|
||
<div class="flex justify-between items-center">
|
||
<button id="historyClearStep2Back" class="text-sm text-gray-500 hover:text-gray-700">返回</button>
|
||
<button id="historyClearStep2Next" class="px-4 py-2 text-sm rounded-md bg-red-500 text-white opacity-60 cursor-not-allowed" disabled>确认短语</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="historyClearStep3" class="space-y-3" hidden aria-hidden="true">
|
||
<div class="bg-red-50 border border-red-100 rounded-lg p-4 text-sm text-red-700">
|
||
<p class="font-semibold">最终确认:</p>
|
||
<p id="historyClearFinalMessage" class="mt-1 leading-relaxed">历史记录一旦清空,将无法恢复。请确保已备份需要的数据。</p>
|
||
</div>
|
||
<div class="flex justify-between items-center">
|
||
<button id="historyClearStep3Back" class="text-sm text-gray-500 hover:text-gray-700">返回</button>
|
||
<button id="historyClearExecute" class="px-4 py-2 text-sm rounded-md bg-red-600 text-white hover:bg-red-700">永久删除</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===================== -->
|
||
<!-- 模型搜索弹窗 -->
|
||
<!-- ===================== -->
|
||
<div id="modelSearchOverlay" class="hidden fixed inset-0 z-[60] bg-slate-900/40 backdrop-blur-sm flex items-center justify-center p-4">
|
||
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col overflow-hidden border border-slate-100">
|
||
<div class="flex items-center justify-between p-5 border-b border-slate-100">
|
||
<h3 id="modelSearchTitle" class="text-lg font-bold text-slate-800">选择模型</h3>
|
||
<button id="modelSearchCloseBtn" class="p-2 text-slate-400 hover:text-slate-700 hover:bg-slate-100 rounded-lg transition-colors">
|
||
<iconify-icon icon="carbon:close" width="20"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
<div class="p-5 flex flex-col flex-1 min-h-0 gap-4">
|
||
<div class="relative shrink-0">
|
||
<iconify-icon icon="carbon:search" class="absolute left-3.5 top-1/2 -translate-y-1/2 text-slate-400" width="20"></iconify-icon>
|
||
<input id="modelSearchInput" type="search" class="w-full pl-11 pr-4 py-3 bg-slate-50 border-transparent rounded-xl focus:bg-white focus:ring-2 transition-all text-sm" placeholder="输入关键词搜索模型..." autocomplete="off" style="border-color: var(--color-border-medium); outline-color: var(--color-primary);">
|
||
</div>
|
||
<div class="flex-1 overflow-y-auto min-h-0 -mr-2 pr-2">
|
||
<div id="modelSearchList" class="space-y-2">
|
||
<!-- 动态填充模型列表 -->
|
||
</div>
|
||
<div id="modelSearchEmpty" class="hidden flex flex-col items-center justify-center py-12 text-slate-400">
|
||
<iconify-icon icon="carbon:search-locate" width="40" class="mb-2 opacity-50"></iconify-icon>
|
||
<p class="text-sm">未找到匹配的模型</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 模型与Key管理弹窗 -->
|
||
<!-- ===================== -->
|
||
<div id="modelKeyManagerModal" class="fixed inset-0 z-50 bg-slate-900/40 backdrop-blur-sm flex items-center justify-center hidden p-4 md:p-8">
|
||
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-5xl max-h-[90vh] flex flex-col overflow-hidden border border-slate-100">
|
||
<!-- 弹窗头部 -->
|
||
<div class="flex justify-between items-center p-5 border-b border-slate-100 bg-slate-50/50">
|
||
<div class="flex items-center gap-3">
|
||
<div class="w-10 h-10 rounded-xl bg-slate-200 text-slate-700 flex items-center justify-center">
|
||
<iconify-icon icon="carbon:settings-check" width="24"></iconify-icon>
|
||
</div>
|
||
<div>
|
||
<h3 class="text-lg font-bold text-slate-800">模型与密钥配置</h3>
|
||
<p class="text-xs text-slate-500">集中管理所有 AI 服务的连接信息</p>
|
||
</div>
|
||
</div>
|
||
<button id="closeModelKeyManager" class="p-2 text-slate-400 hover:text-slate-700 hover:bg-slate-100 rounded-lg transition-colors">
|
||
<iconify-icon icon="carbon:close" width="24"></iconify-icon>
|
||
</button>
|
||
</div>
|
||
<!-- 主体内容区 - 左右布局 -->
|
||
<div class="flex flex-1 overflow-hidden flex-col md:flex-row">
|
||
<!-- 左侧模型/源站点列表 -->
|
||
<div id="modelListColumn" class="w-full md:w-64 bg-slate-50 md:border-r border-slate-100 overflow-y-auto p-4 space-y-1">
|
||
<!-- 模型/源站点列表将由 JS 动态填充 -->
|
||
</div>
|
||
<!-- 右侧配置项与Key池管理 -->
|
||
<div class="flex-1 p-6 overflow-y-auto bg-white">
|
||
<!-- 当前选中模型/源站点的配置项 -->
|
||
<div id="modelConfigColumn" class="mb-8">
|
||
<!-- 配置项将由 JS 动态填充 -->
|
||
</div>
|
||
<!-- API Key 管理界面 (由 key-manager-ui.js 渲染) -->
|
||
<div id="keyManagerColumn" class="border-t border-slate-100 pt-6">
|
||
<!-- KeyManagerUI 实例将渲染到这里 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===================== -->
|
||
<!-- 版权声明浮动卡片 -->
|
||
<!-- ===================== -->
|
||
<div id="copyrightModal" class="fixed inset-0 z-50 bg-black bg-opacity-30 flex items-center justify-center hidden px-4 py-8">
|
||
<div class="copyright-card-ani p-6 md:p-10 w-full max-w-6xl relative max-h-[90vh] flex flex-col">
|
||
<button id="closeCopyrightModal" class="absolute top-4 right-4 text-gray-400 hover:text-red-500 transition-colors z-10">
|
||
<iconify-icon icon="carbon:close" width="24"></iconify-icon>
|
||
</button>
|
||
<div class="text-center mb-5 md:mb-6">
|
||
<h2 class="text-2xl md:text-3xl font-extrabold tracking-tight mb-2">关于 Paper Burner X</h2>
|
||
</div>
|
||
<div class="text-gray-700 space-y-4 md:space-y-5 text-sm md:text-base font-normal overflow-y-auto pr-3 flex-1">
|
||
<div class="leading-relaxed text-left space-y-3">
|
||
<p class="text-sm md:text-base text-gray-700 font-normal">
|
||
这是一款<strong>开源的、在浏览器中即开即用的 AI 工作站</strong>,专为扫除海量的 PDF 文献、复杂的公式和跨语言的障碍。
|
||
</p>
|
||
<p class="text-sm md:text-base text-gray-700 font-normal">
|
||
它为需要进行精细、长文本阅读的研究人员和深度学习者设计,致力于将复杂的文档处理、翻译和分析流程整合到单一、流畅的体验中。
|
||
</p>
|
||
<div class="border-l-4 p-4 rounded-r-lg" style="background-color: var(--indigo-50); border-color: var(--color-primary);">
|
||
<p class="text-sm text-gray-700">
|
||
<strong style="color: var(--indigo-700);">GitHub:</strong> <a href="https://github.com/Feather-2/paper-burner" target="_blank" class="underline" style="color: var(--color-primary);">github.com/Feather-2/paper-burner</a><br>
|
||
<strong style="color: var(--indigo-700);">在线体验:</strong> <a href="https://paperburner.viwoplus.site/" target="_blank" class="underline" style="color: var(--color-primary);">paperburner.viwoplus.site</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<h3 class="text-base md:text-lg font-bold mb-3 flex items-center" style="color: var(--indigo-700);">
|
||
<iconify-icon icon="carbon:checkmark-outline" class="mr-2" width="20" style="color: var(--color-primary);"></iconify-icon>
|
||
目前实现了:
|
||
</h3>
|
||
<div class="space-y-4">
|
||
<div class="bg-gradient-to-r to-slate-50 p-4 rounded-lg border" style="from: var(--indigo-50); border-color: var(--indigo-100);">
|
||
<h4 class="text-base font-semibold text-gray-800 mb-2 flex items-center">
|
||
<iconify-icon icon="carbon:ai-status" class="mr-2" width="18" style="color: var(--color-primary);"></iconify-icon>
|
||
前端 Agent 驱动的智能检索
|
||
</h4>
|
||
<p class="text-sm text-gray-700 leading-relaxed">
|
||
我们在前端实现了一个 Agentic RAG 系统。通过赋予 AI 全局的文章结构和一系列工具(如 grep, vector search, fetch等等),AI 能够自主决策、多步推理,并在长文本中实现复杂的分析和信息提取任务。
|
||
</p>
|
||
</div>
|
||
<div class="bg-gradient-to-r from-green-50 to-emerald-50 p-4 rounded-lg border border-green-100">
|
||
<h4 class="text-base font-semibold text-gray-800 mb-2 flex items-center">
|
||
<iconify-icon icon="carbon:flash" class="mr-2 text-green-600" width="18"></iconify-icon>
|
||
高性能批量处理
|
||
</h4>
|
||
<p class="text-sm text-gray-700 leading-relaxed">
|
||
支持多种文档格式(PDF/DOCX/EPUB 等)和代码库的直接导入。利用并发 OCR 和翻译,并结合术语库(支持数万词条快速匹配),显著提升了文献处理效率。
|
||
</p>
|
||
</div>
|
||
<div class="bg-gradient-to-r from-purple-50 to-pink-50 p-4 rounded-lg border border-purple-100">
|
||
<h4 class="text-base font-semibold text-gray-800 mb-2 flex items-center">
|
||
<iconify-icon icon="carbon:settings-adjust" class="mr-2 text-purple-600" width="18"></iconify-icon>
|
||
高可扩展性与本地化
|
||
</h4>
|
||
<p class="text-sm text-gray-700 leading-relaxed">
|
||
目前所有数据均在浏览器本地,支持用户接入自定义 AI 模型端点,并提供了配套的 OCR Server 和 Docker 部署选项(开发中),让用户未来可以实现完全离线的本地化使用。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<p class="text-sm text-gray-600 italic mt-4 text-center">
|
||
希望这个工具能成为研究人员和知识工作者的得力助手,欢迎试用和提出宝贵意见!
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<h3 class="text-base md:text-lg font-bold mb-3 flex items-center" style="color: var(--indigo-700);">
|
||
<iconify-icon icon="carbon:document" class="mr-2" width="20" style="color: var(--color-primary);"></iconify-icon>
|
||
具体介绍
|
||
</h3>
|
||
<div class="space-y-4">
|
||
<div>
|
||
<h4 class="text-base font-semibold text-gray-800 mb-2">一体化的文档处理引擎</h4>
|
||
<ul class="list-disc pl-5 text-sm text-gray-600 space-y-1.5 leading-relaxed">
|
||
<li><strong>广泛的格式支持:</strong>能够处理 PDF、DOCX、PPTX、EPUB、Markdown 甚至代码注释等多种格式,并支持导出为 DOCX、MD 等常用格式。</li>
|
||
<li><strong>智能导入与处理:</strong>不仅支持本地文件上传,更可一键从 GitHub 仓库或任意 URL 导入内容,自动完成解析。PDF可以使用OCR (支持mineru/doc2x等) 与翻译引擎,并实现保留原文格式翻译功能(基于mineru,目前优化中,并会支持更多模型)。</li>
|
||
<li><strong>术语备择库:</strong>进行了性能优化,支持一次性导入数万条术语并进行快速匹配。</li>
|
||
<li>支持自定义模型端点,可以支持检测、多key、快捷导出等机制,使用灵活。</li>
|
||
</ul>
|
||
</div>
|
||
<div>
|
||
<h4 class="text-base font-semibold text-gray-800 mb-2">为深度阅读优化的交互体验</h4>
|
||
<ul class="list-disc pl-5 text-sm text-gray-600 space-y-1.5 leading-relaxed">
|
||
<li><strong>沉浸式对照阅读:</strong>提供智能对齐的段落级原译文对照、文档结构目录(TOC)、高亮与标注功能,先进行无障碍的阅读,再进行AI总结。</li>
|
||
<li><strong>增强学术内容展示:</strong>针对学术场景,特别优化了复杂公式的渲染。</li>
|
||
<li><strong>结构化信息提取:</strong>内置了"文献矩阵"等实用工具,能够将非结构化的论文内容,智能提取为清晰的结构化数据,方便进行横向对比和分析。</li>
|
||
</ul>
|
||
</div>
|
||
<div>
|
||
<h4 class="text-base font-semibold text-gray-800 mb-2">不止于问答:前端 Agent 驱动的智能分析</h4>
|
||
<p class="text-sm text-gray-600 leading-relaxed mb-2">
|
||
我们在纯前端环境中,实现了一个长文本Agent。少量文本下,将使用全量的策略;而当提供长文本时候,使用长文本Agent。
|
||
</p>
|
||
<ul class="list-disc pl-5 text-sm text-gray-600 space-y-1.5 leading-relaxed">
|
||
<li><strong>赋予 AI 全局视野:</strong>我们为 AI 构建了"分层意群/地图",让它在处理长文本时拥有对全文结构的整体认知。</li>
|
||
<li><strong>为 AI 配备工具箱:</strong>我们给予 AI 一系列工具,如精确匹配的 grep、向量搜索 vector search、内容抓取 fetch 等。AI 会根据你的问题,自主分析并决定调用哪种工具组合来寻找最佳答案。</li>
|
||
<li><strong>上述皆在纯前端实现,浏览器打开即用</strong></li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<h3 class="text-base md:text-lg font-bold mb-3 flex items-center" style="color: var(--indigo-700);">
|
||
<iconify-icon icon="carbon:road" class="mr-2" width="20" style="color: var(--color-primary);"></iconify-icon>
|
||
项目正在活跃地迭代
|
||
</h3>
|
||
<ul class="list-disc pl-5 text-sm text-gray-600 space-y-2 leading-relaxed">
|
||
<li><strong>完全本地化部署:</strong>正在开发Docker 部署方案,还提供了可自托管的 OCR Server,最终目标是让用户可以完全在离线环境中使用全部功能。</li>
|
||
<li><strong>从单文档到多文档:</strong>下一个里程碑是将能力从分析单篇文献,扩展到处理多篇文献,并基于此开发能自动生成文献综述的综述 Agent,成为真正的 AI 研究助理。</li>
|
||
</ul>
|
||
</div>
|
||
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
|
||
<h3 class="text-base font-bold text-gray-800 mb-2 flex items-center">
|
||
<iconify-icon icon="carbon:license" class="mr-2 text-gray-600" width="18"></iconify-icon>
|
||
基于 AGPL 3.0 开源
|
||
</h3>
|
||
<p class="text-sm text-gray-600 leading-relaxed">
|
||
本项目基于 <a href="https://github.com/baoyudu/paper-burner" target="_blank" class="underline" style="color: var(--color-primary);">baoyudu/paper-burner</a>(该项目是基于mistral ocr的pdf极简翻译工具),并已在原始项目上进行了重构和各方面极多内容的扩充。为避免和Paper Burner原项目名称产生重复,为示尊重和区分,该分支项目改名Paper Burner X。
|
||
</p>
|
||
</div>
|
||
<div class="border-t-2 border-dashed pt-5 mt-5 text-center space-y-3" style="border-color: var(--indigo-200);">
|
||
<p class="text-sm md:text-base text-gray-600 font-normal">探索本分支的更多细节或贡献您的智慧:</p>
|
||
<a href="https://github.com/Feather-2/paper-burner" target="_blank" class="main-btn inline-flex items-center justify-center px-7 py-3 text-sm md:text-base font-bold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2" style="background-color: var(--color-primary); color: white;">
|
||
<iconify-icon icon="carbon:logo-github" class="mr-2" width="20"></iconify-icon>
|
||
访问新分支 (Feather-2/paper-burner)
|
||
</a>
|
||
<p class="text-xs text-gray-400 font-normal pt-1">
|
||
您的每一个建议对我们都至关重要!欢迎前往新分支的 <a href="https://github.com/Feather-2/paper-burner/issues" target="_blank" class="underline font-semibold" style="color: var(--color-primary);">Issue 区</a> 分享您的想法或参与讨论。
|
||
</p>
|
||
<p class="text-xs text-gray-400 italic">
|
||
致敬 <a href="https://github.com/baoyudu/paper-burner" target="_blank" class="underline" style="color: var(--color-primary);">Paper Burner</a> 开源项目,Paper Burner X 在社区基础上持续扩展与迭代。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// App Shell 导航逻辑
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const historyPanel = document.getElementById('historyPanel');
|
||
const modelKeyManagerModal = document.getElementById('modelKeyManagerModal');
|
||
|
||
// Desktop Sidebar Buttons
|
||
const sidebarSettingsBtn = document.getElementById('sidebarSettingsBtn');
|
||
|
||
if (sidebarSettingsBtn && modelKeyManagerModal) {
|
||
sidebarSettingsBtn.addEventListener('click', () => {
|
||
// 触发模型管理器的打开逻辑(与主界面的"设置"按钮保持一致)
|
||
const modelKeyManagerBtn = document.getElementById('modelKeyManagerBtn');
|
||
if (modelKeyManagerBtn) {
|
||
modelKeyManagerBtn.click();
|
||
} else {
|
||
// 后备方案:直接打开弹窗(但可能是空的)
|
||
modelKeyManagerModal.classList.remove('hidden');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Mobile Sidebar Logic
|
||
const appSidebar = document.getElementById('appSidebar');
|
||
const sidebarOverlay = document.getElementById('sidebarOverlay');
|
||
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
|
||
const sidebarCloseBtn = document.getElementById('sidebarCloseBtn');
|
||
|
||
function openSidebar() {
|
||
appSidebar?.classList.add('mobile-open');
|
||
sidebarOverlay?.classList.add('show');
|
||
}
|
||
|
||
function closeSidebar() {
|
||
appSidebar?.classList.remove('mobile-open');
|
||
sidebarOverlay?.classList.remove('show');
|
||
}
|
||
|
||
if (mobileMenuBtn) mobileMenuBtn.addEventListener('click', openSidebar);
|
||
if (sidebarCloseBtn) sidebarCloseBtn.addEventListener('click', closeSidebar);
|
||
if (sidebarOverlay) sidebarOverlay.addEventListener('click', closeSidebar);
|
||
|
||
// Desktop Sidebar Collapse Logic
|
||
const sidebarToggleBtn = document.getElementById('sidebarToggleBtn');
|
||
const sidebarToggleIcon = document.getElementById('sidebarToggleIcon');
|
||
const sidebarLogo = document.getElementById('sidebarLogo');
|
||
const LOGO_FULL = 'public/h_with_name.svg';
|
||
const LOGO_PURE = 'public/pure.svg';
|
||
|
||
function setSidebarState(collapsed) {
|
||
if (collapsed) {
|
||
appSidebar.classList.add('collapsed');
|
||
if (sidebarToggleIcon) sidebarToggleIcon.setAttribute('icon', 'carbon:side-panel-open');
|
||
if (sidebarLogo) sidebarLogo.src = LOGO_PURE;
|
||
} else {
|
||
appSidebar.classList.remove('collapsed');
|
||
if (sidebarToggleIcon) sidebarToggleIcon.setAttribute('icon', 'carbon:side-panel-close');
|
||
if (sidebarLogo) sidebarLogo.src = LOGO_FULL;
|
||
}
|
||
localStorage.setItem('pbx_sidebar_collapsed', collapsed);
|
||
}
|
||
|
||
// Init state from local storage
|
||
const isCollapsed = localStorage.getItem('pbx_sidebar_collapsed') === 'true';
|
||
// 初始加载时不启用动画,避免闪烁 (可选优化,这里暂不复杂化)
|
||
setSidebarState(isCollapsed);
|
||
|
||
if (sidebarToggleBtn) {
|
||
sidebarToggleBtn.addEventListener('click', () => {
|
||
const willCollapse = !appSidebar.classList.contains('collapsed');
|
||
setSidebarState(willCollapse);
|
||
});
|
||
}
|
||
});
|
||
|
||
// 一闪而过的提示文字淡入淡出动画 (用于"配置模型与Key管理"提示)
|
||
window.addEventListener('DOMContentLoaded', function() {
|
||
var tip = document.getElementById('flashConfigTip');
|
||
if (tip) {
|
||
tip.classList.add('show'); // 淡入
|
||
setTimeout(function() {
|
||
tip.classList.remove('show');
|
||
tip.classList.add('hide'); // 淡出
|
||
}, 1200); // 持续显示1.2秒
|
||
setTimeout(function() {
|
||
tip.style.display = 'none'; // 动画结束后隐藏元素
|
||
}, 1800); // 总共1.8秒后隐藏
|
||
}
|
||
});
|
||
|
||
// 版权声明弹窗逻辑
|
||
(function() {
|
||
var modal = document.getElementById('copyrightModal');
|
||
var showBtn = document.getElementById('showCopyrightModal');
|
||
var closeBtn = document.getElementById('closeCopyrightModal');
|
||
var alreadyShown = localStorage.getItem('paperBurnerBranchInfoShown');
|
||
|
||
function openModal() {
|
||
if (modal) modal.classList.remove('hidden');
|
||
}
|
||
|
||
function closeModal() {
|
||
if (modal) modal.classList.add('hidden');
|
||
}
|
||
|
||
if (showBtn) {
|
||
showBtn.addEventListener('click', openModal);
|
||
}
|
||
if (closeBtn) {
|
||
closeBtn.addEventListener('click', function() {
|
||
closeModal();
|
||
// 确保用户手动关闭后,标记为已显示,避免下次自动弹出
|
||
localStorage.setItem('paperBurnerBranchInfoShown', 'true');
|
||
});
|
||
}
|
||
// 点击遮罩关闭
|
||
if (modal) {
|
||
modal.addEventListener('click', function(e) {
|
||
if (e.target === modal) {
|
||
closeModal();
|
||
localStorage.setItem('paperBurnerBranchInfoShown', 'true');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 首次访问自动展开"关于本分支"卡片 (如果 Landing Page 未显示)
|
||
// var landingPageIsActive = !localStorage.getItem('paperBurnerLandingPageShown'); // This logic is no longer needed here
|
||
// if (!alreadyShown && modal && !landingPageIsActive) { // This logic is no longer needed here
|
||
// openModal();
|
||
// }
|
||
})();
|
||
|
||
// Landing Page Logic Removed - main app is now shown by default // This line and related block will be removed by the next instruction.
|
||
|
||
// 获取 GitHub Stars 数量(使用统一模块)
|
||
(function() {
|
||
const starsElement = document.getElementById('githubStars');
|
||
if (!starsElement || !window.GitHubStars) return;
|
||
|
||
window.GitHubStars.getStars()
|
||
.then(result => {
|
||
starsElement.innerHTML = '<iconify-icon icon="carbon:star" width="10"></iconify-icon><span>' + result.formatted + ' stars</span>';
|
||
})
|
||
.catch(error => {
|
||
console.warn('[Index] Failed to fetch GitHub stars:', error);
|
||
// 失败时隐藏 stars 显示
|
||
starsElement.style.display = 'none';
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|