From fc11ce8871be323b2e2604032095f581644ba715 Mon Sep 17 00:00:00 2001 From: Mimikko-zeus Date: Wed, 7 Jan 2026 00:47:07 +0800 Subject: [PATCH] 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. --- .env.example | 4 + .gitignore | 38 +++ executor/sandbox_runner.py | 4 +- llm/__pycache__/prompts.cpython-310.pyc | Bin 2920 -> 4425 bytes llm/__pycache__/prompts.cpython-313.pyc | Bin 2979 -> 4506 bytes llm/prompts.py | 98 ++++-- requirements.txt | 7 + ui/__pycache__/chat_view.cpython-310.pyc | Bin 4434 -> 4412 bytes ui/__pycache__/chat_view.cpython-313.pyc | Bin 7224 -> 7224 bytes .../task_guide_view.cpython-310.pyc | Bin 7811 -> 11758 bytes .../task_guide_view.cpython-313.pyc | Bin 22485 -> 19577 bytes ui/task_guide_view.py | 294 +++++++----------- 12 files changed, 237 insertions(+), 208 deletions(-) create mode 100644 .env.example create mode 100644 .gitignore diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7b4d2ff --- /dev/null +++ b/.env.example @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5bf6fe5 --- /dev/null +++ b/.gitignore @@ -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 + diff --git a/executor/sandbox_runner.py b/executor/sandbox_runner.py index 920178c..89b0110 100644 --- a/executor/sandbox_runner.py +++ b/executor/sandbox_runner.py @@ -46,11 +46,13 @@ class SandboxRunner: self.input_dir = self.workspace / "input" self.output_dir = self.workspace / "output" self.logs_dir = self.workspace / "logs" + self.codes_dir = self.workspace / "codes" # 确保目录存在 self.input_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.codes_dir.mkdir(parents=True, exist_ok=True) 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: 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') return task_id, code_path diff --git a/llm/__pycache__/prompts.cpython-310.pyc b/llm/__pycache__/prompts.cpython-310.pyc index 6386c57146ae1cbebbb634607cc064dfde5248dd..c2f75db4aa1590f778d774b29f4e008b98b2fdc9 100644 GIT binary patch delta 2021 zcmZuyT}<0n6b=wd;!$Z+rKwWe>6U5}w1I9@H)&{{)`@mPqlNWHov5}nbsX@T#F>9u zScMWc{!)?xGlc;~G=%`IAfbYmkOZ=)NqgSgp0?p@J5M}LnpAD)+8NMxmU11R?>nE* zIrp4v{qEfxw7B(>prZM+LLwTrEsAK|pTmF>d=kM!@?mGvBe zN+zzP`VD({D?3h6>iu5a+hZ8UscOUt4kWb05^#yFUiKQQM*3A5HwfeIk{V5@_p>-VfM>e2 z!5$#+3_%vBao?C8o7Xos00gt!B{CxGGr-ZfdT&Y{eVQL#0fgqfUWOJ~&M$yxexe5t z_Tkky?p~#W)rb`tUgS70!Svjsl9?`G)u`1a6mr3GA5KN|L`+MJg5ouoz`TC~9JK6+ zcE3}fn}!wa-?9M@>t!5Bbn)a5Fz}szE&C7{@CO(zUXIiTQYV>5f@}HiD40EdpG%}$ zcaXs-jubGF@foTBDybF7%5(-VmIGUT<4$EoUZO?9OGotF1XR^5AW%WxP`|r`7iaa{ zAjyN$7J>^(Dy)vIZSP}KeZ%o4*6ZbNf()M6(89fD;h`TqJm*6|K&&so@gl1CxmuX5 zY#8LZ0OJp|w~<{v-9|HBw7pwyj&1|%WsY}~tku}`ZoSWa$RHFH>o{8SF@Dh$ZEA0- zJM%6$!)-^9Zguf)hGc2h!Q29MI;1}7r=;P>v-^tdo1sp9eL;;rkk^V9ivKBEZYln# zs3xK%Lh4EJZ6MM;J5KwM8w0!_~?$-kCVw-A=TA>-~iS$orv zs`*%pZgTu}A>g8!f_&CsA0TTnxDT6$KUoPs=Yt~(#gMBip;NIqJM9IzdFGJ^+3 zAR8F5UD25DB;(sN25|!}UoHK#+<}-IE-$QY3@y)<`kaQSxfeM59ZsdTvJo{bQ={=NN~erdNQfxVrplSqNKm!XrU;G4`u}VU7e4qlez8q)JRxeoH1vWMA^LOvjZfG zi8M}(gSL{2>a(3nE};&uk)YyxehTV$Rv`tmqJ^$Ta2W-4k^vIXN#tb(v9kVL)1`~f zy881qWP&Oyj#gAvQ3lt8{G7PkrPi<$EWopVv9iL{*CrBp{jQQqYY)>Erf-mtct(_X zzu{vnf<)7dAUJ7G@{92Lw+*Yu#d=>bnRbpxBv21?K`2HCWMZli;Z=SwT1RCbo_W1oL$PgoDXI6jiKj6h~G6qc zoDs$gvWbY=H&}~c!MCT-t>kW@8j)wdkr+WF-zkp1&WH{q@m}~+kzRaet1JPEoGaZA_ptn?y^{2FQ2Y+ tHq?J|{?z&U(--7R)U$*8;qz>@Snbxs@UvO>7u$>Nw!f_JS<7wZ{{q|od!7IQ delta 505 zcmX@9^g>KKpO=@50SH!T#%2ogGB7*_agYHckmCTv#TpZ}Wd)+xQrM%|Q#hhHQaFPd zG`S|G_%e2Hu4NWxoII0d4U;I-W_MOQ#>stbViM2SHN4or;rWym&pNh0Yu@~{an_UF z4NrDz?kNYNb$~UzA;3keHmRpP5%sTEeBP@M7QOr|r8i ziuc$5#JZo6~eAlvPbLYR`GmFbm zPvL3*Dxgu51^LV-$8gwAUdoZ{*1zh>-u)oYENFk)-u86YWGjXIVjYFzjM9?K936#% z#FC7h%p@I!tm6DU9fjoLGKJ@BJGr=?>|XqQLF2O(3!ly14>WvoA*c7`>zoHnL;PL* ztrVWFoA@~ diff --git a/llm/__pycache__/prompts.cpython-313.pyc b/llm/__pycache__/prompts.cpython-313.pyc index adcaa0c8b8bec3442751fce46555a3290fb5e5b9..3a5c4aaaf750e6db55e16c363e927995c74eba4d 100644 GIT binary patch delta 2038 zcmZuyT~HHO6yD`WV5?QZA4J6~&M4S`wmMFSscm)8PNq~)Y@M-o(ij$5O|t3kMnQ*3 zX!$LPfQwK-1|y(oA>e=#NFaUcYw4@g=}d>*-OUr}*q8RDJ$DnqpWd0hd-wc&`<-*o zJ$Gnp-=2NX*R5kH7(c%M6*rYdQA_0G#Yj#po*(Yqw=3rY{WevRb|_g`Q;{yDRb&Y1 z6^xK^>8*mboo7FD{hUNm3~rxLyGGT%22@@vRr7X)=j7#33vPd;eK#MO4}&ri9Ek+l zbb0G?sE=WkyUn<{Nf-1ng@_joNUVj)VV0_`+(lG~w3!LK(134w)Ty8v98_d)Wd07A ziq%euvx0%r-Rz14pWq*kh;nC8>3JIMnFfu8w^~_?#PN0!Jfr>1PfSz#U(3Gas#20$UUyvXrmXzRd$YHM>m@lX#{0&hcLfH<3j7bIk~nJ=-+ zp;mzB9jx6^TSIpBM2&^DqLtmMcwa8oaC909$2A@!5B2E<-9 zTVbwYE%H#R&7_0IGlxUp;y_?I?448sjY{t%A=lNbO-w|>KBazwVVwU!8(ODVie_lZWquW*?J1@QL zQmo5~&LMDiDL*fculD=#{Ow3!PJK9+7x#4$BY~Bif?fB~C(&uKu%c+P@J_n~zdxu; ztIVABHBns73y2UkkuC)+{+|d}wQ5wviF#=H1;s8Mf{-9X5^7he$3+P3L1)WK%8C=k znyAmd2Iw@Ac{%D1Y73pOfDpeo1h0nl&QbJk+(eHb99TGD4oN>}4${<+Sw!rcT;l1h z%1ac@@MCRy1l?GPzQ}I2v314h23wcN?~5SF`=tb~5ICjNcn#L?QCd2R5lr~|L3HC4 z?sd?w9vL7RiC?d5ims5m(@rMifgbs{v^x3^`Fwh<{Bi1D`SDk6(rKM%(PL!BRjmOU%J7YXiA)jXc zy1pK6RvI3$8m3q>P1AoSCDSRf%~7O9P~<8&H2PcKqCVwrUAhD9nTwqeV*&5P9L z*ukW&I~J*|SY}2p9m}MWvd9Ouf778@)}fpui`16bWjZxGcaho@%Vx4q0tQ)Z+3_ot JDXfE^{RefbZ?ymb delta 555 zcmbQGyjWcQGcPX}0}!mxjLj6}Wng#=;=lkSl<`?(qPjd^5nC{O5qmI45l1?wCfCFS zU&ijumCVA7lc%z*VG?E9?96J%sMq;^&nzxwWrZiZcR%e~_CqMxs8c(H%O z^C>Hyb!>muy!mP4tS7r0p6u+Hyq-;$Sx--I@@+OLwdb?fJ>5IiN})WzD7&~IF*#K~ zGq0eugiBZ9#lFc;+jl|aC-bw5tG!&@_j2aSXZ;hNFWJQv{j`7mi{`ygmu!5oW$E)- zEzg=;pSEp{Ej69o#jh|qja`z-nt5^~dxGTig=?NQEPuXh*|WLxLC!SPQ+V3H3aE85 zFQ3`u2oBrH3psM#`d2;KyC3A#1?^AU+n(;4Y^9K2tfNqzQCgCjqoYueSdx*GnWUqT zRh*xvqmW!&rto}iCl}X~-HV?uXneL};j_8>frd}c<@BC>ne%{Yh`)=!mBQ0?^Pcx@ zc-p%C$==B?8+O0gH<3#L2*5-^QD$C=h7u&ip04Y8)-hr7T^_f|YP_|K$&;7xR!+9y zQ=44Fr!n~u5VP`^PHyAxMK+#p~10+5$Gcq!MWMXDy z`5?ru5XE?bLFR)HqnyA62B{AM;*!!A7(_o9F^S7uU=aQwq#$K{fkEtp3nQz9+yw@a U4-#AwRuEcD<|2bo5f9Kn0QV5!o&W#< diff --git a/llm/prompts.py b/llm/prompts.py index c425833..91ca255 100644 --- a/llm/prompts.py +++ b/llm/prompts.py @@ -3,6 +3,39 @@ 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 # ======================================== @@ -11,7 +44,7 @@ INTENT_CLASSIFICATION_SYSTEM = """你是一个意图分类器。判断用户输 规则: - chat: 闲聊、问答、知识查询(如天气、新闻、解释概念) -- execution: 需要操作本地文件的任务(如复制、移动、重命名、整理文件) +- execution: 需要操作本地文件的任务(如复制、移动、重命名、整理、转换文件) 只输出JSON,格式: {"label": "chat或execution", "confidence": 0.0到1.0, "reason": "简短中文理由"}""" @@ -33,21 +66,20 @@ EXECUTION_PLAN_SYSTEM = """你是一个任务规划助手。根据用户需求 4. 绝不修改或删除原始文件 5. 不进行任何网络操作 -输出格式(中文): +输出格式(中文,简洁): ## 任务理解 -[简述用户想做什么] +[一句话简述] ## 执行步骤 1. [步骤1] 2. [步骤2] -... ## 输入输出 -- 输入目录: workspace/input -- 输出目录: workspace/output +- 输入: workspace/input +- 输出: workspace/output -## 风险提示 -[可能失败的情况]""" +## 注意事项 +[可能的问题]""" EXECUTION_PLAN_USER = """用户需求:{user_input} @@ -58,22 +90,24 @@ EXECUTION_PLAN_USER = """用户需求:{user_input} # 代码生成 Prompt # ======================================== -CODE_GENERATION_SYSTEM = """你是一个 Python 代码生成器。根据执行计划生成安全的文件处理代码。 +CODE_GENERATION_SYSTEM = f"""你是一个 Python 代码生成器。根据执行计划生成安全的文件处理代码。 -硬性约束: -1. 只能操作 workspace/input 和 workspace/output 目录 -2. 禁止使用: requests, socket, urllib, subprocess, os.system +【硬性约束 - 必须遵守】 +1. 只能操作 workspace/input(读取)和 workspace/output(写入)目录 +2. 禁止使用: requests, socket, urllib, subprocess, os.system, eval, exec 3. 禁止删除文件: os.remove, shutil.rmtree, os.unlink 4. 禁止访问 workspace 外的任何路径 -5. 只使用标准库: os, shutil, pathlib, json, csv 等 +5. 必须处理异常,打印清晰的错误信息 -代码模板: +{ALLOWED_LIBRARIES} + +【代码模板 - 必须按此格式】 ```python import os import shutil from pathlib import Path -# 工作目录 +# 工作目录(固定,不要修改) WORKSPACE = Path(__file__).parent INPUT_DIR = WORKSPACE / "input" OUTPUT_DIR = WORKSPACE / "output" @@ -82,15 +116,32 @@ def main(): # 确保输出目录存在 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__": main() ``` -只输出 Python 代码,不要其他解释。""" +只输出 Python 代码块,不要其他解释。""" CODE_GENERATION_USER = """执行计划: {execution_plan} @@ -107,14 +158,14 @@ CODE_GENERATION_USER = """执行计划: SAFETY_REVIEW_SYSTEM = """你是一个代码安全审查员。检查代码是否符合安全规范。 检查项: -1. 是否只操作 workspace 目录 -2. 是否有网络请求代码 -3. 是否有危险的文件删除操作 -4. 是否有执行外部命令的代码 +1. 是否只操作 workspace/input 和 workspace/output 目录 +2. 是否有网络请求代码(requests, socket, urllib) +3. 是否有危险的文件删除操作(os.remove, shutil.rmtree) +4. 是否有执行外部命令的代码(subprocess, os.system) 5. 代码逻辑是否与用户需求一致 输出JSON格式: -{"pass": true或false, "reason": "中文审查结论"}""" +{"pass": true或false, "reason": "中文审查结论,一句话"}""" SAFETY_REVIEW_USER = """用户需求:{user_input} @@ -127,4 +178,3 @@ SAFETY_REVIEW_USER = """用户需求:{user_input} ``` 请进行安全审查。""" - diff --git a/requirements.txt b/requirements.txt index 08157af..9c2eec5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,13 @@ # conda activate localagent # pip install -r requirements.txt +# 核心依赖 python-dotenv>=1.0.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 # 文件编码检测 diff --git a/ui/__pycache__/chat_view.cpython-310.pyc b/ui/__pycache__/chat_view.cpython-310.pyc index ad4db0aa909a9e106cc73238baaa9ab2934b39e8..a78f5077ae07d3fa225611ccc4d7b9301f200cb9 100644 GIT binary patch delta 144 zcmV;B0B`@&BD^9EZw(C!00000Ks8-yQ?U(<2LX$dng>xm@4SfRrh+2nq>bf>w&s?H z5nPk>-h~=%I|}sG;S&kF!1qpaB5| ylivx10d$jO3g-cMvrG#*0s()MhYj}umXm)D`~jSkd=HWVqLU90(E(1A;Sh0ECPWkf delta 166 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 literal 7811 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 diff --git a/ui/__pycache__/task_guide_view.cpython-313.pyc b/ui/__pycache__/task_guide_view.cpython-313.pyc index 98063b72acda76a207b0fccf444e806edfc2fa99..af7422e7aacdedbb729fc5849c2748fe944b3276 100644 GIT binary patch delta 6410 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 DMr53p diff --git a/ui/task_guide_view.py b/ui/task_guide_view.py index b69544d..50bcfa3 100644 --- a/ui/task_guide_view.py +++ b/ui/task_guide_view.py @@ -1,6 +1,6 @@ """ 任务引导视图组件 -执行任务的引导式 UI - 支持文件拖拽和 Markdown 渲染 +执行任务的引导式 UI - 简化版 """ import tkinter as tk @@ -8,7 +8,6 @@ from tkinter import scrolledtext, messagebox from tkinter import ttk from typing import Callable, Optional, List from pathlib import Path -import shutil import re @@ -22,20 +21,13 @@ class MarkdownText(tk.Text): def _setup_tags(self): """设置 Markdown 样式标签""" # 标题样式 - self.tag_configure('h1', font=('Microsoft YaHei UI', 14, 'bold'), foreground='#ffd54f', spacing1=10, spacing3=5) - self.tag_configure('h2', font=('Microsoft YaHei UI', 12, 'bold'), foreground='#81c784', spacing1=8, spacing3=4) - self.tag_configure('h3', font=('Microsoft YaHei UI', 11, 'bold'), foreground='#4fc3f7', spacing1=6, spacing3=3) + self.tag_configure('h1', font=('Microsoft YaHei UI', 13, 'bold'), foreground='#ffd54f', 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', 10, 'bold'), foreground='#4fc3f7', spacing1=4, spacing3=2) # 列表样式 - self.tag_configure('bullet', foreground='#ce93d8', lmargin1=20, lmargin2=35) - self.tag_configure('numbered', foreground='#ce93d8', lmargin1=20, lmargin2=35) - - # 代码样式 - 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('bullet', foreground='#ce93d8', lmargin1=15, lmargin2=30) + self.tag_configure('numbered', foreground='#ce93d8', lmargin1=15, lmargin2=30) # 普通文本 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') # 无序列表 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): match = re.match(r'^(\d+\.)\s(.*)$', stripped) 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: - # 处理行内格式 - self._render_inline(line + '\n') - - def _render_inline(self, text: str): - """渲染行内 Markdown(粗体、斜体、代码)""" - # 简化处理:直接插入普通文本 - # 完整实现需要更复杂的解析 - self.insert(tk.END, text, 'normal') + self.insert(tk.END, line + '\n', 'normal') -class DropZone(tk.Frame): - """文件拖拽区域""" +class FileZone(tk.Frame): + """简化的文件区域 - 仅显示打开文件夹按钮""" def __init__( self, @@ -96,81 +81,35 @@ class DropZone(tk.Frame): super().__init__(parent, **kwargs) self.target_dir = target_dir 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._create_widgets(title) - self._setup_drag_drop() - self._refresh_file_list() 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( - title_frame, - text="📂", + color = '#4fc3f7' if self.is_input else '#81c784' + + self.open_btn = tk.Button( + self, + text=f"📂 {title}", font=('Microsoft YaHei UI', 10), bg='#424242', - fg='white', + fg=color, + activebackground='#616161', + activeforeground=color, relief=tk.FLAT, + padx=15, + pady=8, cursor='hand2', 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, text="0 个文件", @@ -179,52 +118,33 @@ class DropZone(tk.Frame): bg='#2d2d2d' ) self.count_label.pack(pady=(0, 5)) - - def _setup_drag_drop(self): - """设置拖拽功能(Windows 需要 windnd 库,这里用简化方案)""" - # 由于 Tkinter 原生不支持文件拖拽,使用点击打开文件夹的方式 - self.drop_label.bind('', lambda e: self._open_folder()) + + self._refresh_count() def _open_folder(self): """打开目标文件夹""" import os os.startfile(str(self.target_dir)) - def _refresh_file_list(self): - """刷新文件列表""" - self.file_listbox.delete(0, tk.END) - + def _refresh_count(self): + """刷新文件计数""" files = list(self.target_dir.glob('*')) 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)} 个文件") + def refresh(self): + """刷新""" + self._refresh_count() + def get_files(self) -> List[Path]: """获取目录中的文件列表""" files = list(self.target_dir.glob('*')) 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: """ - 任务引导视图 - - 小白引导式界面,包含: - - 意图识别结果 - - 文件拖拽区域(输入/输出) - - 执行计划展示(Markdown 渲染) - - 风险提示 - - 执行按钮 + 任务引导视图 - 简化版 """ def __init__( @@ -250,73 +170,72 @@ class TaskGuideView: def _create_widgets(self): """创建 UI 组件""" - # 主框架 + # 主框架 - 使用 Canvas 实现滚动 self.frame = tk.Frame(self.parent, bg='#1e1e1e') # 标题 title_label = tk.Label( self.frame, text="执行任务确认", - font=('Microsoft YaHei UI', 16, 'bold'), + font=('Microsoft YaHei UI', 14, 'bold'), fg='#ffd54f', bg='#1e1e1e' ) - title_label.pack(pady=(10, 15)) + title_label.pack(pady=(5, 10)) - # 上半部分:文件区域 + # 文件区域(横向排列) file_section = tk.Frame(self.frame, bg='#1e1e1e') file_section.pack(fill=tk.X, padx=10, pady=5) # 输入文件区域 input_frame = tk.LabelFrame( file_section, - text=" 📥 输入文件 ", - font=('Microsoft YaHei UI', 11, 'bold'), + text=" 📥 输入 ", + font=('Microsoft YaHei UI', 10, 'bold'), fg='#4fc3f7', bg='#1e1e1e', relief=tk.GROOVE ) input_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5)) - self.input_zone = DropZone( + self.input_zone = FileZone( input_frame, - title="待处理文件", + title="打开输入文件夹", target_dir=self.input_dir, is_input=True, 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_frame.pack(side=tk.LEFT, padx=10) - tk.Label( - arrow_frame, - text="➡️", - font=('Microsoft YaHei UI', 20), + arrow_label = tk.Label( + file_section, + text="→", + font=('Microsoft YaHei UI', 16, 'bold'), fg='#ffd54f', bg='#1e1e1e' - ).pack(pady=30) + ) + arrow_label.pack(side=tk.LEFT, padx=5) # 输出文件区域 output_frame = tk.LabelFrame( file_section, - text=" 📤 输出文件 ", - font=('Microsoft YaHei UI', 11, 'bold'), + text=" 📤 输出 ", + font=('Microsoft YaHei UI', 10, 'bold'), fg='#81c784', bg='#1e1e1e', relief=tk.GROOVE ) output_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0)) - self.output_zone = DropZone( + self.output_zone = FileZone( output_frame, - title="处理结果", + title="打开输出文件夹", target_dir=self.output_dir, is_input=False, 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() @@ -335,142 +254,150 @@ class TaskGuideView: section = tk.LabelFrame( self.frame, text=" 🎯 意图识别 ", - font=('Microsoft YaHei UI', 11, 'bold'), + font=('Microsoft YaHei UI', 10, 'bold'), fg='#81c784', bg='#1e1e1e', 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( section, text="", - font=('Microsoft YaHei UI', 10), + font=('Microsoft YaHei UI', 9), fg='#d4d4d4', bg='#1e1e1e', wraplength=650, 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): """创建执行计划区域(支持 Markdown)""" section = tk.LabelFrame( self.frame, text=" 📄 执行计划 ", - font=('Microsoft YaHei UI', 11, 'bold'), + font=('Microsoft YaHei UI', 10, 'bold'), fg='#ce93d8', bg='#1e1e1e', 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 + text_frame = tk.Frame(section, bg='#2d2d2d') + text_frame.pack(fill=tk.BOTH, expand=True, padx=3, pady=3) + self.plan_text = MarkdownText( - section, + text_frame, wrap=tk.WORD, bg='#2d2d2d', fg='#d4d4d4', relief=tk.FLAT, - height=8, - padx=10, - pady=10 + height=6, + padx=8, + 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) 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): """创建风险提示区域""" section = tk.LabelFrame( self.frame, text=" ⚠️ 安全提示 ", - font=('Microsoft YaHei UI', 11, 'bold'), + font=('Microsoft YaHei UI', 10, 'bold'), fg='#ffb74d', bg='#1e1e1e', 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( section, - text="• 所有操作仅在 workspace 目录内进行\n• 原始文件不会被修改或删除\n• 执行代码已通过安全检查", - font=('Microsoft YaHei UI', 10), + text="• 所有操作仅在 workspace 目录内进行 • 原始文件不会被修改或删除 • 执行代码已通过安全检查", + font=('Microsoft YaHei UI', 9), fg='#d4d4d4', bg='#1e1e1e', 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): """创建按钮区域""" 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( button_frame, - text="🔄 刷新文件", - font=('Microsoft YaHei UI', 10), + text="🔄 刷新", + font=btn_font, + width=btn_width, + height=btn_height, bg='#424242', fg='white', activebackground='#616161', activeforeground='white', relief=tk.FLAT, - padx=15, - pady=5, cursor='hand2', command=self._refresh_all ) - self.refresh_btn.pack(side=tk.LEFT, padx=(0, 10)) - - # 取消按钮 - 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.refresh_btn.pack(side=tk.LEFT) # 执行按钮 self.execute_btn = tk.Button( button_frame, text="🚀 开始执行", - font=('Microsoft YaHei UI', 12, 'bold'), + font=('Microsoft YaHei UI', 10, 'bold'), + width=btn_width, + height=btn_height, bg='#4caf50', fg='white', activebackground='#66bb6a', activeforeground='white', relief=tk.FLAT, - padx=30, - pady=8, cursor='hand2', command=self._on_execute_clicked ) 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): """刷新所有文件列表""" - self.input_zone._refresh_file_list() - self.output_zone._refresh_file_list() + self.input_zone.refresh() + self.output_zone.refresh() def _on_execute_clicked(self): """执行按钮点击""" # 刷新文件列表 - self.input_zone._refresh_file_list() + self.input_zone.refresh() # 检查 input 目录是否有文件 files = self.input_zone.get_files() @@ -489,7 +416,7 @@ class TaskGuideView: def set_intent_result(self, reason: str, confidence: float): """设置意图识别结果""" self.intent_label.config( - text=f"识别结果: 执行任务 (置信度: {confidence:.0%})\n原因: {reason}" + text=f"识别结果: 执行任务 (置信度: {confidence:.0%}) | 原因: {reason}" ) def set_execution_plan(self, plan: str): @@ -505,14 +432,15 @@ class TaskGuideView: state = tk.NORMAL if enabled else tk.DISABLED self.execute_btn.config(state=state) self.cancel_btn.config(state=state) + self.refresh_btn.config(state=state) def refresh_output(self): """刷新输出文件列表""" - self.output_zone._refresh_file_list() + self.output_zone.refresh() 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() def hide(self):