163b12493b
Per spec FR1 + Phase 1.4 + architecture feedback (data != view): - Data classes DiffHunk, DiffFile -> src/patch_modal.py (alongside PendingPatch; all patch-domain data) - Operations parse_diff/parse_hunk_header/get_line_color/apply_patch_to_file (called by gui_2) -> src/gui_2.py - GUI is a pure view; data lives elsewhere; no new files per AGENTS.md Tests: tests/test_diff_viewer.py imports from src.gui_2 (parse_diff/apply_patch_to_file) and src.patch_modal (DiffFile/DiffHunk).
124 lines
3.7 KiB
Python
124 lines
3.7 KiB
Python
from dataclasses import dataclass, field
|
|
from typing import Optional, Callable, List
|
|
|
|
|
|
@dataclass
|
|
class DiffHunk:
|
|
header: str
|
|
lines: List[str]
|
|
old_start: int
|
|
old_count: int
|
|
new_start: int
|
|
new_count: int
|
|
|
|
@dataclass
|
|
class DiffFile:
|
|
old_path: str
|
|
new_path: str
|
|
hunks: List[DiffHunk] = field(default_factory=list)
|
|
|
|
@dataclass
|
|
class PendingPatch:
|
|
patch_text: str = ""
|
|
file_paths: List[str] = field(default_factory=list)
|
|
generated_by: str = ""
|
|
timestamp: float = 0.0
|
|
|
|
EMPTY_PATCH: PendingPatch = PendingPatch()
|
|
|
|
class PatchModalManager:
|
|
def __init__(self):
|
|
self._pending_patch: PendingPatch = EMPTY_PATCH
|
|
self._show_modal: bool = False
|
|
self._on_apply_callback: Optional[Callable[[str], bool]] = None
|
|
self._on_reject_callback: Optional[Callable[[], None]] = None
|
|
|
|
def request_patch_approval(self, patch_text: str, file_paths: List[str], generated_by: str = "Tier 4 QA") -> bool:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_close_modal, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
|
|
"""
|
|
from time import time
|
|
self._pending_patch = PendingPatch(
|
|
patch_text=patch_text,
|
|
file_paths=file_paths,
|
|
generated_by=generated_by,
|
|
timestamp=time()
|
|
)
|
|
self._show_modal = True
|
|
return True
|
|
|
|
def get_pending_patch(self) -> "PendingPatch":
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_patch_modal_manager_init, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
|
|
"""
|
|
return self._pending_patch
|
|
|
|
def is_modal_shown(self) -> bool:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_close_modal, tests/test_patch_modal.py:test_patch_modal_manager_init, tests/test_patch_modal.py:test_reject_patch, tests/test_patch_modal.py:test_request_patch_approval, tests/test_patch_modal.py:test_reset]
|
|
"""
|
|
return self._show_modal
|
|
|
|
def set_apply_callback(self, callback: Callable[[str], bool]) -> None:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_apply_callback, tests/test_patch_modal.py:test_reset]
|
|
"""
|
|
self._on_apply_callback = callback
|
|
|
|
def set_reject_callback(self, callback: Callable[[], None]) -> None:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reset]
|
|
"""
|
|
self._on_reject_callback = callback
|
|
|
|
def apply_patch(self, patch_text: str) -> bool:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_apply_callback]
|
|
"""
|
|
if self._on_apply_callback:
|
|
return self._on_apply_callback(patch_text)
|
|
return False
|
|
|
|
def reject_patch(self) -> None:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_reject_callback, tests/test_patch_modal.py:test_reject_patch]
|
|
"""
|
|
self._pending_patch = EMPTY_PATCH
|
|
self._show_modal = False
|
|
if self._on_reject_callback:
|
|
self._on_reject_callback()
|
|
|
|
def close_modal(self) -> None:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_close_modal]
|
|
"""
|
|
self._show_modal = False
|
|
|
|
def reset(self) -> None:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_reset]
|
|
"""
|
|
self._pending_patch = EMPTY_PATCH
|
|
self._show_modal = False
|
|
self._on_apply_callback = None
|
|
self._on_reject_callback = None
|
|
|
|
_patch_modal_manager: Optional[PatchModalManager] = None
|
|
|
|
def get_patch_modal_manager() -> PatchModalManager:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_get_patch_modal_manager_singleton]
|
|
"""
|
|
global _patch_modal_manager
|
|
if _patch_modal_manager is None:
|
|
_patch_modal_manager = PatchModalManager()
|
|
return _patch_modal_manager
|
|
|
|
def reset_patch_modal_manager() -> None:
|
|
"""
|
|
[C: tests/test_patch_modal.py:test_get_patch_modal_manager_singleton]
|
|
"""
|
|
global _patch_modal_manager
|
|
if _patch_modal_manager:
|
|
_patch_modal_manager.reset()
|
|
_patch_modal_manager = None |