#!/usr/bin/env python3 """ Advanced input handler for PR Assistant with editor mode, file inclusion, and image support. """ import base64 import mimetypes import re import readline from pathlib import Path from typing import Optional # from pr.ui.colors import Colors # Avoid import issues class AdvancedInputHandler: """Handles advanced input with editor mode, file inclusion, and image support.""" def __init__(self): self.editor_mode = False self.setup_readline() def setup_readline(self): """Setup readline with basic completer.""" try: # Simple completer that doesn't interfere def completer(text, state): return None readline.set_completer(completer) readline.parse_and_bind("tab: complete") except: pass # Readline not available def toggle_editor_mode(self): """Toggle between simple and editor input modes.""" self.editor_mode = not self.editor_mode mode = "Editor" if self.editor_mode else "Simple" print(f"\nSwitched to {mode.lower()} input mode.") def get_input(self, prompt: str = "You> ") -> Optional[str]: """Get input from user, handling different modes.""" try: if self.editor_mode: return self._get_editor_input(prompt) else: return self._get_simple_input(prompt) except KeyboardInterrupt: return None except EOFError: return None def _get_simple_input(self, prompt: str) -> Optional[str]: """Get simple input with file completion.""" try: user_input = input(prompt).strip() if not user_input: return "" # Check for special commands if user_input.lower() == "/editor": self.toggle_editor_mode() return self.get_input(prompt) # Recurse to get new input # Process file inclusions and images processed_input = self._process_input(user_input) return processed_input except KeyboardInterrupt: return None def _get_editor_input(self, prompt: str) -> Optional[str]: """Get multi-line input for editor mode.""" try: print("Editor mode: Enter your message. Type 'END' on a new line to finish.") print("Type '/simple' to switch back to simple mode.") lines = [] while True: try: line = input() if line.strip().lower() == "end": break elif line.strip().lower() == "/simple": self.toggle_editor_mode() return self.get_input(prompt) # Switch back and get input lines.append(line) except EOFError: break content = "\n".join(lines).strip() if not content: return "" # Process file inclusions and images processed_content = self._process_input(content) return processed_content except KeyboardInterrupt: return None def _process_input(self, text: str) -> str: """Process input text for file inclusions and images.""" # Process @[filename] inclusions text = self._process_file_inclusions(text) # Process image inclusions (look for image file paths) text = self._process_image_inclusions(text) return text def _process_file_inclusions(self, text: str) -> str: """Replace @[filename] with file contents.""" def replace_file(match): filename = match.group(1).strip() try: path = Path(filename).expanduser().resolve() if path.exists() and path.is_file(): with open(path, encoding="utf-8", errors="replace") as f: content = f.read() return f"\n--- File: {filename} ---\n{content}\n--- End of {filename} ---\n" else: return f"[File not found: {filename}]" except Exception as e: return f"[Error reading file {filename}: {e}]" # Replace @[filename] patterns pattern = r"@\[([^\]]+)\]" return re.sub(pattern, replace_file, text) def _process_image_inclusions(self, text: str) -> str: """Process image file references and encode them.""" # Find potential image file paths words = text.split() processed_parts = [] for word in words: # Check if it's a file path that exists and is an image try: path = Path(word.strip()).expanduser().resolve() if path.exists() and path.is_file(): mime_type, _ = mimetypes.guess_type(str(path)) if mime_type and mime_type.startswith("image/"): # Encode image with open(path, "rb") as f: image_data = base64.b64encode(f.read()).decode("utf-8") # Replace with data URL processed_parts.append( f"[Image: {path.name}]\ndata:{mime_type};base64,{image_data}\n" ) continue except: pass processed_parts.append(word) return " ".join(processed_parts) # Global instance input_handler = AdvancedInputHandler() def get_advanced_input(prompt: str = "You> ") -> Optional[str]: """Get advanced input from user.""" return input_handler.get_input(prompt)