feat(hot-reload): Complete Phase 2 refactor and document UI delegation pattern
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
- **Comprehensive Logging:** Aggressively log all actions, API payloads, tool calls, and executed scripts. Maintain timestamped JSON-L and markdown logs to ensure total transparency and debuggability.
|
||||
- **Mandatory ImGui Verification:** All changes to the GUI (`gui_2.py`) MUST be verified using the custom AST linter (`scripts/check_imgui_scopes.py`) to ensure all ImGui scopes (begin/end, push/pop) are properly matched. Developers should prioritize the use of `src/imgui_scopes.py` context managers (`imscope`) over manual push/pop calls.
|
||||
- **Modular Controller Pattern:** To prevent "God Object" bloat in core controllers (like `AppController`), all state-independent or utility logic must be moved to module-level functions. Functions requiring class state should accept the instance as an explicit dependency (`def logic(controller: AppController, ...)`). Massive `if/elif` dispatch blocks must be refactored into handler maps (dictionaries) of module-level functions.
|
||||
- **UI Delegation for Hot-Reload:** All complex ImGui rendering logic must be extracted from the `App` class into module-level functions named `render_xxx(app: App)`. The `App` class should only contain thin delegation wrappers (`def _render_xxx(self): render_xxx(self)`). This architecture is mandatory for supporting state-preserving hot-reloads of the UI logic.
|
||||
- **Dependency Minimalism:** Limit external dependencies where possible. For instance, prefer standard library modules (like `urllib` and `html.parser` for web tools) over heavy third-party packages.
|
||||
|
||||
## Phase 5: Heavy Curation & Structural Integrity (MANDATORY)
|
||||
|
||||
@@ -19,6 +19,7 @@ For deep implementation details when planning or implementing tracks, consult `d
|
||||
- **Full Control over Vendor APIs:** Exposing detailed API metrics and configuring deep agent capabilities directly within the GUI.
|
||||
- **Context & Memory Management:** Better visualization and management of token usage and context memory. Features an independent **Context Composition** panel decoupled from the project whitelist, with directory-grouped listings and per-file **View Modes** (Full, Summary, Skeleton, Outline, None). Includes a **Visual Slice Editor** for creating fuzzy-anchored line ranges with **Annotations** (tags and comments), and **View Presets** for saving named configurations of view settings. Features a dedicated **'Context' role** for manual injections and **Context Presets** for saving and loading complete compositions. Allows assigning specific context presets to MMA agent personas for granular cognitive load isolation.
|
||||
- **Manual "Vibe Coding" Assistant:** Serving as an auxiliary, multi-provider assistant that natively interacts with the codebase via sandboxed PowerShell scripts and MCP-like file tools, emphasizing manual developer oversight and explicit confirmation.
|
||||
- **State-Preserving Hot Reload:** Supports selective, manual hot-reloading of Python modules (including the main GUI logic) via a delegation-based architecture. This allows for rapid UI iteration without losing application state or restarting the session.
|
||||
|
||||
## Key Features
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
- **Hybrid Shader Pipeline:** Utilizes an optimized `ImDrawList`-based batching technique to simulate UI effects (shadows, soft borders) instead of complex GPU post-processing, eliminating the overhead of heavy GPU-resident shaders.
|
||||
- **Interface-Driven Development (IDD):** Enforces a "Stub-and-Resolve" pattern where cross-module dependencies are resolved by generating signatures/contracts before implementation.
|
||||
- **UI Concern Isolation:** Enforces a strict separation between the GUI layer (Tkinter/ImGui) and the business logic (AppController). All platform-native UI actions, such as file and directory selection, are handled exclusively within the GUI layer.
|
||||
- **UI Delegation Pattern:** Employs module-level rendering functions decoupled from the `App` class. This enables selective hot-reloading of UI logic by swapping function references at runtime while maintaining a stable state object (`app: App`).
|
||||
- **Manual Hot-Reload Pipeline:** Implements a `HotReloader` utility that manages module invalidation and state preservation, triggered by keyboard shortcuts (Ctrl+Alt+R) or GUI controls.
|
||||
|
||||
ter/ImGui) and the business logic (AppController). All platform-native UI actions, such as file and directory selection, are handled exclusively within the GUI layer.
|
||||
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ This file tracks all major tracks for the project. Each track has its own detail
|
||||
|
||||
---
|
||||
|
||||
2. [ ] **Track: Hot Reload Python Codebase (Phase 2)**
|
||||
2. [x] **Track: Hot Reload Python Codebase (Phase 2)**
|
||||
*Link: [./tracks/hot_reload_python_20260516/](./tracks/hot_reload_python_20260516/)*
|
||||
*Goal: Implement selective, state-preserving hot-reload for src/gui_2.py with delegation pattern refactor, manual trigger via Ctrl+Alt+R and GUI button, and visual error tint feedback on failure.*
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
from __future__ import annotations
|
||||
import ast
|
||||
import sys
|
||||
import re
|
||||
|
||||
def transform_file(file_path: str) -> None:
|
||||
"""Refactors App._render_xxx methods to module-level functions."""
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
lines = f.read().splitlines()
|
||||
tree = ast.parse("\n".join(lines))
|
||||
app_class = next((n for n in tree.body if isinstance(n, ast.ClassDef) and n.name == "App"), None)
|
||||
if not app_class: return
|
||||
render_methods = [
|
||||
m for m in app_class.body
|
||||
if isinstance(m, ast.FunctionDef) and m.name.startswith("_render_") and m.name != "_render_window_if_open"
|
||||
]
|
||||
render_names = {m.name for m in render_methods}
|
||||
render_methods.sort(key=lambda x: x.lineno, reverse=True)
|
||||
extracted = []
|
||||
for m in render_methods:
|
||||
s_idx = m.lineno - 1
|
||||
e_idx = m.end_lineno
|
||||
if m.decorator_list: s_idx = m.decorator_list[0].lineno - 1
|
||||
m_lines = lines[s_idx:e_idx]
|
||||
func_name = m.name.lstrip("_")
|
||||
processed = []
|
||||
for l in m_lines:
|
||||
processed.append(l[1:] if l.startswith(" ") else l)
|
||||
def_line_idx = next((i for i, l in enumerate(processed) if l.strip().startswith("def ")), -1)
|
||||
if def_line_idx != -1:
|
||||
l = processed[def_line_idx]
|
||||
l = l.replace(f"def {m.name}(", f"def {func_name}(", 1)
|
||||
l = re.sub(r"\bself\b", "app: App", l, count=1)
|
||||
processed[def_line_idx] = l
|
||||
for i in range(def_line_idx + 1, len(processed)):
|
||||
for m_name in render_names:
|
||||
t_func = m_name.lstrip("_")
|
||||
# Replace self._render_xxx( with render_xxx(app,
|
||||
# then fix up (app, ) if it was empty.
|
||||
processed[i] = processed[i].replace(f"self.{m_name}(", f"{t_func}(app, ")
|
||||
processed[i] = processed[i].replace(f"(app, )", "(app)")
|
||||
processed[i] = re.sub(r"(?<![a-zA-Z0-9_])self\.", "app.", processed[i])
|
||||
processed[i] = re.sub(r"(?<![a-zA-Z0-9_])self(?![a-zA-Z0-9_:])", "app", processed[i])
|
||||
extracted.append("\n".join(processed))
|
||||
arg_names = [a.arg for a in m.args.args if a.arg != "self"]
|
||||
kw_names = [a.arg for a in m.args.kwonlyargs]
|
||||
call_args = ["self"] + arg_names + [f"{k}={k}" for k in kw_names]
|
||||
orig_def_line = m_lines[0]
|
||||
if m.decorator_list:
|
||||
for l in m_lines:
|
||||
if l.strip().startswith("def "):
|
||||
orig_def_line = l
|
||||
break
|
||||
wrapper = [orig_def_line, f" {func_name}({', '.join(call_args)})"]
|
||||
lines[s_idx:e_idx] = wrapper
|
||||
extracted.reverse()
|
||||
final = "\n".join(lines) + "\n\n" + "\n\n".join(extracted) + "\n"
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(final)
|
||||
print(f"Transformed {file_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1: transform_file(sys.argv[1])
|
||||
+4138
-3860
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user