Compare commits

...

7 Commits

16 changed files with 351 additions and 242 deletions

View File

@ -44,11 +44,11 @@
--slate-900: #111827; --slate-900: #111827;
/* Indigo Palette (Primary) - Slightly more sophisticated */ /* Indigo Palette (Primary) - Slightly more sophisticated */
--indigo-50: #eef2ff; --indigo-50: #FFFFFF;
--indigo-100: #e0e7ff; --indigo-100: #f3f4f5;
--indigo-500: #6366f1; --indigo-500: #0949ec;
--indigo-600: #4f46e5; --indigo-600: #003dd6;
--indigo-700: #4338ca; --indigo-700: rgb(45, 32, 185);
/* Semantic Mapping */ /* Semantic Mapping */
--color-primary: var(--indigo-600); --color-primary: var(--indigo-600);

View File

@ -56,6 +56,8 @@
#immersive-toc-area.immersive-panel { #immersive-toc-area.immersive-panel {
padding: var(--spacing-xs) var(--spacing-sm) 0 var(--spacing-sm); padding: var(--spacing-xs) var(--spacing-sm) 0 var(--spacing-sm);
border-right: 1px solid var(--color-border-light); border-right: 1px solid var(--color-border-light);
/* 隐藏沉浸模式侧边栏 */
display: none;
} }
#immersive-chatbot-area.immersive-panel { #immersive-chatbot-area.immersive-panel {
@ -788,6 +790,7 @@ body.immersive-active #immersive-main-content-area .container {
#immersive-toc-area { #immersive-toc-area {
min-width: 200px; min-width: 200px;
} }
#immersive-chatbot-area { #immersive-chatbot-area {

View File

@ -34,7 +34,7 @@ body {
display: flex; display: flex;
gap: 1.5rem; /* 大幅增加间距,更现代 */ gap: 1.5rem; /* 大幅增加间距,更现代 */
border-bottom: 1px solid var(--color-border-light); border-bottom: 1px solid var(--color-border-light);
margin-bottom: 2.5rem; /* margin-bottom: 2.5rem; */
padding-bottom: 0; padding-bottom: 0;
} }

View File

@ -78,7 +78,7 @@ h2 {
.history-export-controls { .history-export-controls {
position: relative; position: relative;
margin-bottom: 16px; /* margin-bottom: 16px; */
} }
.export-panel-backdrop { .export-panel-backdrop {

View File

@ -48,7 +48,8 @@
color: #94a3b8 !important; /* slate-400 */ color: #94a3b8 !important; /* slate-400 */
border: 1px solid #e2e8f0 !important; border: 1px solid #e2e8f0 !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05) !important; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05) !important;
display: flex !important; /* 隐藏沉浸模式的出口 */
display: flex;
align-items: center !important; align-items: center !important;
justify-content: center !important; justify-content: center !important;
cursor: pointer !important; cursor: pointer !important;

View File

@ -986,6 +986,7 @@ body.immersive-dragging .immersive-resize-handle {
#immersive-toc-area { #immersive-toc-area {
min-width: 200px; min-width: 200px;
} }
#immersive-chatbot-area { #immersive-chatbot-area {

View File

@ -543,80 +543,43 @@
<!-- 侧边栏 (Responsive) --> <!-- 侧边栏 (Responsive) -->
<!-- ===================== --> <!-- ===================== -->
<aside id="appSidebar" class="app-sidebar"> <aside id="appSidebar" class="app-sidebar">
<div class="px-6 py-5 flex items-center justify-between transition-all" id="sidebarHeader">
<!-- <a href="views/landing/landing-page.html" class="hover:opacity-80 transition-opacity" title="返回落地页">
<img id="sidebarLogo" src="public/h_with_name.svg" class="h-8 transition-all" alt="Paper Burner X">
</a> -->
<!-- 占位元素 -->
<span></span>
<!-- Desktop Collapse Button -->
<button id="sidebarToggleBtn" class="hidden md:flex text-slate-400 hover:text-slate-600 p-1.5 hover:bg-slate-100 rounded-md transition-colors" title="切换侧边栏">
<iconify-icon id="sidebarToggleIcon" icon="carbon:side-panel-close" width="20"></iconify-icon>
</button>
<!-- Mobile Close Button -->
<button id="sidebarCloseBtn" class="md:flex md:hidden 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>
<nav class="flex-1 px-3 py-4 space-y-1 overflow-y-auto custom-scrollbar overflow-x-hidden"> <nav class="flex-1 px-3 py-4 space-y-1 overflow-y-auto custom-scrollbar overflow-x-hidden">
<div class="nav-section-title px-3 mb-2 text-[11px] font-bold text-slate-400 uppercase tracking-wider transition-opacity">
主要功能
</div>
<div class="nav-item active cursor-pointer" title="工作台">
<iconify-icon icon="carbon:dashboard" class="nav-icon"></iconify-icon>
<span class="nav-text transition-opacity">工作台</span>
</div>
<!-- 可折叠的历史记录菜单 (Split Action) --> <!-- 可折叠的历史记录菜单 (Split Action) -->
<div id="historyMenuContainer"> <div id="historyMenuContainer" class="mb-4">
<div class="flex items-stretch mb-0.5 select-none group"> <div
<div id="sidebarHistoryMainBtn" class="flex-1 flex items-center px-[0.875rem] py-[0.75rem] text-[0.9375rem] font-medium text-slate-600 hover:bg-slate-100 hover:text-slate-900 cursor-pointer rounded-lg transition-all" title="打开完整历史记录面板"> class="flex items-stretch select-none group bg-white border border-slate-200 overflow-hidden">
<iconify-icon icon="carbon:time" class="nav-icon mr-[0.75rem] text-[1.25rem] text-slate-400 group-hover:text-slate-500 transition-colors"></iconify-icon> <div id="sidebarHistoryMainBtn"
<span class="nav-text transition-opacity">历史记录</span> 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>
<button id="sidebarHistoryToggleBtn" class="flex items-center justify-center px-2 hover:bg-slate-100 text-slate-300 hover:text-slate-500 cursor-pointer rounded-r-lg transition-colors" title="展开/收起最近记录"> <div id="sidebarHistoryQuickList"
<iconify-icon icon="carbon:chevron-right" class="transition-transform duration-200" id="sidebarHistoryChevron" width="16"></iconify-icon> class="hidden py-1 space-y-0.5 pl-2 transition-all bg-white border border-slate-200">
</button> <!-- JS 将在此处填充最近记录 -->
</div> <div class="px-3 py-2 text-xs text-slate-400 text-center">加载中...</div>
<div id="sidebarHistoryQuickList" class="hidden py-1 space-y-0.5 pl-2 transition-all"> </div>
<!-- JS 将在此处填充最近记录 --> </div>
<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> <div class="my-4 border-t border-slate-100 mx-3 transition-all" id="sidebarDivider"></div>
<!-- <div class="nav-section-title px-3 mb-2 text-[11px] font-bold text-slate-400 uppercase tracking-wider transition-opacity">
系统
</div>
<div id="sidebarSettingsBtn" class="nav-item cursor-pointer" title="全局设置">
<iconify-icon icon="carbon:settings" class="nav-icon"></iconify-icon>
<span class="nav-text transition-opacity">全局设置</span>
</div> -->
</nav> </nav>
<!-- 删除界面会加载不出来 -->
<div class="p-3 transition-all" id="sidebarFooter">
<div class="p-4 overflow-hidden">
</div>
<div class="text-center py-1 mt-1 nav-text transition-opacity">
<span id="showCopyrightModal" class="text-[11px] text-slate-400 hover:text-slate-600 transition-colors cursor-pointer select-none">
</span>
</div>
</div>
<!-- 收起状态的底部按钮 -->
<div id="sidebarFooterCollapsed" class="hidden flex-col items-center p-3">
<a href="https://github.com/Feather-2/paper-burner" target="_blank" class="w-10 h-10 flex items-center justify-center text-slate-400 hover:text-slate-600 transition-colors" title="GitHub 仓库">
<iconify-icon icon="mdi:github" width="28"></iconify-icon>
</a>
</div>
</aside> </aside>
<!-- ===================== --> <!-- ===================== -->
<!-- 主内容区 --> <!-- 主内容区 -->
<!-- ===================== --> <!-- ===================== -->
<main class="app-main flex flex-col"> <main class="app-main flex flex-col">
<!-- 移动端 Header --> <!-- 移动端 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"> <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"> <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"> <img src="public/pure.svg" class="w-7 h-7" alt="PBX Logo">
@ -630,16 +593,16 @@
<iconify-icon icon="carbon:menu" width="24"></iconify-icon> <iconify-icon icon="carbon:menu" width="24"></iconify-icon>
</button> </button>
</div> </div>
</header> </header> -->
<!-- 滚动内容容器 --> <!-- 滚动内容容器 -->
<div class="flex-1 overflow-y-auto custom-scrollbar"> <div class="flex-1 overflow-y-auto custom-scrollbar">
<div id="mainAppContainer" class="container mx-auto px-4 py-6 md:py-8 max-w-6xl animate-fade-in-up"> <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) --> <!-- 顶部欢迎区 (Modern Dashboard Style) -->
<!-- ===================== --> <!-- ===================== -->
<div class="workspace-header"> <!-- <div class="workspace-header">
<div class="workspace-header-content"> <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);"> <h1 class="text-3xl md:text-4xl font-bold text-slate-900 mb-4 tracking-tight" style="color: var(--slate-900);">
可学可学可学可学可学测试提交后vercel更新是否成功 可学可学可学可学可学测试提交后vercel更新是否成功
@ -648,7 +611,7 @@
配置多种OCR 引擎与 AI 翻译模型。上传文档后Paper Burner X将给您流畅的阅读与处理体验。 配置多种OCR 引擎与 AI 翻译模型。上传文档后Paper Burner X将给您流畅的阅读与处理体验。
</p> </p>
</div> </div>
</div> </div> -->
<!-- ===================== --> <!-- ===================== -->
<!-- 主功能区 --> <!-- 主功能区 -->
@ -660,8 +623,8 @@
<!-- ===================== --> <!-- ===================== -->
<!-- 卡片 1: OCR 文档解析 --> <!-- 卡片 1: OCR 文档解析 -->
<!-- ===================== --> <!-- ===================== -->
<div class="modern-card p-4 md:p-6"> <div class="modern-card p-4 md:p-6 hidden">
<!--
<div class="mb-6"> <div class="mb-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center"> <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> <iconify-icon icon="carbon:api" class="mr-2 text-indigo-500" width="24"></iconify-icon>
@ -682,12 +645,12 @@
</div> </div>
</div> </div>
</div> </div>
-->
<!-- ===================== --> <!-- ===================== -->
<!-- OCR 设置 --> <!-- OCR 设置 -->
<!-- ===================== --> <!-- ===================== -->
<div> <div>
<div class="section-header justify-between"> <div class="section-header justify-between">
<h2 class="section-title"> <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);"> <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);">
@ -695,14 +658,14 @@
</div> </div>
OCR 文档解析 OCR 文档解析
</h2> </h2>
<!-- <div class="flex items-center"> <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> <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管理"> <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> <iconify-icon icon="carbon:settings" width="18"></iconify-icon>
<span>设置</span> <span>设置</span>
</button> </button>
</div> --> </div>
</div> </div>
<!-- OCR 引擎选择(简化版) --> <!-- OCR 引擎选择(简化版) -->
<div> <div>
@ -717,10 +680,10 @@
<div id="localOcrHint" class="text-xs text-gray-500 mt-1 hidden"> <div id="localOcrHint" class="text-xs text-gray-500 mt-1 hidden">
<strong>本地解析</strong>:适用于文字型 PDF非扫描件免费且快速但不支持图片和复杂排版的识别 <strong>本地解析</strong>:适用于文字型 PDF非扫描件免费且快速但不支持图片和复杂排版的识别
</div> </div>
</div> </div>
<!-- MinerU 翻译模式配置(仅当选择 MinerU 时显示) --> <!-- MinerU 翻译模式配置(仅当选择 MinerU 时显示) -->
<div id="mineruTranslationModeConfig" class="hidden mt-4"> <div id="mineruTranslationModeConfig" class="hidden mt-4">
<div class="border border-gray-200 rounded-lg bg-gray-50 p-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> <label class="block text-sm font-medium text-gray-700 mb-3">MinerU 翻译模式</label>
<div class="space-y-2"> <div class="space-y-2">
@ -740,14 +703,14 @@
</label> </label>
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
</div><!-- /.modern-card (OCR) -->
<!-- ===================== --> <!-- ===================== -->
<!-- 卡片 2: 翻译与分析 --> <!-- 卡片 2: 翻译与分析 -->
<!-- ===================== --> <!-- ===================== -->
<div class="modern-card p-4 md:p-6"> <div class="modern-card p-4 md:p-6 hidden">
<div class="section-header"> <div class="section-header">
<h2 class="section-title"> <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);"> <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);">
@ -990,7 +953,7 @@
<!-- ===================== --> <!-- ===================== -->
<!-- 卡片 3: 高级设置 --> <!-- 卡片 3: 高级设置 -->
<!-- ===================== --> <!-- ===================== -->
<div class="modern-card p-4 md:p-6"> <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"> <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"> <h2 class="section-title">
@ -1237,7 +1200,7 @@
<div class="section-header justify-between"> <div class="section-header justify-between">
<h2 class="section-title"> <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);"> <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="carbon:cloud-upload" width="20"></iconify-icon> <iconify-icon icon="mdi:upload" width="20"></iconify-icon>
</div> </div>
文件上传 文件上传
</h2> </h2>
@ -1253,7 +1216,7 @@
<input type="file" id="folderInput" class="hidden" webkitdirectory mozdirectory 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);"> <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="carbon:cloud-upload" width="40"></iconify-icon> <iconify-icon icon="mdi:upload" width="40"></iconify-icon>
</div> </div>
<h3 class="text-lg font-semibold text-slate-700 mb-2">点击或拖拽文件到此处</h3> <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"> <p class="text-sm text-slate-500 mb-6 max-w-md mx-auto">
@ -1270,17 +1233,6 @@
选择文件夹 选择文件夹
</button> </button>
</div> </div>
<div class="mt-4 flex flex-wrap justify-center gap-4 text-sm">
<button id="urlImportBtn" class="text-slate-500 hover:text-purple-600 transition-colors flex items-center gap-1.5">
<iconify-icon icon="carbon:link" width="16"></iconify-icon>
URL 导入
</button>
<span class="text-slate-300">|</span>
<button id="githubImportBtn" class="text-slate-500 hover:text-slate-800 transition-colors flex items-center gap-1.5">
<iconify-icon icon="carbon:logo-github" width="16"></iconify-icon>
GitHub 导入
</button>
</div>
</div> </div>
<!-- 已选文件列表 --> <!-- 已选文件列表 -->
@ -1373,9 +1325,13 @@
<!-- 处理按钮 --> <!-- 处理按钮 -->
<div class="flex justify-center mt-8 mb-12"> <div class="flex justify-center mt-8 mb-12">
<button id="processBtn" 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);"> <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> <iconify-icon icon="carbon:rocket" width="24"></iconify-icon>
<span>开始智能处理</span> <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> </button>
</div> </div>
</div> </div>
@ -1452,12 +1408,6 @@
<span>下载全部结果 (ZIP)</span> <span>下载全部结果 (ZIP)</span>
</button> </button>
</div> </div>
<!-- Moved disclaimer text inside the scrollable container -->
<div class="flex justify-center mt-8 mb-6">
<p class="text-center text-gray-400 text-xs select-none px-4">
注意AI 模型翻译结果仅供参考,最终内容请以原文为准
</p>
</div>
</div> </div>
</div><!-- /#mainAppContainer --> </div><!-- /#mainAppContainer -->
</div><!-- /.flex-1.overflow-y-auto --> </div><!-- /.flex-1.overflow-y-auto -->

View File

@ -7,7 +7,7 @@
<title>Paper Burner X - AI文献阅读与智能分析平台</title> <title>Paper Burner X - AI文献阅读与智能分析平台</title>
<link rel="icon" type="image/svg+xml" href="public/pure.svg"> <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> <!-- 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>
123
<!-- XSS 防护DOMPurify - 用于清理 AI 生成的 HTML 内容 --> <!-- XSS 防护DOMPurify - 用于清理 AI 生成的 HTML 内容 -->
<script src="https://gcore.jsdelivr.net/npm/dompurify@3.0.6/dist/purify.min.js"></script> <script src="https://gcore.jsdelivr.net/npm/dompurify@3.0.6/dist/purify.min.js"></script>
@ -231,7 +231,7 @@
pointer-events: none; pointer-events: none;
} }
/* 新增:右侧大 Logo 背景装饰 */ /* 新增:右侧大 Logo 背景装饰 */
.workspace-header::after { /* .workspace-header::after {
content: ''; content: '';
position: absolute; position: absolute;
right: -20px; right: -20px;
@ -242,10 +242,11 @@
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
background-size: contain; background-size: contain;
opacity: 0.07; /* 极低透明度,仅作纹理 */ 极低透明度,仅作纹理
opacity: 0.07;
transform: rotate(-10deg); transform: rotate(-10deg);
pointer-events: none; pointer-events: none;
} } */
.workspace-header-content { .workspace-header-content {
position: relative; position: relative;
z-index: 1; z-index: 1;
@ -532,30 +533,7 @@
</style> </style>
</head> </head>
<body class="bg-slate-50 min-h-screen"> <body class="bg-slate-50 min-h-screen">
<script>
// 可选:每天一次跳转到落地页(仅在非后端模式且未强制 backend 时启用)
(function() {
try {
var currentPath = window.location.pathname;
// 如果明确进入后端模式(通过查询参数或全局变量),则不跳转落地页,避免影响使用
var params = new URLSearchParams(window.location.search);
var forcedMode = (params.get('mode') || '').toLowerCase();
var envMode = (window.ENV_DEPLOYMENT_MODE || '').toLowerCase();
var shouldSkip = (forcedMode === 'backend') || (envMode === 'backend');
if (shouldSkip) return;
if (currentPath.indexOf('landing-page.html') !== -1) return;
var today = new Date().toDateString();
var lastShownDate = localStorage.getItem('paperBurnerLandingLastShown');
if (lastShownDate !== today) {
localStorage.setItem('paperBurnerLandingLastShown', today);
var basePath = currentPath.substring(0, currentPath.lastIndexOf('/') + 1);
window.location.href = basePath + 'views/landing/landing-page.html';
}
} catch (e) { /* ignore */ }
})();
</script>
<!-- Mobile Sidebar Overlay --> <!-- Mobile Sidebar Overlay -->
<div id="sidebarOverlay" class="sidebar-overlay md:hidden"></div> <div id="sidebarOverlay" class="sidebar-overlay md:hidden"></div>
@ -566,9 +544,11 @@
<!-- ===================== --> <!-- ===================== -->
<aside id="appSidebar" class="app-sidebar"> <aside id="appSidebar" class="app-sidebar">
<div class="px-6 py-5 flex items-center justify-between transition-all" id="sidebarHeader"> <div class="px-6 py-5 flex items-center justify-between transition-all" id="sidebarHeader">
<a href="views/landing/landing-page.html" class="hover:opacity-80 transition-opacity" title="返回落地页"> <!-- <a href="views/landing/landing-page.html" class="hover:opacity-80 transition-opacity" title="返回落地页">
<img id="sidebarLogo" src="public/h_with_name.svg" class="h-8 transition-all" alt="Paper Burner X"> <img id="sidebarLogo" src="public/h_with_name.svg" class="h-8 transition-all" alt="Paper Burner X">
</a> </a> -->
<!-- 占位元素 -->
<span></span>
<!-- Desktop Collapse Button --> <!-- Desktop Collapse Button -->
<button id="sidebarToggleBtn" class="hidden md:flex text-slate-400 hover:text-slate-600 p-1.5 hover:bg-slate-100 rounded-md transition-colors" title="切换侧边栏"> <button id="sidebarToggleBtn" class="hidden md:flex text-slate-400 hover:text-slate-600 p-1.5 hover:bg-slate-100 rounded-md transition-colors" title="切换侧边栏">
<iconify-icon id="sidebarToggleIcon" icon="carbon:side-panel-close" width="20"></iconify-icon> <iconify-icon id="sidebarToggleIcon" icon="carbon:side-panel-close" width="20"></iconify-icon>
@ -579,13 +559,7 @@
</button> </button>
</div> </div>
<nav class="flex-1 px-3 py-4 space-y-1 overflow-y-auto custom-scrollbar overflow-x-hidden"> <nav class="flex-1 px-3 py-4 space-y-1 overflow-y-auto custom-scrollbar overflow-x-hidden">
<div class="nav-section-title px-3 mb-2 text-[11px] font-bold text-slate-400 uppercase tracking-wider transition-opacity">
主要功能
</div>
<div class="nav-item active cursor-pointer" title="工作台">
<iconify-icon icon="carbon:dashboard" class="nav-icon"></iconify-icon>
<span class="nav-text transition-opacity">工作台</span>
</div>
<!-- 可折叠的历史记录菜单 (Split Action) --> <!-- 可折叠的历史记录菜单 (Split Action) -->
<div id="historyMenuContainer"> <div id="historyMenuContainer">
@ -605,34 +579,22 @@
</div> </div>
<div class="my-4 border-t border-slate-100 mx-3 transition-all" id="sidebarDivider"></div> <div class="my-4 border-t border-slate-100 mx-3 transition-all" id="sidebarDivider"></div>
<div class="nav-section-title px-3 mb-2 text-[11px] font-bold text-slate-400 uppercase tracking-wider transition-opacity"> <!-- <div class="nav-section-title px-3 mb-2 text-[11px] font-bold text-slate-400 uppercase tracking-wider transition-opacity">
系统 系统
</div> </div>
<div id="sidebarSettingsBtn" class="nav-item cursor-pointer" title="全局设置"> <div id="sidebarSettingsBtn" class="nav-item cursor-pointer" title="全局设置">
<iconify-icon icon="carbon:settings" class="nav-icon"></iconify-icon> <iconify-icon icon="carbon:settings" class="nav-icon"></iconify-icon>
<span class="nav-text transition-opacity">全局设置</span> <span class="nav-text transition-opacity">全局设置</span>
</div> </div> -->
</nav> </nav>
<!-- 删除界面会加载不出来 -->
<div class="p-3 transition-all" id="sidebarFooter"> <div class="p-3 transition-all" id="sidebarFooter">
<div class="p-4 bg-slate-50 rounded-2xl border border-slate-100 overflow-hidden"> <div class="p-4 overflow-hidden">
<a href="https://github.com/Feather-2/paper-burner" target="_blank" class="flex items-start gap-3 hover:opacity-80 transition-opacity" title="GitHub 仓库">
<div class="w-10 h-10 rounded-xl bg-white border border-slate-200 flex items-center justify-center text-slate-400 shadow-sm shrink-0">
<iconify-icon icon="carbon:logo-github" width="24"></iconify-icon>
</div>
<div class="flex-1 min-w-0 nav-text transition-opacity">
<div class="flex items-center gap-1.5 mb-1">
<span id="githubStars" class="inline-flex items-center gap-1 text-xs text-slate-500 bg-slate-100 px-2 py-0.5 rounded-full">
<iconify-icon icon="carbon:star" width="10"></iconify-icon>
<span>加载中...</span>
</span>
</div>
<p class="text-xs text-slate-500">支持自部署,欢迎星标</p>
</div>
</a>
</div> </div>
<div class="text-center py-1 mt-1 nav-text transition-opacity"> <div class="text-center py-1 mt-1 nav-text transition-opacity">
<span id="showCopyrightModal" class="text-[11px] text-slate-400 hover:text-slate-600 transition-colors cursor-pointer select-none"> <span id="showCopyrightModal" class="text-[11px] text-slate-400 hover:text-slate-600 transition-colors cursor-pointer select-none">
Paper Burner X 丨 关于
</span> </span>
</div> </div>
</div> </div>
@ -648,7 +610,7 @@
<!-- 主内容区 --> <!-- 主内容区 -->
<!-- ===================== --> <!-- ===================== -->
<main class="app-main flex flex-col"> <main class="app-main flex flex-col">
<!-- 移动端 Header --> <!-- 移动端 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"> <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"> <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"> <img src="public/pure.svg" class="w-7 h-7" alt="PBX Logo">
@ -662,7 +624,7 @@
<iconify-icon icon="carbon:menu" width="24"></iconify-icon> <iconify-icon icon="carbon:menu" width="24"></iconify-icon>
</button> </button>
</div> </div>
</header> </header> -->
<!-- 滚动内容容器 --> <!-- 滚动内容容器 -->
<div class="flex-1 overflow-y-auto custom-scrollbar"> <div class="flex-1 overflow-y-auto custom-scrollbar">
@ -671,7 +633,7 @@
<!-- ===================== --> <!-- ===================== -->
<!-- 顶部欢迎区 (Modern Dashboard Style) --> <!-- 顶部欢迎区 (Modern Dashboard Style) -->
<!-- ===================== --> <!-- ===================== -->
<div class="workspace-header"> <!-- <div class="workspace-header">
<div class="workspace-header-content"> <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);"> <h1 class="text-3xl md:text-4xl font-bold text-slate-900 mb-4 tracking-tight" style="color: var(--slate-900);">
可学可学可学可学可学测试提交后vercel更新是否成功 可学可学可学可学可学测试提交后vercel更新是否成功
@ -680,7 +642,7 @@
配置多种OCR 引擎与 AI 翻译模型。上传文档后Paper Burner X将给您流畅的阅读与处理体验。 配置多种OCR 引擎与 AI 翻译模型。上传文档后Paper Burner X将给您流畅的阅读与处理体验。
</p> </p>
</div> </div>
</div> </div> -->
<!-- ===================== --> <!-- ===================== -->
<!-- 主功能区 --> <!-- 主功能区 -->
@ -692,8 +654,8 @@
<!-- ===================== --> <!-- ===================== -->
<!-- 卡片 1: OCR 文档解析 --> <!-- 卡片 1: OCR 文档解析 -->
<!-- ===================== --> <!-- ===================== -->
<div class="modern-card p-4 md:p-6"> <div class="modern-card p-4 md:p-6 hidden">
<!--
<div class="mb-6"> <div class="mb-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center"> <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> <iconify-icon icon="carbon:api" class="mr-2 text-indigo-500" width="24"></iconify-icon>
@ -714,12 +676,12 @@
</div> </div>
</div> </div>
</div> </div>
-->
<!-- ===================== --> <!-- ===================== -->
<!-- OCR 设置 --> <!-- OCR 设置 -->
<!-- ===================== --> <!-- ===================== -->
<div> <div>
<div class="section-header justify-between"> <div class="section-header justify-between">
<h2 class="section-title"> <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);"> <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);">
@ -734,7 +696,7 @@
<span>设置</span> <span>设置</span>
</button> </button>
</div> </div>
</div> </div>
<!-- OCR 引擎选择(简化版) --> <!-- OCR 引擎选择(简化版) -->
<div> <div>
@ -749,10 +711,10 @@
<div id="localOcrHint" class="text-xs text-gray-500 mt-1 hidden"> <div id="localOcrHint" class="text-xs text-gray-500 mt-1 hidden">
<strong>本地解析</strong>:适用于文字型 PDF非扫描件免费且快速但不支持图片和复杂排版的识别 <strong>本地解析</strong>:适用于文字型 PDF非扫描件免费且快速但不支持图片和复杂排版的识别
</div> </div>
</div> </div>
<!-- MinerU 翻译模式配置(仅当选择 MinerU 时显示) --> <!-- MinerU 翻译模式配置(仅当选择 MinerU 时显示) -->
<div id="mineruTranslationModeConfig" class="hidden mt-4"> <div id="mineruTranslationModeConfig" class="hidden mt-4">
<div class="border border-gray-200 rounded-lg bg-gray-50 p-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> <label class="block text-sm font-medium text-gray-700 mb-3">MinerU 翻译模式</label>
<div class="space-y-2"> <div class="space-y-2">
@ -772,14 +734,14 @@
</label> </label>
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
</div><!-- /.modern-card (OCR) -->
<!-- ===================== --> <!-- ===================== -->
<!-- 卡片 2: 翻译与分析 --> <!-- 卡片 2: 翻译与分析 -->
<!-- ===================== --> <!-- ===================== -->
<div class="modern-card p-4 md:p-6"> <div class="modern-card p-4 md:p-6 hidden">
<div class="section-header"> <div class="section-header">
<h2 class="section-title"> <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);"> <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);">
@ -1022,7 +984,7 @@
<!-- ===================== --> <!-- ===================== -->
<!-- 卡片 3: 高级设置 --> <!-- 卡片 3: 高级设置 -->
<!-- ===================== --> <!-- ===================== -->
<div class="modern-card p-4 md:p-6"> <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"> <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"> <h2 class="section-title">
@ -1516,6 +1478,8 @@
<script src="js/utils/github-stars.js"></script> <script src="js/utils/github-stars.js"></script>
<!-- 自动探测后端:若 /api/健康检查通过将切换为 backend也可用 ?mode=backend 强制 --> <!-- 自动探测后端:若 /api/健康检查通过将切换为 backend也可用 ?mode=backend 强制 -->
<script>window.ENV_DEPLOYMENT_MODE = 'auto';</script> <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/api/api.js?v=2"></script>
<script src="js/storage/storage.js"></script> <script src="js/storage/storage.js"></script>
<!-- 先初始化存储适配器(提供 isFrontendMode 标记) --> <!-- 先初始化存储适配器(提供 isFrontendMode 标记) -->

119
js/app.js
View File

@ -43,6 +43,24 @@ function escapeHtml(str) {
}); });
} }
/**
* ArrayBuffer 转换为 Base64 字符串
* @param {ArrayBuffer} buffer - 需要转换的 ArrayBuffer
* @returns {string} Base64 编码的字符串
*/
function arrayBufferToBase64(buffer) {
if (!buffer) return null;
const bytes = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer || []);
if (!bytes.length) return null;
let binary = '';
const chunkSize = 0x8000;
for (let i = 0; i < bytes.length; i += chunkSize) {
const chunk = bytes.subarray(i, i + chunkSize);
binary += String.fromCharCode.apply(null, chunk);
}
return btoa(binary);
}
// ===================== // =====================
// 全局状态变量 // 全局状态变量
// ===================== // =====================
@ -523,6 +541,7 @@ function setupEventListeners() {
const githubImportBtn = document.getElementById('githubImportBtn'); const githubImportBtn = document.getElementById('githubImportBtn');
const clearBtn = document.getElementById('clearFilesBtn'); const clearBtn = document.getElementById('clearFilesBtn');
const processBtn = document.getElementById('processBtn'); const processBtn = document.getElementById('processBtn');
const onlyReadBtn = document.getElementById('onlyReadBtn');
const downloadBtn = document.getElementById('downloadAllBtn'); const downloadBtn = document.getElementById('downloadAllBtn');
const formatFilterContainer = document.getElementById('fileFormatFilters'); const formatFilterContainer = document.getElementById('fileFormatFilters');
const batchToggle = document.getElementById('batchModeToggle'); const batchToggle = document.getElementById('batchModeToggle');
@ -752,6 +771,7 @@ function setupEventListeners() {
// 处理和下载 // 处理和下载
processBtn.addEventListener('click', handleProcessClick); processBtn.addEventListener('click', handleProcessClick);
onlyReadBtn.addEventListener('click', handleReadClick);
downloadBtn.addEventListener('click', handleDownloadClick); downloadBtn.addEventListener('click', handleDownloadClick);
if (typeof window.updateDeeplxTargetLangHint === 'function') { if (typeof window.updateDeeplxTargetLangHint === 'function') {
@ -1119,7 +1139,7 @@ function refreshFormatFilters() {
`); `);
}); });
fragments.push('<div class="flex-grow"></div><button type="button" id="resetFormatFilters" class="text-xs text-blue-600">重置</button>'); // fragments.push('<div class="flex-grow"></div><button type="button" id="resetFormatFilters" class="text-xs text-blue-600">重置</button>');
container.innerHTML = `<div class="flex flex-wrap gap-2 items-center">${fragments.join('')}</div>`; container.innerHTML = `<div class="flex flex-wrap gap-2 items-center">${fragments.join('')}</div>`;
} }
@ -1868,7 +1888,8 @@ async function handleProcessClick() {
retryAttempts.clear(); retryAttempts.clear();
allResults = new Array(filesToProcess.length); allResults = new Array(filesToProcess.length);
updateProcessButtonState(pdfFiles, isProcessing); updateProcessButtonState(pdfFiles, isProcessing);
showProgressSection(); // 隐藏处理进度
// showProgressSection();
addProgressLog('=== 开始批量处理 ==='); addProgressLog('=== 开始批量处理 ===');
// 5. 获取并发和重试设置等... // 5. 获取并发和重试设置等...
@ -2131,8 +2152,17 @@ async function handleProcessClick() {
activeBatchSession = null; activeBatchSession = null;
isProcessing = false; isProcessing = false;
updateProcessButtonState(pdfFiles, isProcessing); updateProcessButtonState(pdfFiles, isProcessing);
showResultsSection(successCount, skippedCount, errorCount, filesToProcess.length); // 不显示处理完成
// showResultsSection(successCount, skippedCount, errorCount, filesToProcess.length);
saveProcessedFilesRecord(processedFilesRecord); saveProcessedFilesRecord(processedFilesRecord);
// 跳转到历史细节界面
const successfulResult = allResults.find(r => r && r.file && !r.error && !r.skipped);
if (filesToProcess.length === 1 && successfulResult && successfulResult.file) {
const recordId = `${successfulResult.file.name}_${successfulResult.file.size}`;
window.location.href = `views/history/history_detail.html?id=${encodeURIComponent(recordId)}`;
} else {
window.location.href = 'history.html';
}
allResults = allResults.filter(r => r !== undefined && r !== null); allResults = allResults.filter(r => r !== undefined && r !== null);
console.log("Final results count:", allResults.length); console.log("Final results count:", allResults.length);
@ -2154,6 +2184,89 @@ function handleDownloadClick() {
} }
} }
// =====================
// 仅阅读(不做处理直接跳转)
// =====================
/**
* 处理点击"仅阅读"按钮的事件
* 如果已有处理结果直接跳转历史详情
* 如果没有处理结果但有上传文件则将文件保存到历史记录后跳转
*/
async function handleReadClick() {
// 1. 优先检查是否有已处理的结果
if (allResults.length > 0) {
const successfulResult = allResults.find(r => r && r.file && !r.error && !r.skipped);
if (successfulResult && successfulResult.file) {
const recordId = `${successfulResult.file.name}_${successfulResult.file.size}`;
window.location.href = `views/history/history_detail.html?id=${encodeURIComponent(recordId)}`;
return;
}
}
// 2. 检查是否有上传的文件,直接保存到历史记录
if (pdfFiles.length > 0) {
const file = pdfFiles[0]; // 只处理第一个文件
const recordId = `${file.name}_${file.size}`;
try {
// 读取文件内容为 base64
const arrayBuffer = await file.arrayBuffer();
const base64Content = arrayBufferToBase64(arrayBuffer);
// 获取文件扩展名和类型
const ext = file.name.split('.').pop().toLowerCase();
const fileType = ext;
// 创建历史记录对象
const record = {
id: recordId,
name: file.name,
size: file.size,
time: new Date().toISOString(),
ocr: '',
translation: '',
images: [],
ocrChunks: [],
translatedChunks: [],
fileType: fileType,
targetLanguage: '',
originalEncoding: 'binary',
originalBinary: base64Content,
originalExtension: ext,
ocrEngine: null,
ocrSource: null,
translationModelName: 'none',
batchId: null,
batchOrder: null,
batchTotal: null,
batchTemplate: null,
batchFormats: null,
batchStartedAt: null
};
// 保存到数据库
if (typeof window.storageAdapter !== 'undefined' && typeof window.storageAdapter.saveResultToDB === 'function') {
await window.storageAdapter.saveResultToDB(record);
} else if (typeof saveResultToDB === 'function') {
await saveResultToDB(record);
} else {
showNotification('存储功能不可用', 'error');
return;
}
// 跳转到历史详情页面
window.location.href = `views/history/history_detail.html?id=${encodeURIComponent(recordId)}`;
} catch (error) {
console.error('保存文件到历史记录失败:', error);
showNotification(`保存失败: ${error.message}`, 'error');
}
} else {
showNotification('请先上传文件', 'warning');
}
}
// ===================== // =====================
// 内置提示模板获取 // 内置提示模板获取
// ===================== // =====================

View File

@ -1222,7 +1222,7 @@ function initChatbotUI() {
</div> </div>
<!-- 标题栏 (可拖拽移动窗口) --> <!-- 标题栏 (可拖拽移动窗口) -->
<div id="chatbot-title-bar" class="chatbot-draggable-header" style="padding:12px 24px;display:flex;align-items:center;gap:8px;border-bottom:1px dashed rgba(0,0,0,0.1);flex-shrink:0;"> <div id="chatbot-title-bar" class="chatbot-draggable-header" style="padding:12px 24px;display:flex;align-items:center;gap:8px;border-bottom:1px dashed rgba(0,0,0,0.1);flex-shrink:0;">
<div style="width:32px;height:32px;border-radius:16px;background:linear-gradient(135deg,#3b82f6,#1d4ed8);display:flex;align-items:center;justify-content:center;"> <div style="width:32px;height:32px;border-radius:16px;background:linear-gradient(135deg,#3b82f6,#1d4ed8);display:flex;align-items:center;justify-content:center;display:none;">
<i class="fa-solid fa-robot" style="font-size: 14px; color: white;"></i> <i class="fa-solid fa-robot" style="font-size: 14px; color: white;"></i>
</div> </div>
<span style="font-weight:600;font-size:1.05em;color:#111;">AI 智能助手</span> <span style="font-weight:600;font-size:1.05em;color:#111;">AI 智能助手</span>

View File

@ -63,8 +63,8 @@ document.addEventListener('DOMContentLoaded', function() {
const timeStr = `${timeObj.getMonth() + 1}/${timeObj.getDate()} ${String(timeObj.getHours()).padStart(2, '0')}:${String(timeObj.getMinutes()).padStart(2, '0')}`; const timeStr = `${timeObj.getMonth() + 1}/${timeObj.getDate()} ${String(timeObj.getHours()).padStart(2, '0')}:${String(timeObj.getMinutes()).padStart(2, '0')}`;
return ` return `
<div class="group flex items-center gap-2 px-2 py-1.5 text-[13px] text-slate-600 hover:bg-slate-100 hover:text-slate-900 transition-colors cursor-pointer rounded-md mx-2 mb-0.5" onclick="showHistoryDetail('${safeId}')" title="${name}\n${timeObj.toLocaleString()}"> <div class="group flex items-center gap-2 px-2 py-1.5 text-[13px] text-[#000f33] hover:bg-[#000f33] hover:text-[#ffffff] transition-colors cursor-pointer rounded-md mx-2 mb-0.5" onclick="showHistoryDetail('${safeId}')" title="${name}\n${timeObj.toLocaleString()}">
<iconify-icon icon="carbon:document" width="14" class="flex-shrink-0 text-slate-400 group-hover:text-slate-500 transition-colors"></iconify-icon>
<span class="truncate flex-1">${name}</span> <span class="truncate flex-1">${name}</span>
<span class="text-[10px] text-slate-400 flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity">${timeStr}</span> <span class="text-[10px] text-slate-400 flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity">${timeStr}</span>
</div> </div>

View File

@ -466,15 +466,11 @@ async function triggerReprocess(includeTranslation) {
return; return;
} }
// 显示处理中 Toast
showToast('正在准备文档...', 'info');
try { try {
// 将 Base64 转为 File 对象 // 将 Base64 转为 File 对象
const file = base64ToFile(pdfBase64, docName); const file = base64ToFile(pdfBase64, docName);
// 执行 OCR
showToast('正在进行 OCR 识别...', 'info');
const ocrResult = await performOcr(file, (current, total, msg) => { const ocrResult = await performOcr(file, (current, total, msg) => {
showToast(`${msg || 'OCR 处理中'} (${current}/${total})`, 'info', 5000); showToast(`${msg || 'OCR 处理中'} (${current}/${total})`, 'info', 5000);
@ -493,7 +489,6 @@ async function triggerReprocess(includeTranslation) {
// 如果需要翻译,执行翻译 // 如果需要翻译,执行翻译
if (includeTranslation) { if (includeTranslation) {
showToast('正在进行翻译...', 'info');
try { try {
const translationResult = await performTranslation(ocrResult.markdown, (current, total, msg) => { const translationResult = await performTranslation(ocrResult.markdown, (current, total, msg) => {
@ -515,7 +510,6 @@ async function triggerReprocess(includeTranslation) {
} }
// 保存到 IndexedDB // 保存到 IndexedDB
showToast('正在保存...', 'info');
await saveResultToDB(window.data); await saveResultToDB(window.data);
// 刷新页面显示 // 刷新页面显示
@ -710,7 +704,6 @@ async function triggerReprocessWithMinerU() {
const savedMineruMode = localStorage.getItem('mineruMode'); const savedMineruMode = localStorage.getItem('mineruMode');
try { try {
showToast('正在使用 MinerU 处理文档...', 'info');
// 临时设置 OCR 配置为 MinerU + 结构化翻译模式 // 临时设置 OCR 配置为 MinerU + 结构化翻译模式
localStorage.setItem('ocrEngine', 'mineru'); localStorage.setItem('ocrEngine', 'mineru');

View File

@ -218,10 +218,17 @@
isTocDockResizing = false; // Reset flag isTocDockResizing = false; // Reset flag
} }
function enterImmersiveMode() { function enterImmersiveMode(options = {}) {
const { silent = false } = options;
// 检查是否为移动端设备 // 检查是否为移动端设备
if (window.innerWidth <= 700) { if (window.innerWidth <= 700) {
console.warn('拒绝在移动端≤700px进入沉浸式布局'); console.warn('拒绝在移动端≤700px进入沉浸式布局');
// 即使不进入沉浸模式,也需要显示页面
document.documentElement.classList.remove('immersive-pending');
document.documentElement.classList.add('immersive-ready');
document.body.classList.remove('immersive-pending');
document.body.classList.add('immersive-ready');
return; return;
} }
@ -238,19 +245,41 @@
if (!immersiveChatbotArea) missingElements.push('immersiveChatbotArea'); if (!immersiveChatbotArea) missingElements.push('immersiveChatbotArea');
if (!immersiveDockPlaceholderElement) missingElements.push('immersiveDockPlaceholderElement (logic error if this happens)'); if (!immersiveDockPlaceholderElement) missingElements.push('immersiveDockPlaceholderElement (logic error if this happens)');
console.log('[enterImmersiveMode] 元素检查:', {
immersiveContainer: !!immersiveContainer,
mainPageContainer: !!mainPageContainer,
tocPopupElement: !!tocPopupElement,
chatbotModalElement: !!chatbotModalElement,
dockElement: !!dockElement,
immersiveTocArea: !!immersiveTocArea,
immersiveMainArea: !!immersiveMainArea,
immersiveChatbotArea: !!immersiveChatbotArea
});
if (missingElements.length > 0) { if (missingElements.length > 0) {
console.warn('Immersive mode elements not found:', missingElements.join(', ')); console.warn('Immersive mode elements not found:', missingElements.join(', '));
// 元素缺失时也需要显示页面
document.body.classList.remove('immersive-pending');
document.body.classList.add('immersive-ready');
return; return;
} }
isImmersiveActive = true; isImmersiveActive = true;
storeOriginalPositions(); storeOriginalPositions();
// 添加进入动画类 // 静默模式:跳过动画,直接设置状态
document.body.classList.add('immersive-entering'); if (silent) {
immersiveContainer.style.opacity = '0'; document.body.classList.add('immersive-active', 'no-scroll');
immersiveContainer.style.transform = 'scale(0.95)'; immersiveContainer.style.display = 'flex';
immersiveContainer.style.transition = 'opacity 0.4s ease, transform 0.4s ease'; immersiveContainer.style.opacity = '1';
immersiveContainer.style.transform = 'scale(1)';
} else {
// 添加进入动画类
document.body.classList.add('immersive-entering');
immersiveContainer.style.opacity = '0';
immersiveContainer.style.transform = 'scale(0.95)';
immersiveContainer.style.transition = 'opacity 0.4s ease, transform 0.4s ease';
}
// Append TOC content first // Append TOC content first
if (tocPopupElement && immersiveTocArea) { if (tocPopupElement && immersiveTocArea) {
@ -305,26 +334,35 @@
} }
} }
document.body.classList.add('immersive-active', 'no-scroll'); // 静默模式下已经设置了 display 和 opacity跳过动画
immersiveContainer.style.display = 'flex'; if (!silent) {
document.body.classList.add('immersive-active', 'no-scroll');
// 简单的强制重新计算,修复初始化时的布局问题 immersiveContainer.style.display = 'flex';
setTimeout(() => {
if (immersiveContainer) { // 简单的强制重新计算,修复初始化时的布局问题
immersiveContainer.offsetHeight; // 触发重新布局
}
}, 0);
// 动画进入效果
requestAnimationFrame(() => {
immersiveContainer.style.opacity = '1';
immersiveContainer.style.transform = 'scale(1)';
setTimeout(() => { setTimeout(() => {
document.body.classList.remove('immersive-entering'); if (immersiveContainer) {
immersiveContainer.style.transition = ''; immersiveContainer.offsetHeight; // 触发重新布局
}, 400); }
}); }, 0);
// 动画进入效果
requestAnimationFrame(() => {
immersiveContainer.style.opacity = '1';
immersiveContainer.style.transform = 'scale(1)';
setTimeout(() => {
document.body.classList.remove('immersive-entering');
immersiveContainer.style.transition = '';
}, 400);
});
}
// 显示页面(移除 pending 状态)
document.documentElement.classList.remove('immersive-pending');
document.documentElement.classList.add('immersive-ready');
document.body.classList.remove('immersive-pending');
document.body.classList.add('immersive-ready');
if (toggleBtn) { if (toggleBtn) {
toggleBtn.innerHTML = '<i class="fas fa-compress-alt"></i>'; toggleBtn.innerHTML = '<i class="fas fa-compress-alt"></i>';
@ -393,7 +431,6 @@
localStorage.setItem(LS_IMMERSIVE_KEY, 'true'); localStorage.setItem(LS_IMMERSIVE_KEY, 'true');
document.dispatchEvent(new CustomEvent('immersiveModeEntered')); document.dispatchEvent(new CustomEvent('immersiveModeEntered'));
} }
function exitImmersiveMode() { function exitImmersiveMode() {
reQueryDynamicElements(); reQueryDynamicElements();
isImmersiveActive = false; isImmersiveActive = false;
@ -697,11 +734,21 @@
simpleToggleBtn.innerHTML = '<i class="fas fa-eye-slash"></i>'; simpleToggleBtn.innerHTML = '<i class="fas fa-eye-slash"></i>';
simpleToggleBtn.title = '退出简单沉浸模式'; simpleToggleBtn.title = '退出简单沉浸模式';
localStorage.setItem(LS_SIMPLE_IMMERSIVE_KEY, 'true'); localStorage.setItem(LS_SIMPLE_IMMERSIVE_KEY, 'true');
// 自动进入全屏
document.documentElement.requestFullscreen().catch(err => {
console.warn('无法进入全屏模式:', err);
});
} else { } else {
// 退出简单沉浸模式 // 退出简单沉浸模式
document.body.classList.remove('simple-immersive-pdf-mode'); document.body.classList.remove('simple-immersive-pdf-mode');
document.body.classList.remove('simple-immersive-pdf-mode'); document.body.classList.remove('simple-immersive-pdf-mode');
// 如果在全屏状态,退出全屏
if (document.fullscreenElement) {
document.exitFullscreen();
}
// 恢复container的padding // 恢复container的padding
if (mainContainer && mainContainer.dataset.originalPadding !== undefined) { if (mainContainer && mainContainer.dataset.originalPadding !== undefined) {
mainContainer.style.padding = mainContainer.dataset.originalPadding; mainContainer.style.padding = mainContainer.dataset.originalPadding;
@ -758,21 +805,36 @@
// Restore immersive state from localStorage // Restore immersive state from localStorage
const savedImmersiveState = localStorage.getItem(LS_IMMERSIVE_KEY); const savedImmersiveState = localStorage.getItem(LS_IMMERSIVE_KEY);
if (savedImmersiveState === 'true') {
// 历史详情页默认进入沉浸模式,只有用户明确退出过才不进入
// 注意null 表示首次访问,'true' 表示用户之前处于沉浸模式
// 只有 'false' 才表示用户主动退出过沉浸模式
const shouldEnterImmersive = savedImmersiveState !== 'false';
console.log('[ImmersiveLayout] savedImmersiveState:', savedImmersiveState, 'shouldEnterImmersive:', shouldEnterImmersive);
if (shouldEnterImmersive) {
// 检查是否为移动端,如果是则不恢复沉浸模式 // 检查是否为移动端,如果是则不恢复沉浸模式
if (window.innerWidth <= 700) { if (window.innerWidth <= 700) {
console.log('检测到移动端设备,不恢复沉浸式布局状态'); console.log('检测到移动端设备,不进入沉浸式布局');
localStorage.setItem(LS_IMMERSIVE_KEY, 'false'); // 清除保存的状态 localStorage.setItem(LS_IMMERSIVE_KEY, 'false');
return; // 显示页面
document.documentElement.classList.remove('immersive-pending');
document.documentElement.classList.add('immersive-ready');
document.body.classList.remove('immersive-pending');
document.body.classList.add('immersive-ready');
} else {
// 立即进入沉浸模式(静默模式,无动画)
console.log('[ImmersiveLayout] 准备进入沉浸模式, isImmersiveActive:', isImmersiveActive);
enterImmersiveMode({ silent: true });
console.log('[ImmersiveLayout] enterImmersiveMode 调用完成, isImmersiveActive:', isImmersiveActive);
} }
} else {
// Slight delay to ensure other initializations (like TOC, Chatbot) can occur first // 不需要沉浸模式,直接显示页面
// especially if they also interact with elements moved by immersive mode. document.documentElement.classList.remove('immersive-pending');
setTimeout(() => { document.documentElement.classList.add('immersive-ready');
if (!isImmersiveActive) { // Check again in case of race conditions or manual toggle document.body.classList.remove('immersive-pending');
enterImmersiveMode(); document.body.classList.add('immersive-ready');
}
}, 200); // Adjust delay if needed
} }
// Restore simple immersive state from localStorage // Restore simple immersive state from localStorage

View File

@ -2000,7 +2000,7 @@
--doorhanger-icon-opacity: 0.9; --doorhanger-icon-opacity: 0.9;
--main-color: rgba(12, 12, 13, 1); --main-color: rgba(12, 12, 13, 1);
--body-bg-color: rgba(212, 212, 215, 1); --body-bg-color: #f3f4f5;
--progressBar-color: rgba(10, 132, 255, 1); --progressBar-color: rgba(10, 132, 255, 1);
--progressBar-bg-color: rgba(221, 221, 222, 1); --progressBar-bg-color: rgba(221, 221, 222, 1);
--progressBar-blend-color: rgba(116, 177, 239, 1); --progressBar-blend-color: rgba(116, 177, 239, 1);
@ -2011,7 +2011,7 @@
--sidebar-narrow-bg-color: rgba(212, 212, 215, 0.9); --sidebar-narrow-bg-color: rgba(212, 212, 215, 0.9);
--sidebar-toolbar-bg-color: rgba(245, 246, 247, 1); --sidebar-toolbar-bg-color: rgba(245, 246, 247, 1);
--toolbar-bg-color: rgba(249, 249, 250, 1); --toolbar-bg-color: #ffffff;
--toolbar-border-color: rgba(184, 184, 184, 1); --toolbar-border-color: rgba(184, 184, 184, 1);
--toolbar-box-shadow: 0 1px 0 var(--toolbar-border-color); --toolbar-box-shadow: 0 1px 0 var(--toolbar-border-color);
--toolbar-border-bottom: none; --toolbar-border-bottom: none;
@ -2019,7 +2019,7 @@
rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.25),
0 1px 0 rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1); 0 1px 0 rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1);
--toolbarSidebar-border-bottom: none; --toolbarSidebar-border-bottom: none;
--button-hover-color: rgba(221, 222, 223, 1); --button-hover-color: #e6ecfb;
--toggled-btn-color: rgba(0, 0, 0, 1); --toggled-btn-color: rgba(0, 0, 0, 1);
--toggled-btn-bg-color: rgba(0, 0, 0, 0.3); --toggled-btn-bg-color: rgba(0, 0, 0, 0.3);
--toggled-hover-active-btn-color: rgba(0, 0, 0, 0.4); --toggled-hover-active-btn-color: rgba(0, 0, 0, 0.4);
@ -2308,6 +2308,7 @@ body {
z-index: 9999; z-index: 9999;
cursor: default; cursor: default;
font: message-box; font: message-box;
background-color: var(--toolbar-bg-color);
} }
:is(.toolbar, .editorParamsToolbar, .findbar, #sidebarContainer) :is(.toolbar, .editorParamsToolbar, .findbar, #sidebarContainer)
@ -2345,7 +2346,7 @@ body {
position: relative; position: relative;
height: 32px; height: 32px;
background-color: var(--toolbar-bg-color); background-color: var(--toolbar-bg-color);
box-shadow: var(--toolbar-box-shadow); /* box-shadow: var(--toolbar-box-shadow); */
border-bottom: var(--toolbar-border-bottom); border-bottom: var(--toolbar-border-bottom);
} }
@ -3181,7 +3182,7 @@ a:is(.toolbarButton, .secondaryToolbarButton)[href="#"] {
user-select: none; user-select: none;
} }
#thumbnailView { #thumbnailView {
width: calc(100% - 60px); width: 100%;
padding: 10px 30px 0; padding: 10px 30px 0;
} }

View File

@ -29,6 +29,9 @@ See https://github.com/adobe-type-tools/cmap-resources
<!-- This snippet is used in production (included from viewer.html) --> <!-- This snippet is used in production (included from viewer.html) -->
<link rel="resource" type="application/l10n" href="locale/locale.properties"> <link rel="resource" type="application/l10n" href="locale/locale.properties">
<!-- 引入tailwindcss -->
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<script src="../build/pdf.js"></script> <script src="../build/pdf.js"></script>
<link rel="stylesheet" href="viewer.css"> <link rel="stylesheet" href="viewer.css">
@ -269,19 +272,20 @@ See https://github.com/adobe-type-tools/cmap-resources
<span id="numPages" class="toolbarLabel"></span> <span id="numPages" class="toolbarLabel"></span>
</div> </div>
<div id="toolbarViewerRight"> <div id="toolbarViewerRight">
<button id="openFile" class="toolbarButton hiddenLargeView" title="Open File" tabindex="31" data-l10n-id="open_file"> <!-- 直接注释会PDF加载失败 -->
<button id="openFile" class="toolbarButton hiddenLargeView hidden" title="Open File" tabindex="31" data-l10n-id="open_file">
<span data-l10n-id="open_file_label">Open</span> <span data-l10n-id="open_file_label">Open</span>
</button> </button>
<button id="print" class="toolbarButton hiddenMediumView" title="Print" tabindex="32" data-l10n-id="print"> <button id="print" class="toolbarButton hiddenMediumView hidden" title="Print" tabindex="32" data-l10n-id="print">
<span data-l10n-id="print_label">Print</span> <span data-l10n-id="print_label">Print</span>
</button> </button>
<button id="download" class="toolbarButton hiddenMediumView" title="Save" tabindex="33" data-l10n-id="save"> <button id="download" class="toolbarButton hiddenMediumView hidden" title="Save" tabindex="33" data-l10n-id="save">
<span data-l10n-id="save_label">Save</span> <span data-l10n-id="save_label">Save</span>
</button> </button>
<div class="verticalToolbarSeparator hiddenMediumView"></div> <div class="verticalToolbarSeparator hiddenMediumView hidden"></div>
<div id="editorModeButtons" class="splitToolbarButton toggled" role="radiogroup"> <div id="editorModeButtons" class="splitToolbarButton toggled" role="radiogroup">
<button id="editorFreeText" class="toolbarButton" disabled="disabled" title="Text" role="radio" aria-checked="false" aria-controls="editorFreeTextParamsToolbar" tabindex="34" data-l10n-id="editor_free_text2"> <button id="editorFreeText" class="toolbarButton" disabled="disabled" title="Text" role="radio" aria-checked="false" aria-controls="editorFreeTextParamsToolbar" tabindex="34" data-l10n-id="editor_free_text2">

View File

@ -86,10 +86,27 @@
<link rel="stylesheet" href="../../css/history_detail.css" /> <link rel="stylesheet" href="../../css/history_detail.css" />
<!-- All CSS now imported via history_detail.css (Phase 9 完成) --> <!-- All CSS now imported via history_detail.css (Phase 9 完成) -->
<!-- 页面初始隐藏,等待沉浸模式初始化 -->
<style>
html.immersive-pending body,
body.immersive-pending {
opacity: 0;
}
html.immersive-ready body,
body.immersive-ready {
opacity: 1;
transition: opacity 0.2s ease;
}
</style>
<script>
// 在 DOM 解析前就添加类,避免闪烁
document.documentElement.classList.add('immersive-pending');
</script>
</head> </head>
<body> <body class="immersive-pending">
<!-- Standard Immersive Mode Toggle Button --> <!-- Standard Immersive Mode Toggle Button -->
<button id="toggle-immersive-btn" class="tiny-round-btn" title="进入沉浸式布局"> <button id="toggle-immersive-btn" class="tiny-round-btn hidden" style="display: none;" title="进入沉浸式布局">
<i class="fas fa-expand-alt"></i> <i class="fas fa-expand-alt"></i>
<!-- Default icon for entering immersive mode --> <!-- Default icon for entering immersive mode -->
</button> </button>
@ -400,9 +417,9 @@
<h2 id="fileName">历史详情</h2> <h2 id="fileName">历史详情</h2>
<div class="meta" id="fileMeta"> <div class="meta" id="fileMeta">
<div class="meta-info"> <div class="meta-info">
<span id="fileMetaTime"></span> <!-- <span id="fileMetaTime"></span>
<span class="meta-separator">|</span> <span class="meta-separator">|</span>
<span id="fileMetaImages"></span> <span id="fileMetaImages"></span> -->
</div> </div>
<button <button
class="meta-export-trigger" class="meta-export-trigger"