|
"""
|
|
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
|
|
|
|
|
|
class AdvancedInputHandler:
|
|
"""Handles advanced input with editor mode, file inclusion, and image support."""
|
|
|
|
def __init__(self):
|
|
self.editor_mode = False
|
|
|
|
def setup_readline(self):
|
|
"""Setup readline with basic completer."""
|
|
try:
|
|
|
|
def completer(text, state):
|
|
return None
|
|
|
|
readline.set_completer(completer)
|
|
readline.parse_and_bind("tab: complete")
|
|
except:
|
|
pass
|
|
|
|
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 ""
|
|
if user_input.lower() == "/editor":
|
|
self.toggle_editor_mode()
|
|
return self.get_input(prompt)
|
|
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)
|
|
lines.append(line)
|
|
except EOFError:
|
|
break
|
|
content = "\n".join(lines).strip()
|
|
if not content:
|
|
return ""
|
|
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."""
|
|
text = self._process_file_inclusions(text)
|
|
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():
|
|
mime_type, _ = mimetypes.guess_type(str(path))
|
|
if mime_type and (mime_type.startswith("text/") or mime_type in ["application/json", "application/xml"]):
|
|
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"
|
|
elif mime_type and not mime_type.startswith("image/"): # Handle other binary files
|
|
with open(path, "rb") as f:
|
|
binary_data = base64.b64encode(f.read()).decode("utf-8")
|
|
return f"\n--- Binary File: {filename} ({mime_type}) ---\ndata:{mime_type};base64,{binary_data}\n--- End of {filename} ---\n"
|
|
else:
|
|
return f"[File not included (unsupported type or already handled as image): {filename}]"
|
|
else:
|
|
return f"[File not found: {filename}]"
|
|
except Exception as e:
|
|
return f"[Error reading file {filename}: {e}]"
|
|
|
|
pattern = "@\\[([^\\]]+)\\]"
|
|
return re.sub(pattern, replace_file, text)
|
|
|
|
def _process_image_inclusions(self, text: str) -> str:
|
|
"""Process image file references and encode them."""
|
|
words = text.split()
|
|
processed_parts = []
|
|
for word in words:
|
|
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/"):
|
|
with open(path, "rb") as f:
|
|
image_data = base64.b64encode(f.read()).decode("utf-8")
|
|
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)
|
|
|
|
|
|
input_handler = AdvancedInputHandler()
|
|
|
|
|
|
def get_advanced_input(prompt: str = "You> ") -> Optional[str]:
|
|
"""Get advanced input from user."""
|
|
return input_handler.get_input(prompt)
|