171 lines
5.7 KiB
Python
Raw Normal View History

2025-11-04 05:57:23 +01:00
#!/usr/bin/env python3
"""
Advanced input handler for PR Assistant with editor mode, file inclusion, and image support.
"""
import base64
import mimetypes
2025-11-04 08:09:12 +01:00
import re
2025-11-04 05:57:23 +01:00
import readline
from pathlib import Path
from typing import Optional
2025-11-04 08:09:12 +01:00
2025-11-04 05:57:23 +01:00
# 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)
2025-11-04 08:09:12 +01:00
readline.parse_and_bind("tab: complete")
2025-11-04 05:57:23 +01:00
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
2025-11-04 08:09:12 +01:00
if user_input.lower() == "/editor":
2025-11-04 05:57:23 +01:00
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:
2025-11-04 08:10:37 +01:00
print("Editor mode: Enter your message. Type 'END' on a new line to finish.")
2025-11-04 05:57:23 +01:00
print("Type '/simple' to switch back to simple mode.")
lines = []
while True:
try:
line = input()
2025-11-04 08:09:12 +01:00
if line.strip().lower() == "end":
2025-11-04 05:57:23 +01:00
break
2025-11-04 08:09:12 +01:00
elif line.strip().lower() == "/simple":
2025-11-04 05:57:23 +01:00
self.toggle_editor_mode()
return self.get_input(prompt) # Switch back and get input
lines.append(line)
except EOFError:
break
2025-11-04 08:09:12 +01:00
content = "\n".join(lines).strip()
2025-11-04 05:57:23 +01:00
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."""
2025-11-04 08:09:12 +01:00
2025-11-04 05:57:23 +01:00
def replace_file(match):
filename = match.group(1).strip()
try:
path = Path(filename).expanduser().resolve()
if path.exists() and path.is_file():
2025-11-04 08:09:12 +01:00
with open(path, encoding="utf-8", errors="replace") as f:
2025-11-04 05:57:23 +01:00
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
2025-11-04 08:09:12 +01:00
pattern = r"@\[([^\]]+)\]"
2025-11-04 05:57:23 +01:00
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))
2025-11-04 08:09:12 +01:00
if mime_type and mime_type.startswith("image/"):
2025-11-04 05:57:23 +01:00
# Encode image
2025-11-04 08:09:12 +01:00
with open(path, "rb") as f:
image_data = base64.b64encode(f.read()).decode("utf-8")
2025-11-04 05:57:23 +01:00
# Replace with data URL
2025-11-04 08:09:12 +01:00
processed_parts.append(
f"[Image: {path.name}]\ndata:{mime_type};base64,{image_data}\n"
)
2025-11-04 05:57:23 +01:00
continue
except:
pass
processed_parts.append(word)
2025-11-04 08:09:12 +01:00
return " ".join(processed_parts)
2025-11-04 05:57:23 +01:00
# 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)