fix package bug

This commit is contained in:
zhukang 2025-03-02 18:56:33 +08:00
parent 6d40a8afaf
commit 9539cb6cb8
30 changed files with 3215 additions and 191 deletions

View File

@ -0,0 +1,49 @@
# 更新日志
## v0.1.6 (2025-03-02)
- 修复ConfigParser导入问题
- 修复系统托盘功能
- 添加darkdetect模块支持深色/浅色模式自动检测
- 改进通知系统样式和错误处理
- 优化UI响应性能
- 添加uv环境打包支持
## v0.1.5 (2025-02-25)
- 添加系统托盘功能
- 增强通知系统
- 改进错误处理
- 添加最近保存列表
- 桌面通知系统
- 开机自启动支持
## v0.1.4 (2025-02-20)
- 添加AI模型集成
- 支持OpenAI兼容API
- 支持Ollama本地模型
- AI辅助标题提取、分类和标签生成
## v0.1.3 (2025-02-15)
- 增强图片处理功能
- 支持剪贴板图片保存
- 自动提取HTML内容中的图片
- 支持网络图片和base64编码图片
- 图文混合内容处理
## v0.1.2 (2025-02-10)
- 界面改进
- 全新扁平化设计风格
- 添加最近保存列表
- 桌面通知系统
## v0.1.1 (2025-02-05)
- 功能增强
- 添加可视化分类管理器
- 多线程处理避免UI卡顿
- 异步图片下载和处理
- 内存使用优化
## v0.1.0 (2025-02-01)
- 初始版本发布
- 基本文本捕获功能
- 简单的保存和分类
- 基础用户界面

View File

@ -215,6 +215,7 @@ save_location/
2. 使用 PyInstaller 打包
```bash
cd project\llmclipboard ; pyinstaller --clean llmclipboard.spec
.venv\Scripts\python.exe -m pyinstaller --clean llmclipboard.spec
```
@ -318,6 +319,14 @@ save_location/
- 异步图片下载和处理
- 内存使用优化
## v0.1.6 (2025-03-02)
- 修复ConfigParser导入问题
- 修复系统托盘功能
- 添加darkdetect模块支持深色/浅色模式自动检测
- 改进通知系统样式和错误处理
- 优化UI响应性能
- 添加uv环境打包支持
-
## 🤝 贡献指南
1. Fork 项目

View File

@ -0,0 +1,38 @@
@echo off
echo Building LLMClipboard v0.1.6...
:: Clean old build files
if exist build rmdir /s /q build
if exist dist rmdir /s /q dist
:: Build with pyinstaller
echo Building application...
.venv\Scripts\python.exe -m PyInstaller --clean --onefile --windowed --name LLMClipboard --icon=resources/icon_256x256.ico --add-data "resources;resources" run_app.py
:: Check if successful
if exist dist\LLMClipboard.exe (
echo Build successful! Executable is at dist\LLMClipboard.exe
:: Copy resources folder to dist
echo Copying resources to dist folder...
if not exist dist\resources mkdir dist\resources
xcopy /s /y resources dist\resources\
:: Create a README.txt in the dist folder
echo Creating README.txt in dist folder...
echo LLMClipboard v0.1.6 > dist\README.txt
echo ====================== >> dist\README.txt
echo. >> dist\README.txt
echo Instructions: >> dist\README.txt
echo 1. Double-click LLMClipboard.exe to start the application >> dist\README.txt
echo 2. The application will run in the system tray >> dist\README.txt
echo 3. Double-click the system tray icon to open the main window >> dist\README.txt
echo 4. Right-click the system tray icon to access the menu >> dist\README.txt
echo. >> dist\README.txt
echo If you encounter any issues, please check error_log.txt and config_error.txt files >> dist\README.txt
) else (
echo Build failed, please check error messages.
)
echo Build process completed.
pause

View File

@ -0,0 +1,116 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
LLMClipboard 调试脚本
用于调试LLMClipboard应用程序提供详细的错误信息和环境检查
"""
import traceback
import sys
import os
import importlib
import time
import platform
def check_imports():
"""检查必要的导入是否可用"""
required_modules = [
'PyQt6', 'configparser', 'darkdetect', 'pynput', 'win32clipboard',
'win32con', 'keyboard', 'html2text', 'PIL', 'requests'
]
results = {}
for module in required_modules:
try:
importlib.import_module(module)
results[module] = "成功"
except ImportError as e:
results[module] = f"失败: {str(e)}"
return results
def check_resources():
"""检查资源文件是否存在"""
resources_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources")
if not os.path.exists(resources_dir):
return f"资源目录不存在: {resources_dir}"
icon_path = os.path.join(resources_dir, "icon.png")
if not os.path.exists(icon_path):
return f"图标文件不存在: {icon_path}"
return "资源文件检查通过"
def get_system_info():
"""获取系统信息"""
info = {
"系统": platform.system(),
"版本": platform.version(),
"架构": platform.architecture(),
"Python版本": sys.version,
"可执行文件": sys.executable,
"当前目录": os.getcwd(),
"脚本路径": os.path.abspath(__file__),
"冻结状态": getattr(sys, 'frozen', False)
}
return info
def main():
"""主函数"""
# 创建调试日志文件
debug_log_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'debug_log.txt')
with open(debug_log_path, 'w', encoding='utf-8') as f:
# 写入时间戳
f.write(f"=== LLMClipboard 调试日志 ===\n")
f.write(f"时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
# 写入系统信息
f.write("=== 系统信息 ===\n")
for key, value in get_system_info().items():
f.write(f"{key}: {value}\n")
f.write("\n")
# 检查导入
f.write("=== 模块导入检查 ===\n")
import_results = check_imports()
for module, result in import_results.items():
f.write(f"{module}: {result}\n")
f.write("\n")
# 检查资源
f.write("=== 资源文件检查 ===\n")
f.write(f"{check_resources()}\n\n")
# 尝试启动应用程序
f.write("=== 应用程序启动 ===\n")
try:
f.write("尝试导入 llmclipboard.app...\n")
from llmclipboard.app import main as app_main
f.write("尝试启动应用程序...\n")
app_main()
f.write("应用程序正常退出\n")
except Exception as e:
f.write(f"错误: {str(e)}\n")
f.write(traceback.format_exc())
# 打印到控制台
print(f"错误: {str(e)}")
print(traceback.format_exc())
# 在打包环境中,确保错误信息被记录
if getattr(sys, 'frozen', False):
error_log_path = os.path.join(os.path.dirname(sys.executable), 'error_log.txt')
with open(error_log_path, 'a', encoding='utf-8') as error_file:
error_file.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 错误: {str(e)}\n")
traceback.print_exc(file=error_file)
return 1
print(f"调试日志已写入: {debug_log_path}")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,41 @@
from PIL import Image
import os
import argparse
# 定义所需的图标尺寸
icon_sizes = {
'16x16': (16, 16),
'32x32': (32, 32),
'48x48': (48, 48),
'64x64': (64, 64),
'128x128': (128, 128),
'256x256': (256, 256),
'512x512': (512, 512)
}
def generate_icons(input_image_path, output_directory):
# 确保输出目录存在
os.makedirs(output_directory, exist_ok=True)
# 打开输入图像
with Image.open(input_image_path) as img:
for size_name, size in icon_sizes.items():
# 调整图像大小
icon = img.resize(size, Image.LANCZOS) # 使用LANCZOS替代ANTIALIAS
# 构建输出文件路径
output_path_png = os.path.join(output_directory, f'icon_{size_name}.png')
output_path_ico = os.path.join(output_directory, f'icon_{size_name}.ico')
# 保存PNG图标
icon.save(output_path_png, format='PNG')
print(f'生成PNG图标: {output_path_png}')
# 保存ICO图标
icon.save(output_path_ico, format='ICO')
print(f'生成ICO图标: {output_path_ico}')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='生成多种尺寸的图标')
parser.add_argument('input_image', type=str, help='输入图像路径')
parser.add_argument('output_dir', type=str, help='输出目录')
args = parser.parse_args()
generate_icons(args.input_image, args.output_dir)

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
LLMClipboard 导入测试脚本
"""
import sys
import os
import traceback
# 添加项目根目录到路径
print(f"当前工作目录: {os.getcwd()}")
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
print(f"Python路径: {sys.path}")
try:
print("尝试导入MainWindow和NotificationDialog")
from llmclipboard.gui import MainWindow, NotificationDialog
print("成功导入MainWindow和NotificationDialog")
print("尝试导入TextCaptureService")
from llmclipboard.app import TextCaptureService
print("成功导入TextCaptureService")
# 尝试导入apply_stylesheet函数
try:
print("尝试导入apply_stylesheet")
from qt_material import apply_stylesheet
print("成功导入apply_stylesheet")
except ImportError as e:
print(f"qt_material导入错误: {e}")
# 如果qt_material未安装创建一个空的apply_stylesheet函数
print("qt_material未安装使用空的apply_stylesheet函数")
except Exception as e:
print(f"导入模块时出错: {e}")
traceback.print_exc()

View File

@ -10,7 +10,7 @@ import threading
import sys
import logging
from logging.handlers import RotatingFileHandler
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QApplication, QWidget
from llmclipboard.gui import MainWindow
from llmclipboard.document_processor import DocumentProcessor
import io
@ -429,7 +429,15 @@ class TextCaptureService:
# 保存图片
image.save(image_path)
# 获取图片文件大小和尺寸信息
file_size = os.path.getsize(image_path)
file_size_str = self.format_file_size(file_size)
img_width, img_height = image.size
self.logger.info(f"图片已保存到: {image_path}")
self.logger.info(f"图片大小: {file_size_str}")
self.logger.info(f"图片尺寸: {img_width}x{img_height}")
# 创建Markdown内容
md_content = f"""---
@ -437,6 +445,8 @@ title: 图片 {timestamp}
date: {time.strftime("%Y-%m-%d %H:%M:%S")}
tags: 图片
category: 图片
size: {file_size_str}
dimensions: {img_width}x{img_height}
---
![图片](./{image_filename})
@ -449,15 +459,28 @@ category: 图片
self.logger.info(f"Markdown文件已保存到: {md_path}")
# 构建详细的通知消息
notification_message = f"图片尺寸: {img_width}x{img_height}\n"
notification_message += f"文件大小: {file_size_str}\n"
notification_message += f"保存到: {md_path}"
# 在GUI中显示保存成功的消息
if hasattr(self, 'gui') and self.gui:
self.gui.show_notification(
"图片已保存",
f"保存到: {md_path}",
"info",
["打开文件", "打开文件夹"],
md_path
)
try:
self.gui.show_notification(
"图片已保存",
notification_message,
"info",
["打开文件", "打开文件夹"],
md_path
)
except Exception as e:
# 如果通知显示失败,记录错误并尝试使用基本消息
self.logger.error(f"显示图片保存通知失败: {str(e)}")
try:
self.gui.show_message(f"图片已保存到: {md_path}")
except Exception as e2:
self.logger.error(f"显示基本消息也失败: {str(e2)}")
return md_path
@ -465,7 +488,10 @@ category: 图片
error_msg = f"保存图片失败: {str(e)}"
self.logger.error(error_msg)
if hasattr(self, 'gui') and self.gui:
self.gui.show_message(error_msg, error=True)
try:
self.gui.show_message(error_msg, error=True)
except Exception as e2:
self.logger.error(f"显示错误消息失败: {str(e2)}")
return None
def append_images_to_markdown(self, md_path, images):
@ -485,6 +511,10 @@ category: 图片
# 准备添加的图片内容
image_content = "\n\n## 相关图片\n\n"
# 图片信息汇总
total_size = 0
image_info_list = []
# 保存每张图片并添加引用
for i, image in enumerate(images):
try:
@ -495,10 +525,27 @@ category: 图片
# 保存图片
image.save(image_path)
# 获取图片信息
file_size = os.path.getsize(image_path)
total_size += file_size
img_width, img_height = image.size
# 记录图片信息
image_info = {
'path': image_path,
'size': file_size,
'size_str': self.format_file_size(file_size),
'dimensions': f"{img_width}x{img_height}"
}
image_info_list.append(image_info)
self.logger.info(f"附加图片已保存到: {image_path}")
self.logger.info(f"图片大小: {image_info['size_str']}")
self.logger.info(f"图片尺寸: {image_info['dimensions']}")
# 添加图片引用
image_content += f"![图片{i+1}](./{image_filename})\n\n"
image_content += f"![图片{i+1}](./{image_filename}) *{image_info['dimensions']} - {image_info['size_str']}*\n\n"
except Exception as e:
self.logger.error(f"保存附加图片失败: {e}")
@ -507,9 +554,43 @@ category: 图片
f.write(original_content + image_content)
self.logger.info(f"已将 {len(images)} 张图片添加到Markdown文件: {md_path}")
self.logger.info(f"总图片大小: {self.format_file_size(total_size)}")
# 构建通知消息
if image_info_list:
notification_message = f"已添加 {len(images)} 张图片到文档\n"
notification_message += f"总大小: {self.format_file_size(total_size)}\n"
for i, info in enumerate(image_info_list[:3]): # 只显示前3张图片的信息
notification_message += f"图片{i+1}: {info['dimensions']} - {info['size_str']}\n"
if len(image_info_list) > 3:
notification_message += f"... 以及 {len(image_info_list) - 3} 张其他图片\n"
notification_message += f"文件: {md_path}"
# 显示通知
if hasattr(self, 'gui') and self.gui:
try:
self.gui.show_notification(
"图片已添加到文档",
notification_message,
"info",
["打开文件", "打开文件夹"],
md_path
)
except Exception as e:
self.logger.error(f"显示图片添加通知失败: {str(e)}")
try:
self.gui.show_message(f"已添加 {len(images)} 张图片到: {md_path}")
except Exception as e2:
self.logger.error(f"显示基本消息也失败: {str(e2)}")
except Exception as e:
self.logger.error(f"添加图片到Markdown文件失败: {e}")
error_msg = f"添加图片到Markdown文件失败: {e}"
self.logger.error(error_msg)
if hasattr(self, 'gui') and self.gui:
try:
self.gui.show_message(error_msg, error=True)
except Exception as e2:
self.logger.error(f"显示错误消息失败: {str(e2)}")
def save_to_markdown(self, content):
"""保存内容到Markdown文件"""
@ -526,20 +607,40 @@ category: 图片
with open(result['path'], 'w', encoding='utf-8') as f:
f.write(result['content'])
# 获取文件大小
file_size = os.path.getsize(result['path'])
file_size_str = self.format_file_size(file_size)
self.logger.info(f"文件保存成功: {result['path']}")
self.logger.info(f"文件大小: {file_size_str}")
self.logger.info(f"标题: {result['title']}")
self.logger.info(f"分类: {result['category']}")
self.logger.info(f"标签: {', '.join(result['tags'])}")
# 构建详细的通知消息
notification_message = f"标题: {result['title']}\n"
notification_message += f"分类: {result['category']}\n"
notification_message += f"大小: {file_size_str}\n"
notification_message += f"标签: {', '.join(result['tags'])}\n"
notification_message += f"保存到: {result['path']}"
# 在GUI中显示保存成功的消息和操作选项
if hasattr(self, 'gui') and self.gui:
self.gui.show_notification(
"内容已保存",
f"标题: {result['title']}\n保存到: {result['path']}",
"info",
["打开文件", "打开文件夹"],
result['path']
)
try:
self.gui.show_notification(
"内容已保存",
notification_message,
"info",
["打开文件", "打开文件夹"],
result['path']
)
except Exception as e:
# 如果通知显示失败,记录错误并尝试使用基本消息
self.logger.error(f"显示通知失败: {str(e)}")
try:
self.gui.show_message(f"内容已保存到: {result['path']}")
except Exception as e2:
self.logger.error(f"显示基本消息也失败: {str(e2)}")
return result['path']
@ -547,8 +648,22 @@ category: 图片
error_msg = f"保存文件失败: {str(e)}"
self.logger.error(error_msg)
if hasattr(self, 'gui') and self.gui:
self.gui.show_message(error_msg, error=True)
try:
self.gui.show_message(error_msg, error=True)
except Exception as e2:
self.logger.error(f"显示错误消息失败: {str(e2)}")
return None
def format_file_size(self, size_bytes):
"""格式化文件大小显示"""
if size_bytes < 1024:
return f"{size_bytes} B"
elif size_bytes < 1024 * 1024:
return f"{size_bytes/1024:.1f} KB"
elif size_bytes < 1024 * 1024 * 1024:
return f"{size_bytes/(1024*1024):.1f} MB"
else:
return f"{size_bytes/(1024*1024*1024):.1f} GB"
def simulate_copy(self):
keyboard.press('ctrl')
@ -558,28 +673,149 @@ category: 图片
time.sleep(0.1) # 等待复制操作完成
def start(self):
self.running = True
self._mouse_listener = mouse.Listener(on_click=self.on_click)
self._mouse_listener.start()
self.logger.info("Text capture service started")
"""启动文本捕获服务"""
try:
if self.running and self._mouse_listener is not None:
self.logger.warning("服务已经在运行中,忽略重复启动请求")
return
self.logger.info("正在启动文本捕获服务...")
self.running = True
# 创建并启动鼠标监听器
try:
self._mouse_listener = mouse.Listener(on_click=self.on_click)
self._mouse_listener.start()
self.logger.info("鼠标监听器启动成功")
except Exception as e:
self.logger.error(f"启动鼠标监听器失败: {e}")
self.running = False
raise Exception(f"启动鼠标监听器失败: {e}")
self.logger.info("文本捕获服务启动成功")
except Exception as e:
self.logger.error(f"启动文本捕获服务失败: {e}")
self.running = False
# 确保清理任何可能已创建的资源
if self._mouse_listener:
try:
self._mouse_listener.stop()
except:
pass
self._mouse_listener = None
raise Exception(f"启动文本捕获服务失败: {e}")
def stop(self):
self.running = False
if self._mouse_listener:
self._mouse_listener.stop()
"""停止文本捕获服务"""
try:
if not self.running:
self.logger.warning("服务已经停止,忽略重复停止请求")
return
self.logger.info("正在停止文本捕获服务...")
self.running = False
# 停止鼠标监听器
if self._mouse_listener:
try:
self._mouse_listener.stop()
self.logger.info("鼠标监听器停止成功")
except Exception as e:
self.logger.error(f"停止鼠标监听器失败: {e}")
finally:
self._mouse_listener = None
# 停止键盘监听器(如果存在)
if self._keyboard_listener:
try:
self._keyboard_listener.stop()
self.logger.info("键盘监听器停止成功")
except Exception as e:
self.logger.error(f"停止键盘监听器失败: {e}")
finally:
self._keyboard_listener = None
self.logger.info("文本捕获服务停止成功")
except Exception as e:
self.logger.error(f"停止文本捕获服务失败: {e}")
# 确保设置状态为已停止,即使发生异常
self.running = False
self._mouse_listener = None
self.logger.info("Text capture service stopped")
self._keyboard_listener = None
# 全局变量
app = None
window = None
service = None
tray_icon = None # 添加全局变量保存托盘图标引用
def main():
# 创建QApplication实例
app = QApplication(sys.argv)
global app, window, service, tray_icon
# 创建主窗口
window = MainWindow()
window.show()
try:
# 创建QApplication实例
app = QApplication(sys.argv)
# 设置应用程序属性,确保所有窗口关闭后应用程序不会退出
app.setQuitOnLastWindowClosed(False)
# 创建主窗口
from llmclipboard.gui import MainWindow
window = MainWindow()
# 保存服务实例的引用
service = window.service
# 保存系统托盘图标的引用
tray_icon = window.tray_icon
# 确保系统托盘图标可见
if tray_icon is not None and not tray_icon.isVisible():
print("确保系统托盘图标可见")
tray_icon.show()
# 确保tray_icon不会被垃圾回收在应用程序退出前调用cleanup
app.aboutToQuit.connect(cleanup)
# 显示主窗口
window.show()
# 运行应用程序
return app.exec()
except Exception as e:
print(f"Application error: {str(e)}")
logging.error(f"Application error: {str(e)}", exc_info=True)
# 在打包环境中,确保错误信息被记录
if getattr(sys, 'frozen', False):
import traceback
error_log_path = os.path.join(os.path.dirname(sys.executable), 'error_log.txt')
with open(error_log_path, 'a', encoding='utf-8') as f:
f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - Error: {str(e)}\n")
traceback.print_exc(file=f)
return 1
def cleanup():
"""清理资源,确保应用程序正确退出"""
global tray_icon, service
# 运行应用程序
sys.exit(app.exec())
print("Cleaning up resources...")
# 停止服务
if service is not None and hasattr(service, 'running') and service.running:
print("Stopping service...")
service.stop()
# 隐藏并销毁系统托盘图标
if tray_icon is not None:
print("Hiding tray icon...")
tray_icon.hide()
tray_icon.setVisible(False)
tray_icon = None
if __name__ == "__main__":
main()
# 使用try-except块捕获可能的异常
try:
sys.exit(main())
except Exception as e:
print(f"Application error: {e}")
sys.exit(1)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,445 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
LLMClipboard 通知样式测试脚本
用于测试各种通知样式颜色和交互
"""
import sys
import os
import logging
import traceback
# 添加项目根目录到路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('notification_style_test.log', encoding='utf-8')
]
)
logger = logging.getLogger('notification_style_test')
try:
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QLabel,
QHBoxLayout, QComboBox, QLineEdit, QTextEdit, QGroupBox, QSlider, QCheckBox,
QColorDialog, QDialog
)
from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QColor
# 导入LLMClipboard模块
from llmclipboard.gui import NotificationDialog
# 尝试导入apply_stylesheet函数
try:
from qt_material import apply_stylesheet
except ImportError:
# 如果qt_material未安装创建一个空的apply_stylesheet函数
def apply_stylesheet(widget, theme=None):
logger.warning(f"qt_material未安装无法应用主题: {theme}")
except Exception as e:
logger.error(f"导入模块时出错: {str(e)}")
traceback.print_exc()
sys.exit(1)
# 创建一个简化版的通知对话框直接使用NotificationDialog类
class SimpleNotificationTester(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("LLMClipboard 通知样式测试")
self.resize(800, 600)
# 初始化颜色
self.bg_color = QColor("#f0f0f0")
self.title_color = QColor("#333333")
self.content_color = QColor("#555555")
# 创建通知队列
self.notification_queue = []
self.is_showing_notification = False
# 创建UI
self.setup_ui()
def setup_ui(self):
"""设置UI"""
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
# 通知类型选择
type_group = QGroupBox("通知类型")
type_layout = QHBoxLayout()
type_group.setLayout(type_layout)
type_label = QLabel("类型:")
self.type_combo = QComboBox()
self.type_combo.addItems(["info", "success", "warning", "error"])
type_layout.addWidget(type_label)
type_layout.addWidget(self.type_combo)
# 通知内容
content_group = QGroupBox("通知内容")
content_layout = QVBoxLayout()
content_group.setLayout(content_layout)
title_layout = QHBoxLayout()
title_label = QLabel("标题:")
self.title_edit = QLineEdit("测试通知")
title_layout.addWidget(title_label)
title_layout.addWidget(self.title_edit)
message_layout = QVBoxLayout()
message_label = QLabel("消息内容:")
self.message_edit = QTextEdit("这是一条测试通知消息用于测试LLMClipboard的通知系统。")
self.message_edit.setMaximumHeight(100)
message_layout.addWidget(message_label)
message_layout.addWidget(self.message_edit)
buttons_layout = QHBoxLayout()
buttons_label = QLabel("按钮(逗号分隔):")
self.buttons_edit = QLineEdit("确定,取消")
buttons_layout.addWidget(buttons_label)
buttons_layout.addWidget(self.buttons_edit)
content_layout.addLayout(title_layout)
content_layout.addLayout(message_layout)
content_layout.addLayout(buttons_layout)
# 通知设置
settings_group = QGroupBox("通知设置")
settings_layout = QVBoxLayout()
settings_group.setLayout(settings_layout)
duration_layout = QHBoxLayout()
duration_label = QLabel("持续时间(秒):")
self.duration_slider = QSlider(Qt.Orientation.Horizontal)
self.duration_slider.setMinimum(1)
self.duration_slider.setMaximum(10)
self.duration_slider.setValue(5)
self.duration_value_label = QLabel("5")
self.duration_slider.valueChanged.connect(lambda v: self.duration_value_label.setText(str(v)))
duration_layout.addWidget(duration_label)
duration_layout.addWidget(self.duration_slider)
duration_layout.addWidget(self.duration_value_label)
auto_close_layout = QHBoxLayout()
self.auto_close_check = QCheckBox("自动关闭")
self.auto_close_check.setChecked(True)
auto_close_layout.addWidget(self.auto_close_check)
settings_layout.addLayout(duration_layout)
settings_layout.addLayout(auto_close_layout)
# 样式设置
style_group = QGroupBox("样式设置")
style_layout = QVBoxLayout()
style_group.setLayout(style_layout)
bg_color_layout = QHBoxLayout()
bg_color_label = QLabel("背景颜色:")
self.bg_color_button = QPushButton()
self.bg_color_button.setStyleSheet(f"background-color: {self.bg_color.name()};")
self.bg_color_button.setFixedSize(30, 20)
self.bg_color_button.clicked.connect(lambda: self.choose_color("bg"))
bg_color_layout.addWidget(bg_color_label)
bg_color_layout.addWidget(self.bg_color_button)
bg_color_layout.addStretch()
title_color_layout = QHBoxLayout()
title_color_label = QLabel("标题颜色:")
self.title_color_button = QPushButton()
self.title_color_button.setStyleSheet(f"background-color: {self.title_color.name()};")
self.title_color_button.setFixedSize(30, 20)
self.title_color_button.clicked.connect(lambda: self.choose_color("title"))
title_color_layout.addWidget(title_color_label)
title_color_layout.addWidget(self.title_color_button)
title_color_layout.addStretch()
content_color_layout = QHBoxLayout()
content_color_label = QLabel("内容颜色:")
self.content_color_button = QPushButton()
self.content_color_button.setStyleSheet(f"background-color: {self.content_color.name()};")
self.content_color_button.setFixedSize(30, 20)
self.content_color_button.clicked.connect(lambda: self.choose_color("content"))
content_color_layout.addWidget(content_color_label)
content_color_layout.addWidget(self.content_color_button)
content_color_layout.addStretch()
style_layout.addLayout(bg_color_layout)
style_layout.addLayout(title_color_layout)
style_layout.addLayout(content_color_layout)
# 测试按钮
buttons_layout = QHBoxLayout()
test_button = QPushButton("测试通知")
test_button.clicked.connect(self.test_notification)
queue_button = QPushButton("测试通知队列")
queue_button.clicked.connect(self.test_notification_queue)
dialog_button = QPushButton("测试直接对话框")
dialog_button.clicked.connect(self.test_direct_dialog)
buttons_layout.addWidget(test_button)
buttons_layout.addWidget(queue_button)
buttons_layout.addWidget(dialog_button)
# 状态标签
self.status_label = QLabel("就绪")
self.status_label.setStyleSheet("color: blue;")
# 添加所有组件到主布局
main_layout.addWidget(type_group)
main_layout.addWidget(content_group)
main_layout.addWidget(settings_group)
main_layout.addWidget(style_group)
main_layout.addLayout(buttons_layout)
main_layout.addWidget(self.status_label)
def choose_color(self, target):
"""选择颜色"""
try:
if target == "bg":
color = QColorDialog.getColor(self.bg_color, self)
if color.isValid():
self.bg_color = color
self.bg_color_button.setStyleSheet(f"background-color: {color.name()};")
elif target == "title":
color = QColorDialog.getColor(self.title_color, self)
if color.isValid():
self.title_color = color
self.title_color_button.setStyleSheet(f"background-color: {color.name()};")
elif target == "content":
color = QColorDialog.getColor(self.content_color, self)
if color.isValid():
self.content_color = color
self.content_color_button.setStyleSheet(f"background-color: {color.name()};")
except Exception as e:
logger.error(f"选择颜色时出错: {str(e)}")
def update_status(self, message):
"""更新状态标签"""
try:
self.status_label.setText(message)
logger.info(message)
# 5秒后重置状态
QTimer.singleShot(5000, lambda: self.status_label.setText("就绪"))
except Exception as e:
logger.error(f"更新状态标签时出错: {str(e)}")
def get_notification_params(self):
"""获取通知参数"""
try:
notification_type = self.type_combo.currentText()
title = self.title_edit.text()
message = self.message_edit.toPlainText()
buttons = [btn.strip() for btn in self.buttons_edit.text().split(",") if btn.strip()]
duration = self.duration_slider.value() * 1000 # 转换为毫秒
auto_close = self.auto_close_check.isChecked()
return {
"type": notification_type,
"title": title,
"message": message,
"buttons": buttons,
"duration": duration,
"auto_close": auto_close
}
except Exception as e:
logger.error(f"获取通知参数时出错: {str(e)}")
return None
def show_notification(self, title, message, notification_type="info", buttons=None, file_path=None, duration=0):
"""显示通知"""
try:
# 将通知添加到队列
self.notification_queue.append({
"title": title,
"message": message,
"type": notification_type,
"buttons": buttons,
"file_path": file_path,
"duration": duration
})
# 如果当前没有显示通知,则开始处理队列
if not self.is_showing_notification:
self._process_notification_queue()
except Exception as e:
logger.error(f"添加通知到队列时出错: {str(e)}")
def _process_notification_queue(self):
"""处理通知队列"""
try:
if not self.notification_queue:
self.is_showing_notification = False
return
self.is_showing_notification = True
notification = self.notification_queue.pop(0)
# 创建通知对话框
dialog = NotificationDialog(
self,
notification["title"],
notification["message"],
notification["type"],
notification["buttons"],
notification["file_path"]
)
# 应用自定义样式
style = f"""
QDialog {{
background-color: {self.bg_color.name()};
border: 1px solid #cccccc;
border-radius: 5px;
}}
QLabel#title_label {{
color: {self.title_color.name()};
font-size: 14px;
font-weight: bold;
}}
QLabel#message_label {{
color: {self.content_color.name()};
}}
"""
dialog.setStyleSheet(style)
# 显示对话框
dialog.show()
# 如果需要自动关闭
if notification["duration"] > 0:
QTimer.singleShot(notification["duration"], dialog.close)
# 当对话框关闭时,处理下一个通知
dialog.finished.connect(self._process_notification_queue)
except Exception as e:
logger.error(f"处理通知队列时出错: {str(e)}")
self.is_showing_notification = False
def test_notification(self):
"""测试通知"""
try:
params = self.get_notification_params()
if not params:
self.update_status("获取通知参数失败")
return
self.update_status(f"正在测试通知: {params['title']}")
self.show_notification(
params["title"],
params["message"],
params["type"],
params["buttons"] if params["buttons"] else None,
None, # 文件路径
params["duration"] if params["auto_close"] else 0
)
except Exception as e:
logger.error(f"测试通知时出错: {str(e)}")
self.update_status(f"显示通知失败: {e}")
def test_notification_queue(self):
"""测试通知队列"""
try:
params = self.get_notification_params()
if not params:
self.update_status("获取通知参数失败")
return
self.update_status("正在测试通知队列...")
# 发送多个通知
for i in range(3):
try:
self.show_notification(
f"{params['title']} {i+1}",
f"{params['message']}\n这是第 {i+1} 个队列通知",
params["type"],
params["buttons"] if params["buttons"] else None,
None, # 文件路径
params["duration"] if params["auto_close"] else 0
)
except Exception as e:
logger.error(f"显示通知队列失败: {e}")
self.update_status(f"显示通知队列失败: {e}")
except Exception as e:
logger.error(f"测试通知队列时出错: {str(e)}")
def test_direct_dialog(self):
"""测试直接创建通知对话框"""
try:
params = self.get_notification_params()
if not params:
self.update_status("获取通知参数失败")
return
self.update_status("正在测试直接创建通知对话框...")
# 创建自定义样式
style = f"""
QDialog {{
background-color: {self.bg_color.name()};
border: 1px solid #cccccc;
border-radius: 5px;
}}
QLabel#title_label {{
color: {self.title_color.name()};
font-size: 14px;
font-weight: bold;
}}
QLabel#message_label {{
color: {self.content_color.name()};
}}
"""
# 创建通知对话框
dialog = NotificationDialog(
self,
params["title"],
params["message"],
params["type"],
params["buttons"] if params["buttons"] else None,
None # 文件路径
)
# 应用自定义样式
dialog.setStyleSheet(style)
# 显示对话框
dialog.show()
# 如果需要自动关闭
if params["auto_close"]:
QTimer.singleShot(params["duration"], dialog.close)
except Exception as e:
logger.error(f"测试直接创建通知对话框时出错: {str(e)}")
self.update_status(f"创建通知对话框失败: {e}")
def main():
try:
app = QApplication(sys.argv)
window = SimpleNotificationTester()
window.show()
sys.exit(app.exec())
except Exception as e:
logger.error(f"主函数执行时出错: {str(e)}")
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,124 @@
# LLMClipboard 通知系统改进文档
## 通知系统概述
LLMClipboard的通知系统经过全面改进现在提供了更详细、更可靠的用户反馈机制涵盖了不同的操作场景和错误情况。通知系统的设计遵循以下原则
1. **多层次通知策略**:实现了多种通知方式,从系统托盘通知到弹出对话框,再到基本消息框,确保在各种情况下都能向用户提供反馈
2. **通知队列管理**:支持多个通知的顺序处理,避免通知堆积和冲突
3. **详细的操作反馈**:提供更丰富的操作结果信息,包括文件大小、图片尺寸等详细数据
4. **健壮的错误处理**:在各个层次实现错误捕获和处理,确保即使在出现异常的情况下也能提供适当的用户反馈
5. **视觉区分**:不同类型的通知使用不同的颜色和样式,帮助用户快速识别通知的重要性和类型
## 主要改进
### 1. 通知队列机制
在`gui.py`中实现了通知队列机制,确保多个通知能够有序显示:
```python
# 通知队列
self._notification_queue = []
self._is_processing_notifications = False
def _process_notification_queue(self):
"""处理通知队列"""
if not self._notification_queue or self._is_processing_notifications:
return
self._is_processing_notifications = True
notification = self._notification_queue[0]
# 处理当前通知...
# 通知关闭后会自动处理队列中的下一个通知
```
### 2. 多层次通知策略
实现了多种通知显示方式,并根据可用性自动选择最佳方式:
```python
def show_notification(self, title, message, notification_type="info", buttons=None, file_path=None, duration=3000):
"""显示通知"""
try:
# 尝试使用系统托盘通知
if self.tray_icon and self.tray_icon.isVisible():
# 系统托盘通知实现...
else:
# 使用弹出对话框
# 对话框通知实现...
except Exception as e:
# 错误处理和备用通知机制
self.logger.error(f"显示通知失败: {e}")
try:
# 尝试使用基本消息框
self.show_message(message)
except Exception as e2:
self.logger.error(f"显示基本消息也失败: {e2}")
```
### 3. 详细的文件操作反馈
在文件保存操作中添加了更详细的反馈信息:
```python
# 构建通知消息
notification_message = f"已保存到: {file_path}\n"
notification_message += f"标题: {title}\n"
notification_message += f"分类: {category}\n"
notification_message += f"大小: {self.format_file_size(file_size)}\n"
if tags:
notification_message += f"标签: {', '.join(tags)}"
```
### 4. 图片保存增强
改进了图片保存功能,添加了图片尺寸和大小信息:
```python
# 获取图片信息
img_width, img_height = image.size
file_size = os.path.getsize(image_path)
# 构建通知消息
notification_message = f"图片已保存到: {image_path}\n"
notification_message += f"尺寸: {img_width}x{img_height}\n"
notification_message += f"大小: {self.format_file_size(file_size)}"
```
### 5. 通知对话框样式增强
为不同类型的通知设计了不同的样式:
```python
# 根据通知类型设置样式
if notification_type == "error":
style += "background-color: #ffebee; border-left: 4px solid #f44336;"
elif notification_type == "warning":
style += "background-color: #fff8e1; border-left: 4px solid #ffc107;"
elif notification_type == "success":
style += "background-color: #e8f5e9; border-left: 4px solid #4caf50;"
else: # info
style += "background-color: #e3f2fd; border-left: 4px solid #2196f3;"
```
## 测试工具
为了验证通知系统的功能和性能,创建了两个测试脚本:
1. **notification_test.py**:测试各种通知场景和错误处理机制
2. **notification_style_test.py**:测试不同类型的通知样式和交互
这些测试脚本可以帮助开发者验证通知系统在不同情况下的表现,确保通知系统的可靠性和用户体验。
## 未来改进方向
1. **通知自定义**:允许用户自定义通知的样式、持续时间和行为
2. **通知历史**:实现通知历史记录功能,让用户可以查看过去的通知
3. **通知分组**:对相似的通知进行分组,减少通知数量
4. **通知优先级**:实现通知优先级机制,确保重要通知能够优先显示
5. **跨平台兼容性**:进一步增强在不同操作系统上的通知兼容性
## 结论
通过这些改进LLMClipboard的通知系统现在能够提供更详细、更可靠的用户反馈无论是在正常操作还是错误情况下都能确保用户获得清晰的信息。这些改进不仅增强了应用程序的用户体验也提高了应用程序的可靠性和健壮性。

View File

@ -0,0 +1,216 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
LLMClipboard 通知系统测试脚本
用于测试各种通知场景和错误处理机制
"""
import sys
import os
import time
import logging
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QLabel
from PyQt6.QtCore import Qt, QTimer
# 添加项目根目录到路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# 导入LLMClipboard模块
from llmclipboard.gui import MainWindow
from llmclipboard.app import TextCaptureService
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('notification_test.log', encoding='utf-8')
]
)
logger = logging.getLogger('notification_test')
class NotificationTestWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("LLMClipboard 通知测试")
self.resize(500, 400)
# 创建主窗口实例
self.main_window = MainWindow()
# 创建服务实例
self.service = TextCaptureService()
self.main_window.service = self.service
self.service.gui = self.main_window
# 创建测试界面
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
# 添加标题
title = QLabel("LLMClipboard 通知系统测试")
title.setStyleSheet("font-size: 18px; font-weight: bold;")
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(title)
# 添加测试按钮
self.add_test_button(layout, "测试基本通知", self.test_basic_notification)
self.add_test_button(layout, "测试文件保存通知", self.test_file_save_notification)
self.add_test_button(layout, "测试图片保存通知", self.test_image_save_notification)
self.add_test_button(layout, "测试多个通知队列", self.test_notification_queue)
self.add_test_button(layout, "测试错误通知", self.test_error_notification)
self.add_test_button(layout, "测试托盘图标通知", self.test_tray_notification)
self.add_test_button(layout, "测试备用通知机制", self.test_fallback_notification)
# 添加状态标签
self.status_label = QLabel("就绪")
self.status_label.setStyleSheet("font-style: italic;")
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.status_label)
# 显示主窗口
self.main_window.show()
def add_test_button(self, layout, text, callback):
"""添加测试按钮"""
button = QPushButton(text)
button.setMinimumHeight(40)
button.clicked.connect(callback)
layout.addWidget(button)
def update_status(self, message):
"""更新状态标签"""
self.status_label.setText(message)
logger.info(message)
# 5秒后重置状态
QTimer.singleShot(5000, lambda: self.status_label.setText("就绪"))
def test_basic_notification(self):
"""测试基本通知"""
self.update_status("正在测试基本通知...")
self.main_window.show_notification(
"测试通知",
"这是一个基本的测试通知消息",
"info"
)
def test_file_save_notification(self):
"""测试文件保存通知"""
self.update_status("正在测试文件保存通知...")
# 创建临时内容
content = """# 测试文档
这是一个测试文档内容用于测试文件保存通知功能
## 测试章节
* 项目1
* 项目2
* 项目3
## 代码示例
```python
def hello_world():
print("Hello, World!")
```
"""
# 保存内容
self.service.save_to_markdown(content)
def test_image_save_notification(self):
"""测试图片保存通知"""
self.update_status("正在测试图片保存通知...")
try:
# 创建测试图片
from PIL import Image, ImageDraw
# 创建一个彩色测试图像
img = Image.new('RGB', (800, 600), color=(73, 109, 137))
d = ImageDraw.Draw(img)
# 绘制一些图形
d.rectangle([(200, 100), (600, 500)], fill=(255, 255, 255), outline=(255, 0, 0))
d.ellipse([(250, 150), (550, 450)], fill=(0, 255, 0))
d.line([(200, 100), (600, 500)], fill=(0, 0, 255), width=5)
# 保存图片
self.service.save_image(img)
except Exception as e:
logger.error(f"创建测试图片失败: {e}")
self.update_status(f"创建测试图片失败: {e}")
def test_notification_queue(self):
"""测试多个通知队列"""
self.update_status("正在测试多个通知队列...")
# 发送多个通知
for i in range(5):
self.main_window.show_notification(
f"测试通知 {i+1}",
f"这是第 {i+1} 个测试通知消息,测试通知队列处理",
"info"
)
def test_error_notification(self):
"""测试错误通知"""
self.update_status("正在测试错误通知...")
self.main_window.show_notification(
"错误通知测试",
"这是一个错误通知测试消息",
"error"
)
def test_tray_notification(self):
"""测试托盘图标通知"""
self.update_status("正在测试托盘图标通知...")
# 确保托盘图标存在
if hasattr(self.main_window, 'tray_icon') and self.main_window.tray_icon is not None:
self.main_window.show_tray_message(
"托盘通知测试",
"这是一个托盘图标通知测试消息",
"info",
3000
)
else:
self.update_status("托盘图标不可用,无法测试托盘通知")
def test_fallback_notification(self):
"""测试备用通知机制"""
self.update_status("正在测试备用通知机制...")
# 临时禁用托盘图标,强制使用备用通知
if hasattr(self.main_window, 'tray_icon'):
original_tray = self.main_window.tray_icon
self.main_window.tray_icon = None
# 显示通知 (将使用备用机制)
self.main_window.show_notification(
"备用通知测试",
"这是一个备用通知机制测试消息",
"info"
)
# 恢复托盘图标
self.main_window.tray_icon = original_tray
else:
self.update_status("无法测试备用通知机制,托盘图标不存在")
def main():
app = QApplication(sys.argv)
window = NotificationTestWindow()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,45 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
LLMClipboard 启动脚本
用于启动LLMClipboard应用程序避免相对导入问题
"""
import sys
import os
import logging
# 添加当前目录到Python路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# 设置基本日志配置
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
def main():
"""启动应用程序的主函数"""
try:
# 导入应用程序模块
from llmclipboard.app import main as app_main
# 运行应用程序
return app_main()
except Exception as e:
logging.error(f"启动应用程序时出错: {e}", exc_info=True)
# 在打包环境中,确保错误信息被记录
if getattr(sys, 'frozen', False):
import traceback
import time
error_log_path = os.path.join(os.path.dirname(sys.executable), 'error_log.txt')
with open(error_log_path, 'a', encoding='utf-8') as f:
f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - Error: {str(e)}\n")
traceback.print_exc(file=f)
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,157 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
LLMClipboard 打包测试脚本
用于测试打包后的应用程序是否能够正确运行
"""
import os
import sys
import subprocess
import time
import shutil
def print_header(title):
"""打印标题"""
print("\n" + "=" * 60)
print(f" {title} ".center(60, "="))
print("=" * 60)
def run_command(command, cwd=None):
"""运行命令并返回输出"""
print(f"运行命令: {command}")
try:
result = subprocess.run(
command,
shell=True,
cwd=cwd,
capture_output=True,
text=True,
encoding='utf-8'
)
return result.returncode, result.stdout, result.stderr
except Exception as e:
return -1, "", str(e)
def test_build():
"""测试构建过程"""
print_header("测试构建过程")
# 运行build.bat
print("运行build.bat...")
code, stdout, stderr = run_command("build.bat")
if code != 0:
print("构建失败!")
print(f"错误信息: {stderr}")
return False
# 检查dist目录
if not os.path.exists("dist"):
print("构建失败: dist目录不存在")
return False
# 检查可执行文件
if not os.path.exists("dist/LLMClipboard.exe"):
print("构建失败: LLMClipboard.exe不存在")
return False
print("构建成功!")
return True
def test_resources():
"""测试资源文件"""
print_header("测试资源文件")
# 检查resources目录
if not os.path.exists("dist/resources"):
print("资源目录不存在: dist/resources")
return False
# 检查图标文件
if not os.path.exists("dist/resources/icon.png"):
print("图标文件不存在: dist/resources/icon.png")
return False
print("资源文件检查通过!")
return True
def test_executable():
"""测试可执行文件"""
print_header("测试可执行文件")
# 创建测试目录
test_dir = "test_dist"
if os.path.exists(test_dir):
shutil.rmtree(test_dir)
os.makedirs(test_dir)
# 复制可执行文件和资源
print("复制可执行文件和资源到测试目录...")
shutil.copy("dist/LLMClipboard.exe", test_dir)
shutil.copytree("dist/resources", os.path.join(test_dir, "resources"))
# 运行可执行文件
print("运行可执行文件...")
print("注意: 将在5秒后自动关闭应用程序")
# 启动应用程序
process = subprocess.Popen(
os.path.join(test_dir, "LLMClipboard.exe"),
cwd=test_dir
)
# 等待5秒
time.sleep(5)
# 检查进程是否仍在运行
if process.poll() is None:
print("应用程序成功启动并保持运行")
# 终止进程
process.terminate()
process.wait(timeout=5)
result = True
else:
print("应用程序启动失败或已崩溃")
result = False
# 检查是否生成了错误日志
error_log = os.path.join(test_dir, "error_log.txt")
if os.path.exists(error_log):
print(f"发现错误日志: {error_log}")
with open(error_log, 'r', encoding='utf-8') as f:
print(f.read())
result = False
# 清理测试目录
print("清理测试目录...")
shutil.rmtree(test_dir)
return result
def main():
"""主函数"""
print_header("LLMClipboard 打包测试")
# 测试构建
if not test_build():
print("构建测试失败,终止测试")
return 1
# 测试资源文件
if not test_resources():
print("资源文件测试失败,终止测试")
return 1
# 测试可执行文件
if not test_executable():
print("可执行文件测试失败")
return 1
print_header("测试结果")
print("所有测试通过!")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,324 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
系统托盘功能测试脚本
"""
import sys
import os
import logging
from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QWidget, QPushButton, QVBoxLayout, QLabel
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import Qt
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('tray_test.log', encoding='utf-8')
]
)
logger = logging.getLogger('TrayTest')
class TrayTestWindow(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
self.create_tray_icon()
def init_ui(self):
"""初始化UI"""
self.setWindowTitle('系统托盘测试')
self.setGeometry(300, 300, 400, 300)
layout = QVBoxLayout()
# 添加说明标签
label = QLabel('这是一个系统托盘功能测试程序。关闭窗口后,应用程序将最小化到系统托盘。')
label.setWordWrap(True)
layout.addWidget(label)
# 添加测试按钮
self.show_message_button = QPushButton('显示托盘消息')
self.show_message_button.clicked.connect(self.test_show_message)
layout.addWidget(self.show_message_button)
self.toggle_icon_button = QPushButton('切换托盘图标')
self.toggle_icon_button.clicked.connect(self.test_toggle_icon)
layout.addWidget(self.toggle_icon_button)
self.quit_button = QPushButton('退出应用程序')
self.quit_button.clicked.connect(self.quit_application)
layout.addWidget(self.quit_button)
self.setLayout(layout)
def create_tray_icon(self):
"""创建系统托盘图标"""
logger.info("创建系统托盘图标")
# 检查系统是否支持系统托盘
if not QSystemTrayIcon.isSystemTrayAvailable():
logger.error("系统不支持系统托盘功能")
return False
try:
# 创建一个永久存在的QWidget作为托盘图标的父对象
self._tray_parent = QWidget()
self._tray_parent.setVisible(False)
# 创建系统托盘图标
self.tray_icon = QSystemTrayIcon(self._tray_parent)
# 创建托盘菜单
self.tray_menu = QMenu()
# 添加菜单项
self.show_action = self.tray_menu.addAction("显示窗口")
self.show_action.triggered.connect(self.show_and_activate)
self.tray_menu.addSeparator()
self.test_message_action = self.tray_menu.addAction("测试托盘消息")
self.test_message_action.triggered.connect(self.test_show_message)
self.test_icon_action = self.tray_menu.addAction("测试切换图标")
self.test_icon_action.triggered.connect(self.test_toggle_icon)
self.tray_menu.addSeparator()
self.quit_action = self.tray_menu.addAction("退出")
self.quit_action.triggered.connect(self.quit_application)
# 设置托盘菜单
self.tray_icon.setContextMenu(self.tray_menu)
# 设置图标
# 尝试使用系统图标
self.icon1 = QIcon.fromTheme("dialog-information")
self.icon2 = QIcon.fromTheme("dialog-warning")
# 如果系统图标不可用,使用标准图标
if self.icon1.isNull():
self.icon1 = self.style().standardIcon(self.style().StandardPixmap.SP_DialogApplyButton)
if self.icon2.isNull():
self.icon2 = self.style().standardIcon(self.style().StandardPixmap.SP_DialogWarningButton)
# 设置初始图标
self.tray_icon.setIcon(self.icon1)
self.current_icon = 1
# 连接托盘图标的激活信号
self.tray_icon.activated.connect(self.on_tray_activated)
# 显示托盘图标
self.tray_icon.show()
# 显示启动消息
self.show_tray_message("系统托盘测试", "应用程序已启动并显示在系统托盘")
logger.info("系统托盘图标创建完成")
return True
except Exception as e:
logger.exception(f"创建系统托盘图标失败: {str(e)}")
return False
def show_tray_message(self, title, message, icon_type="info", duration=2000):
"""显示系统托盘消息"""
if not hasattr(self, 'tray_icon') or self.tray_icon is None:
logger.error(f"无法显示托盘消息: {title} - {message}")
return False
logger.info(f"显示托盘消息: {title} - {message}")
# 设置图标类型
icon = None
try:
# 尝试使用枚举值 (PyQt6 6.0.0+)
if icon_type == "info":
icon = QSystemTrayIcon.MessageIcon.Information
elif icon_type == "warning":
icon = QSystemTrayIcon.MessageIcon.Warning
elif icon_type == "error":
icon = QSystemTrayIcon.MessageIcon.Critical
else:
icon = QSystemTrayIcon.MessageIcon.Information
except:
# 尝试使用整数值 (旧版PyQt6)
try:
if icon_type == "info":
icon = QSystemTrayIcon.Information
elif icon_type == "warning":
icon = QSystemTrayIcon.Warning
elif icon_type == "error":
icon = QSystemTrayIcon.Critical
else:
icon = QSystemTrayIcon.Information
except:
# 使用整数值
if icon_type == "info":
icon = 1 # Information
elif icon_type == "warning":
icon = 2 # Warning
elif icon_type == "error":
icon = 3 # Critical
else:
icon = 1 # Information
# 尝试显示消息
try:
self.tray_icon.showMessage(title, message, icon, duration)
return True
except Exception as e:
logger.exception(f"显示托盘消息失败: {str(e)}")
# 尝试使用整数值作为图标类型
try:
# 将枚举转换为整数
if isinstance(icon, object) and hasattr(icon, "value"):
icon_int = icon.value
else:
icon_int = 1 # 默认为Information
self.tray_icon.showMessage(title, message, icon_int, duration)
return True
except Exception as e2:
logger.exception(f"使用整数值显示托盘消息也失败: {str(e2)}")
return False
def on_tray_activated(self, reason):
"""处理托盘图标激活事件"""
logger.info(f"托盘图标被激活,原因: {reason}")
try:
# 获取激活原因的枚举值
try:
# 尝试使用枚举值 (PyQt6 6.0.0+)
double_click = QSystemTrayIcon.ActivationReason.DoubleClick
trigger = QSystemTrayIcon.ActivationReason.Trigger
except:
# 尝试使用整数值 (旧版PyQt6)
try:
double_click = QSystemTrayIcon.DoubleClick
trigger = QSystemTrayIcon.Trigger
except:
# 使用整数值
double_click = 2 # DoubleClick
trigger = 3 # Trigger
# 处理双击事件
if reason == double_click:
logger.info("托盘图标被双击,显示主窗口")
self.show_and_activate()
# 处理单击事件
elif reason == trigger:
logger.info("托盘图标被单击")
# 在某些系统上,单击可能不会自动显示上下文菜单
except Exception as e:
logger.exception(f"处理托盘图标激活事件失败: {str(e)}")
def show_and_activate(self):
"""显示并激活窗口"""
logger.info("显示并激活窗口")
self.show()
self.setWindowState(self.windowState() & ~Qt.WindowState.WindowMinimized)
self.activateWindow()
def test_show_message(self):
"""测试显示托盘消息"""
logger.info("测试显示托盘消息")
self.show_tray_message(
"测试消息",
"这是一条测试消息,用于验证系统托盘通知功能是否正常工作。",
"info"
)
def test_toggle_icon(self):
"""测试切换托盘图标"""
logger.info("测试切换托盘图标")
if not hasattr(self, 'tray_icon') or self.tray_icon is None:
logger.error("托盘图标不存在,无法切换图标")
return
try:
if self.current_icon == 1:
self.tray_icon.setIcon(self.icon2)
self.current_icon = 2
self.show_tray_message("图标已切换", "当前使用警告图标")
else:
self.tray_icon.setIcon(self.icon1)
self.current_icon = 1
self.show_tray_message("图标已切换", "当前使用信息图标")
except Exception as e:
logger.exception(f"切换托盘图标失败: {str(e)}")
def quit_application(self):
"""退出应用程序"""
logger.info("退出应用程序")
try:
# 隐藏托盘图标
if hasattr(self, 'tray_icon') and self.tray_icon is not None:
logger.info("隐藏托盘图标")
self.tray_icon.hide()
# 退出应用程序
logger.info("调用QApplication.quit()")
QApplication.quit()
except Exception as e:
logger.exception(f"退出应用程序失败: {str(e)}")
# 强制退出
QApplication.exit(1)
def closeEvent(self, event):
"""处理窗口关闭事件"""
logger.info("处理窗口关闭事件")
try:
# 如果系统托盘可用且托盘图标已创建,则最小化到系统托盘
if (QSystemTrayIcon.isSystemTrayAvailable() and
hasattr(self, 'tray_icon') and
self.tray_icon is not None and
self.tray_icon.isVisible()):
logger.info("最小化到系统托盘")
# 显示提示消息
self.show_tray_message(
"系统托盘测试",
"应用程序已最小化到系统托盘,双击托盘图标可以重新显示窗口"
)
# 隐藏主窗口
self.hide()
# 忽略关闭事件,防止应用程序退出
event.ignore()
else:
# 系统托盘不可用,正常关闭应用程序
logger.info("系统托盘不可用,正常关闭应用程序")
event.accept()
except Exception as e:
logger.exception(f"处理窗口关闭事件失败: {str(e)}")
# 出现异常时,默认接受关闭事件
event.accept()
def main():
"""主函数"""
logger.info("应用程序启动")
app = QApplication(sys.argv)
# 设置应用程序不会在最后一个窗口关闭时退出
app.setQuitOnLastWindowClosed(False)
window = TrayTestWindow()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()

View File

@ -1,6 +1,109 @@
version = 1
requires-python = ">=3.10"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
]
[[package]]
name = "anyio"
version = "4.8.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "idna" },
{ name = "sniffio" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 },
]
[[package]]
name = "certifi"
version = "2025.1.31"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
]
[[package]]
name = "charset-normalizer"
version = "3.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 },
{ url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 },
{ url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 },
{ url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 },
{ url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 },
{ url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 },
{ url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 },
{ url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 },
{ url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 },
{ url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 },
{ url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 },
{ url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 },
{ url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 },
{ url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 },
{ url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 },
{ url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 },
{ url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 },
{ url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 },
{ url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 },
{ url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 },
{ url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 },
{ url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 },
{ url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 },
{ url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 },
{ url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 },
{ url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 },
{ url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
{ url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
{ url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
{ url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
{ url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
{ url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
{ url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
{ url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
{ url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
{ url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
{ url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
{ url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
{ url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
{ url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
{ url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
{ url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
{ url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
{ url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
{ url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
{ url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
{ url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
{ url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
{ url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
{ url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
{ url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
{ url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
{ url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "configparser"
version = "7.1.0"
@ -19,18 +122,82 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl", hash = "sha256:a7509ccf517eaad92b31c214f593dbcf138ea8a43b2935406bbd565e15527a85", size = 8955 },
]
[[package]]
name = "distro"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 },
]
[[package]]
name = "evdev"
version = "1.9.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d1/99/4d24bb6db12fc170a5f209f4c9108054a2c84d289d1e7f743e979b202023/evdev-1.9.1.tar.gz", hash = "sha256:dc640a064cb1c9fe1f8b970dc2039945a2a275d7b7ee62284bf427238abe45ee", size = 33349 }
[[package]]
name = "exceptiongroup"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
]
[[package]]
name = "h11"
version = "0.14.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
]
[[package]]
name = "html2text"
version = "2024.2.26"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1a/43/e1d53588561e533212117750ee79ad0ba02a41f52a08c1df3396bd466c05/html2text-2024.2.26.tar.gz", hash = "sha256:05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32", size = 56527 }
[[package]]
name = "httpcore"
version = "1.0.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "jieba"
version = "0.42.1"
@ -49,6 +216,65 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
]
[[package]]
name = "jiter"
version = "0.8.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/70/90bc7bd3932e651486861df5c8ffea4ca7c77d28e8532ddefe2abc561a53/jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d", size = 163007 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f2/f3/8c11e0e87bd5934c414f9b1cfae3cbfd4a938d4669d57cb427e1c4d11a7f/jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b", size = 303381 },
{ url = "https://files.pythonhosted.org/packages/ea/28/4cd3f0bcbf40e946bc6a62a82c951afc386a25673d3d8d5ee461f1559bbe/jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393", size = 311718 },
{ url = "https://files.pythonhosted.org/packages/0d/17/57acab00507e60bd954eaec0837d9d7b119b4117ff49b8a62f2b646f32ed/jiter-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c826a221851a8dc028eb6d7d6429ba03184fa3c7e83ae01cd6d3bd1d4bd17d", size = 335465 },
{ url = "https://files.pythonhosted.org/packages/74/b9/1a3ddd2bc95ae17c815b021521020f40c60b32137730126bada962ef32b4/jiter-0.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d35c864c2dff13dfd79fb070fc4fc6235d7b9b359efe340e1261deb21b9fcb66", size = 355570 },
{ url = "https://files.pythonhosted.org/packages/78/69/6d29e2296a934199a7d0dde673ecccf98c9c8db44caf0248b3f2b65483cb/jiter-0.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f557c55bc2b7676e74d39d19bcb8775ca295c7a028246175d6a8b431e70835e5", size = 381383 },
{ url = "https://files.pythonhosted.org/packages/22/d7/fbc4c3fb1bf65f9be22a32759b539f88e897aeb13fe84ab0266e4423487a/jiter-0.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:580ccf358539153db147e40751a0b41688a5ceb275e6f3e93d91c9467f42b2e3", size = 390454 },
{ url = "https://files.pythonhosted.org/packages/4d/a0/3993cda2e267fe679b45d0bcc2cef0b4504b0aa810659cdae9737d6bace9/jiter-0.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af102d3372e917cffce49b521e4c32c497515119dc7bd8a75665e90a718bbf08", size = 345039 },
{ url = "https://files.pythonhosted.org/packages/b9/ef/69c18562b4c09ce88fab5df1dcaf643f6b1a8b970b65216e7221169b81c4/jiter-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cadcc978f82397d515bb2683fc0d50103acff2a180552654bb92d6045dec2c49", size = 376200 },
{ url = "https://files.pythonhosted.org/packages/4d/17/0b5a8de46a6ab4d836f70934036278b49b8530c292b29dde3483326d4555/jiter-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba5bdf56969cad2019d4e8ffd3f879b5fdc792624129741d3d83fc832fef8c7d", size = 511158 },
{ url = "https://files.pythonhosted.org/packages/6c/b2/c401a0a2554b36c9e6d6e4876b43790d75139cf3936f0222e675cbc23451/jiter-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3b94a33a241bee9e34b8481cdcaa3d5c2116f575e0226e421bed3f7a6ea71cff", size = 503956 },
{ url = "https://files.pythonhosted.org/packages/d4/02/a0291ed7d72c0ac130f172354ee3cf0b2556b69584de391463a8ee534f40/jiter-0.8.2-cp310-cp310-win32.whl", hash = "sha256:6e5337bf454abddd91bd048ce0dca5134056fc99ca0205258766db35d0a2ea43", size = 202846 },
{ url = "https://files.pythonhosted.org/packages/ad/20/8c988831ae4bf437e29f1671e198fc99ba8fe49f2895f23789acad1d1811/jiter-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:4a9220497ca0cb1fe94e3f334f65b9b5102a0b8147646118f020d8ce1de70105", size = 204414 },
{ url = "https://files.pythonhosted.org/packages/cb/b0/c1a7caa7f9dc5f1f6cfa08722867790fe2d3645d6e7170ca280e6e52d163/jiter-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2dd61c5afc88a4fda7d8b2cf03ae5947c6ac7516d32b7a15bf4b49569a5c076b", size = 303666 },
{ url = "https://files.pythonhosted.org/packages/f5/97/0468bc9eeae43079aaa5feb9267964e496bf13133d469cfdc135498f8dd0/jiter-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a6c710d657c8d1d2adbbb5c0b0c6bfcec28fd35bd6b5f016395f9ac43e878a15", size = 311934 },
{ url = "https://files.pythonhosted.org/packages/e5/69/64058e18263d9a5f1e10f90c436853616d5f047d997c37c7b2df11b085ec/jiter-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9584de0cd306072635fe4b89742bf26feae858a0683b399ad0c2509011b9dc0", size = 335506 },
{ url = "https://files.pythonhosted.org/packages/9d/14/b747f9a77b8c0542141d77ca1e2a7523e854754af2c339ac89a8b66527d6/jiter-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a90a923338531b7970abb063cfc087eebae6ef8ec8139762007188f6bc69a9f", size = 355849 },
{ url = "https://files.pythonhosted.org/packages/53/e2/98a08161db7cc9d0e39bc385415890928ff09709034982f48eccfca40733/jiter-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21974d246ed0181558087cd9f76e84e8321091ebfb3a93d4c341479a736f099", size = 381700 },
{ url = "https://files.pythonhosted.org/packages/7a/38/1674672954d35bce3b1c9af99d5849f9256ac8f5b672e020ac7821581206/jiter-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32475a42b2ea7b344069dc1e81445cfc00b9d0e3ca837f0523072432332e9f74", size = 389710 },
{ url = "https://files.pythonhosted.org/packages/f8/9b/92f9da9a9e107d019bcf883cd9125fa1690079f323f5a9d5c6986eeec3c0/jiter-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9931fd36ee513c26b5bf08c940b0ac875de175341cbdd4fa3be109f0492586", size = 345553 },
{ url = "https://files.pythonhosted.org/packages/44/a6/6d030003394e9659cd0d7136bbeabd82e869849ceccddc34d40abbbbb269/jiter-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0820f4a3a59ddced7fce696d86a096d5cc48d32a4183483a17671a61edfddc", size = 376388 },
{ url = "https://files.pythonhosted.org/packages/ad/8d/87b09e648e4aca5f9af89e3ab3cfb93db2d1e633b2f2931ede8dabd9b19a/jiter-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ffc86ae5e3e6a93765d49d1ab47b6075a9c978a2b3b80f0f32628f39caa0c88", size = 511226 },
{ url = "https://files.pythonhosted.org/packages/77/95/8008ebe4cdc82eac1c97864a8042ca7e383ed67e0ec17bfd03797045c727/jiter-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5127dc1abd809431172bc3fbe8168d6b90556a30bb10acd5ded41c3cfd6f43b6", size = 504134 },
{ url = "https://files.pythonhosted.org/packages/26/0d/3056a74de13e8b2562e4d526de6dac2f65d91ace63a8234deb9284a1d24d/jiter-0.8.2-cp311-cp311-win32.whl", hash = "sha256:66227a2c7b575720c1871c8800d3a0122bb8ee94edb43a5685aa9aceb2782d44", size = 203103 },
{ url = "https://files.pythonhosted.org/packages/4e/1e/7f96b798f356e531ffc0f53dd2f37185fac60fae4d6c612bbbd4639b90aa/jiter-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cde031d8413842a1e7501e9129b8e676e62a657f8ec8166e18a70d94d4682855", size = 206717 },
{ url = "https://files.pythonhosted.org/packages/a1/17/c8747af8ea4e045f57d6cfd6fc180752cab9bc3de0e8a0c9ca4e8af333b1/jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f", size = 302027 },
{ url = "https://files.pythonhosted.org/packages/3c/c1/6da849640cd35a41e91085723b76acc818d4b7d92b0b6e5111736ce1dd10/jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44", size = 310326 },
{ url = "https://files.pythonhosted.org/packages/06/99/a2bf660d8ccffee9ad7ed46b4f860d2108a148d0ea36043fd16f4dc37e94/jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f", size = 334242 },
{ url = "https://files.pythonhosted.org/packages/a7/5f/cea1c17864828731f11427b9d1ab7f24764dbd9aaf4648a7f851164d2718/jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60", size = 356654 },
{ url = "https://files.pythonhosted.org/packages/e9/13/62774b7e5e7f5d5043efe1d0f94ead66e6d0f894ae010adb56b3f788de71/jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57", size = 379967 },
{ url = "https://files.pythonhosted.org/packages/ec/fb/096b34c553bb0bd3f2289d5013dcad6074948b8d55212aa13a10d44c5326/jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e", size = 389252 },
{ url = "https://files.pythonhosted.org/packages/17/61/beea645c0bf398ced8b199e377b61eb999d8e46e053bb285c91c3d3eaab0/jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887", size = 345490 },
{ url = "https://files.pythonhosted.org/packages/d5/df/834aa17ad5dcc3cf0118821da0a0cf1589ea7db9832589278553640366bc/jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d", size = 376991 },
{ url = "https://files.pythonhosted.org/packages/67/80/87d140399d382fb4ea5b3d56e7ecaa4efdca17cd7411ff904c1517855314/jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152", size = 510822 },
{ url = "https://files.pythonhosted.org/packages/5c/37/3394bb47bac1ad2cb0465601f86828a0518d07828a650722e55268cdb7e6/jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29", size = 503730 },
{ url = "https://files.pythonhosted.org/packages/f9/e2/253fc1fa59103bb4e3aa0665d6ceb1818df1cd7bf3eb492c4dad229b1cd4/jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e", size = 203375 },
{ url = "https://files.pythonhosted.org/packages/41/69/6d4bbe66b3b3b4507e47aa1dd5d075919ad242b4b1115b3f80eecd443687/jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c", size = 204740 },
{ url = "https://files.pythonhosted.org/packages/6c/b0/bfa1f6f2c956b948802ef5a021281978bf53b7a6ca54bb126fd88a5d014e/jiter-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84", size = 301190 },
{ url = "https://files.pythonhosted.org/packages/a4/8f/396ddb4e292b5ea57e45ade5dc48229556b9044bad29a3b4b2dddeaedd52/jiter-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4", size = 309334 },
{ url = "https://files.pythonhosted.org/packages/7f/68/805978f2f446fa6362ba0cc2e4489b945695940656edd844e110a61c98f8/jiter-0.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587", size = 333918 },
{ url = "https://files.pythonhosted.org/packages/b3/99/0f71f7be667c33403fa9706e5b50583ae5106d96fab997fa7e2f38ee8347/jiter-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c", size = 356057 },
{ url = "https://files.pythonhosted.org/packages/8d/50/a82796e421a22b699ee4d2ce527e5bcb29471a2351cbdc931819d941a167/jiter-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18", size = 379790 },
{ url = "https://files.pythonhosted.org/packages/3c/31/10fb012b00f6d83342ca9e2c9618869ab449f1aa78c8f1b2193a6b49647c/jiter-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6", size = 388285 },
{ url = "https://files.pythonhosted.org/packages/c8/81/f15ebf7de57be488aa22944bf4274962aca8092e4f7817f92ffa50d3ee46/jiter-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef", size = 344764 },
{ url = "https://files.pythonhosted.org/packages/b3/e8/0cae550d72b48829ba653eb348cdc25f3f06f8a62363723702ec18e7be9c/jiter-0.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1", size = 376620 },
{ url = "https://files.pythonhosted.org/packages/b8/50/e5478ff9d82534a944c03b63bc217c5f37019d4a34d288db0f079b13c10b/jiter-0.8.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9", size = 510402 },
{ url = "https://files.pythonhosted.org/packages/8e/1e/3de48bbebbc8f7025bd454cedc8c62378c0e32dd483dece5f4a814a5cb55/jiter-0.8.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05", size = 503018 },
{ url = "https://files.pythonhosted.org/packages/d5/cd/d5a5501d72a11fe3e5fd65c78c884e5164eefe80077680533919be22d3a3/jiter-0.8.2-cp313-cp313-win32.whl", hash = "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a", size = 203190 },
{ url = "https://files.pythonhosted.org/packages/51/bf/e5ca301245ba951447e3ad677a02a64a8845b185de2603dabd83e1e4b9c6/jiter-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865", size = 203551 },
{ url = "https://files.pythonhosted.org/packages/2f/3c/71a491952c37b87d127790dd7a0b1ebea0514c6b6ad30085b16bbe00aee6/jiter-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca", size = 308347 },
{ url = "https://files.pythonhosted.org/packages/a0/4c/c02408042e6a7605ec063daed138e07b982fdb98467deaaf1c90950cf2c6/jiter-0.8.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0", size = 342875 },
{ url = "https://files.pythonhosted.org/packages/91/61/c80ef80ed8a0a21158e289ef70dac01e351d929a1c30cb0f49be60772547/jiter-0.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566", size = 202374 },
]
[[package]]
name = "keyboard"
version = "0.13.5"
@ -63,7 +289,7 @@ wheels = [
[[package]]
name = "llmclipboard"
version = "0.1.4"
version = "0.1.5"
source = { virtual = "." }
dependencies = [
{ name = "configparser" },
@ -71,10 +297,13 @@ dependencies = [
{ name = "html2text" },
{ name = "jieba" },
{ name = "keyboard" },
{ name = "openai" },
{ name = "pillow" },
{ name = "pynput" },
{ name = "pyqt6" },
{ name = "pywin32" },
{ name = "qt-material" },
{ name = "requests" },
]
[package.metadata]
@ -84,10 +313,13 @@ requires-dist = [
{ name = "html2text" },
{ name = "jieba" },
{ name = "keyboard" },
{ name = "openai" },
{ name = "pillow" },
{ name = "pynput" },
{ name = "pyqt6" },
{ name = "pywin32" },
{ name = "qt-material" },
{ name = "requests" },
]
[[package]]
@ -148,6 +380,181 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
]
[[package]]
name = "openai"
version = "1.65.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "distro" },
{ name = "httpx" },
{ name = "jiter" },
{ name = "pydantic" },
{ name = "sniffio" },
{ name = "tqdm" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f6/03/0bbf201a7e44920d892db0445874c8111be4255cb9495379df18d6d36ea1/openai-1.65.2.tar.gz", hash = "sha256:729623efc3fd91c956f35dd387fa5c718edd528c4bed9f00b40ef290200fb2ce", size = 359185 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/3b/722ed868cb56f70264190ed479b38b3e46d14daa267d559a3fe3bd9061cf/openai-1.65.2-py3-none-any.whl", hash = "sha256:27d9fe8de876e31394c2553c4e6226378b6ed85e480f586ccfe25b7193fb1750", size = 473206 },
]
[[package]]
name = "pillow"
version = "11.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/50/1c/2dcea34ac3d7bc96a1fd1bd0a6e06a57c67167fec2cff8d95d88229a8817/pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8", size = 3229983 },
{ url = "https://files.pythonhosted.org/packages/14/ca/6bec3df25e4c88432681de94a3531cc738bd85dea6c7aa6ab6f81ad8bd11/pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192", size = 3101831 },
{ url = "https://files.pythonhosted.org/packages/d4/2c/668e18e5521e46eb9667b09e501d8e07049eb5bfe39d56be0724a43117e6/pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2", size = 4314074 },
{ url = "https://files.pythonhosted.org/packages/02/80/79f99b714f0fc25f6a8499ecfd1f810df12aec170ea1e32a4f75746051ce/pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26", size = 4394933 },
{ url = "https://files.pythonhosted.org/packages/81/aa/8d4ad25dc11fd10a2001d5b8a80fdc0e564ac33b293bdfe04ed387e0fd95/pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07", size = 4353349 },
{ url = "https://files.pythonhosted.org/packages/84/7a/cd0c3eaf4a28cb2a74bdd19129f7726277a7f30c4f8424cd27a62987d864/pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482", size = 4476532 },
{ url = "https://files.pythonhosted.org/packages/8f/8b/a907fdd3ae8f01c7670dfb1499c53c28e217c338b47a813af8d815e7ce97/pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e", size = 4279789 },
{ url = "https://files.pythonhosted.org/packages/6f/9a/9f139d9e8cccd661c3efbf6898967a9a337eb2e9be2b454ba0a09533100d/pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269", size = 4413131 },
{ url = "https://files.pythonhosted.org/packages/a8/68/0d8d461f42a3f37432203c8e6df94da10ac8081b6d35af1c203bf3111088/pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49", size = 2291213 },
{ url = "https://files.pythonhosted.org/packages/14/81/d0dff759a74ba87715509af9f6cb21fa21d93b02b3316ed43bda83664db9/pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a", size = 2625725 },
{ url = "https://files.pythonhosted.org/packages/ce/1f/8d50c096a1d58ef0584ddc37e6f602828515219e9d2428e14ce50f5ecad1/pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65", size = 2375213 },
{ url = "https://files.pythonhosted.org/packages/dd/d6/2000bfd8d5414fb70cbbe52c8332f2283ff30ed66a9cde42716c8ecbe22c/pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457", size = 3229968 },
{ url = "https://files.pythonhosted.org/packages/d9/45/3fe487010dd9ce0a06adf9b8ff4f273cc0a44536e234b0fad3532a42c15b/pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35", size = 3101806 },
{ url = "https://files.pythonhosted.org/packages/e3/72/776b3629c47d9d5f1c160113158a7a7ad177688d3a1159cd3b62ded5a33a/pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2", size = 4322283 },
{ url = "https://files.pythonhosted.org/packages/e4/c2/e25199e7e4e71d64eeb869f5b72c7ddec70e0a87926398785ab944d92375/pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070", size = 4402945 },
{ url = "https://files.pythonhosted.org/packages/c1/ed/51d6136c9d5911f78632b1b86c45241c712c5a80ed7fa7f9120a5dff1eba/pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6", size = 4361228 },
{ url = "https://files.pythonhosted.org/packages/48/a4/fbfe9d5581d7b111b28f1d8c2762dee92e9821bb209af9fa83c940e507a0/pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1", size = 4484021 },
{ url = "https://files.pythonhosted.org/packages/39/db/0b3c1a5018117f3c1d4df671fb8e47d08937f27519e8614bbe86153b65a5/pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2", size = 4287449 },
{ url = "https://files.pythonhosted.org/packages/d9/58/bc128da7fea8c89fc85e09f773c4901e95b5936000e6f303222490c052f3/pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96", size = 4419972 },
{ url = "https://files.pythonhosted.org/packages/5f/bb/58f34379bde9fe197f51841c5bbe8830c28bbb6d3801f16a83b8f2ad37df/pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f", size = 2291201 },
{ url = "https://files.pythonhosted.org/packages/3a/c6/fce9255272bcf0c39e15abd2f8fd8429a954cf344469eaceb9d0d1366913/pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761", size = 2625686 },
{ url = "https://files.pythonhosted.org/packages/c8/52/8ba066d569d932365509054859f74f2a9abee273edcef5cd75e4bc3e831e/pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71", size = 2375194 },
{ url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 },
{ url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 },
{ url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 },
{ url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 },
{ url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 },
{ url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 },
{ url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 },
{ url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 },
{ url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 },
{ url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 },
{ url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 },
{ url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 },
{ url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 },
{ url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 },
{ url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 },
{ url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 },
{ url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 },
{ url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 },
{ url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 },
{ url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 },
{ url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 },
{ url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 },
{ url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 },
{ url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 },
{ url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 },
{ url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 },
{ url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 },
{ url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 },
{ url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 },
{ url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 },
{ url = "https://files.pythonhosted.org/packages/fa/c5/389961578fb677b8b3244fcd934f720ed25a148b9a5cc81c91bdf59d8588/pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90", size = 3198345 },
{ url = "https://files.pythonhosted.org/packages/c4/fa/803c0e50ffee74d4b965229e816af55276eac1d5806712de86f9371858fd/pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb", size = 3072938 },
{ url = "https://files.pythonhosted.org/packages/dc/67/2a3a5f8012b5d8c63fe53958ba906c1b1d0482ebed5618057ef4d22f8076/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442", size = 3400049 },
{ url = "https://files.pythonhosted.org/packages/e5/a0/514f0d317446c98c478d1872497eb92e7cde67003fed74f696441e647446/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83", size = 3422431 },
{ url = "https://files.pythonhosted.org/packages/cd/00/20f40a935514037b7d3f87adfc87d2c538430ea625b63b3af8c3f5578e72/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f", size = 3446208 },
{ url = "https://files.pythonhosted.org/packages/28/3c/7de681727963043e093c72e6c3348411b0185eab3263100d4490234ba2f6/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73", size = 3509746 },
{ url = "https://files.pythonhosted.org/packages/41/67/936f9814bdd74b2dfd4822f1f7725ab5d8ff4103919a1664eb4874c58b2f/pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0", size = 2626353 },
]
[[package]]
name = "pydantic"
version = "2.10.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 },
]
[[package]]
name = "pydantic-core"
version = "2.27.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/bc/fed5f74b5d802cf9a03e83f60f18864e90e3aed7223adaca5ffb7a8d8d64/pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa", size = 1895938 },
{ url = "https://files.pythonhosted.org/packages/71/2a/185aff24ce844e39abb8dd680f4e959f0006944f4a8a0ea372d9f9ae2e53/pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c", size = 1815684 },
{ url = "https://files.pythonhosted.org/packages/c3/43/fafabd3d94d159d4f1ed62e383e264f146a17dd4d48453319fd782e7979e/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a", size = 1829169 },
{ url = "https://files.pythonhosted.org/packages/a2/d1/f2dfe1a2a637ce6800b799aa086d079998959f6f1215eb4497966efd2274/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5", size = 1867227 },
{ url = "https://files.pythonhosted.org/packages/7d/39/e06fcbcc1c785daa3160ccf6c1c38fea31f5754b756e34b65f74e99780b5/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c", size = 2037695 },
{ url = "https://files.pythonhosted.org/packages/7a/67/61291ee98e07f0650eb756d44998214231f50751ba7e13f4f325d95249ab/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7", size = 2741662 },
{ url = "https://files.pythonhosted.org/packages/32/90/3b15e31b88ca39e9e626630b4c4a1f5a0dfd09076366f4219429e6786076/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a", size = 1993370 },
{ url = "https://files.pythonhosted.org/packages/ff/83/c06d333ee3a67e2e13e07794995c1535565132940715931c1c43bfc85b11/pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236", size = 1996813 },
{ url = "https://files.pythonhosted.org/packages/7c/f7/89be1c8deb6e22618a74f0ca0d933fdcb8baa254753b26b25ad3acff8f74/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962", size = 2005287 },
{ url = "https://files.pythonhosted.org/packages/b7/7d/8eb3e23206c00ef7feee17b83a4ffa0a623eb1a9d382e56e4aa46fd15ff2/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9", size = 2128414 },
{ url = "https://files.pythonhosted.org/packages/4e/99/fe80f3ff8dd71a3ea15763878d464476e6cb0a2db95ff1c5c554133b6b83/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af", size = 2155301 },
{ url = "https://files.pythonhosted.org/packages/2b/a3/e50460b9a5789ca1451b70d4f52546fa9e2b420ba3bfa6100105c0559238/pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4", size = 1816685 },
{ url = "https://files.pythonhosted.org/packages/57/4c/a8838731cb0f2c2a39d3535376466de6049034d7b239c0202a64aaa05533/pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31", size = 1982876 },
{ url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 },
{ url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 },
{ url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 },
{ url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 },
{ url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 },
{ url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 },
{ url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 },
{ url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 },
{ url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 },
{ url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 },
{ url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 },
{ url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 },
{ url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 },
{ url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 },
{ url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
{ url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
{ url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
{ url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
{ url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
{ url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
{ url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
{ url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
{ url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
{ url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
{ url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
{ url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
{ url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
{ url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
{ url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
{ url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
{ url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
{ url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 },
{ url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 },
{ url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 },
{ url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 },
{ url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 },
{ url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 },
{ url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 },
{ url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 },
{ url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 },
{ url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 },
{ url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
{ url = "https://files.pythonhosted.org/packages/46/72/af70981a341500419e67d5cb45abe552a7c74b66326ac8877588488da1ac/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e", size = 1891159 },
{ url = "https://files.pythonhosted.org/packages/ad/3d/c5913cccdef93e0a6a95c2d057d2c2cba347815c845cda79ddd3c0f5e17d/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8", size = 1768331 },
{ url = "https://files.pythonhosted.org/packages/f6/f0/a3ae8fbee269e4934f14e2e0e00928f9346c5943174f2811193113e58252/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3", size = 1822467 },
{ url = "https://files.pythonhosted.org/packages/d7/7a/7bbf241a04e9f9ea24cd5874354a83526d639b02674648af3f350554276c/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f", size = 1979797 },
{ url = "https://files.pythonhosted.org/packages/4f/5f/4784c6107731f89e0005a92ecb8a2efeafdb55eb992b8e9d0a2be5199335/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133", size = 1987839 },
{ url = "https://files.pythonhosted.org/packages/6d/a7/61246562b651dff00de86a5f01b6e4befb518df314c54dec187a78d81c84/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc", size = 1998861 },
{ url = "https://files.pythonhosted.org/packages/86/aa/837821ecf0c022bbb74ca132e117c358321e72e7f9702d1b6a03758545e2/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50", size = 2116582 },
{ url = "https://files.pythonhosted.org/packages/81/b0/5e74656e95623cbaa0a6278d16cf15e10a51f6002e3ec126541e95c29ea3/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9", size = 2151985 },
{ url = "https://files.pythonhosted.org/packages/63/37/3e32eeb2a451fddaa3898e2163746b0cffbbdbb4740d38372db0490d67f3/pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151", size = 2004715 },
]
[[package]]
name = "pynput"
version = "1.7.8"
@ -2938,6 +3345,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ee/b1/4bdeb054c0a7630a5813ade00db1d75730557c427653b15911e92455738b/qt_material-2.14-py3-none-any.whl", hash = "sha256:ceddd9b3aca167e5d620644ce350c3df4c0220c5df457ab6059f634d7ac747e3", size = 1687961 },
]
[[package]]
name = "requests"
version = "2.32.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
]
[[package]]
name = "six"
version = "1.17.0"
@ -2946,3 +3368,42 @@ sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
[[package]]
name = "tqdm"
version = "4.67.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]
[[package]]
name = "urllib3"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
]