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()