From 7e384beff63acd09dda8bfc6bd9815450ad62a6d Mon Sep 17 00:00:00 2001 From: Tobia Rigon <42972324+TobiaRigon@users.noreply.github.com> Date: Tue, 15 Jul 2025 14:37:42 +0200 Subject: [PATCH] feat: support chunked output and json export --- README.md | 8 ++++++-- promptpack/gui.py | 13 ++++++++++--- promptpack/settings.py | 1 + promptpack/utils.py | 33 +++++++++++++++++++++++++++++---- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index aed161d..516c27a 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ - Saves and loads user settings to/from a JSON file - Option to format output as Markdown with code blocks, headings, and file separators - Switch between dark and light mode, con pulsanti e campi scuri quando il tema è impostato su "dark" +- Supports chunked export quando il totale supera il limite di token impostato +- Possibilità di esportare anche in JSON strutturato ## Requirements @@ -50,6 +52,7 @@ Il file `promptpack.py` avvia semplicemente l'applicazione. - Markdown output - Include file headings - Use code blocks for each file + - Export JSON 4. **Live Preview**: Enables a real-time preview of the final output file. 5. **Destination Folder**: Choose where the final file will be saved. 6. **Generate**: Creates a Markdown or plain text file containing the selected source files, formatted according to your settings. @@ -65,7 +68,8 @@ User preferences are saved in a file named `promptpack_settings.json` in the sam "excluded_files": [".env", "README.md"], "as_markdown": true, "include_heading": true, - "use_code_block": true + "use_code_block": true, + "export_json": false } ``` @@ -97,7 +101,7 @@ body { - Hidden folders and ignored files are shown but deselected by default. - All preview and configuration windows inherit the custom icon (`promptpack.ico`), if available. - La finestra "Select Files to Include" adotta uno sfondo scuro quando è attivo il tema dark. -- Per evitare rallentamenti, l'anteprima e l'esportazione interrompono la raccolta dei contenuti al raggiungimento di 200 000 token. +- Per evitare rallentamenti, l'anteprima interrompe la raccolta dei contenuti al raggiungimento di 200 000 token. L'esportazione divide automaticamente l'output in più file quando viene superata questa soglia. ## License diff --git a/promptpack/gui.py b/promptpack/gui.py index f3b3132..38d3b3d 100644 --- a/promptpack/gui.py +++ b/promptpack/gui.py @@ -42,6 +42,7 @@ def __init__(self, root: tk.Tk): self.include_heading = tk.BooleanVar(value=self.settings["include_heading"]) self.use_code_block = tk.BooleanVar(value=self.settings["use_code_block"]) self.theme = tk.StringVar(value=self.settings.get("theme", "dark")) + self.export_json = tk.BooleanVar(value=self.settings.get("export_json", False)) self.enable_preview = tk.BooleanVar(value=False) self.start_folder = tk.StringVar() @@ -238,6 +239,7 @@ def prompt_list(title, key): ttk.Checkbutton(win, text="Markdown Format", variable=self.as_markdown).pack(pady=5) ttk.Checkbutton(win, text="Include File Headings", variable=self.include_heading).pack(pady=5) ttk.Checkbutton(win, text="Use Code Blocks", variable=self.use_code_block).pack(pady=5) + ttk.Checkbutton(win, text="Export JSON", variable=self.export_json).pack(pady=5) ttk.Label(win, text="Theme", style="Heading.TLabel").pack(padx=10, pady=(20, 5)) @@ -250,6 +252,7 @@ def save_and_close(): "as_markdown": self.as_markdown.get(), "include_heading": self.include_heading.get(), "use_code_block": self.use_code_block.get(), + "export_json": self.export_json.get(), "theme": self.theme.get(), } save_settings(new_settings) @@ -457,7 +460,9 @@ def get_preview_text(self, included_files): preview_lines = self.generate_preview_lines(self.start_folder.get(), included_files) full_text = "\n".join(preview_lines) token_count = estimate_token_count(full_text) - header = f"Token estimate: {token_count}\n{'='*40}\n" + max_tokens = self.settings.get("max_tokens", 200000) + remaining = max_tokens - token_count + header = f"Token estimate: {token_count} | Remaining: {remaining}\n{'='*40}\n" return header + full_text def generate_preview_lines(self, start_folder, included_files): @@ -503,7 +508,7 @@ def generate(self): messagebox.showerror("Error", "No files selected") return try: - output_path = generate_output( + output_files = generate_output( self.start_folder.get(), self.dest_folder.get(), list(self.selected_files), @@ -511,7 +516,9 @@ def generate(self): self.include_heading.get(), self.use_code_block.get(), self.settings.get("max_tokens", 200000), + self.export_json.get(), ) - messagebox.showinfo("Done", f"File generated: {output_path}") + msg = "\n".join(str(p) for p in output_files) + messagebox.showinfo("Done", f"File generated:\n{msg}") except Exception as e: messagebox.showerror("Error", str(e)) diff --git a/promptpack/settings.py b/promptpack/settings.py index f7fa2ec..8477e17 100644 --- a/promptpack/settings.py +++ b/promptpack/settings.py @@ -14,6 +14,7 @@ "theme": "dark", # Numero massimo di token da elaborare in anteprima o export "max_tokens": 200_000, + "export_json": False, } diff --git a/promptpack/utils.py b/promptpack/utils.py index 22cf994..dfa7230 100644 --- a/promptpack/utils.py +++ b/promptpack/utils.py @@ -1,4 +1,5 @@ import os +import json from pathlib import Path from datetime import datetime from tempfile import NamedTemporaryFile @@ -36,13 +37,27 @@ def generate_output( include_heading: bool, use_code_block: bool, max_tokens: int = 200000, + export_json: bool = False, ): lines = [] + json_files = [] project_name = Path(start_folder).name date_str = datetime.now().strftime('%Y%m%d') header = f"Project: {project_name} - {date_str}\n\n" lines.append(header) token_count = estimate_token_count(header) + chunk_index = 1 + output_paths = [] + + def write_chunk(data, index): + ext = 'md' if as_markdown else 'txt' + suffix = f"-{index}" if index > 1 else "" + out_path = Path(dest_folder) / f"{project_name}-{date_str}{suffix}.{ext}" + out_path.write_text(''.join(data), encoding='utf-8') + output_paths.append(out_path) + return [header] + + json_entries = [] for path in included_files: try: @@ -61,11 +76,21 @@ def generate_output( block = ''.join(new_lines) block_tokens = estimate_token_count(block) if token_count + block_tokens > max_tokens: - break + lines = write_chunk(lines, chunk_index) + chunk_index += 1 + token_count = estimate_token_count(header) lines.append(block) token_count += block_tokens + json_entries.append({"path": str(rel_path), "content": content}) + + if lines: + write_chunk(lines, chunk_index) + + if export_json: + json_path = Path(dest_folder) / f"{project_name}-{date_str}.json" + with open(json_path, "w", encoding="utf-8") as f: + json.dump({"project": project_name, "date": date_str, "files": json_entries}, f, indent=2) + output_paths.append(json_path) - output_file = Path(dest_folder) / f"{project_name}-{date_str}.{ 'md' if as_markdown else 'txt' }" - output_file.write_text(''.join(lines), encoding='utf-8') - return output_file + return output_paths