Last active
August 25, 2025 00:25
-
-
Save Goatghosts/d5d8e9dc1373eb29b3a7a4580b3a9d48 to your computer and use it in GitHub Desktop.
Простой помощник для быстрого создания промптов из содержимого каталога.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import pickle | |
import threading | |
import tkinter as tk | |
from tkinter import filedialog, scrolledtext, messagebox | |
import logging | |
import fnmatch | |
CONFIG_FILE = os.path.join(os.path.dirname(__file__), ".prompt_gui.pkl") | |
logging.basicConfig( | |
level=logging.INFO, | |
format="%(asctime)s [%(levelname)s]: %(message)s", | |
datefmt="%Y-%m-%d %H:%M:%S", | |
) | |
logger = logging.getLogger("prompt_gui") | |
def load_cfg(): | |
if os.path.exists(CONFIG_FILE): | |
with open(CONFIG_FILE, "rb") as f: | |
cfg = pickle.load(f) | |
logger.info(f"Загружена конфигурация: {cfg}") | |
return cfg | |
cfg = {"dir": "", "exts": [".ts", ".svelte"], "bl": []} | |
logger.info(f"Создана новая конфигурация: {cfg}") | |
return cfg | |
def save_cfg(cfg): | |
with open(CONFIG_FILE, "wb") as f: | |
pickle.dump(cfg, f) | |
logger.info(f"Конфигурация сохранена: {cfg}") | |
def dir_in_blacklist(rel_root, bl): | |
if rel_root in (".", ""): | |
return False | |
parts = rel_root.split("/") | |
for pat in bl: | |
if fnmatch.fnmatch(rel_root, pat): | |
return True | |
for part in parts: | |
if fnmatch.fnmatch(part, pat) or part == pat: | |
return True | |
return False | |
def file_in_blacklist(rel_path, file_name, bl, bl_set): | |
if file_name in bl_set or rel_path in bl_set: | |
return True | |
for pat in bl: | |
if fnmatch.fnmatch(file_name, pat) or fnmatch.fnmatch(rel_path, pat): | |
return True | |
return False | |
def generate_prompt(path, exts, bl): | |
output = [] | |
bl_set = set(bl) | |
count_files = 0 | |
for root, dirs, files in os.walk(path): | |
rel_root = os.path.relpath(root, path).replace("\\", "/") | |
if dir_in_blacklist(rel_root, bl): | |
logger.info(f"Пропущена папка по чёрному списку: {rel_root}") | |
continue | |
for file in files: | |
rel_path = file if rel_root in (".", "") else f"{rel_root}/{file}" | |
if file_in_blacklist(rel_path, file, bl, bl_set): | |
logger.info(f"Пропущен файл по чёрному списку: {rel_path}") | |
continue | |
if any(file.endswith(ext) for ext in exts): | |
count_files += 1 | |
full_path = os.path.abspath(os.path.join(root, file)).replace("\\", "/") | |
output.append(full_path) | |
output.append("```") | |
try: | |
with open(full_path, encoding="utf-8") as f: | |
for line in f: | |
if not line.lstrip().startswith("//"): | |
output.append(line.rstrip("\n")) | |
except Exception as e: | |
logger.error(f"Ошибка чтения {full_path}: {e}") | |
output.append("```") | |
output.append("") | |
result = "\n".join(output) | |
logger.info(f"Сгенерировано файлов: {count_files}, длина текста: {len(result)} символов") | |
return result | |
class App: | |
def __init__(self, root): | |
self.root = root | |
root.title("Prompt Builder") | |
root.geometry("950x600") | |
logger.info("Запуск приложения") | |
self.cfg = load_cfg() | |
tk.Label(root, text="Директория проекта:").pack(anchor="w", padx=5) | |
self.dir_var = tk.StringVar(value=self.cfg["dir"]) | |
dir_frm = tk.Frame(root) | |
dir_frm.pack(fill="x", padx=5, pady=2) | |
tk.Entry(dir_frm, textvariable=self.dir_var).pack(side="left", fill="x", expand=True) | |
tk.Button(dir_frm, text="📁", command=self.choose_dir).pack(side="left", padx=2) | |
main_frm = tk.Frame(root) | |
main_frm.pack(fill="both", expand=True, padx=5, pady=5) | |
self.exts = self.create_column(main_frm, "Типы файлов", self.cfg["exts"]) | |
self.bl = self.create_column(main_frm, "Чёрный список (папки/файлы)", self.cfg["bl"]) | |
self.text = scrolledtext.ScrolledText(main_frm, wrap="none") | |
self.text.grid(row=0, column=2, sticky="nsew", rowspan=2, padx=(5, 0)) | |
main_frm.columnconfigure(2, weight=3) | |
main_frm.rowconfigure(0, weight=1) | |
btn_frm = tk.Frame(root) | |
btn_frм = btn_frm | |
btn_frm.pack(fill="x", padx=5, pady=5) | |
tk.Button(btn_frm, text="Сгенерировать", command=self.generate).pack(side="left", expand=True, fill="x") | |
tk.Button(btn_frm, text="Копировать", command=self.copy).pack(side="left", expand=True, fill="x") | |
self.dir_var.trace_add("write", self.autosave) | |
def create_column(self, parent, title, items): | |
frm = tk.Frame(parent) | |
frm.grid(sticky="nswe", padx=2) | |
parent.columnconfigure(parent.grid_size()[0] - 1, weight=1) | |
tk.Label(frm, text=title).pack(anchor="w") | |
list_frm = tk.Frame(frm) | |
list_frm.pack(fill="both", expand=True) | |
lb = tk.Listbox(list_frm) | |
lb.pack(side="left", fill="both", expand=True) | |
sb = tk.Scrollbar(list_frm, command=lb.yview) | |
sb.pack(side="right", fill="y") | |
lb.config(yscrollcommand=sb.set) | |
for i in items: | |
lb.insert("end", i) | |
entry_frm = tk.Frame(frm) | |
entry_frm.pack(fill="x") | |
entry = tk.Entry(entry_frm) | |
entry.pack(side="left", fill="x", expand=True) | |
tk.Button(entry_frm, text="➕", command=lambda: self.add_item(lb, entry)).pack(side="left") | |
tk.Button(frm, text="❌ Удалить выбранное", command=lambda: self.del_item(lb)).pack(fill="x", pady=2) | |
lb.bind("<<ListboxSelect>>", lambda e: self.autosave()) | |
return lb | |
def add_item(self, lb, entry): | |
txt = entry.get().strip() | |
if txt: | |
if lb == self.exts and not txt.startswith("."): | |
txt = "." + txt | |
lb.insert("end", txt) | |
entry.delete(0, "end") | |
logger.info(f"Добавлен элемент: {txt}") | |
self.autosave() | |
def del_item(self, lb): | |
sel = lb.curselection() | |
if sel: | |
txt = lb.get(sel[0]) | |
lb.delete(sel[0]) | |
logger.info(f"Удалён элемент: {txt}") | |
self.autosave() | |
def choose_dir(self): | |
p = filedialog.askdirectory() | |
if p: | |
self.dir_var.set(p) | |
logger.info(f"Выбрана директория: {p}") | |
def generate(self): | |
path = self.dir_var.get() | |
exts = self.exts.get(0, "end") | |
bl = self.bl.get(0, "end") | |
if not path or not os.path.isdir(path): | |
messagebox.showwarning("Ошибка", "Укажите корректный путь") | |
logger.warning("Попытка генерации с некорректным путём") | |
return | |
self.text.delete("1.0", "end") | |
logger.info(f"Запуск генерации для {path}, расширения: {exts}, blacklist: {bl}") | |
threading.Thread(target=self.worker, args=(path, exts, bl), daemon=True).start() | |
def worker(self, p, e, bl): | |
result = generate_prompt(p, e, bl) | |
self.text.after(0, lambda: self.text.insert("end", result)) | |
logger.info("Генерация завершена успешно") | |
def copy(self): | |
txt = self.text.get("1.0", "end") | |
self.root.clipboard_clear() | |
self.root.clipboard_append(txt) | |
logger.info(f"Скопировано {len(txt.strip())} символов") | |
def autosave(self, *a): | |
self.cfg["dir"] = self.dir_var.get() | |
self.cfg["exts"] = self.exts.get(0, "end") | |
self.cfg["bl"] = self.bl.get(0, "end") | |
save_cfg(self.cfg) | |
if __name__ == "__main__": | |
root = tk.Tk() | |
App(root) | |
root.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment