This commit is contained in:
2025-11-02 22:06:42 +08:00
parent e71b69db5f
commit c2aa65193d
19 changed files with 427 additions and 274 deletions

View File

@@ -12,7 +12,7 @@ _current_language = "zh"
# 翻译词典
_translations = {}
# 语言文件目录
_LANG_DIR = Path("app/resource/lang").absolute()
_LANG_DIR = Path("_internal/lang").absolute()
# print(Path("app/resource/lang").absolute())

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

View File

@@ -1,103 +0,0 @@
{
"你好": "Hello",
"我的文件": "My Files",
"存储配额": "Storage Quota",
"任务管理": "Task Management",
"应用信息": "Application Information",
"此页面正在建设中...": "This page is under construction...",
"语言设置:": "Language Settings:",
"中文": "Chinese",
"English": "English",
"上传": "Upload",
"设置": "Settings",
"确定": "OK",
"取消": "Cancel",
"关闭": "Close",
"刷新": "Refresh",
"删除": "Delete",
"重命名": "Rename",
"移动": "Move",
"复制": "Copy",
"分享": "Share",
"新建文件夹": "New Folder",
"文件信息": "File Info",
"大小": "Size",
"类型": "Type",
"修改时间": "Modified Time",
"创建时间": "Created Time",
"状态": "Status",
"进度": "Progress",
"速度": "Speed",
"剩余时间": "Remaining Time",
"暂停": "Pause",
"继续": "Resume",
"重试": "Retry",
"完成": "Completed",
"失败": "Failed",
"等待中": "Waiting",
"进行中": "In Progress",
"仓内搜索": "Search Within Folder",
"站内搜索": "Search Within Site",
"搜索文件": "Search Files",
"添加标签": "Add Tag",
"标签名称": "Tag Name",
"标签通配符": "Tag Expression",
"文件上传": "File Upload",
"文件下载": "File Download",
"用户组基础容量": "User Group Basic Capacity",
"有效容量包附加附加容量": "Valid Capacity Package Additional Capacity",
"已使用容量": "Used Capacity",
"总容量": "Total Capacity",
"LeonPan": "LeonPan",
"修改昵称": "Modify Nickname",
"用户信息": "User Information",
"修改头像": "Modify Avatar",
"用户头像": "User Avatar",
"点击修改头像": "Click to Modify Avatar",
"电子邮箱": "Email Address",
"当前用户组": "Current User Group",
"用户注册时间": "User Registration Time",
"修改成功": "Modification Successful",
"昵称修改成功": "Nickname Modification Successful",
"选择图片": "Select Image",
"头像修改成功": "Avatar Modification Successful",
"选择下载保存路径": "Select Download Save Path",
"选择文件夹": "Select Folder",
"下载保存路径修改成功": "Download Save Path Modification Successful",
"背景图片设置": "Background Image Settings",
"官方背景图": "Official Background Image",
"选择自定义背景": "Select Custom Background",
"图片背景透明度": "Image Background Opacity",
"设置图片背景透明度": "Set Image Background Opacity",
"透明度": "Opacity",
"透明度范围": "Opacity Range",
"选择自定义图片,选择后请不要更改图片位置": "Select Custom Image, Do Not Change Image Location After Selection",
"隐私协议": "Privacy Policy",
"用户协议": "User Agreement",
"官方预设背景图片": "Official Preset Background Images",
"选择背景图片": "Select Background Image",
"自定义背景图片": "Custom Background Image",
"选择保存路径": "Select Save Path",
"用户昵称": "User Nickname",
"更新设置": "Update Settings",
"检查更新": "Check for Updates",
"检查是否有新版本可用": "Check if new version is available",
"当前版本": "Current Version",
"发现新版本": "New Version Found",
"最新版本": "Latest Version",
"更新内容": "Update Content",
"立即更新": "Update Now",
"稍后更新": "Update Later",
"检查更新失败": "Failed to Check Updates",
"无法连接到更新服务器,请稍后再试。": "Failed to connect to update server, please try again later.",
"开启自动更新": "Enable Auto Update",
"在应用启动时自动检查更新": "Automatically check for updates when the application starts",
"已是最新版本": "Already Latest Version",
"语言设置": "Language Settings",
"下载": "download",
"预览": "preview",
"进入": "enter",
"刷新当前": "refresh current",
"上传文件": "upload file",
"设置存储策略": "Set Storage Strategy"
}

View File

@@ -1,103 +0,0 @@
{
"你好": "你好",
"我的文件": "我的文件",
"存储配额": "存储配额",
"任务管理": "任务管理",
"应用信息": "应用信息",
"此页面正在建设中...": "此页面正在建设中...",
"语言设置:": "语言设置:",
"中文": "中文",
"English": "English",
"上传": "上传",
"设置": "设置",
"确定": "确定",
"取消": "取消",
"关闭": "关闭",
"刷新": "刷新",
"删除": "删除",
"重命名": "重命名",
"移动": "移动",
"复制": "复制",
"分享": "分享",
"新建文件夹": "新建文件夹",
"文件信息": "文件信息",
"大小": "大小",
"类型": "类型",
"修改时间": "修改时间",
"创建时间": "创建时间",
"状态": "状态",
"进度": "进度",
"速度": "速度",
"剩余时间": "剩余时间",
"暂停": "暂停",
"继续": "继续",
"重试": "重试",
"完成": "完成",
"失败": "失败",
"等待中": "等待中",
"进行中": "进行中",
"仓内搜索": "仓内搜索",
"站内搜索": "站内搜索",
"搜索文件": "搜索文件",
"添加标签": "添加标签",
"标签名称": "标签名称",
"标签通配符": "标签通配符",
"文件上传": "文件上传",
"文件下载": "文件下载",
"用户组基础容量": "用户组基础容量",
"有效容量包附加附加容量": "有效容量包附加附加容量",
"已使用容量": "已使用容量",
"总容量": "总容量",
"LeonPan": "LeonPan",
"修改昵称": "修改昵称",
"用户信息": "用户信息",
"修改头像": "修改头像",
"用户头像": "用户头像",
"点击修改头像": "点击修改头像",
"电子邮箱": "电子邮箱",
"当前用户组": "当前用户组",
"用户注册时间": "用户注册时间",
"修改成功": "修改成功",
"昵称修改成功": "昵称修改成功",
"选择图片": "选择图片",
"头像修改成功": "头像修改成功",
"选择下载保存路径": "选择下载保存路径",
"选择文件夹": "选择文件夹",
"下载保存路径修改成功": "下载保存路径修改成功",
"背景图片设置": "背景图片设置",
"官方背景图": "官方背景图",
"选择自定义背景": "选择自定义背景",
"图片背景透明度": "图片背景透明度",
"设置图片背景透明度": "设置图片背景透明度",
"透明度": "透明度",
"透明度范围": "透明度范围",
"选择自定义图片,选择后请不要更改图片位置": "选择自定义图片,选择后请不要更改图片位置",
"隐私协议": "隐私协议",
"用户协议": "用户协议",
"官方预设背景图片": "官方预设背景图片",
"选择背景图片": "选择背景图片",
"自定义背景图片": "自定义背景图片",
"选择保存路径": "选择保存路径",
"用户昵称": "用户昵称",
"更新设置": "更新设置",
"检查更新": "检查更新",
"检查是否有新版本可用": "检查是否有新版本可用",
"当前版本": "当前版本",
"发现新版本": "发现新版本",
"最新版本": "最新版本",
"更新内容": "更新内容",
"立即更新": "立即更新",
"稍后更新": "稍后更新",
"检查更新失败": "检查更新失败",
"无法连接到更新服务器,请稍后再试。": "无法连接到更新服务器,请稍后再试。",
"开启自动更新": "开启自动更新",
"在应用启动时自动检查更新": "在应用启动时自动检查更新",
"已是最新版本": "已是最新版本",
"语言设置": "语言设置",
"下载": "下载",
"预览": "预览",
"进入": "进入",
"刷新当前": "刷新当前",
"上传文件": "上传文件",
"设置存储策略": "设置存储策略"
}

View File

@@ -268,6 +268,12 @@ class PreviewTextBox(MessageBoxBase):
self._id = _id
self.isChanged = False
# 初始化关键变量
self.isContentSaved = False # 明确初始化保存状态
self.httpd = None # 服务器实例引用
self.server_thread = None # 服务器线程引用
self.tempFilePath = None # 临时文件路径
# 设置编辑器HTML文件路径 - 修正为项目根目录的_internal文件夹
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
self.editor_index = os.path.join(project_root, "_internal/editor_main/index.html")
@@ -348,7 +354,10 @@ class PreviewTextBox(MessageBoxBase):
)
# 隐藏进度条
self.saveProgressBar.hide()
QTimer.singleShot(700, self.accept)
# 直接调用accept()关闭窗口,不再使用定时器延迟
# 确保窗口立即关闭并返回成功结果
self.accept()
def _errorSave(self, msg):
logger.error(f"文本文件保存失败文件ID: {self._id}, 错误: {msg}")
@@ -432,14 +441,9 @@ class PreviewTextBox(MessageBoxBase):
def setTextContent(self, content):
"""设置文本内容并在外部浏览器中打开"""
# 检查内容是否已保存,如果已保存则不允许再次编辑
if hasattr(self, 'isContentSaved') and self.isContentSaved:
logger.warning("内容已保存,不允许再次编辑")
self.placeholderLabel.setText('''<div style='text-align:center; padding:20px;'>
<h3>已经保存</h3>
<p>该内容已经保存,不再允许编辑</p>
</div>''')
return
# 每次打开文件时都重置isContentSaved状态为False允许重新编辑
self.isContentSaved = False
logger.info(f"重置isContentSaved为False文件ID: {self._id}")
logger.info(f"文本文件加载成功,原始内容长度: {len(content)}字符")
@@ -563,10 +567,10 @@ class PreviewTextBox(MessageBoxBase):
attempts = 0
while attempts < max_attempts:
try:
# 设置处理器的实例引用
EditorHTTPRequestHandler.preview_box_instance = self
# 使用自定义处理器创建服务器
self.httpd = ReuseTCPServer(("127.0.0.1", port), EditorHTTPRequestHandler)
# 保存当前实例的引用,以便处理器可以访问
self.httpd.RequestHandlerClass.preview_box_instance = self
logger.info(f"Web服务器成功在端口 {port} 启动")
break
except OSError as e:
@@ -616,87 +620,44 @@ class PreviewTextBox(MessageBoxBase):
self.pollingTimer.start()
def _checkBrowserContent(self):
"""检查浏览器是否已保存内容"""
# 首先尝试从localStorage读取
try:
import json
import os
import base64
# 尝试获取localStorage数据
# 在Windows上localStorage通常存储在用户的AppData目录中
# 由于直接访问localStorage有困难我们使用一个更可靠的方法
# 1. 首先检查临时文件
if self.tempFilePath and os.path.exists(self.tempFilePath):
try:
with open(self.tempFilePath, 'r', encoding='utf-8') as f:
data = json.load(f)
if data.get('saved', False):
logger.info("检测到浏览器已保存内容(通过临时文件)")
self._processSavedContent(data)
return
except Exception as e:
logger.error(f"读取临时文件失败: {e}")
# 2. 作为备用方案,检查是否有特定的保存文件
# 这个文件可以由浏览器通过特定的方法创建
import tempfile
app_data_dir = os.path.join(tempfile.gettempdir(), 'LeonPan')
os.makedirs(app_data_dir, exist_ok=True)
save_file_path = os.path.join(app_data_dir, 'editor_content.json')
if os.path.exists(save_file_path):
try:
with open(save_file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
if data.get('saved', False):
logger.info("检测到浏览器已保存内容(通过备用文件)")
self._processSavedContent(data)
# 删除备用文件
os.unlink(save_file_path)
except Exception as e:
logger.error(f"读取备用保存文件失败: {e}")
except Exception as e:
logger.error(f"检查浏览器内容时出错: {e}")
"""检查浏览器是否已保存内容 - 现在仅作为备用方案"""
# 由于我们使用POST请求直接传递内容这里不再需要轮询检查文件
# 轮询可以保留作为备用方案但主要保存机制是通过POST请求
pass
def _processSavedContent(self, data):
"""处理已保存的内容"""
# 停止轮询
self.pollingTimer.stop()
logger.info(f"开始处理保存的内容文件ID: {self._id}")
# 更新内容
import base64
encoded_content = data.get('content', '')
try:
self.editorContent = base64.b64decode(encoded_content).decode('utf-8')
logger.info(f"内容解码成功,长度: {len(self.editorContent)}字符")
except Exception as e:
logger.error(f"解码内容失败: {e}")
return
# 标记内容已保存
self.isContentSaved = True
# 不再标记为已保存让_saveContent方法处理保存逻辑
# 直接调用_saveContent方法保存内容到服务器不再重置状态
logger.info(f"调用_saveContent方法保存内容到服务器文件ID: {self._id}")
self._saveContent(self.editorContent)
# 更新占位符文本,显示已保存信息
# 这里不再显示成功信息因为_saveContent会通过_successSave方法显示
# 也不再设置定时器重置状态,因为成功保存后窗口会直接关闭
def _resetSaveState(self):
"""重置保存状态,允许再次编辑和保存"""
self.isContentSaved = False
logger.info(f"已重置保存状态允许再次编辑和保存文件ID: {self._id}")
# 恢复默认的占位符文本
self.placeholderLabel.setText('''<div style='text-align:center; padding:20px;'>
<h3>已经保存</h3>
<p>内容已从浏览器同步到应用并已保存</p>
<p>该内容将不再允许编辑</p>
<h3>文本编辑已在外部浏览器中打开</h3>
<p><strong>重要提示:</strong>完成编辑后,请点击浏览器中的"保存并返回"按钮</p>
<p>应用正在自动检测...</p>
</div>''')
# 禁用保存按钮
self.saveButton.setEnabled(False)
# 清理临时文件
if self.tempFilePath and os.path.exists(self.tempFilePath):
try:
os.unlink(self.tempFilePath)
self.tempFilePath = None
except Exception as e:
logger.error(f"删除临时文件失败: {e}")
def handleError(self, error_msg):
@@ -741,15 +702,10 @@ class PreviewTextBox(MessageBoxBase):
<script>
let editor;
require.config({{ paths: {{ 'vs': './vs' }} }});
require.config({ paths: { 'vs': './vs' } });
require(['vs/editor/editor.main'], function() {{
editor = monaco.editor.create(document.getElementById('container'), {{
value: `{content.replace('`', '\\`')}`,
}});
// 重新获取编辑器实例以确保正确初始化
setTimeout(() => {{
editor = monaco.editor.getModels()[0] ? monaco.editor.getModels()[0].getContainerInfo().domNode.monacoEditor : null;
}}, 100);'\\`'}}`,
language: '{language}',
theme: 'vs-dark',
automaticLayout: true,
@@ -760,7 +716,11 @@ class PreviewTextBox(MessageBoxBase):
lineNumbers: 'on',
readOnly: false,
fontFamily: 'Consolas, "Microsoft YaHei", monospace'
}});
}});
// 重新获取编辑器实例以确保正确初始化
setTimeout(() => {{
editor = monaco.editor.getModels()[0] ? monaco.editor.getModels()[0].getContainerInfo().domNode.monacoEditor : null;
}}, 100);
// 添加内容变化监听器
editor.onDidChangeModelContent(function() {{
@@ -833,9 +793,40 @@ class PreviewTextBox(MessageBoxBase):
# JavaScript执行完成的处理已移除
def __del__(self):
"""析构函数,确保清理资源"""
# 停止轮询定时器
self.pollingTimer.stop()
# 停止HTTP服务器
if hasattr(self, 'httpd'):
try:
self.httpd.shutdown()
self.httpd.server_close()
logger.info("Web服务器已停止")
except Exception as e:
logger.warning(f"停止Web服务器时出错: {e}")
# 删除临时文件
if hasattr(self, 'tempFilePath') and self.tempFilePath and os.path.exists(self.tempFilePath):
try:
os.unlink(self.tempFilePath)
logger.info(f"临时文件已删除: {self.tempFilePath}")
except Exception as e:
logger.warning(f"删除临时文件失败: {e}")
def _saveContent(self, content):
"""保存编辑器内容并提交修改"""
logger.info(f"保存文本文件修改文件ID: {self._id}")
# 确保断开之前可能存在的连接,避免多次连接
if hasattr(self, 'saveTextThread') and self.saveTextThread:
try:
self.saveTextThread.successUpdated.disconnect()
self.saveTextThread.errorUpdated.disconnect()
except:
pass
# 创建新的保存线程
self.saveTextThread = UpdateFileContentThread(
self._id,
content,