fixing bugs in gui_2.py

This commit is contained in:
2026-05-11 22:19:04 -04:00
parent 26ef81a30e
commit 5e947d50fe
+164 -153
View File
@@ -8,6 +8,7 @@ import sys
import os
import shutil
import copy
import threading
from pathlib import Path
from tkinter import filedialog, Tk
from typing import Optional, Any
@@ -1769,133 +1770,144 @@ class App:
if self._show_ast_inspector:
imgui.open_popup('AST Inspector')
self._show_ast_inspector = False
expanded, opened = imgui.begin_popup_modal('AST Inspector', True, imgui.WindowFlags_.always_auto_resize)
if not opened:
return
if self.ui_inspecting_ast_file is None:
imgui.close_current_popup()
imgui.end_popup()
return
f_item = self.ui_inspecting_ast_file
f_path = f_item.path if hasattr(f_item, "path") else str(f_item)
if f_path != self._cached_ast_file_path:
outline = ""
try:
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)
else:
outline = mcp_client.ts_cpp_get_code_outline(f_path)
except Exception as e:
outline = f"Error fetching outline: {e}"
self._cached_ast_nodes = []
import re
pattern = re.compile(r'^(\s*)\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)')
stack = [] # (indent, name)
for line in outline.splitlines():
m = pattern.match(line)
if m:
indent_str, kind, name, start_ln, end_ln = m.groups()
indent = len(indent_str)
while stack and stack[-1][0] >= indent:
stack.pop()
stack.append((indent, name))
full_path = '::'.join([s[1] for s in stack])
self._cached_ast_nodes.append({
'indent': indent,
'kind': kind,
'name': name,
'full_path': full_path,
'start_line': int(start_ln),
'end_line': int(end_ln)
})
try:
content = mcp_client.read_file(f_path)
self._cached_ast_file_lines = content.splitlines()
except Exception:
self._cached_ast_file_lines = ["Error loading file content."]
self._cached_ast_file_path = f_path
imgui.text(f"Inspecting AST: {f_path}")
imgui.separator()
#region: AST Inspector
expanded, opened = imgui.begin_popup_modal('AST Inspector', True, imgui.WindowFlags_.always_auto_resize)
if opened:
if expanded:
if self.ui_inspecting_ast_file is None:
imgui.close_current_popup()
else:
f_item = self.ui_inspecting_ast_file
f_path = f_item.path if hasattr(f_item, "path") else str(f_item)
if f_path != self._cached_ast_file_path:
outline = ""
try:
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)
else:
outline = mcp_client.ts_cpp_get_code_outline(f_path)
except Exception as e:
outline = f"Error fetching outline: {e}"
self._cached_ast_nodes = []
import re
pattern = re.compile(r'^(\s*)\[(.*?)\] (.*?) \(Lines (\d+)-(\d+)\)')
stack = [] # (indent, name)
for line in outline.splitlines():
m = pattern.match(line)
if m:
indent_str, kind, name, start_ln, end_ln = m.groups()
indent = len(indent_str)
while stack and stack[-1][0] >= indent:
stack.pop()
stack.append((indent, name))
full_path = '::'.join([s[1] for s in stack])
self._cached_ast_nodes.append({
'indent': indent,
'kind': kind,
'name': name,
'full_path': full_path,
'start_line': int(start_ln),
'end_line': int(end_ln)
})
try:
content = mcp_client.read_file(f_path)
self._cached_ast_file_lines = content.splitlines()
except Exception:
self._cached_ast_file_lines = ["Error loading file content."]
self._cached_ast_file_path = f_path
imgui.text(f"Inspecting AST: {f_path}")
imgui.separator()
#region: ast_dual_pane
if imgui.begin_table('ast_dual_pane', 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v):
imgui.table_next_column()
#region: LEFT COLUMN (Tree) ---
if imgui.begin_child("ast_tree_scroll", imgui.ImVec2(0, 600), True):
if not self._cached_ast_nodes:
imgui.text("No AST nodes found or error fetching outline.")
else:
for node in self._cached_ast_nodes:
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}")
imgui.same_line(imgui.get_window_width() - 200)
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'
imgui.same_line()
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.end_child()
#endregion: LEFT COLUMN (Tree)
imgui.table_next_column()
#region: RIGHT COLUMN (Content) ---
if imgui.begin_child("ast_content_scroll", imgui.ImVec2(0, 600), True):
if not hasattr(self, '_cached_ast_file_lines') or not self._cached_ast_file_lines:
imgui.text("No file content loaded.")
else:
draw_list = imgui.get_window_draw_list()
for i, line_text in enumerate(self._cached_ast_file_lines):
line_num = i + 1
# Prioritize the most specific node (deepest indent) that covers the line
deepest_node = None
for node in self._cached_ast_nodes:
if node['start_line'] <= line_num <= node['end_line']:
if deepest_node is None or node['indent'] > deepest_node['indent']:
deepest_node = node
mode = 'hide'
if deepest_node:
mode = f_item.ast_mask.get(deepest_node['full_path'], 'hide')
pos = imgui.get_cursor_screen_pos()
line_height = imgui.get_text_line_height()
if mode == 'def':
# Green, alpha 0.2
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 255, 0, 0.2)))
elif mode == 'sig':
# Blue, alpha 0.2
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 0, 255, 0.2)))
imgui.text(f"{line_num:4} | {line_text}")
imgui.end_child()
#endregion: RIGHT COLUMN (Content) ---
imgui.end_table()
#endregion: ast_dual_pane
imgui.separator()
if imgui.button("Close", imgui.ImVec2(120, 0)):
self.ui_inspecting_ast_file = None
imgui.close_current_popup()
imgui.end_popup()
#endregion: AST Inspector
if imgui.begin_table('ast_dual_pane', 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v):
imgui.table_next_column()
# --- LEFT COLUMN (Tree) ---
imgui.begin_child("ast_tree_scroll", imgui.ImVec2(0, 600), True)
if not self._cached_ast_nodes:
imgui.text("No AST nodes found or error fetching outline.")
else:
for node in self._cached_ast_nodes:
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}")
imgui.same_line(imgui.get_window_width() - 200)
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'
imgui.same_line()
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.end_child()
imgui.table_next_column()
# --- RIGHT COLUMN (Content) ---
imgui.begin_child("ast_content_scroll", imgui.ImVec2(0, 600), True)
if not hasattr(self, '_cached_ast_file_lines') or not self._cached_ast_file_lines:
imgui.text("No file content loaded.")
else:
draw_list = imgui.get_window_draw_list()
for i, line_text in enumerate(self._cached_ast_file_lines):
line_num = i + 1
# Prioritize the most specific node (deepest indent) that covers the line
deepest_node = None
for node in self._cached_ast_nodes:
if node['start_line'] <= line_num <= node['end_line']:
if deepest_node is None or node['indent'] > deepest_node['indent']:
deepest_node = node
mode = 'hide'
if deepest_node:
mode = f_item.ast_mask.get(deepest_node['full_path'], 'hide')
pos = imgui.get_cursor_screen_pos()
line_height = imgui.get_text_line_height()
if mode == 'def':
# Green, alpha 0.2
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 255, 0, 0.2)))
elif mode == 'sig':
# Blue, alpha 0.2
draw_list.add_rect_filled(pos, imgui.ImVec2(pos.x + imgui.get_content_region_avail().x, pos.y + line_height), imgui.get_color_u32(vec4(0, 0, 255, 0.2)))
imgui.text(f"{line_num:4} | {line_text}")
imgui.end_child()
imgui.end_table()
imgui.separator()
if imgui.button("Close", imgui.ImVec2(120, 0)):
if not opened:
self.ui_inspecting_ast_file = None
imgui.close_current_popup()
imgui.end_popup()
def _render_save_workspace_profile_modal(self) -> None:
if self._show_save_workspace_profile_modal:
@@ -1929,26 +1941,25 @@ class App:
def _render_add_context_files_modal(self) -> None:
if imgui.begin_popup_modal("Select Context Files", None, imgui.WindowFlags_.always_auto_resize)[0]:
imgui.text("Select files from project to add to context:")
imgui.begin_child("ctx_picker_list", imgui.ImVec2(600, 300), True)
from src import models
# Create a temporary selection set if not initialized
if not hasattr(self, '_ui_picker_selected'):
self._ui_picker_selected = set()
for f in self.files:
fpath = f.path if hasattr(f, 'path') else str(f)
# Skip if already in context
if any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in self.context_files):
continue
is_sel = fpath in self._ui_picker_selected
clicked, new_sel = imgui.checkbox(f"{fpath}##picker_{fpath}", is_sel)
if clicked:
if new_sel:
self._ui_picker_selected.add(fpath)
else:
self._ui_picker_selected.discard(fpath)
imgui.end_child()
if imgui.begin_child("ctx_picker_list", imgui.ImVec2(600, 300), True):
from src import models
# Create a temporary selection set if not initialized
if not hasattr(self, '_ui_picker_selected'):
self._ui_picker_selected = set()
for f in self.files:
fpath = f.path if hasattr(f, 'path') else str(f)
# Skip if already in context
if any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in self.context_files):
continue
is_sel = fpath in self._ui_picker_selected
clicked, new_sel = imgui.checkbox(f"{fpath}##picker_{fpath}", is_sel)
if clicked:
if new_sel:
self._ui_picker_selected.add(fpath)
else:
self._ui_picker_selected.discard(fpath)
imgui.end_child()
imgui.separator()
if imgui.button("Add Selected", imgui.ImVec2(120, 0)):
@@ -2993,11 +3004,11 @@ class App:
self._file_stats_queue = []
if not hasattr(self, '_file_stats_worker_active'):
self._file_stats_worker_active = False
if imgui.collapsing_header("Context Composition"):
total_lines = 0
total_ast = 0
missing_keys = []
for f in self.context_files:
f_path = f.path if hasattr(f, "path") else str(f)
@@ -3009,7 +3020,7 @@ class App:
stats = self._file_stats_cache[cache_key]
total_lines += stats.get("lines", 0)
total_ast += stats.get("ast_elements", 0)
# Process one missing key per frame or spawn a worker
if missing_keys and not self._file_stats_worker_active:
def _stats_worker():
@@ -3020,12 +3031,11 @@ class App:
self._file_stats_cache[key] = aggregate.compute_file_stats(path)
finally:
self._file_stats_worker_active = False
import threading
threading.Thread(target=_stats_worker, daemon=True).start()
#region: Batch Action Bar imgui.text("Batch:")
imgui.same_line()
# imgui.same_line()
for mode in ["full", "summary", "skeleton", "outline", "masked", "none"]:
if imgui.button(f"{mode.capitalize()}##batch"):
for f in self.context_files:
@@ -3067,9 +3077,9 @@ class App:
#endregion: Batch Action Bar
imgui.dummy(imgui.ImVec2(0, 4))
grouped_files = aggregate.group_files_by_dir(self.context_files)
if imgui.begin_table("ctx_comp_table", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders):
imgui.table_setup_column("File", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200)
@@ -3180,6 +3190,7 @@ class App:
imgui.same_line()
imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]")
imgui.tree_pop()
imgui.end_table()
# Context Composition collasping header