From df2724cf25ee03728326996a3a6ed4019495cfa9 Mon Sep 17 00:00:00 2001 From: Treadgold Date: Wed, 18 Feb 2026 13:41:39 +1300 Subject: [PATCH] initial commit --- alpha.py | 719 +++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 78 +++++ requirements.txt | Bin 0 -> 194 bytes run.ps1 | 42 +++ run.sh | 80 ++++++ 5 files changed, 919 insertions(+) create mode 100644 alpha.py create mode 100644 readme.md create mode 100644 requirements.txt create mode 100644 run.ps1 create mode 100644 run.sh diff --git a/alpha.py b/alpha.py new file mode 100644 index 0000000..e17a98f --- /dev/null +++ b/alpha.py @@ -0,0 +1,719 @@ +import os +import json +from pathlib import Path +import webview + +html_content = """ + + + + + + +
+ + + +
+ +
+
+ +
+
+
Select a folder from the tree
+
+ +
+
+ Output Preview +
+ + +
+
+
+
+
+
+ + + + +""" + + +class API: + def load_tree(self, root_path): + try: + root = Path(root_path) + if not root.exists() or not root.is_dir(): + return {"error": "Invalid directory path"} + + unreadable = [] + tree = self._build_tree(root, root, "", unreadable) + return {"tree": tree, "unreadable": unreadable} + except Exception as e: + return {"error": str(e)} + + def _build_tree(self, root, path, relative_path, unreadable): + tree = {} + files = [] + # Try to list directory contents; if we can't, record as unreadable and treat as empty + try: + items = list(path.iterdir()) + except (PermissionError, OSError): + rel = relative_path or path.name + unreadable.append(rel.replace("\\", "/")) + return tree + + # Sort items and handle errors per entry so one bad file/dir + # doesn't cause the whole folder to appear empty + for item in sorted(items, key=lambda x: (not x.is_dir(), x.name.lower())): + item_rel = f"{relative_path}/{item.name}" if relative_path else item.name + try: + if item.is_dir(): + tree[item.name] = self._build_tree(root, item, item_rel, unreadable) + else: + files.append(item.name) + except (PermissionError, OSError): + # Skip entries we can't stat or descend into, but record them + unreadable.append(item_rel.replace("\\", "/")) + continue + + if files: + tree['_files'] = files + + return tree + + def generate_output(self, root_path, selection_state): + try: + root = Path(root_path) + if not root.exists(): + return {"error": "Invalid directory path"} + + output = [] + self._collect_files(root, root, selection_state, output) + + content = "\n\n".join(output) + return {"content": content} + except Exception as e: + return {"error": str(e)} + + def _collect_files(self, root, current_path, selection_state, output): + relative_path = str(current_path.relative_to(root)).replace('\\', '/') + # We always walk the directory tree, but will only include files + # whose effective state is "selected". + + try: + items = list(current_path.iterdir()) + except (PermissionError, OSError): + return + + for item in sorted(items, key=lambda x: (not x.is_dir(), x.name.lower())): + item_relative = str(item.relative_to(root)).replace('\\', '/') + item_state = self._get_effective_state(item_relative, selection_state) + + try: + if item.is_dir(): + self._collect_files(root, item, selection_state, output) + elif item.is_file(): + # Only include files that are effectively selected + if item_state != 'selected': + continue + try: + with open(item, 'r', encoding='utf-8', errors='ignore') as f: + content = f.read() + + file_entry = f"=== {item_relative} ===\n{content}" + output.append(file_entry) + except Exception: + # Skip files we can't open/read + pass + except (PermissionError, OSError): + # Skip entries we can't stat or descend into + continue + + def _get_effective_state(self, path, selection_state): + if path in selection_state: + return selection_state[path] + + # Check parent states + parts = path.split('/') + # range(len(parts) - 1, 0, -1) stops before 0. + # We need it to include 0 to check '' (root) + for i in range(len(parts) - 1, -1, -1): + parent_path = '/'.join(parts[:i]) + if parent_path in selection_state: + state = selection_state[parent_path] + if state in ['selected', 'unselected']: + return state + + # Logical default: no explicit or inherited selection + return 'default' + + +def main(): + api = API() + window = webview.create_window( + 'File Tree Selector', + html=html_content, + js_api=api, + width=1200, + height=800 + ) + webview.start() + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..bcd28e6 --- /dev/null +++ b/readme.md @@ -0,0 +1,78 @@ +# File Tree Selector + +A desktop GUI tool for browsing a directory tree, selecting files/folders, and exporting their contents as a single concatenated text block — useful for feeding code into LLMs. + +## What it does + +- Displays a navigable file tree for any directory you specify +- Green checkbox = include in output, red = exclude +- Selection cascades: mark a folder green and all its children are included unless individually excluded +- Unreadable/permission-denied files are shown with strikethrough and cannot be selected +- "Generate" concatenates all selected files into a preview pane; "Copy All" puts it on your clipboard + +## Requirements + +- Python 3.8+ +- `pywebview` and its platform dependencies (see below) + +### Linux (additional system packages required) + +pywebview on Linux requires GTK and WebKit2 — these cannot be installed via pip: + +```bash +# Ubuntu/Debian +sudo apt install python3-gi python3-gi-cairo gir1.2-webkit2-4.0 + +# If on a newer distro (Ubuntu 24+), try: +sudo apt install python3-gi python3-gi-cairo gir1.2-webkit2-4.1 +``` + +### Windows + +No additional system packages required. pywebview uses the built-in WebView2 runtime (included with Windows 10/11). + +### macOS + +No additional system packages required. pywebview uses the built-in WebKit. + +## Running the app + +### Linux / macOS + +```bash +bash run.sh +``` + +The script will create a `.venv`, install Python dependencies, and launch the app. On Linux, it checks for GTK dependencies first and exits with instructions if they're missing. + +### Windows + +```powershell +.\run.ps1 +``` + +Run from PowerShell. If you get an execution policy error: + +```powershell +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned +``` + +## Project structure + +``` +. +├── alpha.py # Main application (latest version) +├── run.sh # Setup + launch script for Linux/macOS +├── run.ps1 # Setup + launch script for Windows +├── requirements.txt # Python dependencies +└── README.md +``` + +## Usage + +1. Launch the app +2. Type (or paste) an absolute path into the path bar and press Enter +3. The left panel shows the folder tree; click a folder to view its contents on the right +4. Click a checkbox to toggle include/exclude — red = excluded, green = included +5. Use the root folder checkbox to include/exclude everything at once, then carve out exceptions +6. Click **Generate** to preview the output, then **Copy All** to copy to clipboard \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e9126268af558923f6f343a1c13934b8f721b46 GIT binary patch literal 194 zcmYL?Sqgw47=-6J=qMek#TEz^lgi6ipC!Z(GR}Hmotz@K%BWVQUL&^@yegn+a literal 0 HcmV?d00001 diff --git a/run.ps1 b/run.ps1 new file mode 100644 index 0000000..3636aa5 --- /dev/null +++ b/run.ps1 @@ -0,0 +1,42 @@ +# PowerShell script to ensure virtual environment exists, install dependencies, and run alpha.py + +# Get the script directory +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +Set-Location $scriptDir + +# Path to virtual environment activation script +$venvActivate = ".venv\Scripts\Activate.ps1" + +# Function to create virtual environment and install dependencies +function Setup-Venv { + Write-Host "Creating virtual environment..." -ForegroundColor Green + python -m venv .venv + + if (-Not (Test-Path $venvActivate)) { + Write-Host "Error: Failed to create virtual environment." -ForegroundColor Red + exit 1 + } + + Write-Host "Activating virtual environment..." -ForegroundColor Green + & $venvActivate + + if (Test-Path "requirements.txt") { + Write-Host "Installing dependencies from requirements.txt..." -ForegroundColor Green + pip install --upgrade pip + pip install -r requirements.txt + } else { + Write-Host "No requirements.txt found, skipping dependency installation." -ForegroundColor Yellow + } +} + +# Check if virtual environment exists +if (Test-Path $venvActivate) { + Write-Host "Activating virtual environment..." -ForegroundColor Green + & $venvActivate +} else { + Setup-Venv +} + +# Run the application +Write-Host "Running alpha.py..." -ForegroundColor Green +python alpha.py diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..d180c17 --- /dev/null +++ b/run.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# Cross-platform helper to set up .venv, install dependencies, and run alpha.py + +set -e + +# Get the directory of the script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +VENV_DIR=".venv" + +# List of essential Python packages +REQUIRED_PACKAGES=("pywebview") + +# Function to check Linux GTK dependencies +check_gtk_linux() { + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Try importing gi in python + python3 - </dev/null +try: + import gi +except ImportError: + import sys + sys.exit(1) +EOF + if [[ $? -ne 0 ]]; then + echo -e "\e[31mError: GTK dependencies missing on Linux.\e[0m" + echo "Install them with:" + echo " sudo apt install python3-gi python3-gi-cairo gir1.2-webkit2-4.0" + exit 1 + fi + fi +} + +# Function to create virtual environment and install dependencies +setup_venv() { + echo -e "\e[32mCreating virtual environment...\e[0m" + python3 -m venv "$VENV_DIR" + + if [ ! -f "$VENV_DIR/bin/activate" ]; then + echo -e "\e[31mError: Failed to create virtual environment.\e[0m" + exit 1 + fi + + echo -e "\e[32mActivating virtual environment...\e[0m" + source "$VENV_DIR/bin/activate" + + # Upgrade pip first + pip install --upgrade pip + + # Install pywebview and other dependencies + for pkg in "${REQUIRED_PACKAGES[@]}"; do + pip install "$pkg" + done + + # Install packages from requirements.txt if it exists + if [ -f "requirements.txt" ]; then + echo -e "\e[32mInstalling dependencies from requirements.txt...\e[0m" + pip install -r requirements.txt + else + echo -e "\e[33mNo requirements.txt found, skipping additional dependencies.\e[0m" + fi + + echo -e "\e[32mVirtual environment ready.\e[0m" +} + +# 1️⃣ Check GTK dependencies on Linux +check_gtk_linux + +# 2️⃣ Activate existing venv or create one +if [ -f "$VENV_DIR/bin/activate" ]; then + echo -e "\e[32mActivating existing virtual environment...\e[0m" + source "$VENV_DIR/bin/activate" +else + setup_venv +fi + +# 3️⃣ Run the application +echo -e "\e[32mRunning alpha.py...\e[0m" +python alpha.py