paper-burner/views/history/history_detail.html

1214 lines
48 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>历史详情</title>
<!-- CDN 性能优化DNS 预连接 - 使用淘宝 npmmirror 镜像加速 -->
<link rel="dns-prefetch" href="https://registry.npmmirror.com" />
<link rel="preconnect" href="https://registry.npmmirror.com" crossorigin />
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com" />
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin />
<!-- KaTeX 公式渲染样式 -->
<link
rel="stylesheet"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"
/>
<!-- KaTeX 字体预加载 - 优化慢网络环境下的加载性能 -->
<link
rel="preload"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/fonts/KaTeX_Main-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/fonts/KaTeX_Main-Bold.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/fonts/KaTeX_Math-Italic.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/fonts/KaTeX_Size1-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/fonts/KaTeX_Size2-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/fonts/KaTeX_AMS-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/fonts/KaTeX_Size3-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/fonts/KaTeX_Size4-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
type="text/css"
rel="stylesheet"
href="https://gcore.jsdelivr.net/npm/jsmind/style/jsmind.css"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
/>
<!-- Iconify 图标库(用于侧边栏) -->
<script src="https://gcore.jsdelivr.net/npm/iconify-icon@2.0.0/dist/iconify-icon.min.js"></script>
<!-- Main CSS Entry Point (100% Modular Architecture) -->
<link rel="stylesheet" href="../../css/history_detail.css" />
<!-- 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>
<body class="immersive-pending">
<!-- Standard Immersive Mode Toggle Button 进入沉浸式布局-->
<button id="toggle-immersive-btn" class="tiny-round-btn disvisible hidden" style="display: none;" title="">
<i class="fas fa-expand-alt"></i>
<!-- Default icon for entering immersive mode -->
</button>
<!-- Simple Immersive Mode Toggle Button -->
<button id="toggle-simple-immersive-btn" class="tiny-round-btn" style="top: 70px !important;" title="进入简单沉浸模式">
<i class="fas fa-eye"></i>
<!-- Icon for simple immersive mode -->
</button>
<!-- Immersive Layout Container -->
<div
id="immersive-layout-container"
style="
display: none; /* Initially hidden */
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 1999; /* Below toggle button, above most other things */
background-color: var(
--body-bg-color,
#fff
); /* Match body background */
flex-direction: column;
"
>
<!-- Removed immersive-layout-header -->
<div
id="immersive-content-area"
style="display: flex; flex: 1; overflow: hidden"
>
<!-- TOC Area -->
<div id="immersive-toc-area" class="immersive-panel" style="width: 15%">
<!-- TOC will be rendered or moved here -->
</div>
<!-- Resizable Handle 1 -->
<div
class="immersive-resize-handle"
data-target-prev="immersive-toc-area"
data-target-next="immersive-main-content-area"
></div>
<!-- Main Content Area (where original .container's content will go) -->
<div
id="immersive-main-content-area"
class="immersive-panel"
style="flex: 1"
>
<!-- Original .container content (tabs, tab content etc.) will be programmatically moved here -->
</div>
<!-- Resizable Handle 2 -->
<div
class="immersive-resize-handle"
data-target-prev="immersive-main-content-area"
data-target-next="immersive-chatbot-area"
></div>
<!-- Chatbot Area -->
<div
id="immersive-chatbot-area"
class="immersive-panel"
style="width: 20%"
>
<!-- Chatbot will be rendered or moved here -->
</div>
</div>
<!-- Removed Placeholder for Dock in immersive mode -->
</div>
<!-- ========== App Shell 包装器(非沉浸模式) ========== -->
<div class="app-shell" id="app-shell">
<!-- 移动端侧边栏遮罩 -->
<div class="sidebar-overlay" id="sidebarOverlay"></div>
<!-- ========== 统一侧边栏 ========== -->
<aside class="app-sidebar" id="appSidebar">
<!-- 侧边栏头部 -->
<div class="sidebar-header">
<!-- 占位元素 -->
<span></span>
<!-- 桌面端收起按钮 -->
<button
class="sidebar-toggle-btn"
id="sidebarToggleBtn"
title="切换侧边栏"
>
<iconify-icon
icon="carbon:side-panel-close"
width="20"
id="sidebarToggleIcon"
></iconify-icon>
</button>
<!-- 移动端关闭按钮 -->
<button class="sidebar-close-btn" id="sidebarCloseBtn">
<iconify-icon icon="carbon:close" width="20"></iconify-icon>
</button>
</div>
<!-- 侧边栏导航 -->
<nav class="sidebar-nav">
<!-- 主要功能区 -->
<div class="nav-section-title">主要功能</div>
<!-- 回到工作台 -->
<a href="../../index.html" class="nav-item">
<iconify-icon
icon="carbon:dashboard"
class="nav-icon"
></iconify-icon>
<span class="nav-text">回到工作台</span>
</a>
<!-- 分隔线 -->
<div class="sidebar-divider"></div>
<!-- 文档结构区 -->
<div class="nav-section-title">文档结构</div>
<!-- TOC 目录(可折叠) -->
<div class="sidebar-section expanded" id="sidebarTocSection">
<button class="sidebar-section-header" id="sidebarTocToggle">
<span class="sidebar-section-title">
<iconify-icon icon="carbon:list"></iconify-icon>
<span>目录</span>
</span>
<iconify-icon
icon="carbon:chevron-right"
class="sidebar-section-chevron"
width="16"
></iconify-icon>
</button>
<div class="sidebar-section-content">
<ul class="sidebar-toc-list" id="sidebarTocList">
<!-- TOC 项目将由 JavaScript 填充 -->
<li class="sidebar-empty-state">
<div>加载中...</div>
</li>
</ul>
</div>
</div>
</nav>
<!-- 侧边栏底部(紧凑布局) -->
<div class="sidebar-footer-compact">
<div class="sidebar-progress" id="sidebarReadingProgress">0%</div>
<div class="sidebar-quick-stats">
<div
class="sidebar-quick-stat"
data-stat-type="highlight"
title="高亮"
>
<iconify-icon icon="carbon:highlight" width="14"></iconify-icon>
<span id="sidebarHighlightCount">0</span>
</div>
<div
class="sidebar-quick-stat"
data-stat-type="annotation"
title="批注"
>
<iconify-icon icon="carbon:annotation" width="14"></iconify-icon>
<span id="sidebarAnnotationCount">0</span>
</div>
<div class="sidebar-quick-stat-divider"></div>
<div class="sidebar-quick-stat" title="图片">
<iconify-icon icon="carbon:image" width="14"></iconify-icon>
<span id="sidebarImageCount">0</span>
</div>
<div class="sidebar-quick-stat" title="公式">
<iconify-icon icon="carbon:formula" width="14"></iconify-icon>
<span id="sidebarFormulaCount">0</span>
</div>
<div class="sidebar-quick-stat" title="表格">
<iconify-icon icon="carbon:table" width="14"></iconify-icon>
<span id="sidebarTableCount">0</span>
</div>
<div class="sidebar-quick-stat" title="总字数">
<iconify-icon
icon="carbon:character-whole-number"
width="14"
></iconify-icon>
<span id="sidebarWordCount">0</span>
</div>
<div
class="sidebar-quick-stat"
data-stat-type="reference"
title="文献"
>
<iconify-icon
icon="carbon:document-multiple-01"
width="14"
></iconify-icon>
<span id="sidebarReferenceCount">0</span>
</div>
</div>
<button
class="sidebar-settings-btn"
id="sidebarSettingsLink"
title="设置"
>
<iconify-icon icon="carbon:settings" width="18"></iconify-icon>
</button>
</div>
<!-- 隐藏的统计数据容器已移除,所有元素已在底部显示 -->
</aside>
<!-- ========== 主内容区 ========== -->
<main class="app-main">
<!-- 移动端顶部栏 -->
<div class="mobile-header">
<div class="mobile-header-title">
<span>文档详情</span>
</div>
<button class="mobile-menu-btn" id="mobileMenuBtn">
<iconify-icon icon="carbon:menu" width="24"></iconify-icon>
</button>
</div>
<!-- 旧的 TOC 和 Dock隐藏但保留以便向后兼容 -->
<div style="display: none" id="legacy-toc-dock-wrapper">
<!-- ===================== -->
<!-- 浮动TOC目录按钮及悬浮窗 -->
<!-- ===================== -->
<div id="toc-float-btn" class="tiny-round-btn">
<i class="fa fa-list"></i>
</div>
<div id="toc-popup" class="toc-popup-hidden">
<div id="toc-popup-header">
<span><i class="fa fa-list"></i>目录 / TOC</span>
<button id="toc-popup-close-btn">关闭</button>
</div>
<ul id="toc-list"></ul>
</div>
<!-- NEW DOCK STRUCTURE -->
<div id="bottom-left-dock">
<div id="dock-info-stack">
<div class="dock-column dock-labels-column">
<div class="dock-stat-item-wrapper-progress">阅读进度:</div>
<div class="dock-stat-item-wrapper-highlight">
<span class="stat-item-clickable" data-stat-type="highlight"
>高亮: <span id="highlight-count">0</span></span
>
</div>
<div class="dock-stat-item-wrapper-img">
图片: <span id="image-count">0</span>
</div>
<div class="dock-stat-item-wrapper-formula">
公式: <span id="formula-count">0</span>
</div>
</div>
<div class="dock-column dock-values-column">
<div class="dock-stat-item-wrapper-progress">
<span id="reading-progress-percentage-verbose">0</span>%
</div>
<div class="dock-stat-item-wrapper-annotation">
<span class="stat-item-clickable" data-stat-type="annotation"
>批注: <span id="annotation-count">0</span></span
>
</div>
<div class="dock-stat-item-wrapper-tbl">
表格: <span id="table-count">0</span>
</div>
<div class="dock-stat-item-wrapper-words">
总字数: <span id="total-word-count">0</span>
</div>
</div>
</div>
<!-- 底部控制栏:文献 + 设置 + 折叠 -->
<div class="dock-control-bar">
<span id="dock-collapsed-progress-display"
><span id="reading-progress-percentage">0</span>%</span
>
<span class="stat-item-clickable" data-stat-type="reference">
<i class="fa fa-book"></i> <span id="reference-count">0</span>
</span>
<a href="#" id="settings-link" title="设置"
><i class="fa fa-cog"></i
></a>
<a href="#" id="dock-toggle-btn" title="折叠"
><i class="fa fa-chevron-down"></i
></a>
</div>
</div>
</div>
<!-- End legacy-toc-dock-wrapper -->
<!-- ===================== -->
<!-- 主内容容器 -->
<!-- ===================== -->
<div class="container">
<h2 id="fileName">历史详情</h2>
<div class="meta" id="fileMeta">
<div class="meta-info">
<!-- <span id="fileMetaTime"></span>
<span class="meta-separator">|</span>
<span id="fileMetaImages"></span> -->
</div>
<button
class="meta-export-trigger"
id="exportTrigger"
type="button"
aria-haspopup="true"
aria-expanded="false"
>
<i class="fa fa-download"></i>
<span>导出</span>
</button>
</div>
<!-- 标签页导航 -->
<div class="tabs-container">
<button class="tab-btn" id="tab-original-file">
<i class="fa fa-file-o"></i>原始文件
</button>
<button class="tab-btn" id="tab-pdf-compare">PDF对照</button>
<button class="tab-btn" id="tab-ocr">Word文档</button>
<button class="tab-btn" id="tab-translation">仅翻译</button>
<!-- <button class="tab-btn" id="tab-compare">对比</button> -->
<button class="tab-btn" id="tab-chunk-compare">分块对比</button>
</div>
<div class="history-export-controls" id="history-export-controls">
<div class="export-panel-backdrop" id="exportPanelBackdrop"></div>
<div class="export-panel" id="exportPanel">
<div class="export-panel-header">
<div class="export-panel-title">
<i class="fa fa-share-square-o"></i>导出当前视图
</div>
<button
type="button"
class="export-panel-close"
id="exportPanelClose"
aria-label="关闭导出面板"
>
<i class="fa fa-times"></i>
</button>
</div>
<div class="export-panel-body">
<div class="export-format-list">
<button
type="button"
class="export-menu-btn"
data-format="html"
>
<i class="fa fa-code"></i>HTML
</button>
<button
type="button"
class="export-menu-btn"
data-format="pdf"
>
<i class="fa fa-file-pdf-o"></i>PDF
</button>
<button
type="button"
class="export-menu-btn"
data-format="docx"
>
<i class="fa fa-file-word-o"></i>DOCX
</button>
<button
type="button"
class="export-menu-btn"
data-format="markdown"
>
<i class="fa fa-file-text-o"></i>Markdown
</button>
<button
type="button"
class="export-menu-btn"
data-format="original"
>
<i class="fa fa-file-o"></i>原格式
</button>
</div>
<div class="export-config" id="exportConfig">
<div class="export-config-content" id="exportConfigContent">
<div class="export-config-placeholder">
请选择导出格式以查看设置
</div>
</div>
<button
type="button"
class="export-confirm-btn"
id="exportConfirmBtn"
disabled
>
导出
</button>
</div>
</div>
</div>
</div>
<!-- 标签页内容显示区域 -->
<div
id="original-file-content"
class="tab-pane"
style="display: none; padding: 20px"
>
<div
id="original-file-header"
style="
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #eee;
"
>
<h3 style="margin: 0 0 10px 0">
<i class="fa fa-file-o"></i>原始文件
</h3>
<div
id="original-file-info"
style="color: #666; font-size: 14px"
></div>
</div>
<div
id="original-file-viewer"
style="
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
min-height: 300px;
max-height: 70vh;
overflow: auto;
"
>
<p style="color: #999; text-align: center">加载中...</p>
</div>
<div style="margin-top: 15px; text-align: right">
<button
id="original-file-download-btn"
class="export-btn"
style="
padding: 8px 16px;
background: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
"
>
<i class="fa fa-download"></i> 下载原始文件
</button>
</div>
</div>
<div class="tab-content" id="tabContent"></div>
</div>
<!-- ===================== -->
<!-- 自定义上下文菜单 -->
<!-- ===================== -->
<div id="custom-context-menu" class="context-menu-hidden">
<ul>
<li data-action="highlight-block">
高亮此区块
<div class="color-palette">
<span
class="color-option color-yellow"
data-color="yellow"
title="黄色"
></span>
<span
class="color-option color-pink"
data-color="pink"
title="粉色"
></span>
<span
class="color-option color-lightblue"
data-color="lightblue"
title="浅蓝色"
></span>
<span
class="color-option color-lightgreen"
data-color="lightgreen"
title="浅绿色"
></span>
<span
class="color-option color-purple"
data-color="purple"
title="紫色"
></span>
<span
class="color-option color-orange"
data-color="orange"
title="橙色"
></span>
</div>
</li>
<li
data-action="remove-highlight"
id="remove-highlight-option"
style="display: none"
>
取消高亮
</li>
<hr
class="menu-divider"
id="highlight-actions-divider"
style="display: none"
/>
<li
data-action="add-note"
id="add-note-option"
style="display: none"
>
添加批注
</li>
<li
data-action="edit-note"
id="edit-note-option"
style="display: none"
>
编辑批注
</li>
<hr
class="menu-divider"
id="note-actions-divider"
style="display: none"
/>
<li data-action="copy-content" id="copy-content-option">
<i class="fa fa-copy"></i>复制内容
</li>
<!-- 更多操作可以后续添加 -->
</ul>
</div>
<!-- Dock Settings Modal -->
<div id="dock-settings-modal" class="dock-settings-modal modal-overlay">
<!-- Default: not visible -->
<div class="dock-settings-modal-content modal-content">
<!-- ADDED modal-content class -->
<span
class="dock-settings-modal-close-btn"
id="dock-settings-close-btn"
>&times;</span
>
<h2>显示设置</h2>
<div class="checkbox-container">
<!-- Container for checkboxes -->
<div class="checkbox-group">
<input
type="checkbox"
id="ds-readingProgress"
data-config-key="readingProgress"
/>
<label for="ds-readingProgress">阅读进度</label>
</div>
<div class="checkbox-group">
<input
type="checkbox"
id="ds-highlights"
data-config-key="highlights"
/>
<label for="ds-highlights">高亮</label>
</div>
<div class="checkbox-group">
<input
type="checkbox"
id="ds-annotations"
data-config-key="annotations"
/>
<label for="ds-annotations">批注</label>
</div>
<div class="checkbox-group">
<input
type="checkbox"
id="ds-images"
data-config-key="images"
/>
<label for="ds-images">图片</label>
</div>
<div class="checkbox-group">
<input
type="checkbox"
id="ds-tables"
data-config-key="tables"
/>
<label for="ds-tables">表格</label>
</div>
<div class="checkbox-group">
<input
type="checkbox"
id="ds-formulas"
data-config-key="formulas"
/>
<label for="ds-formulas">公式</label>
</div>
<div class="checkbox-group">
<input type="checkbox" id="ds-words" data-config-key="words" />
<label for="ds-words">总字数</label>
</div>
</div>
<!-- 新增TOC显示模式设置部分 -->
<h3>目录(TOC)设置</h3>
<div class="radio-container">
<div class="radio-group">
<input
type="radio"
id="toc-mode-both"
name="toc-mode"
value="both"
checked
/>
<label for="toc-mode-both">双语目录</label>
</div>
<div class="radio-group">
<input
type="radio"
id="toc-mode-ocr"
name="toc-mode"
value="ocr"
/>
<label for="toc-mode-ocr">仅原文目录</label>
</div>
<div class="radio-group">
<input
type="radio"
id="toc-mode-translation"
name="toc-mode"
value="translation"
/>
<label for="toc-mode-translation">仅译文目录</label>
</div>
</div>
<div class="dock-settings-modal-buttons">
<button id="dock-settings-save-btn" class="btn btn-primary">
保存
</button>
<button id="dock-settings-cancel-btn" class="btn btn-secondary">
取消
</button>
</div>
</div>
</div>
<!-- Annotations/Highlights Summary Modal -->
<div id="annotations-summary-modal" class="modal-overlay">
<!-- Default: not visible via CSS -->
<div class="modal-content">
<span class="modal-close-btn" id="annotations-summary-close-btn"
>&times;</span
>
<h2 id="annotations-summary-title">批注与高亮详情</h2>
<div class="annotations-summary-controls">
<label for="annotations-filter-type">类型:</label>
<select id="annotations-filter-type">
<option value="all">全部</option>
<option value="highlighting">仅高亮</option>
<option value="commenting">带批注</option>
</select>
<label for="annotations-filter-content">内容来源:</label>
<select id="annotations-filter-content">
<option value="all">全部 (OCR/翻译)</option>
<option value="ocr">仅 OCR</option>
<option value="translation">仅翻译</option>
</select>
<div
class="annotations-summary-color-filter"
id="annotations-summary-color-filter"
>
<!-- 动态插入颜色多选 -->
</div>
</div>
<div class="table-container">
<table id="annotations-summary-table">
<thead>
<tr>
<th>类型</th>
<th>来源</th>
<th>标识符</th>
<th>文本片段 (预览)</th>
<th>笔记</th>
<th>颜色</th>
<th>操作</th>
</tr>
</thead>
<tbody id="annotations-summary-table-body">
<!-- Rows will be populated by JavaScript -->
</tbody>
</table>
</div>
</div>
</div>
</main>
<!-- End app-main -->
</div>
<!-- End app-shell -->
<!-- ===================== -->
<!-- 依赖的JS库 -->
<!-- ===================== -->
<script src="https://gcore.jsdelivr.net/npm/file-saver@2.0.5/dist/FileSaver.min.js"></script>
<script src="../../js/lib/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>
<!-- DrawioLite DSL System (must load in order) -->
<script src="../../js/chatbot/prompt/drawio-lite-spec.js"></script>
<script src="../../js/chatbot/prompt/drawio-lite-prompt.js"></script>
<script src="../../js/chatbot/utils/drawio-lite-parser.js"></script>
<script src="../../js/storage/storage.js"></script>
<!-- 结构化重试需要 API/Translation/Structured 翻译模块 -->
<script src="../../js/api/api.js"></script>
<script src="../../js/process/utils.js"></script>
<script src="../../js/process/document.js"></script>
<script src="../../js/process/translation.js"></script>
<script src="../../js/process/mineru-structured-translation.js"></script>
<!-- OCR Manager 及适配器 -->
<script src="../../js/process/ocr-manager.js"></script>
<script src="../../js/process/ocr-adapters/base-adapter.js"></script>
<script src="../../js/process/ocr-adapters/mistral-adapter.js"></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>
<script src="../../js/process/ocr.js"></script>
<script src="../../js/process/main.js"></script>
<!-- OCR 设置管理器 -->
<script src="../../js/ui/ocr-settings.js"></script>
<script src="../../js/annotations/annotation_logic.js"></script>
<!-- Defines helpers like checkIfTextIsHighlighted -->
<script src="../../js/annotations/custom_markdown_renderer.js"></script>
<!-- Defines createCustomMarkdownRenderer -->
<script src="../../js/annotations/annotation_highlighter.js"></script>
<!-- Defines applyPreprocessedAnnotations, uses helpers from annotation_logic.js -->
<!-- 移除失效的注释相关脚本引用 -->
<script src="../../js/ui/toc_logic_enhanced.js"></script>
<script src="../../js/ui/toc_scroll_sync.js"></script>
<script src="https://gcore.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script src="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.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 src="https://registry.npmmirror.com/mermaid/10.9.0/files/dist/mermaid.min.js"></script>
<script src="https://registry.npmmirror.com/html2canvas/1.4.1/files/dist/html2canvas.min.js"></script>
<script src="https://registry.npmmirror.com/jspdf/2.5.1/files/dist/jspdf.umd.min.js"></script>
<!-- PDF.js for structured translation PDF comparison (本地版本) -->
<script src="../../public/pdfjs/build/pdf.js"></script>
<script>
if (typeof pdfjsLib !== "undefined") {
// 使用绝对路径确保 Worker 可以正确加载
const baseUrl = window.location.origin + window.location.pathname.split('/views/')[0];
pdfjsLib.GlobalWorkerOptions.workerSrc = baseUrl + "/public/pdfjs/build/pdf.worker.js";
}
</script>
<!-- Phase 3.5: 性能优化配置(必须在所有 chatbot 模块之前加载) -->
<script src="../../js/chatbot/config/performance-config.js"></script>
<!-- 历史页 Chatbot 代理配置:使用阿里云百炼平台,保护 API Key -->
<script src="../../js/config/proxy-config.js"></script>
<script>
// 全局配置:启用代理模式,使用 ProxyConfig 自动检测环境
window.PBX_PROXY_MODE = "proxy";
// PBX_PROXY_BASE_URL 由 proxy-config.js 自动设置:
// - 本地开发: http://localhost:3456
// - 生产环境: /api (相对路径)
// 使用阿里云百炼平台
window.PBX_LLM_PROVIDER = "aliyun";
// 默认模型qwen-plus推荐其他可选qwen-turbo、qwen-max、qwen-long-context
window.PBX_LLM_MODEL = "qwen-plus";
console.log(
"[History Detail] Chatbot 将使用代理服务器:",
window.PBX_PROXY_BASE_URL,
"提供商:阿里云百炼,模型:",
window.PBX_LLM_MODEL,
);
</script>
<!-- 通过代理发送对话消息的函数 -->
<script>
/**
* 通过代理服务器发送对话消息
* @param {string} userMessage - 用户消息内容
* @param {Array} [conversationHistory=[]] - 对话历史 [{role, content}, ...]
* @param {Object} [options={}] - 可选配置
* @returns {Promise<string>} 模型响应
*/
async function sendChatTo3456(
userMessage,
conversationHistory = [],
options = {},
) {
const baseUrl = window.PBX_PROXY_BASE_URL || '/api';
const endpoint = `${baseUrl}/v1/chat/completions`;
const model = options.model || window.PBX_LLM_MODEL || "qwen-plus";
const temperature = options.temperature || 0.7;
const maxTokens = options.maxTokens || 2048;
// 构建消息列表
const messages = [
{
role: "system",
content:
"你是一个智能 AI 助手,请用简洁、准确的语言回答用户的问题。",
},
...conversationHistory,
{ role: "user", content: userMessage },
];
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: model,
messages: messages,
temperature: temperature,
max_tokens: maxTokens,
stream: options.stream || false,
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API 调用失败 (${response.status}): ${errorText}`);
}
const data = await response.json();
return data?.choices?.[0]?.message?.content || JSON.stringify(data);
} catch (error) {
console.error("[sendChatTo3456] 错误:", error);
throw error;
}
}
/**
* 流式发送对话消息
* @param {string} userMessage - 用户消息内容
* @param {Array} [conversationHistory=[]] - 对话历史
* @param {Function} onChunk - 流式回调函数 (chunk) => void
* @param {Object} [options={}] - 可选配置
* @returns {Promise<string>} 完整响应
*/
async function sendChatTo3456Stream(
userMessage,
conversationHistory = [],
onChunk,
options = {},
) {
const baseUrl = window.PBX_PROXY_BASE_URL || '/api';
const endpoint = `${baseUrl}/v1/chat/completions`;
const model = options.model || window.PBX_LLM_MODEL || "qwen-plus";
const temperature = options.temperature || 0.7;
const maxTokens = options.maxTokens || 2048;
const messages = [
{
role: "system",
content:
"你是一个智能 AI 助手,请用简洁、准确的语言回答用户的问题。",
},
...conversationHistory,
{ role: "user", content: userMessage },
];
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: model,
messages: messages,
temperature: temperature,
max_tokens: maxTokens,
stream: true,
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API 调用失败 (${response.status}): ${errorText}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let fullResponse = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
if (!line.trim() || line.trim() === "data: [DONE]") continue;
try {
const jsonStr = line.replace(/^data:\s*/, "");
const parsed = JSON.parse(jsonStr);
const chunk = parsed.choices?.[0]?.delta?.content || "";
if (chunk) {
fullResponse += chunk;
if (onChunk) onChunk(chunk);
}
} catch (e) {
// 忽略解析错误的行
}
}
}
return fullResponse;
} catch (error) {
console.error("[sendChatTo3456Stream] 错误:", error);
throw error;
}
}
// 暴露到全局
window.sendChatTo3456 = sendChatTo3456;
window.sendChatTo3456Stream = sendChatTo3456Stream;
console.log("[sendChatTo3456] 函数已加载到全局作用域");
</script>
<!-- Phase 4.4.3: 本地性能监控工具(仅开发/测试用,不上报数据) -->
<script src="../../js/chatbot/utils/performance-monitor.js"></script>
<script src="../../js/chatbot/utils/mermaid-loader.js"></script>
<!-- XSS 防护DOMPurify - 用于清理 AI 生成的 HTML 内容 -->
<script src="https://registry.npmmirror.com/dompurify/3.0.6/files/dist/purify.min.js"></script>
<!-- XSS 防护:安全的 Markdown 渲染(必须在 markdown-katex-render.js 之前加载) -->
<script src="../../js/chatbot/utils/safe-markdown-render.js"></script>
<!-- Phase 4.2+: KaTeX 公式缓存系统(解决充满公式场景 4.6s 阻塞问题) -->
<script src="../../js/chatbot/utils/katex-cache.js"></script>
<script src="../../js/chatbot/utils/markdown-katex-render-cached.js"></script>
<!-- Phase 4.2+: KaTeX 渐进式渲染系统(解决首次渲染 4.6s 阻塞问题) -->
<script src="../../js/chatbot/utils/katex-progressive-render.js"></script>
<!-- Phase 4.2+: KaTeX 缓存持久化localStorage 自动保存和恢复) -->
<script src="../../js/chatbot/utils/katex-cache-persistence.js"></script>
<script src="../../js/chatbot/prompt/prompt-constructor.js"></script>
<!-- Add this line -->
<!-- Draw.io 布局优化模块(必须在 message-sender.js 之前加载) -->
<script src="../../js/chatbot/utils/drawio-layout-optimizer.js"></script>
<!-- Draw.io 学术增强模块Paper Burner 专属:语义配色 + 学术规范) -->
<script src="../../js/chatbot/utils/drawio-academic-enhancer.js"></script>
<script src="../../js/chatbot/utils/chatbot-utils.js"></script>
<script src="../../js/chatbot/prompt/chatbot-preset.js"></script>
<script src="../../js/chatbot/utils/chatbot-image-utils.js"></script>
<!-- Moved up to be with other utils -->
<script src="../../js/chatbot/utils/chatbot-rendering-utils.js"></script>
<!-- NEW -->
<script src="../../js/chatbot/actions/chatbot-actions.js"></script>
<!-- NEW -->
<script src="../../js/chatbot/core/chat-message-event-manager.js"></script>
<!-- Phase 3: 事件管理器(事件委托优化) -->
<script src="../../js/chatbot/ui/chatbot-message-renderer.js"></script>
<!-- NEW -->
<!-- 新增:意群聚合模块 -->
<script src="../../js/chatbot/agents/semantic-grouper.js"></script>
<script src="../../js/chatbot/agents/semantic-tools.js"></script>
<!-- 新增:向量搜索模块 -->
<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 (detail)",
);
};
s.onerror = function () {
console.error("[Boot] Failed to load EmbeddingClient (detail)");
};
document.head.appendChild(s);
}
} catch (e) {
console.warn("[Boot] Embedding ensure failed (detail):", e.message);
}
})();
</script>
<!-- 新增:重排模块(确保在 semantic-vector-search 之前加载) -->
<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>
<!-- 新增BM25检索模块独立工具 -->
<script src="../../js/chatbot/agents/bm25-search.js"></script>
<!-- ReAct 引擎(推理与工具调用框架 - 模块化版本) -->
<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/tool-registry.js"></script>
<script src="../../js/chatbot/react/token-budget.js"></script>
<script src="../../js/chatbot/react/engine.js"></script>
<script src="../../js/chatbot/react/index.js"></script>
<script src="../../js/chatbot/core/streaming-multi-hop.js"></script>
<!-- Chatbot Core 模块化组件(必须在 chatbot-core.js 之前加载) -->
<script src="../../js/chatbot/core/api-config-builder.js"></script>
<script src="../../js/chatbot/core/llm-caller.js"></script>
<script src="../../js/chatbot/core/chat-history-manager.js"></script>
<script src="../../js/chatbot/core/content-processor.js"></script>
<script src="../../js/chatbot/core/semantic-groups-manager.js"></script>
<script src="../../js/chatbot/core/message-sender.js"></script>
<!-- Chatbot 配置管理模块(独立于翻译模型) -->
<script src="../../js/chatbot/core/chatbot-config-manager.js?v=20251107c"></script>
<!-- Chatbot Core 主入口(依赖上述所有模块) -->
<script src="../../js/chatbot/core/chatbot-core.js"></script>
<script src="../../js/chatbot/renderers/chatbot-mermaid-renderer.js"></script>
<script src="../../js/chatbot/renderers/chatbot-mindmap-renderer.js"></script>
<script src="../../js/chatbot/ui/chatbot-preset-questions-ui.js"></script>
<script src="../../js/chatbot/ui/chatbot-floating-options.js"></script>
<!-- 新增引用 -->
<script src="../../js/chatbot/ui/chatbot-tooltrace-ui.js"></script>
<script src="../../js/chatbot/ui/semantic-groups-ui.js"></script>
<!-- 新增:意群面板 -->
<script src="../../js/chatbot/ui/embedding-config-ui.js"></script>
<!-- 新增:向量搜索配置 -->
<script src="../../js/chatbot/ui/chatbot-ui.js"></script>
<!-- Chatbot 模型配置弹窗(独立配置界面) -->
<script src="../../js/chatbot/ui/chatbot-model-config-modal.js?v=20251107f"></script>
<script src="../../js/chatbot/ui/chatbot-model-selector-ui.js"></script>
<script src="../../js/chatbot/strategy/segmentation-strategy.js"></script>
<script src="../../js/ui/dock/dock_logic.js"></script>
<script src="../../js/processing/markdown_processor_enhanced.js"></script>
<!-- AST-based processor (新架构) -->
<script src="../../js/processing/markdown_processor_ast.js"></script>
<script src="../../js/processing/annotation_plugin_ast.js"></script>
<!-- 注释插件 -->
<script src="../../js/processing/markdown_processor_integration.js"></script>
<!-- 集成层 -->
<script src="../../js/processing/formula_post_processor.js"></script>
<!-- 公式后处理(同步版本,用于导出) -->
<script src="../../js/processing/formula_post_processor_async.js"></script>
<!-- 公式后处理(异步 Worker 版本,用于页面显示) -->
<script src="../../js/processing/markdown_text_fix.js"></script>
<script src="../../js/processing/markdown_processor.js"></script>
<script src="../../js/processing/sub_block_segmenter.js"></script>
<script src="../../js/processing/content-list-to-chunks.js"></script>
<script src="../../js/ui/dock/dock_settings_modal.js"></script>
<!-- ADDED -->
<script src="../../js/annotations/annotations_summary_modal.js"></script>
<!-- ADDED -->
<!-- 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>
<!-- Text Fitting Engine for PDF Translation -->
<script src="../../js/utils/text-fitting.js"></script>
<script src="../../js/utils/text-fitting-integration.js"></script>
<!-- PDF Compare View 模块化组件 -->
<script src="../../js/history/modules/TextFitting.js"></script>
<script src="../../js/history/modules/PDFExporter.js"></script>
<script src="../../js/history/modules/SegmentManager.js"></script>
<!-- PDF Compare View 主类 -->
<script src="../../js/history/history_pdf_compare.js"></script>
<!-- ===================== -->
<!-- 页面核心逻辑脚本 -->
<!-- ===================== -->
<script src="../../js/history/history_detail_globals.js"></script>
<script src="../../js/history/history_detail_render.js"></script>
<script src="../../js/history/history_detail_show_tab.js"></script>
<script src="../../js/history/history_detail_scripts.js"></script>
<script src="../../js/ui/immersive_layout_logic.js"></script>
<!-- 新增沉浸式布局JS -->
<script src="../../js/ui/chunk_compare_optimizer.js"></script>
<!-- 分块对比性能优化器 -->
<script src="../../js/ui/chunk_compare_integration.js"></script>
<!-- 分块对比优化器集成 -->
<script src="../../js/ui/chunk_compare_performance_tester.js"></script>
<!-- 分块对比性能测试器 -->
<script src="../../js/ui/lightbox.js"></script>
<!-- 图片灯箱功能 -->
<!-- 参考文献管理模块 -->
<script src="../../js/processing/reference-detector.js"></script>
<script src="../../js/processing/reference-extractor.js"></script>
<script src="../../js/processing/reference-doi-resolver.js"></script>
<script src="../../js/processing/reference-ai-processor.js"></script>
<script src="../../js/processing/reference-indexer.js"></script>
<script src="../../js/storage/reference-storage.js"></script>
<script src="../../js/ui/reference-manager-ui.js"></script>
<script src="../../js/ui/reference-manager-detail.js"></script>
<!-- 详情页专用 -->
<!-- 统一侧边栏集成 (Non-Immersive Mode) -->
<script src="../../js/ui/sidebar-integration.js"></script>
</body>
</html>