Private
Public Access
0
0

reading more code, slight adjustment to ast structual file editor ux (radio buttons going off viewport)

This commit is contained in:
2026-06-13 11:08:45 -04:00
parent 394020e50c
commit b61a2db01d
3 changed files with 136 additions and 168 deletions
+24 -39
View File
@@ -46,17 +46,12 @@ from src.warmup import WarmupManager
def parse_symbols(text: str) -> list[str]:
"""
Finds all occurrences of '@SymbolName' in text and returns SymbolName.
"""Finds all occurrences of '@SymbolName' in text and returns SymbolName.
SymbolName can be a function, class, or method (e.g. @MyClass, @my_func, @MyClass.my_method).
[C: tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_basic, tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_edge_cases, tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_methods, tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_mixed, tests/test_symbol_lookup.py:TestSymbolLookup.test_parse_symbols_no_symbols]
"""
return re.findall(r"@([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)", text)
def get_symbol_definition(symbol: str, files: list[str]) -> tuple[str, str, int] | None:
"""
[C: tests/test_symbol_lookup.py:TestSymbolLookup.test_get_symbol_definition_found, tests/test_symbol_lookup.py:TestSymbolLookup.test_get_symbol_definition_not_found]
"""
for file_path in files:
result = mcp_client.py_get_symbol_info(file_path, symbol)
if isinstance(result, tuple):
@@ -66,9 +61,6 @@ def get_symbol_definition(symbol: str, files: list[str]) -> tuple[str, str, int]
class ConfirmDialog:
def __init__(self, script: str, base_dir: str) -> None:
"""
[C: src/mcp_client.py:_DDGParser.__init__, src/mcp_client.py:_TextExtractor.__init__]
"""
self._uid = str(uuid.uuid4())
self._script = str(script) if script is not None else ""
self._base_dir = str(base_dir) if base_dir is not None else ""
@@ -1657,10 +1649,7 @@ class AppController:
self._pending_gui_tasks.append({'action': 'set_tool_log_dirty'})
def _process_pending_gui_tasks(self) -> None:
"""
Processes pending GUI tasks from the queue on the main render thread.
[C: src/gui_2.py:App._render_main_interface, tests/test_api_hook_extensions.py:test_app_processes_new_actions, tests/test_gui_updates.py:test_gui_updates_on_event, tests/test_live_gui_integration_v2.py:test_user_request_error_handling, tests/test_live_gui_integration_v2.py:test_user_request_integration_flow, tests/test_mma_orchestration_gui.py:test_handle_ai_response_fallback, tests/test_mma_orchestration_gui.py:test_handle_ai_response_with_stream_id, tests/test_mma_orchestration_gui.py:test_process_pending_gui_tasks_mma_spawn_approval, tests/test_mma_orchestration_gui.py:test_process_pending_gui_tasks_show_track_proposal, tests/test_process_pending_gui_tasks.py:test_gcli_path_updates_adapter, tests/test_process_pending_gui_tasks.py:test_process_pending_gui_tasks_drag, tests/test_process_pending_gui_tasks.py:test_process_pending_gui_tasks_right_click, tests/test_process_pending_gui_tasks.py:test_redundant_calls_in_process_pending_gui_tasks]
"""
"""Processes pending GUI tasks from the queue on the main render thread."""
now = time.time()
if hasattr(self, 'event_queue') and hasattr(self.event_queue, 'websocket_server') and self.event_queue.websocket_server:
if now - self._last_telemetry_time >= 1.0:
@@ -1809,52 +1798,48 @@ class AppController:
with self._disc_entries_lock: self.disc_entries[:] = models.parse_history_entries(disc_data.get("history", []), self.disc_roles)
# UI state
self.ui_output_dir = self.project.get("output", {}).get("output_dir", "./md_gen")
self.ui_files_base_dir = self.project.get("files", {}).get("base_dir", ".")
self.ui_shots_base_dir = self.project.get("screenshots", {}).get("base_dir", ".")
self.ui_output_dir = self.project.get("output", {}).get("output_dir", "./md_gen")
self.ui_files_base_dir = self.project.get("files", {}).get("base_dir", ".")
self.ui_shots_base_dir = self.project.get("screenshots", {}).get("base_dir", ".")
proj_meta = self.project.get("project", {})
self.ui_project_git_dir = proj_meta.get("git_dir", "")
self.ui_project_conductor_dir = self.project.get('conductor', {}).get('dir', 'conductor')
self.ui_project_system_prompt = proj_meta.get("system_prompt", "")
self.ui_gemini_cli_path = self.project.get("gemini_cli", {}).get("binary_path", "gemini")
self.ui_project_git_dir = proj_meta.get("git_dir", "")
self.ui_project_conductor_dir = self.project.get('conductor', {}).get('dir', 'conductor')
self.ui_project_system_prompt = proj_meta.get("system_prompt", "")
self.ui_gemini_cli_path = self.project.get("gemini_cli", {}).get("binary_path", "gemini")
self._update_gcli_adapter(self.ui_gemini_cli_path)
self.ui_word_wrap = proj_meta.get("word_wrap", True)
self.ui_auto_add_history = disc_sec.get("auto_add", False)
self.ui_global_system_prompt = self.config.get("ai", {}).get("system_prompt", "")
self.ui_base_system_prompt = self.config.get("ai", {}).get("base_system_prompt", "")
self.ui_word_wrap = proj_meta.get("word_wrap", True)
self.ui_auto_add_history = disc_sec.get("auto_add", False)
self.ui_global_system_prompt = self.config.get("ai", {}).get("system_prompt", "")
self.ui_base_system_prompt = self.config.get("ai", {}).get("base_system_prompt", "")
self.ui_use_default_base_prompt = self.config.get("ai", {}).get("use_default_base_prompt", True)
self.ui_project_context_marker = proj_meta.get("context_marker", "")
self.ui_project_context_marker = proj_meta.get("context_marker", "")
self.preset_manager = presets.PresetManager(Path(self.active_project_path).parent if self.active_project_path else None)
self.presets = self.preset_manager.load_all()
self.preset_manager = presets.PresetManager(Path(self.active_project_path).parent if self.active_project_path else None)
self.presets = self.preset_manager.load_all()
self.tool_preset_manager = tool_presets.ToolPresetManager(Path(self.active_project_path).parent if self.active_project_path else None)
self.tool_presets = self.tool_preset_manager.load_all_presets()
self.bias_profiles = self.tool_preset_manager.load_all_bias_profiles()
self.tool_presets = self.tool_preset_manager.load_all_presets()
self.bias_profiles = self.tool_preset_manager.load_all_bias_profiles()
mcp_path = self.project.get('project', {}).get('mcp_config_path') or self.config.get('ai', {}).get('mcp_config_path')
if mcp_path:
mcp_p = Path(mcp_path)
if not mcp_p.is_absolute() and self.active_project_path:
mcp_p = Path(self.active_project_path).parent / mcp_path
if mcp_p.exists():
self.mcp_config = models.load_mcp_config(str(mcp_p))
else:
self.mcp_config = models.MCPConfiguration()
if mcp_p.exists(): self.mcp_config = models.load_mcp_config(str(mcp_p))
else: self.mcp_config = models.MCPConfiguration()
else:
self.mcp_config = models.MCPConfiguration()
rag_data = self.config.get('rag')
if rag_data:
self.rag_config = models.RAGConfig.from_dict(rag_data)
else:
self.rag_config = models.RAGConfig()
if rag_data: self.rag_config = models.RAGConfig.from_dict(rag_data)
else: self.rag_config = models.RAGConfig()
self.rag_engine = None
if self.rag_config.enabled:
self._sync_rag_engine()
if self.rag_config.enabled: self._sync_rag_engine()
from src.personas import PersonaManager
self.persona_manager = PersonaManager(Path(self.active_project_path).parent if self.active_project_path else None)
from src.vendor_capabilities import get_capabilities
try:
caps = get_capabilities(self.current_provider, self.current_model)
+111 -128
View File
@@ -147,9 +147,9 @@ def C_NUM() -> imgui.ImVec4: return theme.get_color("status_success")
def C_TRM() -> imgui.ImVec4: return theme.get_color("text_disabled")
def C_SUB() -> imgui.ImVec4: return theme.get_color("text_disabled")
DIR_COLORS: dict[str, typing.Callable[[], imgui.ImVec4]] = {"OUT": C_OUT, "IN": C_IN}
DIR_COLORS: dict[str, typing.Callable[[], imgui.ImVec4]] = {"OUT": C_OUT, "IN": C_IN}
KIND_COLORS: dict[str, typing.Callable[[], imgui.ImVec4]] = {"request": C_REQ, "response": C_RES, "tool_call": C_TC, "tool_result": C_TR, "tool_result_send": C_TRS}
HEAVY_KEYS: set[str] = {"message", "text", "script", "output", "content"}
HEAVY_KEYS: set[str] = {"message", "text", "script", "output", "content"}
def render_text_viewer(app: App, label: str, content: str, text_type: str = 'text', force_open: bool = False, id_suffix: str = "") -> None:
if imgui.button(f"[+]##{id_suffix or str(id(content))}") or force_open:
@@ -3579,13 +3579,11 @@ def render_context_composition_panel(app: App) -> None:
render_context_screenshots(app)
def render_ast_inspector_modal(app: App) -> None:
"""
Renders the 'Structural File Editor' modal dialog.
"""Renders the 'Structural File Editor' modal dialog.
Provides an interactive tree-sitter AST inspector to mask classes, methods, or
functions and extract custom annotated slices (Definition, Signature, Hide).
SSDL Shape:
`[B:open_modal?] -> [Q:outline] -> [I:render_ast_tree]`
SSDL: `[B:open_modal?] -> [Q:outline] -> [I:render_ast_tree]`
ASCII Layout Map:
+-------------------------------------------------------------+
@@ -3622,14 +3620,14 @@ def render_ast_inspector_modal(app: App) -> None:
proj_dir = str(Path(app.controller.active_project_path).parent.resolve()) if getattr(app, 'controller', None) and app.controller.active_project_path else None
mcp_client.configure([{"path": f_path}], [proj_dir] if proj_dir else None)
if f_path.lower().endswith('.py'): outline = mcp_client.py_get_code_outline(f_path)
elif f_path.lower().endswith(('.c', '.h')): outline = mcp_client.ts_c_get_code_outline(f_path)
if f_path.lower().endswith('.py'): outline = mcp_client.py_get_code_outline(f_path)
elif f_path.lower().endswith(('.c', '.h')): outline = mcp_client.ts_c_get_code_outline(f_path)
elif f_path.lower().endswith(('.cpp', '.hpp', '.cxx', '.cc')): outline = mcp_client.ts_cpp_get_code_outline(f_path)
except Exception as e:
outline = f"Error fetching outline: {e}"
app._cached_ast_nodes = []
import re
import re #TODO(Ed): Review local import
pattern = re.compile(r'^(\s*)\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)')
stack = [] # (indent, name)
for line in outline.splitlines():
@@ -3671,74 +3669,83 @@ def render_ast_inspector_modal(app: App) -> None:
# --- LEFT COLUMN: AST Tree & Slice Management ---
imgui.begin_child("ast_tree_scroll", imgui.ImVec2(0, 0), True)
if True:
if imgui.collapsing_header("AST Tree", imgui.TreeNodeFlags_.default_open):
if not getattr(app, '_cached_ast_nodes', None): imgui.text("No AST nodes found.")
else:
# if True:
if imgui.collapsing_header("AST Tree", imgui.TreeNodeFlags_.default_open):
if not getattr(app, '_cached_ast_nodes', None): imgui.text("No AST nodes found.")
else:
with imscope.table('ast tree members', 2, imgui.TableFlags_.resizable | imgui.TableFlags_.sizing_stretch_prop)
imgui.table_setup_column("symbol", imgui.TableColumnFlags_.width_fixed, 400)
imgui.table_setup_column("option", imgui.TableColumnFlags_.width_stretch)
imgui.table_next_row()
imgui.table_next_column()
for node in app._cached_ast_nodes:
imgui.table_next_row()
imgui.table_next_column()
indent = node['indent']
kind = node['kind']
name = node['name']
full_path = node['full_path']
imgui.dummy(imgui.ImVec2(indent * 10, 0))
imgui.same_line()
imgui.text(f"[{kind}] {name}")
if imgui.is_item_hovered():
app._hovered_ast_node = full_path
btn_width = 150
avail_width = imgui.get_content_region_avail().x
do_align = avail_width > btn_width if isinstance(avail_width, (int, float)) else False
if do_align: imgui.same_line(imgui.get_window_width() - btn_width)
else: imgui.same_line()
imgui.table_next_column()
# btn_width = 150
# avail_width = imgui.get_content_region_avail().x
# do_align = avail_width > btn_width if isinstance(avail_width, (int, float)) else False
# if do_align: imgui.same_line((avail_width - btn_width))
# else: imgui.same_line()
# imgui.same_line((avail_width - btn_width) * 0.925)
if not hasattr(f_item, 'ast_mask'): f_item.ast_mask = {}
current_mode = f_item.ast_mask.get(full_path, 'hide')
imgui.push_id(full_path)
if imgui.radio_button("Def", current_mode == 'def'): f_item.ast_mask[full_path] = 'def'
if imgui.radio_button("Def", current_mode == 'def'): f_item.ast_mask[full_path] = 'def'
imgui.same_line()
if imgui.radio_button("Sig", current_mode == 'sig'): f_item.ast_mask[full_path] = 'sig'
if imgui.radio_button("Sig", current_mode == 'sig'): f_item.ast_mask[full_path] = 'sig'
imgui.same_line()
if imgui.radio_button("Hide", current_mode == 'hide'): f_item.ast_mask[full_path] = 'hide'
imgui.pop_id()
imgui.separator()
if imgui.collapsing_header("Custom Slices", imgui.TreeNodeFlags_.default_open):
if not hasattr(f_item, 'custom_slices'): f_item.custom_slices = []
imgui.text_colored(C_IN(), "Highlight lines in right pane to add slices.")
if imgui.button("Add Selection as Slice"):
if getattr(app, '_slice_sel_start', -1) != -1 and getattr(app, '_slice_sel_end', -1) != -1:
s_line = min(app._slice_sel_start, app._slice_sel_end)
e_line = max(app._slice_sel_start, app._slice_sel_end)
from src.fuzzy_anchor import FuzzyAnchor
slice_data = FuzzyAnchor.create_slice(app.text_viewer_content, s_line, e_line)
slice_data['tag'] = ""; slice_data['comment'] = ""
f_item.custom_slices.append(slice_data)
app._slice_sel_start = -1; app._slice_sel_end = -1
imgui.separator()
if imgui.collapsing_header("Custom Slices", imgui.TreeNodeFlags_.default_open):
if not hasattr(f_item, 'custom_slices'): f_item.custom_slices = []
imgui.text_colored(C_IN(), "Highlight lines in right pane to add slices.")
if imgui.button("Add Selection as Slice"):
if getattr(app, '_slice_sel_start', -1) != -1 and getattr(app, '_slice_sel_end', -1) != -1:
s_line = min(app._slice_sel_start, app._slice_sel_end)
e_line = max(app._slice_sel_start, app._slice_sel_end)
from src.fuzzy_anchor import FuzzyAnchor
slice_data = FuzzyAnchor.create_slice(app.text_viewer_content, s_line, e_line)
slice_data['tag'] = ""; slice_data['comment'] = ""
f_item.custom_slices.append(slice_data)
app._slice_sel_start = -1; app._slice_sel_end = -1
imgui.same_line()
if imgui.button("Clear Selection"): app._slice_sel_start = -1; app._slice_sel_end = -1
imgui.same_line()
if imgui.button("Auto-Populate"): app._populate_auto_slices(f_item)
to_remove = -1
tags = app.controller.project.get("context_tags", ["auto-ast", "bug", "feature", "important"])
for idx, slc in enumerate(f_item.custom_slices):
imgui.push_id(f"slc_row_{idx}"); imgui.text(f"#{idx+1}: L{slc['start_line']}-{slc['end_line']}"); imgui.same_line()
current_tag = slc.get('tag', '')
if current_tag not in tags and current_tag: tags.append(current_tag)
tag_idx = tags.index(current_tag) if current_tag in tags else 0
imgui.set_next_item_width(100)
ch_tag, new_tag_idx = imgui.combo("##Tag", tag_idx, tags)
if ch_tag: slc['tag'] = tags[new_tag_idx]
imgui.same_line(); imgui.set_next_item_width(-30); changed_comm, new_comm = imgui.input_text("##Note", slc.get('comment', ''))
if changed_comm: slc['comment'] = new_comm
imgui.same_line()
if imgui.button("Clear Selection"): app._slice_sel_start = -1; app._slice_sel_end = -1
imgui.same_line()
if imgui.button("Auto-Populate"): app._populate_auto_slices(f_item)
to_remove = -1
tags = app.controller.project.get("context_tags", ["auto-ast", "bug", "feature", "important"])
for idx, slc in enumerate(f_item.custom_slices):
imgui.push_id(f"slc_row_{idx}"); imgui.text(f"#{idx+1}: L{slc['start_line']}-{slc['end_line']}"); imgui.same_line()
current_tag = slc.get('tag', '')
if current_tag not in tags and current_tag: tags.append(current_tag)
tag_idx = tags.index(current_tag) if current_tag in tags else 0
imgui.set_next_item_width(100)
ch_tag, new_tag_idx = imgui.combo("##Tag", tag_idx, tags)
if ch_tag: slc['tag'] = tags[new_tag_idx]
imgui.same_line(); imgui.set_next_item_width(-30); changed_comm, new_comm = imgui.input_text("##Note", slc.get('comment', ''))
if changed_comm: slc['comment'] = new_comm
imgui.same_line()
if imgui.button("X"): to_remove = idx
imgui.pop_id()
if to_remove != -1: f_item.custom_slices.pop(to_remove)
if imgui.button("X"): to_remove = idx
imgui.pop_id()
if to_remove != -1: f_item.custom_slices.pop(to_remove)
imgui.end_child()
imgui.table_next_column()
@@ -6275,11 +6282,9 @@ def render_heavy_text(app: App, label: str, content: str, id_suffix: str = "") -
#region: MMA
def render_mma_dashboard(app: App) -> None:
"""
Main MMA dashboard interface.
"""Main MMA dashboard interface.
SSDL Shape:
`[I:focus_selector] -> [I:track_summary] -> [B:epic_planner] -> [I:track_browser] -> [B:global_controls] -> [I:usage_analytics] -> [I:ticket_queue] -> [I:task_dag] => [I:agent_streams]`
SSDL: `[I:focus_selector] -> [I:track_summary] -> [B:epic_planner] -> [I:track_browser] -> [B:global_controls] -> [I:usage_analytics] -> [I:ticket_queue] -> [I:task_dag] => [I:agent_streams]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -6325,13 +6330,11 @@ def render_mma_dashboard(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_mma_dashboard")
def render_mma_modals(app: App) -> None:
"""
Renders all MMA-specific approval and info modals: Tool Execution Approval,
"""Renders all MMA-specific approval and info modals: Tool Execution Approval,
MMA Step Approval (with optional payload edit), MMA Spawn Approval (with
prompt/context editing), and the Cycle Detected error modal.
SSDL Shape:
`[I:pending_queues] => [I:tool_approval_modal] | [I:step_approval_modal] | [I:spawn_approval_modal] | [I:cycle_modal]`
SSDL: `[I:pending_queues] => [I:tool_approval_modal] | [I:step_approval_modal] | [I:spawn_approval_modal] | [I:cycle_modal]`
ASCII Layout Map:
Conditionally opens (at most one at a time):
@@ -6426,13 +6429,11 @@ def render_mma_modals(app: App) -> None:
imgui.end_popup()
def render_mma_track_summary(app: App) -> None:
"""
Renders the active-track summary row: track name, MMA pipeline status, and running
"""Renders the active-track summary row: track name, MMA pipeline status, and running
cost estimate, followed by a colour-coded progress bar and a 4-column breakdown table
(Completed / In Progress / Blocked / Todo) and ETA estimation.
SSDL Shape:
`[I:track_name] -> [I:status_badge] -> [I:cost] -> [I:progress_bar] -> [I:stats_table] -> [I:eta]`
SSDL: `[I:track_name] -> [I:status_badge] -> [I:cost] -> [I:progress_bar] -> [I:stats_table] -> [I:eta]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -6470,11 +6471,9 @@ def render_mma_track_summary(app: App) -> None:
imgui.text_colored(C_LBL(), "ETA:"); imgui.same_line(); imgui.text_colored(C_VAL(), f"~{int(eta_mins)}m ({remaining} tickets remaining)")
def render_mma_epic_planner(app: App) -> None:
"""
Renders the Epic Planning panel for Tier 1 strategy input and planning button.
"""Renders the Epic Planning panel for Tier 1 strategy input and planning button.
SSDL Shape:
`[I:input_textbox] => [B:plan_button]`
SSDL: `[I:input_textbox] => [B:plan_button]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -6482,7 +6481,7 @@ def render_mma_epic_planner(app: App) -> None:
| +-----------------------------------------------------+ |
| | Describe the epic goal... | |
| +-----------------------------------------------------+ |
| [Plan Epic (Tier 1) ] |
| [Plan Epic (Tier 1) ] |
+---------------------------------------------------------+
"""
imgui.text_colored(C_LBL(), 'Epic Planning (Tier 1)')
@@ -6490,12 +6489,10 @@ def render_mma_epic_planner(app: App) -> None:
if imgui.button('Plan Epic (Tier 1)', imgui.ImVec2(-1, 0)): app._cb_plan_epic()
def render_mma_conductor_setup(app: App) -> None:
"""
Renders the Conductor Setup panel. Runs a one-shot project-structure scan and
"""Renders the Conductor Setup panel. Runs a one-shot project-structure scan and
shows the summarised output in a read-only text area.
SSDL Shape:
`[B:run_scan] => [I:summary_text]`
SSDL: `[B:run_scan] => [I:summary_text]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -6511,22 +6508,20 @@ def render_mma_conductor_setup(app: App) -> None:
if app.ui_conductor_setup_summary: imgui.input_text_multiline("##setup_summary", app.ui_conductor_setup_summary, imgui.ImVec2(-1, 120), imgui.InputTextFlags_.read_only)
def render_mma_track_browser(app: App) -> None:
"""
Renders the Track Browser: a table listing all known tracks with status, progress bar,
"""Renders the Track Browser: a table listing all known tracks with status, progress bar,
and a [Load] button per row. Below the table is a form for creating a new track.
SSDL Shape:
`[I:tracks_table] -> [B:load_button] -> [I:create_track_form] => [B:create_track]`
SSDL: `[I:tracks_table] -> [B:load_button] -> [I:create_track_form] => [B:create_track]`
ASCII Layout Map:
+---------------------------------------------------------+
| Track Browser |
| +------------------+--------+----------+---------+ |
| | Title | Status | Progress | Actions | |
| +------------------+--------+----------+---------+ |
| | my-feature-0612 | ACTIVE | [===== ] [Load] | |
| +------------------+--------+----------+---------+ |
| | Title | Status | Progress | Actions | |
| +------------------+--------+----------+---------+ |
| | my-feature-0612 | ACTIVE | [===== ] [Load] | |
| | chore-cleanup | NEW | [ ] [Load] | |
| +------------------+--------+----------+---------+ |
| +------------------+--------+----------+---------+ |
| Create New Track |
| Name: [___________] |
| Description: [_____________________________] |
@@ -6560,13 +6555,11 @@ def render_mma_track_browser(app: App) -> None:
app.ui_new_track_name = ""; app.ui_new_track_desc = ""
def render_mma_global_controls(app: App) -> None:
"""
Renders the MMA global controls bar: Step Mode (HITL) toggle, pipeline status label,
"""Renders the MMA global controls bar: Step Mode (HITL) toggle, pipeline status label,
Pause/Resume engine button, active tier badge, approval-pending blink indicator, and
the Hot Reload shortcut.
SSDL Shape:
`[B:step_mode] -> [I:status] -> [B:pause_resume] -> [I:active_tier] -> [I:approval_blink] -> [B:hot_reload]`
SSDL: `[B:step_mode] -> [I:status] -> [B:pause_resume] -> [I:active_tier] -> [I:approval_blink] -> [B:hot_reload]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -6604,25 +6597,23 @@ def render_mma_global_controls(app: App) -> None:
imgui.same_line(); imgui.text_disabled("(Ctrl+Alt+R)")
def render_mma_usage_section(app: App) -> None:
"""
Renders the Tier Usage table (tokens and estimated cost per tier) and, when expanded,
"""Renders the Tier Usage table (tokens and estimated cost per tier) and, when expanded,
a collapsible Tier Model Config section with per-tier provider, model, tool-preset,
and persona overrides.
SSDL Shape:
`[I:usage_table] -> [B:tier_model_config_header] => [I:combos_per_tier]`
SSDL: `[I:usage_table] -> [B:tier_model_config_header] => [I:combos_per_tier]`
ASCII Layout Map:
+---------------------------------------------------------+
| Tier Usage (Tokens & Cost) |
| +--------+------------------+-------+-------+--------+ |
| | Tier | Model | Input | Output | Cost | |
| +--------+------------------+-------+-------+--------+ |
| | Tier 1 | gemini-2.5-flash | 1,200 | 400 | $0.001 | |
| | TOTAL | | | | $0.004 | |
| +--------+------------------+-------+-------+--------+ |
| +--------+------------------+-------+-------+--------+ |
| | Tier | Model | Input | Output | Cost | |
| +--------+------------------+-------+-------+--------+ |
| | Tier 1 | gemini-2.5-flash | 1,200 | 400 | $0.001 | |
| | TOTAL | | | | $0.004 | |
| +--------+------------------+-------+-------+--------+ |
| > Tier Model Config (collapsible) |
| Tier 1: [gemini v] [gemini-2.5-flash v] [None v] |
| Tier 1: [gemini v] [gemini-2.5-flash v] [None v] |
+---------------------------------------------------------+
"""
imgui.text("Tier Usage (Tokens & Cost)")
@@ -6680,7 +6671,7 @@ def render_mma_ticket_editor(app: App) -> None:
| Editing: ticket-042 |
| Status: in_progress |
| Priority: [medium v] |
| Target: src/gui_2.py |
| Target: src/gui_2.py |
| Depends on: ticket-041, ticket-040 |
| Persona Override: [None v] |
| [Mark Complete] [Delete] |
@@ -6738,13 +6729,11 @@ def render_mma_agent_streams(app: App) -> None:
imgui.end_tab_bar()
def render_tier_stream_panel(app: App, tier_key: str, stream_key: str | None) -> None:
"""
Renders a single tier's live streaming text panel. For Tier 1, 2, and 4 a single scrollable
"""Renders a single tier's live streaming text panel. For Tier 1, 2, and 4 a single scrollable
selectable text area is shown; for Tier 3 (stream_key=None), each worker ticket gets
its own labelled, colour-coded sub-child with an auto-scroll behaviour.
SSDL Shape:
`[I:stream_text] => [I:single_scroll] | [I:worker_sub_children]`
SSDL: `[I:stream_text] => [I:single_scroll] | [I:worker_sub_children]`
ASCII Layout Map (single-stream, e.g. Tier 1):
+---------------------------------------------------------+
@@ -6813,13 +6802,11 @@ def render_tier_stream_panel(app: App, tier_key: str, stream_key: str | None) ->
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tier_stream_panel")
def render_track_proposal_modal(app: App) -> None:
"""
Renders the Track Proposal modal. Shows Tier 1's generated implementation tracks
"""Renders the Track Proposal modal. Shows Tier 1's generated implementation tracks
with editable title and goal fields, and buttons to remove, start individual tracks,
accept all, or cancel.
SSDL Shape:
`[I:proposed_tracks] -> [B:edit_title/goal] -> [B:remove/start] => [B:accept_all | B:cancel]`
SSDL: `[I:proposed_tracks] -> [B:edit_title/goal] -> [B:remove/start] => [B:accept_all | B:cancel]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -6880,13 +6867,11 @@ def render_track_proposal_modal(app: App) -> None:
imgui.end_popup()
def render_ticket_queue(app: App) -> None:
"""
Renders the Ticket Queue Management panel. Provides select-all/none controls,
"""Renders the Ticket Queue Management panel. Provides select-all/none controls,
bulk action buttons, a 7-column scrollable table of tickets with drag-to-reorder,
priority/model combos, status labels, and per-row Kill/Block/Unblock actions.
SSDL Shape:
`[B:select_all/none] -> [B:bulk_actions] -> [I:ticket_table] => [I:ticket_editor?]`
SSDL: `[B:select_all/none] -> [B:bulk_actions] -> [I:ticket_table] => [I:ticket_editor?]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -7013,12 +6998,10 @@ def render_ticket_queue(app: App) -> None:
imgui.end_table()
def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
"""
Renders the interactive node editor DAG visualizer, mapping ticket relationships
"""Renders the interactive node editor DAG visualizer, mapping ticket relationships
and allowing creation or deletion of links.
SSDL Shape:
`[I:task_nodes] -> [B:node_editor_canvas] => [I:link_connections]`
SSDL: `[I:task_nodes] -> [B:node_editor_canvas] => [I:link_connections]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -7089,8 +7072,8 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
tid = str(t.get('id', ''))
if abs(hash(tid + "_out")) == s_id: source_tid = tid
if abs(hash(tid + "_out")) == e_id: source_tid = tid
if abs(hash(tid + "_in")) == s_id: target_tid = tid
if abs(hash(tid + "_in")) == e_id: target_tid = tid
if abs(hash(tid + "_in")) == s_id: target_tid = tid
if abs(hash(tid + "_in")) == e_id: target_tid = tid
if source_tid and target_tid and source_tid != target_tid:
for t in app.active_tickets:
if str(t.get('id', '')) == target_tid:
@@ -7136,17 +7119,17 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
if tid.startswith('T-'):
try: max_id = max(max_id, int(tid[2:]))
except: pass
app.ui_new_ticket_id = f"T-{max_id + 1:03d}"
app.ui_new_ticket_desc = ""
app.ui_new_ticket_id = f"T-{max_id + 1:03d}"
app.ui_new_ticket_desc = ""
app.ui_new_ticket_target = ""
app.ui_new_ticket_deps = ""
app.ui_new_ticket_deps = ""
if app._show_add_ticket_form:
imgui.begin_child("add_ticket_form", imgui.ImVec2(-1, 220), True)
imgui.text_colored(C_VAL(), "New Ticket Details")
_, app.ui_new_ticket_id = imgui.input_text("ID##new_ticket", app.ui_new_ticket_id)
_, app.ui_new_ticket_desc = imgui.input_text_multiline("Description##new_ticket", app.ui_new_ticket_desc, imgui.ImVec2(-1, 60))
_, app.ui_new_ticket_id = imgui.input_text("ID##new_ticket", app.ui_new_ticket_id)
_, app.ui_new_ticket_desc = imgui.input_text_multiline("Description##new_ticket", app.ui_new_ticket_desc, imgui.ImVec2(-1, 60))
_, app.ui_new_ticket_target = imgui.input_text("Target File##new_ticket", app.ui_new_ticket_target)
_, app.ui_new_ticket_deps = imgui.input_text("Depends On (IDs, comma-separated)##new_ticket", app.ui_new_ticket_deps)
_, app.ui_new_ticket_deps = imgui.input_text("Depends On (IDs, comma-separated)##new_ticket", app.ui_new_ticket_deps)
imgui.text("Priority:")
imgui.same_line()
if imgui.begin_combo("##new_prio", app.ui_new_ticket_priority):
+1 -1
View File
@@ -43,7 +43,7 @@ class HotReloader:
cls.is_error_state = True
return False
hm = cls.HOT_MODULES[module_name]
hm = cls.HOT_MODULES[module_name]
state = cls.capture_state(app, hm.state_keys)
try: