reading more code, slight adjustment to ast structual file editor ux (radio buttons going off viewport)
This commit is contained in:
+24
-39
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user