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

This commit is contained in:
ed
2026-06-13 11:08:45 -04:00
parent 394020e50c
commit b61a2db01d
3 changed files with 136 additions and 168 deletions
+8 -23
View File
@@ -46,17 +46,12 @@ from src.warmup import WarmupManager
def parse_symbols(text: str) -> list[str]: 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). 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) 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: 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: for file_path in files:
result = mcp_client.py_get_symbol_info(file_path, symbol) result = mcp_client.py_get_symbol_info(file_path, symbol)
if isinstance(result, tuple): if isinstance(result, tuple):
@@ -66,9 +61,6 @@ def get_symbol_definition(symbol: str, files: list[str]) -> tuple[str, str, int]
class ConfirmDialog: class ConfirmDialog:
def __init__(self, script: str, base_dir: str) -> None: 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._uid = str(uuid.uuid4())
self._script = str(script) if script is not None else "" self._script = str(script) if script is not None else ""
self._base_dir = str(base_dir) if base_dir 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'}) self._pending_gui_tasks.append({'action': 'set_tool_log_dirty'})
def _process_pending_gui_tasks(self) -> None: def _process_pending_gui_tasks(self) -> None:
""" """Processes pending GUI tasks from the queue on the main render thread."""
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]
"""
now = time.time() now = time.time()
if hasattr(self, 'event_queue') and hasattr(self.event_queue, 'websocket_server') and self.event_queue.websocket_server: 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: if now - self._last_telemetry_time >= 1.0:
@@ -1836,25 +1825,21 @@ class AppController:
mcp_p = Path(mcp_path) mcp_p = Path(mcp_path)
if not mcp_p.is_absolute() and self.active_project_path: if not mcp_p.is_absolute() and self.active_project_path:
mcp_p = Path(self.active_project_path).parent / mcp_path mcp_p = Path(self.active_project_path).parent / mcp_path
if mcp_p.exists(): if mcp_p.exists(): self.mcp_config = models.load_mcp_config(str(mcp_p))
self.mcp_config = models.load_mcp_config(str(mcp_p)) else: self.mcp_config = models.MCPConfiguration()
else:
self.mcp_config = models.MCPConfiguration()
else: else:
self.mcp_config = models.MCPConfiguration() self.mcp_config = models.MCPConfiguration()
rag_data = self.config.get('rag') rag_data = self.config.get('rag')
if rag_data: if rag_data: self.rag_config = models.RAGConfig.from_dict(rag_data)
self.rag_config = models.RAGConfig.from_dict(rag_data) else: self.rag_config = models.RAGConfig()
else:
self.rag_config = models.RAGConfig()
self.rag_engine = None self.rag_engine = None
if self.rag_config.enabled: if self.rag_config.enabled: self._sync_rag_engine()
self._sync_rag_engine()
from src.personas import PersonaManager from src.personas import PersonaManager
self.persona_manager = PersonaManager(Path(self.active_project_path).parent if self.active_project_path else None) self.persona_manager = PersonaManager(Path(self.active_project_path).parent if self.active_project_path else None)
from src.vendor_capabilities import get_capabilities from src.vendor_capabilities import get_capabilities
try: try:
caps = get_capabilities(self.current_provider, self.current_model) caps = get_capabilities(self.current_provider, self.current_model)
+42 -59
View File
@@ -3579,13 +3579,11 @@ def render_context_composition_panel(app: App) -> None:
render_context_screenshots(app) render_context_screenshots(app)
def render_ast_inspector_modal(app: App) -> None: 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 Provides an interactive tree-sitter AST inspector to mask classes, methods, or
functions and extract custom annotated slices (Definition, Signature, Hide). functions and extract custom annotated slices (Definition, Signature, Hide).
SSDL Shape: SSDL: `[B:open_modal?] -> [Q:outline] -> [I:render_ast_tree]`
`[B:open_modal?] -> [Q:outline] -> [I:render_ast_tree]`
ASCII Layout Map: ASCII Layout Map:
+-------------------------------------------------------------+ +-------------------------------------------------------------+
@@ -3629,7 +3627,7 @@ def render_ast_inspector_modal(app: App) -> None:
outline = f"Error fetching outline: {e}" outline = f"Error fetching outline: {e}"
app._cached_ast_nodes = [] app._cached_ast_nodes = []
import re import re #TODO(Ed): Review local import
pattern = re.compile(r'^(\s*)\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)') pattern = re.compile(r'^(\s*)\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)')
stack = [] # (indent, name) stack = [] # (indent, name)
for line in outline.splitlines(): for line in outline.splitlines():
@@ -3671,11 +3669,18 @@ def render_ast_inspector_modal(app: App) -> None:
# --- LEFT COLUMN: AST Tree & Slice Management --- # --- LEFT COLUMN: AST Tree & Slice Management ---
imgui.begin_child("ast_tree_scroll", imgui.ImVec2(0, 0), True) imgui.begin_child("ast_tree_scroll", imgui.ImVec2(0, 0), True)
if True: # if True:
if imgui.collapsing_header("AST Tree", imgui.TreeNodeFlags_.default_open): if imgui.collapsing_header("AST Tree", imgui.TreeNodeFlags_.default_open):
if not getattr(app, '_cached_ast_nodes', None): imgui.text("No AST nodes found.") if not getattr(app, '_cached_ast_nodes', None): imgui.text("No AST nodes found.")
else: 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: for node in app._cached_ast_nodes:
imgui.table_next_row()
imgui.table_next_column()
indent = node['indent'] indent = node['indent']
kind = node['kind'] kind = node['kind']
name = node['name'] name = node['name']
@@ -3687,12 +3692,14 @@ def render_ast_inspector_modal(app: App) -> None:
if imgui.is_item_hovered(): if imgui.is_item_hovered():
app._hovered_ast_node = full_path app._hovered_ast_node = full_path
imgui.table_next_column()
btn_width = 150 # btn_width = 150
avail_width = imgui.get_content_region_avail().x # avail_width = imgui.get_content_region_avail().x
do_align = avail_width > btn_width if isinstance(avail_width, (int, float)) else False # 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) # if do_align: imgui.same_line((avail_width - btn_width))
else: imgui.same_line() # else: imgui.same_line()
# imgui.same_line((avail_width - btn_width) * 0.925)
if not hasattr(f_item, 'ast_mask'): f_item.ast_mask = {} if not hasattr(f_item, 'ast_mask'): f_item.ast_mask = {}
current_mode = f_item.ast_mask.get(full_path, 'hide') current_mode = f_item.ast_mask.get(full_path, 'hide')
@@ -6275,11 +6282,9 @@ def render_heavy_text(app: App, label: str, content: str, id_suffix: str = "") -
#region: MMA #region: MMA
def render_mma_dashboard(app: App) -> None: def render_mma_dashboard(app: App) -> None:
""" """Main MMA dashboard interface.
Main MMA dashboard interface.
SSDL Shape: 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]`
`[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: 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") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_mma_dashboard")
def render_mma_modals(app: App) -> None: 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 MMA Step Approval (with optional payload edit), MMA Spawn Approval (with
prompt/context editing), and the Cycle Detected error modal. prompt/context editing), and the Cycle Detected error modal.
SSDL Shape: SSDL: `[I:pending_queues] => [I:tool_approval_modal] | [I:step_approval_modal] | [I:spawn_approval_modal] | [I:cycle_modal]`
`[I:pending_queues] => [I:tool_approval_modal] | [I:step_approval_modal] | [I:spawn_approval_modal] | [I:cycle_modal]`
ASCII Layout Map: ASCII Layout Map:
Conditionally opens (at most one at a time): Conditionally opens (at most one at a time):
@@ -6426,13 +6429,11 @@ def render_mma_modals(app: App) -> None:
imgui.end_popup() imgui.end_popup()
def render_mma_track_summary(app: App) -> None: 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 cost estimate, followed by a colour-coded progress bar and a 4-column breakdown table
(Completed / In Progress / Blocked / Todo) and ETA estimation. (Completed / In Progress / Blocked / Todo) and ETA estimation.
SSDL Shape: SSDL: `[I:track_name] -> [I:status_badge] -> [I:cost] -> [I:progress_bar] -> [I:stats_table] -> [I:eta]`
`[I:track_name] -> [I:status_badge] -> [I:cost] -> [I:progress_bar] -> [I:stats_table] -> [I:eta]`
ASCII Layout Map: 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)") 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: 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: SSDL: `[I:input_textbox] => [B:plan_button]`
`[I:input_textbox] => [B:plan_button]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -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() if imgui.button('Plan Epic (Tier 1)', imgui.ImVec2(-1, 0)): app._cb_plan_epic()
def render_mma_conductor_setup(app: App) -> None: 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. shows the summarised output in a read-only text area.
SSDL Shape: SSDL: `[B:run_scan] => [I:summary_text]`
`[B:run_scan] => [I:summary_text]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6511,12 +6508,10 @@ 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) 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: 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. and a [Load] button per row. Below the table is a form for creating a new track.
SSDL Shape: SSDL: `[I:tracks_table] -> [B:load_button] -> [I:create_track_form] => [B:create_track]`
`[I:tracks_table] -> [B:load_button] -> [I:create_track_form] => [B:create_track]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6560,13 +6555,11 @@ def render_mma_track_browser(app: App) -> None:
app.ui_new_track_name = ""; app.ui_new_track_desc = "" app.ui_new_track_name = ""; app.ui_new_track_desc = ""
def render_mma_global_controls(app: App) -> None: 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 Pause/Resume engine button, active tier badge, approval-pending blink indicator, and
the Hot Reload shortcut. the Hot Reload shortcut.
SSDL Shape: SSDL: `[B:step_mode] -> [I:status] -> [B:pause_resume] -> [I:active_tier] -> [I:approval_blink] -> [B:hot_reload]`
`[B:step_mode] -> [I:status] -> [B:pause_resume] -> [I:active_tier] -> [I:approval_blink] -> [B:hot_reload]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6604,13 +6597,11 @@ def render_mma_global_controls(app: App) -> None:
imgui.same_line(); imgui.text_disabled("(Ctrl+Alt+R)") imgui.same_line(); imgui.text_disabled("(Ctrl+Alt+R)")
def render_mma_usage_section(app: App) -> None: 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, a collapsible Tier Model Config section with per-tier provider, model, tool-preset,
and persona overrides. and persona overrides.
SSDL Shape: SSDL: `[I:usage_table] -> [B:tier_model_config_header] => [I:combos_per_tier]`
`[I:usage_table] -> [B:tier_model_config_header] => [I:combos_per_tier]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6738,13 +6729,11 @@ def render_mma_agent_streams(app: App) -> None:
imgui.end_tab_bar() imgui.end_tab_bar()
def render_tier_stream_panel(app: App, tier_key: str, stream_key: str | None) -> None: 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 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. its own labelled, colour-coded sub-child with an auto-scroll behaviour.
SSDL Shape: SSDL: `[I:stream_text] => [I:single_scroll] | [I:worker_sub_children]`
`[I:stream_text] => [I:single_scroll] | [I:worker_sub_children]`
ASCII Layout Map (single-stream, e.g. Tier 1): 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") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tier_stream_panel")
def render_track_proposal_modal(app: App) -> None: 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, with editable title and goal fields, and buttons to remove, start individual tracks,
accept all, or cancel. accept all, or cancel.
SSDL Shape: SSDL: `[I:proposed_tracks] -> [B:edit_title/goal] -> [B:remove/start] => [B:accept_all | B:cancel]`
`[I:proposed_tracks] -> [B:edit_title/goal] -> [B:remove/start] => [B:accept_all | B:cancel]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -6880,13 +6867,11 @@ def render_track_proposal_modal(app: App) -> None:
imgui.end_popup() imgui.end_popup()
def render_ticket_queue(app: App) -> None: 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, 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. priority/model combos, status labels, and per-row Kill/Block/Unblock actions.
SSDL Shape: SSDL: `[B:select_all/none] -> [B:bulk_actions] -> [I:ticket_table] => [I:ticket_editor?]`
`[B:select_all/none] -> [B:bulk_actions] -> [I:ticket_table] => [I:ticket_editor?]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -7013,12 +6998,10 @@ def render_ticket_queue(app: App) -> None:
imgui.end_table() imgui.end_table()
def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer 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. and allowing creation or deletion of links.
SSDL Shape: SSDL: `[I:task_nodes] -> [B:node_editor_canvas] => [I:link_connections]`
`[I:task_nodes] -> [B:node_editor_canvas] => [I:link_connections]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+