feat: update requirements and enhance task guide UI

- Added core dependencies for file handling (Pillow, openpyxl, python-docx, PyPDF2, chardet) to requirements.txt.
- Modified SandboxRunner to create a dedicated codes directory for task scripts.
- Expanded prompts.py with a list of allowed libraries for code generation.
- Simplified the task guide UI by removing drag-and-drop functionality and enhancing layout and styling for better user experience.
This commit is contained in:
Mimikko-zeus
2026-01-07 00:47:07 +08:00
parent 5fbaa13b38
commit fc11ce8871
12 changed files with 237 additions and 208 deletions

4
.env.example Normal file
View File

@@ -0,0 +1,4 @@
LLM_API_URL=https://api.siliconflow.cn/v1/chat/completions
LLM_API_KEY=sk-fxsxbgatrjjhsnjpkdfgfngukqoqqgitjpxfqfxifcipaqpc
INTENT_MODEL_NAME=Qwen/Qwen2.5-7B-Instruct
GENERATION_MODEL_NAME=Qwen/Qwen2.5-72B-Instruct

38
.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
# Python 编译缓存
__pycache__/
*.py[cod]
*$py.class
*.pyo
*.pyd
# 虚拟环境
.env
.venv/
env/
venv/
ENV/
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# 工作区(运行时生成的文件)
workspace/
# 系统文件
.DS_Store
Thumbs.db
desktop.ini
# 日志
*.log
# 打包
dist/
build/
*.egg-info/
*.spec

View File

@@ -46,11 +46,13 @@ class SandboxRunner:
self.input_dir = self.workspace / "input" self.input_dir = self.workspace / "input"
self.output_dir = self.workspace / "output" self.output_dir = self.workspace / "output"
self.logs_dir = self.workspace / "logs" self.logs_dir = self.workspace / "logs"
self.codes_dir = self.workspace / "codes"
# 确保目录存在 # 确保目录存在
self.input_dir.mkdir(parents=True, exist_ok=True) self.input_dir.mkdir(parents=True, exist_ok=True)
self.output_dir.mkdir(parents=True, exist_ok=True) self.output_dir.mkdir(parents=True, exist_ok=True)
self.logs_dir.mkdir(parents=True, exist_ok=True) self.logs_dir.mkdir(parents=True, exist_ok=True)
self.codes_dir.mkdir(parents=True, exist_ok=True)
def save_task_code(self, code: str, task_id: Optional[str] = None) -> tuple[str, Path]: def save_task_code(self, code: str, task_id: Optional[str] = None) -> tuple[str, Path]:
""" """
@@ -66,7 +68,7 @@ class SandboxRunner:
if not task_id: if not task_id:
task_id = self._generate_task_id() task_id = self._generate_task_id()
code_path = self.workspace / f"task_{task_id}.py" code_path = self.codes_dir / f"task_{task_id}.py"
code_path.write_text(code, encoding='utf-8') code_path.write_text(code, encoding='utf-8')
return task_id, code_path return task_id, code_path

View File

@@ -3,6 +3,39 @@ Prompt 模板集合
所有与 LLM 交互的 Prompt 统一在此管理 所有与 LLM 交互的 Prompt 统一在此管理
""" """
# ========================================
# 可用库列表(用于代码生成约束)
# ========================================
ALLOWED_LIBRARIES = """
可用的 Python 库(只能使用以下库):
标准库:
- os, sys, pathlib - 路径和系统操作
- shutil - 文件复制移动
- json, csv - 数据格式处理
- re - 正则表达式
- datetime - 日期时间
- collections - 集合工具
- itertools - 迭代工具
- hashlib - 哈希计算
- base64 - 编码解码
- zipfile, tarfile - 压缩解压
- glob - 文件匹配
- fnmatch - 文件名匹配
- tempfile - 临时文件
- io - IO操作
- struct - 二进制数据
- math - 数学运算
第三方库:
- PIL/Pillow - 图片处理from PIL import Image
- openpyxl - Excel 处理
- docx - Word 文档处理from docx import Document
- PyPDF2 - PDF 处理
- chardet - 文件编码检测
"""
# ======================================== # ========================================
# 意图识别 Prompt # 意图识别 Prompt
# ======================================== # ========================================
@@ -11,7 +44,7 @@ INTENT_CLASSIFICATION_SYSTEM = """你是一个意图分类器。判断用户输
规则: 规则:
- chat: 闲聊、问答、知识查询(如天气、新闻、解释概念) - chat: 闲聊、问答、知识查询(如天气、新闻、解释概念)
- execution: 需要操作本地文件的任务(如复制、移动、重命名、整理文件) - execution: 需要操作本地文件的任务(如复制、移动、重命名、整理、转换文件)
只输出JSON格式 只输出JSON格式
{"label": "chat或execution", "confidence": 0.0到1.0, "reason": "简短中文理由"}""" {"label": "chat或execution", "confidence": 0.0到1.0, "reason": "简短中文理由"}"""
@@ -33,21 +66,20 @@ EXECUTION_PLAN_SYSTEM = """你是一个任务规划助手。根据用户需求
4. 绝不修改或删除原始文件 4. 绝不修改或删除原始文件
5. 不进行任何网络操作 5. 不进行任何网络操作
输出格式(中文): 输出格式(中文,简洁
## 任务理解 ## 任务理解
[简述用户想做什么] [一句话简述]
## 执行步骤 ## 执行步骤
1. [步骤1] 1. [步骤1]
2. [步骤2] 2. [步骤2]
...
## 输入输出 ## 输入输出
- 输入目录: workspace/input - 输入: workspace/input
- 输出目录: workspace/output - 输出: workspace/output
## 风险提示 ## 注意事项
[可能失败的情况]""" [可能的问题]"""
EXECUTION_PLAN_USER = """用户需求:{user_input} EXECUTION_PLAN_USER = """用户需求:{user_input}
@@ -58,22 +90,24 @@ EXECUTION_PLAN_USER = """用户需求:{user_input}
# 代码生成 Prompt # 代码生成 Prompt
# ======================================== # ========================================
CODE_GENERATION_SYSTEM = """你是一个 Python 代码生成器。根据执行计划生成安全的文件处理代码。 CODE_GENERATION_SYSTEM = f"""你是一个 Python 代码生成器。根据执行计划生成安全的文件处理代码。
硬性约束 硬性约束 - 必须遵守】
1. 只能操作 workspace/input 和 workspace/output 目录 1. 只能操作 workspace/input(读取)和 workspace/output(写入)目录
2. 禁止使用: requests, socket, urllib, subprocess, os.system 2. 禁止使用: requests, socket, urllib, subprocess, os.system, eval, exec
3. 禁止删除文件: os.remove, shutil.rmtree, os.unlink 3. 禁止删除文件: os.remove, shutil.rmtree, os.unlink
4. 禁止访问 workspace 外的任何路径 4. 禁止访问 workspace 外的任何路径
5. 只使用标准库: os, shutil, pathlib, json, csv 等 5. 必须处理异常,打印清晰的错误信息
代码模板: {ALLOWED_LIBRARIES}
【代码模板 - 必须按此格式】
```python ```python
import os import os
import shutil import shutil
from pathlib import Path from pathlib import Path
# 工作目录 # 工作目录(固定,不要修改)
WORKSPACE = Path(__file__).parent WORKSPACE = Path(__file__).parent
INPUT_DIR = WORKSPACE / "input" INPUT_DIR = WORKSPACE / "input"
OUTPUT_DIR = WORKSPACE / "output" OUTPUT_DIR = WORKSPACE / "output"
@@ -82,15 +116,32 @@ def main():
# 确保输出目录存在 # 确保输出目录存在
OUTPUT_DIR.mkdir(exist_ok=True) OUTPUT_DIR.mkdir(exist_ok=True)
# TODO: 实现具体逻辑 # 获取输入文件
input_files = list(INPUT_DIR.glob("*"))
if not input_files:
print("输入目录为空")
return
print("任务完成") success_count = 0
fail_count = 0
for file_path in input_files:
if file_path.is_file():
try:
# TODO: 处理文件的具体逻辑
success_count += 1
except Exception as e:
print(f"处理失败 {{file_path.name}}: {{e}}")
fail_count += 1
print(f"处理完成: 成功 {{success_count}} 个, 失败 {{fail_count}}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()
``` ```
只输出 Python 代码,不要其他解释。""" 只输出 Python 代码,不要其他解释。"""
CODE_GENERATION_USER = """执行计划: CODE_GENERATION_USER = """执行计划:
{execution_plan} {execution_plan}
@@ -107,14 +158,14 @@ CODE_GENERATION_USER = """执行计划:
SAFETY_REVIEW_SYSTEM = """你是一个代码安全审查员。检查代码是否符合安全规范。 SAFETY_REVIEW_SYSTEM = """你是一个代码安全审查员。检查代码是否符合安全规范。
检查项: 检查项:
1. 是否只操作 workspace 目录 1. 是否只操作 workspace/input 和 workspace/output 目录
2. 是否有网络请求代码 2. 是否有网络请求代码requests, socket, urllib
3. 是否有危险的文件删除操作 3. 是否有危险的文件删除操作os.remove, shutil.rmtree
4. 是否有执行外部命令的代码 4. 是否有执行外部命令的代码subprocess, os.system
5. 代码逻辑是否与用户需求一致 5. 代码逻辑是否与用户需求一致
输出JSON格式 输出JSON格式
{"pass": true或false, "reason": "中文审查结论"}""" {"pass": true或false, "reason": "中文审查结论,一句话"}"""
SAFETY_REVIEW_USER = """用户需求:{user_input} SAFETY_REVIEW_USER = """用户需求:{user_input}
@@ -127,4 +178,3 @@ SAFETY_REVIEW_USER = """用户需求:{user_input}
``` ```
请进行安全审查。""" 请进行安全审查。"""

View File

@@ -4,6 +4,13 @@
# conda activate localagent # conda activate localagent
# pip install -r requirements.txt # pip install -r requirements.txt
# 核心依赖
python-dotenv>=1.0.0 python-dotenv>=1.0.0
requests>=2.31.0 requests>=2.31.0
# 文件处理库(代码生成可用)
Pillow>=10.0.0 # 图片处理
openpyxl>=3.1.0 # Excel 处理
python-docx>=1.0.0 # Word 文档处理
PyPDF2>=3.0.0 # PDF 处理
chardet>=5.0.0 # 文件编码检测

View File

@@ -1,6 +1,6 @@
""" """
任务引导视图组件 任务引导视图组件
执行任务的引导式 UI - 支持文件拖拽和 Markdown 渲染 执行任务的引导式 UI - 简化版
""" """
import tkinter as tk import tkinter as tk
@@ -8,7 +8,6 @@ from tkinter import scrolledtext, messagebox
from tkinter import ttk from tkinter import ttk
from typing import Callable, Optional, List from typing import Callable, Optional, List
from pathlib import Path from pathlib import Path
import shutil
import re import re
@@ -22,20 +21,13 @@ class MarkdownText(tk.Text):
def _setup_tags(self): def _setup_tags(self):
"""设置 Markdown 样式标签""" """设置 Markdown 样式标签"""
# 标题样式 # 标题样式
self.tag_configure('h1', font=('Microsoft YaHei UI', 14, 'bold'), foreground='#ffd54f', spacing1=10, spacing3=5) self.tag_configure('h1', font=('Microsoft YaHei UI', 13, 'bold'), foreground='#ffd54f', spacing1=8, spacing3=4)
self.tag_configure('h2', font=('Microsoft YaHei UI', 12, 'bold'), foreground='#81c784', spacing1=8, spacing3=4) self.tag_configure('h2', font=('Microsoft YaHei UI', 11, 'bold'), foreground='#81c784', spacing1=6, spacing3=3)
self.tag_configure('h3', font=('Microsoft YaHei UI', 11, 'bold'), foreground='#4fc3f7', spacing1=6, spacing3=3) self.tag_configure('h3', font=('Microsoft YaHei UI', 10, 'bold'), foreground='#4fc3f7', spacing1=4, spacing3=2)
# 列表样式 # 列表样式
self.tag_configure('bullet', foreground='#ce93d8', lmargin1=20, lmargin2=35) self.tag_configure('bullet', foreground='#ce93d8', lmargin1=15, lmargin2=30)
self.tag_configure('numbered', foreground='#ce93d8', lmargin1=20, lmargin2=35) self.tag_configure('numbered', foreground='#ce93d8', lmargin1=15, lmargin2=30)
# 代码样式
self.tag_configure('code', font=('Consolas', 10), background='#3c3c3c', foreground='#f8f8f2')
# 粗体和斜体
self.tag_configure('bold', font=('Microsoft YaHei UI', 10, 'bold'))
self.tag_configure('italic', font=('Microsoft YaHei UI', 10, 'italic'))
# 普通文本 # 普通文本
self.tag_configure('normal', font=('Microsoft YaHei UI', 10), foreground='#d4d4d4') self.tag_configure('normal', font=('Microsoft YaHei UI', 10), foreground='#d4d4d4')
@@ -64,26 +56,19 @@ class MarkdownText(tk.Text):
self.insert(tk.END, stripped[2:] + '\n', 'h1') self.insert(tk.END, stripped[2:] + '\n', 'h1')
# 无序列表 # 无序列表
elif stripped.startswith('- ') or stripped.startswith('* '): elif stripped.startswith('- ') or stripped.startswith('* '):
self.insert(tk.END, ' ' + stripped[2:] + '\n', 'bullet') self.insert(tk.END, '' + stripped[2:] + '\n', 'bullet')
# 有序列表 # 有序列表
elif re.match(r'^\d+\.\s', stripped): elif re.match(r'^\d+\.\s', stripped):
match = re.match(r'^(\d+\.)\s(.*)$', stripped) match = re.match(r'^(\d+\.)\s(.*)$', stripped)
if match: if match:
self.insert(tk.END, ' ' + match.group(1) + ' ' + match.group(2) + '\n', 'numbered') self.insert(tk.END, match.group(1) + ' ' + match.group(2) + '\n', 'numbered')
# 普通文本 # 普通文本
else: else:
# 处理行内格式 self.insert(tk.END, line + '\n', 'normal')
self._render_inline(line + '\n')
def _render_inline(self, text: str):
"""渲染行内 Markdown粗体、斜体、代码"""
# 简化处理:直接插入普通文本
# 完整实现需要更复杂的解析
self.insert(tk.END, text, 'normal')
class DropZone(tk.Frame): class FileZone(tk.Frame):
"""文件拖拽区域""" """简化的文件区域 - 仅显示打开文件夹按钮"""
def __init__( def __init__(
self, self,
@@ -96,81 +81,35 @@ class DropZone(tk.Frame):
super().__init__(parent, **kwargs) super().__init__(parent, **kwargs)
self.target_dir = target_dir self.target_dir = target_dir
self.is_input = is_input self.is_input = is_input
self.configure(bg='#2d2d2d', relief=tk.GROOVE, bd=2) self.configure(bg='#2d2d2d')
# 确保目录存在 # 确保目录存在
self.target_dir.mkdir(parents=True, exist_ok=True) self.target_dir.mkdir(parents=True, exist_ok=True)
self._create_widgets(title) self._create_widgets(title)
self._setup_drag_drop()
self._refresh_file_list()
def _create_widgets(self, title: str): def _create_widgets(self, title: str):
"""创建组件""" """创建组件"""
# 标题
title_frame = tk.Frame(self, bg='#2d2d2d')
title_frame.pack(fill=tk.X, padx=5, pady=5)
tk.Label(
title_frame,
text=title,
font=('Microsoft YaHei UI', 11, 'bold'),
fg='#4fc3f7' if self.is_input else '#81c784',
bg='#2d2d2d'
).pack(side=tk.LEFT)
# 打开文件夹按钮 # 打开文件夹按钮
open_btn = tk.Button( color = '#4fc3f7' if self.is_input else '#81c784'
title_frame,
text="📂", self.open_btn = tk.Button(
self,
text=f"📂 {title}",
font=('Microsoft YaHei UI', 10), font=('Microsoft YaHei UI', 10),
bg='#424242', bg='#424242',
fg='white', fg=color,
activebackground='#616161',
activeforeground=color,
relief=tk.FLAT, relief=tk.FLAT,
padx=15,
pady=8,
cursor='hand2', cursor='hand2',
command=self._open_folder command=self._open_folder
) )
open_btn.pack(side=tk.RIGHT) self.open_btn.pack(fill=tk.X, padx=5, pady=5)
# 刷新按钮 # 文件计数标签
refresh_btn = tk.Button(
title_frame,
text="🔄",
font=('Microsoft YaHei UI', 10),
bg='#424242',
fg='white',
relief=tk.FLAT,
cursor='hand2',
command=self._refresh_file_list
)
refresh_btn.pack(side=tk.RIGHT, padx=(0, 5))
# 拖拽提示区域
self.drop_label = tk.Label(
self,
text="将文件拖拽到此处\n或点击 📂 打开文件夹",
font=('Microsoft YaHei UI', 10),
fg='#888888',
bg='#3c3c3c',
relief=tk.SUNKEN,
padx=20,
pady=15
)
self.drop_label.pack(fill=tk.X, padx=5, pady=5)
# 文件列表
self.file_listbox = tk.Listbox(
self,
font=('Microsoft YaHei UI', 9),
bg='#2d2d2d',
fg='#d4d4d4',
selectbackground='#0078d4',
relief=tk.FLAT,
height=4
)
self.file_listbox.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 文件计数
self.count_label = tk.Label( self.count_label = tk.Label(
self, self,
text="0 个文件", text="0 个文件",
@@ -179,52 +118,33 @@ class DropZone(tk.Frame):
bg='#2d2d2d' bg='#2d2d2d'
) )
self.count_label.pack(pady=(0, 5)) self.count_label.pack(pady=(0, 5))
def _setup_drag_drop(self): self._refresh_count()
"""设置拖拽功能Windows 需要 windnd 库,这里用简化方案)"""
# 由于 Tkinter 原生不支持文件拖拽,使用点击打开文件夹的方式
self.drop_label.bind('<Button-1>', lambda e: self._open_folder())
def _open_folder(self): def _open_folder(self):
"""打开目标文件夹""" """打开目标文件夹"""
import os import os
os.startfile(str(self.target_dir)) os.startfile(str(self.target_dir))
def _refresh_file_list(self): def _refresh_count(self):
"""刷新文件列表""" """刷新文件计数"""
self.file_listbox.delete(0, tk.END)
files = list(self.target_dir.glob('*')) files = list(self.target_dir.glob('*'))
files = [f for f in files if f.is_file()] files = [f for f in files if f.is_file()]
for f in files:
self.file_listbox.insert(tk.END, f.name)
self.count_label.config(text=f"{len(files)} 个文件") self.count_label.config(text=f"{len(files)} 个文件")
def refresh(self):
"""刷新"""
self._refresh_count()
def get_files(self) -> List[Path]: def get_files(self) -> List[Path]:
"""获取目录中的文件列表""" """获取目录中的文件列表"""
files = list(self.target_dir.glob('*')) files = list(self.target_dir.glob('*'))
return [f for f in files if f.is_file()] return [f for f in files if f.is_file()]
def clear_files(self):
"""清空目录中的文件"""
for f in self.target_dir.glob('*'):
if f.is_file():
f.unlink()
self._refresh_file_list()
class TaskGuideView: class TaskGuideView:
""" """
任务引导视图 任务引导视图 - 简化版
小白引导式界面,包含:
- 意图识别结果
- 文件拖拽区域(输入/输出)
- 执行计划展示Markdown 渲染)
- 风险提示
- 执行按钮
""" """
def __init__( def __init__(
@@ -250,73 +170,72 @@ class TaskGuideView:
def _create_widgets(self): def _create_widgets(self):
"""创建 UI 组件""" """创建 UI 组件"""
# 主框架 # 主框架 - 使用 Canvas 实现滚动
self.frame = tk.Frame(self.parent, bg='#1e1e1e') self.frame = tk.Frame(self.parent, bg='#1e1e1e')
# 标题 # 标题
title_label = tk.Label( title_label = tk.Label(
self.frame, self.frame,
text="执行任务确认", text="执行任务确认",
font=('Microsoft YaHei UI', 16, 'bold'), font=('Microsoft YaHei UI', 14, 'bold'),
fg='#ffd54f', fg='#ffd54f',
bg='#1e1e1e' bg='#1e1e1e'
) )
title_label.pack(pady=(10, 15)) title_label.pack(pady=(5, 10))
# 上半部分:文件区域 # 文件区域(横向排列)
file_section = tk.Frame(self.frame, bg='#1e1e1e') file_section = tk.Frame(self.frame, bg='#1e1e1e')
file_section.pack(fill=tk.X, padx=10, pady=5) file_section.pack(fill=tk.X, padx=10, pady=5)
# 输入文件区域 # 输入文件区域
input_frame = tk.LabelFrame( input_frame = tk.LabelFrame(
file_section, file_section,
text=" 📥 输入文件 ", text=" 📥 输入 ",
font=('Microsoft YaHei UI', 11, 'bold'), font=('Microsoft YaHei UI', 10, 'bold'),
fg='#4fc3f7', fg='#4fc3f7',
bg='#1e1e1e', bg='#1e1e1e',
relief=tk.GROOVE relief=tk.GROOVE
) )
input_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5)) input_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
self.input_zone = DropZone( self.input_zone = FileZone(
input_frame, input_frame,
title="待处理文件", title="打开输入文件",
target_dir=self.input_dir, target_dir=self.input_dir,
is_input=True, is_input=True,
bg='#2d2d2d' bg='#2d2d2d'
) )
self.input_zone.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.input_zone.pack(fill=tk.BOTH, expand=True, padx=3, pady=3)
# 箭头 # 箭头
arrow_frame = tk.Frame(file_section, bg='#1e1e1e') arrow_label = tk.Label(
arrow_frame.pack(side=tk.LEFT, padx=10) file_section,
tk.Label( text="",
arrow_frame, font=('Microsoft YaHei UI', 16, 'bold'),
text="➡️",
font=('Microsoft YaHei UI', 20),
fg='#ffd54f', fg='#ffd54f',
bg='#1e1e1e' bg='#1e1e1e'
).pack(pady=30) )
arrow_label.pack(side=tk.LEFT, padx=5)
# 输出文件区域 # 输出文件区域
output_frame = tk.LabelFrame( output_frame = tk.LabelFrame(
file_section, file_section,
text=" 📤 输出文件 ", text=" 📤 输出 ",
font=('Microsoft YaHei UI', 11, 'bold'), font=('Microsoft YaHei UI', 10, 'bold'),
fg='#81c784', fg='#81c784',
bg='#1e1e1e', bg='#1e1e1e',
relief=tk.GROOVE relief=tk.GROOVE
) )
output_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0)) output_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0))
self.output_zone = DropZone( self.output_zone = FileZone(
output_frame, output_frame,
title="处理结果", title="打开输出文件夹",
target_dir=self.output_dir, target_dir=self.output_dir,
is_input=False, is_input=False,
bg='#2d2d2d' bg='#2d2d2d'
) )
self.output_zone.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.output_zone.pack(fill=tk.BOTH, expand=True, padx=3, pady=3)
# 意图识别结果区域 # 意图识别结果区域
self._create_intent_section() self._create_intent_section()
@@ -335,142 +254,150 @@ class TaskGuideView:
section = tk.LabelFrame( section = tk.LabelFrame(
self.frame, self.frame,
text=" 🎯 意图识别 ", text=" 🎯 意图识别 ",
font=('Microsoft YaHei UI', 11, 'bold'), font=('Microsoft YaHei UI', 10, 'bold'),
fg='#81c784', fg='#81c784',
bg='#1e1e1e', bg='#1e1e1e',
relief=tk.GROOVE relief=tk.GROOVE
) )
section.pack(fill=tk.X, padx=10, pady=5) section.pack(fill=tk.X, padx=10, pady=3)
self.intent_label = tk.Label( self.intent_label = tk.Label(
section, section,
text="", text="",
font=('Microsoft YaHei UI', 10), font=('Microsoft YaHei UI', 9),
fg='#d4d4d4', fg='#d4d4d4',
bg='#1e1e1e', bg='#1e1e1e',
wraplength=650, wraplength=650,
justify=tk.LEFT justify=tk.LEFT
) )
self.intent_label.pack(padx=10, pady=8, anchor=tk.W) self.intent_label.pack(padx=8, pady=5, anchor=tk.W)
def _create_plan_section(self): def _create_plan_section(self):
"""创建执行计划区域(支持 Markdown""" """创建执行计划区域(支持 Markdown"""
section = tk.LabelFrame( section = tk.LabelFrame(
self.frame, self.frame,
text=" 📄 执行计划 ", text=" 📄 执行计划 ",
font=('Microsoft YaHei UI', 11, 'bold'), font=('Microsoft YaHei UI', 10, 'bold'),
fg='#ce93d8', fg='#ce93d8',
bg='#1e1e1e', bg='#1e1e1e',
relief=tk.GROOVE relief=tk.GROOVE
) )
section.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) section.pack(fill=tk.BOTH, expand=True, padx=10, pady=3)
# 使用 Markdown 渲染的 Text # 使用 Markdown 渲染的 Text
text_frame = tk.Frame(section, bg='#2d2d2d')
text_frame.pack(fill=tk.BOTH, expand=True, padx=3, pady=3)
self.plan_text = MarkdownText( self.plan_text = MarkdownText(
section, text_frame,
wrap=tk.WORD, wrap=tk.WORD,
bg='#2d2d2d', bg='#2d2d2d',
fg='#d4d4d4', fg='#d4d4d4',
relief=tk.FLAT, relief=tk.FLAT,
height=8, height=6,
padx=10, padx=8,
pady=10 pady=5
) )
# 添加滚动条 # 添加滚动条
scrollbar = ttk.Scrollbar(section, orient=tk.VERTICAL, command=self.plan_text.yview) scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.plan_text.yview)
self.plan_text.configure(yscrollcommand=scrollbar.set) self.plan_text.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y) scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.plan_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.plan_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
def _create_risk_section(self): def _create_risk_section(self):
"""创建风险提示区域""" """创建风险提示区域"""
section = tk.LabelFrame( section = tk.LabelFrame(
self.frame, self.frame,
text=" ⚠️ 安全提示 ", text=" ⚠️ 安全提示 ",
font=('Microsoft YaHei UI', 11, 'bold'), font=('Microsoft YaHei UI', 10, 'bold'),
fg='#ffb74d', fg='#ffb74d',
bg='#1e1e1e', bg='#1e1e1e',
relief=tk.GROOVE relief=tk.GROOVE
) )
section.pack(fill=tk.X, padx=10, pady=5) section.pack(fill=tk.X, padx=10, pady=3)
self.risk_label = tk.Label( self.risk_label = tk.Label(
section, section,
text="• 所有操作仅在 workspace 目录内进行\n• 原始文件不会被修改或删除\n• 执行代码已通过安全检查", text="• 所有操作仅在 workspace 目录内进行 • 原始文件不会被修改或删除 • 执行代码已通过安全检查",
font=('Microsoft YaHei UI', 10), font=('Microsoft YaHei UI', 9),
fg='#d4d4d4', fg='#d4d4d4',
bg='#1e1e1e', bg='#1e1e1e',
justify=tk.LEFT justify=tk.LEFT
) )
self.risk_label.pack(padx=10, pady=8, anchor=tk.W) self.risk_label.pack(padx=8, pady=5, anchor=tk.W)
def _create_button_section(self): def _create_button_section(self):
"""创建按钮区域""" """创建按钮区域"""
button_frame = tk.Frame(self.frame, bg='#1e1e1e') button_frame = tk.Frame(self.frame, bg='#1e1e1e')
button_frame.pack(fill=tk.X, padx=10, pady=15) button_frame.pack(fill=tk.X, padx=10, pady=10)
# 统一按钮样式
btn_font = ('Microsoft YaHei UI', 10)
btn_width = 12
btn_height = 1
# 刷新文件列表按钮 # 刷新文件列表按钮
self.refresh_btn = tk.Button( self.refresh_btn = tk.Button(
button_frame, button_frame,
text="🔄 刷新文件", text="🔄 刷新",
font=('Microsoft YaHei UI', 10), font=btn_font,
width=btn_width,
height=btn_height,
bg='#424242', bg='#424242',
fg='white', fg='white',
activebackground='#616161', activebackground='#616161',
activeforeground='white', activeforeground='white',
relief=tk.FLAT, relief=tk.FLAT,
padx=15,
pady=5,
cursor='hand2', cursor='hand2',
command=self._refresh_all command=self._refresh_all
) )
self.refresh_btn.pack(side=tk.LEFT, padx=(0, 10)) self.refresh_btn.pack(side=tk.LEFT)
# 取消按钮
self.cancel_btn = tk.Button(
button_frame,
text="取消",
font=('Microsoft YaHei UI', 11),
bg='#616161',
fg='white',
activebackground='#757575',
activeforeground='white',
relief=tk.FLAT,
padx=20,
pady=5,
cursor='hand2',
command=self.on_cancel
)
self.cancel_btn.pack(side=tk.RIGHT, padx=(10, 0))
# 执行按钮 # 执行按钮
self.execute_btn = tk.Button( self.execute_btn = tk.Button(
button_frame, button_frame,
text="🚀 开始执行", text="🚀 开始执行",
font=('Microsoft YaHei UI', 12, 'bold'), font=('Microsoft YaHei UI', 10, 'bold'),
width=btn_width,
height=btn_height,
bg='#4caf50', bg='#4caf50',
fg='white', fg='white',
activebackground='#66bb6a', activebackground='#66bb6a',
activeforeground='white', activeforeground='white',
relief=tk.FLAT, relief=tk.FLAT,
padx=30,
pady=8,
cursor='hand2', cursor='hand2',
command=self._on_execute_clicked command=self._on_execute_clicked
) )
self.execute_btn.pack(side=tk.RIGHT) self.execute_btn.pack(side=tk.RIGHT)
# 取消按钮
self.cancel_btn = tk.Button(
button_frame,
text="取消",
font=btn_font,
width=btn_width,
height=btn_height,
bg='#616161',
fg='white',
activebackground='#757575',
activeforeground='white',
relief=tk.FLAT,
cursor='hand2',
command=self.on_cancel
)
self.cancel_btn.pack(side=tk.RIGHT, padx=(0, 10))
def _refresh_all(self): def _refresh_all(self):
"""刷新所有文件列表""" """刷新所有文件列表"""
self.input_zone._refresh_file_list() self.input_zone.refresh()
self.output_zone._refresh_file_list() self.output_zone.refresh()
def _on_execute_clicked(self): def _on_execute_clicked(self):
"""执行按钮点击""" """执行按钮点击"""
# 刷新文件列表 # 刷新文件列表
self.input_zone._refresh_file_list() self.input_zone.refresh()
# 检查 input 目录是否有文件 # 检查 input 目录是否有文件
files = self.input_zone.get_files() files = self.input_zone.get_files()
@@ -489,7 +416,7 @@ class TaskGuideView:
def set_intent_result(self, reason: str, confidence: float): def set_intent_result(self, reason: str, confidence: float):
"""设置意图识别结果""" """设置意图识别结果"""
self.intent_label.config( self.intent_label.config(
text=f"识别结果: 执行任务 (置信度: {confidence:.0%})\n原因: {reason}" text=f"识别结果: 执行任务 (置信度: {confidence:.0%}) | 原因: {reason}"
) )
def set_execution_plan(self, plan: str): def set_execution_plan(self, plan: str):
@@ -505,14 +432,15 @@ class TaskGuideView:
state = tk.NORMAL if enabled else tk.DISABLED state = tk.NORMAL if enabled else tk.DISABLED
self.execute_btn.config(state=state) self.execute_btn.config(state=state)
self.cancel_btn.config(state=state) self.cancel_btn.config(state=state)
self.refresh_btn.config(state=state)
def refresh_output(self): def refresh_output(self):
"""刷新输出文件列表""" """刷新输出文件列表"""
self.output_zone._refresh_file_list() self.output_zone.refresh()
def show(self): def show(self):
"""显示视图""" """显示视图"""
self.frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) self.frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self._refresh_all() self._refresh_all()
def hide(self): def hide(self):