Skip to content

Instantly share code, notes, and snippets.

@Goatghosts
Last active August 25, 2025 00:25
Show Gist options
  • Save Goatghosts/d5d8e9dc1373eb29b3a7a4580b3a9d48 to your computer and use it in GitHub Desktop.
Save Goatghosts/d5d8e9dc1373eb29b3a7a4580b3a9d48 to your computer and use it in GitHub Desktop.
Простой помощник для быстрого создания промптов из содержимого каталога.
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