回退为初始html
This commit is contained in:
10
.idea/workspace.xml
generated
10
.idea/workspace.xml
generated
@@ -5,14 +5,8 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="6b18b44a-df57-4212-a857-9e291ebe5dd2" name="更改" comment="">
|
<list default="true" id="6b18b44a-df57-4212-a857-9e291ebe5dd2" name="更改" comment="">
|
||||||
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/docutranslate/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/docutranslate/app.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/docutranslate/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/docutranslate/app.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/docutranslate/translater.py" beforeDir="false" afterPath="$PROJECT_DIR$/docutranslate/translater.py" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/docutranslate/utils/convert.py" beforeDir="false" afterPath="$PROJECT_DIR$/docutranslate/utils/convert.py" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/pyproject.toml" beforeDir="false" afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/uv.lock" beforeDir="false" afterPath="$PROJECT_DIR$/uv.lock" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -636,7 +630,7 @@
|
|||||||
<workItem from="1746942954889" duration="100000" />
|
<workItem from="1746942954889" duration="100000" />
|
||||||
<workItem from="1746943155810" duration="556000" />
|
<workItem from="1746943155810" duration="556000" />
|
||||||
<workItem from="1746953672715" duration="6973000" />
|
<workItem from="1746953672715" duration="6973000" />
|
||||||
<workItem from="1746963064346" duration="8379000" />
|
<workItem from="1746963064346" duration="8522000" />
|
||||||
</task>
|
</task>
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
@@ -651,7 +645,7 @@
|
|||||||
<SUITE FILE_PATH="coverage/PDFtranslate$PDFtranslater__1_.coverage" NAME="PDFtranslater (1) 覆盖结果" MODIFIED="1746633258205" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/pdftranslate_packages" />
|
<SUITE FILE_PATH="coverage/PDFtranslate$PDFtranslater__1_.coverage" NAME="PDFtranslater (1) 覆盖结果" MODIFIED="1746633258205" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/pdftranslate_packages" />
|
||||||
<SUITE FILE_PATH="coverage/PDFtranslate$convert.coverage" NAME="convert 覆盖结果" MODIFIED="1746596984213" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/pdftranslate_packages/utils" />
|
<SUITE FILE_PATH="coverage/PDFtranslate$convert.coverage" NAME="convert 覆盖结果" MODIFIED="1746596984213" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/pdftranslate_packages/utils" />
|
||||||
<SUITE FILE_PATH="coverage/PDFtranslate$agent_utils.coverage" NAME="agent_utils 覆盖结果" MODIFIED="1746617703678" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/pdftranslate_packages/utils" />
|
<SUITE FILE_PATH="coverage/PDFtranslate$agent_utils.coverage" NAME="agent_utils 覆盖结果" MODIFIED="1746617703678" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/pdftranslate_packages/utils" />
|
||||||
<SUITE FILE_PATH="coverage/filetranslate$app.coverage" NAME="app 覆盖结果" MODIFIED="1746971401102" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/docutranslate" />
|
<SUITE FILE_PATH="coverage/filetranslate$app.coverage" NAME="app 覆盖结果" MODIFIED="1746971603951" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/docutranslate" />
|
||||||
<SUITE FILE_PATH="coverage/PDFtranslate$markdown_splitter.coverage" NAME="markdown_splitter 覆盖结果" MODIFIED="1746599883603" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/pdftranslate_packages/utils" />
|
<SUITE FILE_PATH="coverage/PDFtranslate$markdown_splitter.coverage" NAME="markdown_splitter 覆盖结果" MODIFIED="1746599883603" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/pdftranslate_packages/utils" />
|
||||||
<SUITE FILE_PATH="coverage/filetranslate$test4.coverage" NAME="test4 覆盖结果" MODIFIED="1746887036353" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests" />
|
<SUITE FILE_PATH="coverage/filetranslate$test4.coverage" NAME="test4 覆盖结果" MODIFIED="1746887036353" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests" />
|
||||||
<SUITE FILE_PATH="coverage/filetranslate$test3.coverage" NAME="test3 覆盖结果" MODIFIED="1746884110572" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests" />
|
<SUITE FILE_PATH="coverage/filetranslate$test3.coverage" NAME="test3 覆盖结果" MODIFIED="1746884110572" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests" />
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ current_translation_state = {
|
|||||||
templates = Jinja2Templates(directory=".") # 假设模板在当前目录或使用字符串模板
|
templates = Jinja2Templates(directory=".") # 假设模板在当前目录或使用字符串模板
|
||||||
|
|
||||||
# --- HTML 模板字符串 ---
|
# --- HTML 模板字符串 ---
|
||||||
# --- HTML 模板字符串 (修改后) ---
|
|
||||||
HTML_TEMPLATE_STR = """
|
HTML_TEMPLATE_STR = """
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
@@ -98,277 +97,102 @@ HTML_TEMPLATE_STR = """
|
|||||||
<title>DocuTranslate</title>
|
<title>DocuTranslate</title>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@latest/css/pico.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@latest/css/pico.min.css">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
body { padding: 20px; font-family: sans-serif; }
|
||||||
--font-size: 14px; /* 减小基础字体大小 */
|
.container { max-width: 900px; margin: auto; }
|
||||||
--form-element-spacing-vertical: 0.6rem; /* 减小表单元素垂直间距 */
|
|
||||||
--form-element-spacing-horizontal: 0.8rem; /* 调整水平间距 */
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
padding: 15px; /* 减小 body padding */
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
|
||||||
font-size: var(--font-size);
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
max-width: 800px; /* 稍微减小容器宽度 */
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
h1, h3 {
|
|
||||||
margin-bottom: 0.8rem;
|
|
||||||
}
|
|
||||||
h1 a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--pico-h1-color);
|
|
||||||
}
|
|
||||||
h1 a:hover {
|
|
||||||
color: var(--pico-primary-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 表单元素 */
|
|
||||||
label, legend {
|
|
||||||
font-size: 0.9em; /* 标签字体稍小 */
|
|
||||||
margin-bottom: 0.2rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
input[type="text"],
|
|
||||||
input[type="password"],
|
|
||||||
input[type="file"],
|
|
||||||
select {
|
|
||||||
font-size: 0.9em; /* 输入框字体稍小 */
|
|
||||||
padding: 0.4rem 0.6rem; /* 减小输入框内边距 */
|
|
||||||
margin-bottom: 0.6rem;
|
|
||||||
}
|
|
||||||
input[type="file"] {
|
|
||||||
padding: 0.3rem 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Details & Summary */
|
|
||||||
details {
|
|
||||||
background-color: var(--pico-card-background-color);
|
|
||||||
border: 1px solid var(--pico-card-border-color);
|
|
||||||
border-radius: var(--pico-border-radius);
|
|
||||||
padding: 0.8rem; /* 调整内边距 */
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
summary {
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.95em; /* 摘要字体大小 */
|
|
||||||
padding: 0.3rem 0;
|
|
||||||
}
|
|
||||||
details[open] > summary {
|
|
||||||
margin-bottom: 0.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fieldset and Checkbox */
|
|
||||||
fieldset {
|
|
||||||
padding: 0.8rem 1rem;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
border-radius: var(--pico-border-radius);
|
|
||||||
}
|
|
||||||
fieldset legend {
|
|
||||||
padding: 0 0.5rem;
|
|
||||||
}
|
|
||||||
label input[type="checkbox"] {
|
|
||||||
margin-right: 0.4rem;
|
|
||||||
vertical-align: middle;
|
|
||||||
width: 0.9em; height: 0.9em; /* 减小复选框大小 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 按钮 */
|
|
||||||
button, a[role="button"] {
|
|
||||||
font-size: 0.85em; /* 减小按钮字体 */
|
|
||||||
padding: 0.5rem 0.8rem; /* 减小按钮内边距 */
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid > div { /* PicoCSS grid 的子元素间距 */
|
|
||||||
margin-bottom: 0; /* 移除 grid 内部div的默认下边距,依赖 input 的 margin-bottom */
|
|
||||||
}
|
|
||||||
.form-group { /* 自定义表单组,用于非 grid 布局 */
|
|
||||||
margin-bottom: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-area {
|
.log-area {
|
||||||
background-color: #f4f6f8; /* 更柔和的背景色 */
|
background-color: #f0f0f0;
|
||||||
border: 1px solid #dfe3e6;
|
border: 1px solid #ccc;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: 200px; /* 调整高度 */
|
height: 250px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
font-family: monospace;
|
||||||
font-size: 0.85em; /* 日志字体稍小 */
|
font-size: 0.9em;
|
||||||
margin-top: 1rem; /* 调整上边距 */
|
margin-top: 20px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
border-radius: var(--pico-border-radius);
|
|
||||||
}
|
|
||||||
.error-message { color: var(--pico-del-color); font-weight: bold; font-size: 0.9em; }
|
|
||||||
.success-message { color: var(--pico-ins-color); font-weight: bold; font-size: 0.9em;}
|
|
||||||
|
|
||||||
#resultArea { margin-top: 1.5rem; }
|
|
||||||
#downloadButtons {
|
|
||||||
display: none;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
padding: 0.8rem;
|
|
||||||
background-color: var(--pico-card-sectionning-background-color);
|
|
||||||
border-radius: var(--pico-border-radius);
|
|
||||||
}
|
|
||||||
#downloadButtons h3 {
|
|
||||||
font-size: 1em;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
#customLangInput {
|
|
||||||
display: none; /* Initially hidden */
|
|
||||||
margin-top: 0.3rem;
|
|
||||||
}
|
}
|
||||||
|
.error-message { color: red; font-weight: bold; }
|
||||||
|
.success-message { color: green; font-weight: bold; }
|
||||||
|
.form-group label { margin-bottom: 0.2rem; display: block;}
|
||||||
|
.form-group input { margin-bottom: 0.8rem; }
|
||||||
|
.button-group { margin-top: 1rem; }
|
||||||
|
.button-group button, .button-group a { margin-right: 0.5rem; }
|
||||||
|
summary { font-weight: bold; cursor: pointer; }
|
||||||
|
label input[type="checkbox"] { margin-right: 0.5rem; vertical-align: middle; }
|
||||||
|
#resultArea { margin-top: 20px; } /* Style for result area */
|
||||||
|
#downloadButtons { display: none; } /* Initially hidden */
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<header style="text-align: center; margin-bottom: 1.5rem;">
|
<h1><a href="https://github.com/xunbu/docutranslate" target="_blank">DocuTranslate</a></h1>
|
||||||
<h1><a href="https://github.com/xunbu/docutranslate" target="_blank">📄 DocuTranslate</a></h1>
|
<form id="translateForm"> <!-- Removed action and method -->
|
||||||
</header>
|
|
||||||
|
|
||||||
<form id="translateForm">
|
|
||||||
<details open>
|
<details open>
|
||||||
<summary>⚙️ API 配置</summary>
|
<summary>API 配置 (所有均为必填项)</summary>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div>
|
<div class="form-group">
|
||||||
<label for="base_url">API 地址</label>
|
<label for="base_url">API 地址 (Base URL)</label>
|
||||||
<input type="text" id="base_url" name="base_url" placeholder="例如: https://api.example.com/v1" required>
|
<input type="text" id="base_url" name="base_url" value="" required>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="form-group">
|
||||||
<label for="apikey">API 密钥</label>
|
<label for="apikey">API 密钥 (API Key)</label>
|
||||||
<input type="password" id="apikey" name="apikey" placeholder="您的 API Key" required>
|
<input type="password" id="apikey" name="apikey" value="" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="model_id">模型 ID</label>
|
<label for="model_id">模型 ID (Model ID)</label>
|
||||||
<input type="text" id="model_id" name="model_id" placeholder="例如: gpt-3.5-turbo" required>
|
<input type="text" id="model_id" name="model_id" value="" required>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<div class="form-group" style="margin-top: 1rem;">
|
<div class="form-group" style="margin-top: 1rem;">
|
||||||
<label for="file">选择文档</label>
|
<label for="file">待翻译文档</label>
|
||||||
<input type="file" id="file" name="file" required>
|
<input type="file" id="file" name="file" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>翻译选项</legend>
|
<legend>选项</legend>
|
||||||
<div class="grid">
|
<label for="to_lang">目标语言: <input type="text" id="to_lang" name="to_lang" value="中文"></label>
|
||||||
<div>
|
<label for="formula_ocr"><input type="checkbox" id="formula_ocr" name="formula_ocr">启用公式识别</label>
|
||||||
<label for="to_lang_select">目标语言</label>
|
<label for="code_ocr"><input type="checkbox" id="code_ocr" name="code_ocr">启用代码块识别</label>
|
||||||
<select id="to_lang_select" name="to_lang_select">
|
<label for="refine_markdown"><input type="checkbox" id="refine_markdown" name="refine_markdown">翻译前优化 Markdown</label>
|
||||||
<option value="中文">中文 (简体)</option>
|
|
||||||
<option value="English">English</option>
|
|
||||||
<option value="日本語">日本語</option>
|
|
||||||
<option value="한국어">한국어</option>
|
|
||||||
<option value="Français">Français</option>
|
|
||||||
<option value="Español">Español</option>
|
|
||||||
<option value="Deutsch">Deutsch</option>
|
|
||||||
<option value="Русский">Русский</option>
|
|
||||||
<option value="custom">自定义...</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" id="to_lang_custom" name="to_lang_custom" placeholder="输入自定义目标语言">
|
|
||||||
</div>
|
|
||||||
<div> <!-- Placeholder for alignment if needed --> </div>
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 0.5rem;">
|
|
||||||
<label for="formula_ocr"><input type="checkbox" id="formula_ocr" name="formula_ocr" role="switch">公式识别</label>
|
|
||||||
<label for="code_ocr"><input type="checkbox" id="code_ocr" name="code_ocr" role="switch">代码块识别</label>
|
|
||||||
<label for="refine_markdown"><input type="checkbox" id="refine_markdown" name="refine_markdown" role="switch">优化 Markdown</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<button type="submit" id="submitButton">翻译文档</button>
|
||||||
<button type="submit" id="submitButton" style="margin-top: 1rem;">开始翻译</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div id="resultArea">
|
<div id="resultArea">
|
||||||
<p id="statusMessage"></p>
|
<p id="statusMessage"></p>
|
||||||
<div id="downloadButtons">
|
<div id="downloadButtons" class="button-group">
|
||||||
<h3>下载翻译结果:</h3>
|
<h3>下载:</h3>
|
||||||
<a id="downloadMarkdown" href="#" role="button" class="secondary">下载 Markdown (.md)</a>
|
<a id="downloadMarkdown" href="#" role="button" class="secondary">下载 Markdown</a>
|
||||||
<a id="downloadHtml" href="#" role="button" class="secondary">下载 HTML (.html)</a>
|
<a id="downloadHtml" href="#" role="button" class="secondary">下载 HTML</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 style="margin-top:1.5rem;">实时日志:</h3>
|
<h3>日志:</h3>
|
||||||
<div class="log-area" id="logArea"></div>
|
<div class="log-area" id="logArea"></div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const base_url_input = document.getElementById('base_url');
|
const base_url_input = document.getElementById('base_url');
|
||||||
const apikey_input = document.getElementById('apikey');
|
const apikey_input = document.getElementById('apikey');
|
||||||
const model_id_input = document.getElementById('model_id');
|
const model_id_input = document.getElementById('model_id');
|
||||||
|
const to_lang_input = document.getElementById('to_lang');
|
||||||
const to_lang_select = document.getElementById('to_lang_select');
|
|
||||||
const to_lang_custom_input = document.getElementById('to_lang_custom');
|
|
||||||
|
|
||||||
const formula_ocr_input = document.getElementById('formula_ocr');
|
const formula_ocr_input = document.getElementById('formula_ocr');
|
||||||
const code_ocr_input = document.getElementById('code_ocr');
|
const code_ocr_input = document.getElementById('code_ocr');
|
||||||
const refine_markdown_input = document.getElementById('refine_markdown');
|
const refine_markdown_input = document.getElementById('refine_markdown');
|
||||||
|
|
||||||
// Function to show/hide custom language input
|
|
||||||
function toggleCustomLangInput() {
|
|
||||||
if (to_lang_select.value === 'custom') {
|
|
||||||
to_lang_custom_input.style.display = 'block';
|
|
||||||
to_lang_custom_input.required = true; // Make custom input required if selected
|
|
||||||
} else {
|
|
||||||
to_lang_custom_input.style.display = 'none';
|
|
||||||
to_lang_custom_input.required = false;
|
|
||||||
to_lang_custom_input.value = ''; // Clear custom input if not selected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
to_lang_select.addEventListener('change', toggleCustomLangInput);
|
|
||||||
|
|
||||||
|
|
||||||
// Load from localStorage
|
|
||||||
if (localStorage.getItem('translator_base_url')) base_url_input.value = localStorage.getItem('translator_base_url');
|
if (localStorage.getItem('translator_base_url')) base_url_input.value = localStorage.getItem('translator_base_url');
|
||||||
if (localStorage.getItem('translator_apikey')) apikey_input.value = localStorage.getItem('translator_apikey');
|
if (localStorage.getItem('translator_apikey')) apikey_input.value = localStorage.getItem('translator_apikey');
|
||||||
if (localStorage.getItem('translator_model_id')) model_id_input.value = localStorage.getItem('translator_model_id');
|
if (localStorage.getItem('translator_model_id')) model_id_input.value = localStorage.getItem('translator_model_id');
|
||||||
|
to_lang_input.value = localStorage.getItem('translator_to_lang') || '中文';
|
||||||
const storedToLang = localStorage.getItem('translator_to_lang');
|
|
||||||
if (storedToLang) {
|
|
||||||
// Check if stored value is one of the predefined options
|
|
||||||
let foundInSelect = false;
|
|
||||||
for (let i = 0; i < to_lang_select.options.length; i++) {
|
|
||||||
if (to_lang_select.options[i].value === storedToLang) {
|
|
||||||
to_lang_select.value = storedToLang;
|
|
||||||
foundInSelect = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!foundInSelect && storedToLang) { // If not in select, it was a custom lang
|
|
||||||
to_lang_select.value = 'custom';
|
|
||||||
to_lang_custom_input.value = storedToLang;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
to_lang_select.value = '中文'; // Default
|
|
||||||
}
|
|
||||||
toggleCustomLangInput(); // Initial check for custom lang display
|
|
||||||
|
|
||||||
formula_ocr_input.checked = localStorage.getItem('translator_formula_ocr') === 'true';
|
formula_ocr_input.checked = localStorage.getItem('translator_formula_ocr') === 'true';
|
||||||
code_ocr_input.checked = localStorage.getItem('translator_code_ocr') === 'true';
|
code_ocr_input.checked = localStorage.getItem('translator_code_ocr') === 'true';
|
||||||
refine_markdown_input.checked = localStorage.getItem('translator_refine_markdown') === 'true';
|
refine_markdown_input.checked = localStorage.getItem('translator_refine_markdown') === 'true';
|
||||||
|
|
||||||
// Save to localStorage
|
|
||||||
function saveToLocalStorage(key, value) { try { localStorage.setItem(key, value); } catch (e) { console.error("Error saving to localStorage:", e); }}
|
function saveToLocalStorage(key, value) { try { localStorage.setItem(key, value); } catch (e) { console.error("Error saving to localStorage:", e); }}
|
||||||
base_url_input.addEventListener('input', () => saveToLocalStorage('translator_base_url', base_url_input.value));
|
base_url_input.addEventListener('input', () => saveToLocalStorage('translator_base_url', base_url_input.value));
|
||||||
apikey_input.addEventListener('input', () => saveToLocalStorage('translator_apikey', apikey_input.value));
|
apikey_input.addEventListener('input', () => saveToLocalStorage('translator_apikey', apikey_input.value));
|
||||||
model_id_input.addEventListener('input', () => saveToLocalStorage('translator_model_id', model_id_input.value));
|
model_id_input.addEventListener('input', () => saveToLocalStorage('translator_model_id', model_id_input.value));
|
||||||
|
to_lang_input.addEventListener('input', () => saveToLocalStorage('translator_to_lang', to_lang_input.value));
|
||||||
function saveLanguagePreference() {
|
|
||||||
let langValue = to_lang_select.value;
|
|
||||||
if (langValue === 'custom') {
|
|
||||||
langValue = to_lang_custom_input.value.trim();
|
|
||||||
}
|
|
||||||
saveToLocalStorage('translator_to_lang', langValue);
|
|
||||||
}
|
|
||||||
to_lang_select.addEventListener('change', saveLanguagePreference);
|
|
||||||
to_lang_custom_input.addEventListener('input', saveLanguagePreference);
|
|
||||||
|
|
||||||
formula_ocr_input.addEventListener('change', () => saveToLocalStorage('translator_formula_ocr', formula_ocr_input.checked));
|
formula_ocr_input.addEventListener('change', () => saveToLocalStorage('translator_formula_ocr', formula_ocr_input.checked));
|
||||||
code_ocr_input.addEventListener('change', () => saveToLocalStorage('translator_code_ocr', code_ocr_input.checked));
|
code_ocr_input.addEventListener('change', () => saveToLocalStorage('translator_code_ocr', code_ocr_input.checked));
|
||||||
refine_markdown_input.addEventListener('change', () => saveToLocalStorage('translator_refine_markdown', refine_markdown_input.checked));
|
refine_markdown_input.addEventListener('change', () => saveToLocalStorage('translator_refine_markdown', refine_markdown_input.checked));
|
||||||
@@ -386,42 +210,21 @@ HTML_TEMPLATE_STR = """
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
submitButton.disabled = true;
|
submitButton.disabled = true;
|
||||||
submitButton.setAttribute('aria-busy', 'true');
|
submitButton.setAttribute('aria-busy', 'true');
|
||||||
submitButton.textContent = '翻译中...'; // Shorter text
|
submitButton.textContent = '处理中...';
|
||||||
if(logArea) logArea.innerHTML = '';
|
if(logArea) logArea.innerHTML = '';
|
||||||
statusMessageElement.textContent = '';
|
statusMessageElement.textContent = '';
|
||||||
statusMessageElement.className = '';
|
statusMessageElement.className = '';
|
||||||
downloadButtonsDiv.style.display = 'none';
|
downloadButtonsDiv.style.display = 'none';
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData(form);
|
||||||
formData.append('base_url', base_url_input.value);
|
|
||||||
formData.append('apikey', apikey_input.value);
|
|
||||||
formData.append('model_id', model_id_input.value);
|
|
||||||
formData.append('file', document.getElementById('file').files[0]);
|
|
||||||
|
|
||||||
let targetLang = to_lang_select.value;
|
|
||||||
if (targetLang === 'custom') {
|
|
||||||
targetLang = to_lang_custom_input.value.trim();
|
|
||||||
}
|
|
||||||
if (!targetLang) { // Basic validation for custom lang
|
|
||||||
statusMessageElement.textContent = '请选择或输入有效的目标语言。';
|
|
||||||
statusMessageElement.className = 'error-message';
|
|
||||||
submitButton.disabled = false;
|
|
||||||
submitButton.removeAttribute('aria-busy');
|
|
||||||
submitButton.textContent = '开始翻译';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
formData.append('to_lang', targetLang);
|
|
||||||
|
|
||||||
formData.append('formula_ocr', formula_ocr_input.checked);
|
|
||||||
formData.append('code_ocr', code_ocr_input.checked);
|
|
||||||
formData.append('refine_markdown', refine_markdown_input.checked);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/translate', { method: 'POST', body: formData });
|
const response = await fetch('/translate', { method: 'POST', body: formData });
|
||||||
const resultData = await response.json(); // Assuming server sends JSON
|
const resultData = await response.json();
|
||||||
|
if (resultData.error) {
|
||||||
if (response.ok && resultData && !resultData.error) {
|
statusMessageElement.textContent = resultData.message;
|
||||||
statusMessageElement.textContent = resultData.message || '翻译成功!';
|
statusMessageElement.className = 'error-message';
|
||||||
|
} else {
|
||||||
|
statusMessageElement.textContent = resultData.message;
|
||||||
statusMessageElement.className = 'success-message';
|
statusMessageElement.className = 'success-message';
|
||||||
if (resultData.download_ready) {
|
if (resultData.download_ready) {
|
||||||
downloadMarkdownLink.href = resultData.markdown_url;
|
downloadMarkdownLink.href = resultData.markdown_url;
|
||||||
@@ -430,23 +233,19 @@ HTML_TEMPLATE_STR = """
|
|||||||
downloadHtmlLink.setAttribute('download', resultData.original_filename_stem + '_translated.html');
|
downloadHtmlLink.setAttribute('download', resultData.original_filename_stem + '_translated.html');
|
||||||
downloadButtonsDiv.style.display = 'block';
|
downloadButtonsDiv.style.display = 'block';
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
statusMessageElement.textContent = '翻译失败: ' + (resultData.message || '未知错误');
|
|
||||||
statusMessageElement.className = 'error-message';
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fetch error:', error);
|
console.error('Fetch error:', error);
|
||||||
statusMessageElement.textContent = '请求翻译失败,请检查网络或服务状态。错误: ' + error.message;
|
statusMessageElement.textContent = '请求翻译失败,请检查网络或服务状态。';
|
||||||
statusMessageElement.className = 'error-message';
|
statusMessageElement.className = 'error-message';
|
||||||
} finally {
|
} finally {
|
||||||
submitButton.disabled = false;
|
submitButton.disabled = false;
|
||||||
submitButton.removeAttribute('aria-busy');
|
submitButton.removeAttribute('aria-busy');
|
||||||
submitButton.textContent = '开始翻译';
|
submitButton.textContent = '翻译文档';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventSource for logs (unchanged, but ensure it works with the new layout)
|
|
||||||
if (typeof(EventSource) !== "undefined") {
|
if (typeof(EventSource) !== "undefined") {
|
||||||
let eventSource;
|
let eventSource;
|
||||||
function connectEventSource() {
|
function connectEventSource() {
|
||||||
@@ -455,10 +254,8 @@ HTML_TEMPLATE_STR = """
|
|||||||
}
|
}
|
||||||
eventSource = new EventSource("/stream-logs");
|
eventSource = new EventSource("/stream-logs");
|
||||||
eventSource.onmessage = function(event) {
|
eventSource.onmessage = function(event) {
|
||||||
if (logArea && event.data !== ":heartbeat") {
|
if (logArea && event.data !== ":heartbeat") { // Ignore heartbeat messages for display
|
||||||
const logEntryDiv = document.createElement('div');
|
logArea.innerHTML += event.data;
|
||||||
logEntryDiv.innerHTML = event.data; // Assuming event.data is already HTML escaped and includes <br>
|
|
||||||
logArea.appendChild(logEntryDiv);
|
|
||||||
logArea.scrollTop = logArea.scrollHeight;
|
logArea.scrollTop = logArea.scrollHeight;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -471,11 +268,12 @@ HTML_TEMPLATE_STR = """
|
|||||||
logArea.appendChild(errorMsgDiv);
|
logArea.appendChild(errorMsgDiv);
|
||||||
logArea.scrollTop = logArea.scrollHeight;
|
logArea.scrollTop = logArea.scrollHeight;
|
||||||
}
|
}
|
||||||
if (eventSource) eventSource.close();
|
eventSource.close(); // Close the failed source
|
||||||
setTimeout(connectEventSource, 5000);
|
// Attempt to reconnect after a delay
|
||||||
|
setTimeout(connectEventSource, 5000); // Reconnect after 5 seconds
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
connectEventSource();
|
connectEventSource(); // Initial connection
|
||||||
} else {
|
} else {
|
||||||
if(logArea) logArea.innerHTML = "抱歉,您的浏览器不支持实时日志更新。";
|
if(logArea) logArea.innerHTML = "抱歉,您的浏览器不支持实时日志更新。";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user