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 os
import shutil import shutil
import copy import copy
import threading
from pathlib import Path from pathlib import Path
from tkinter import filedialog, Tk from tkinter import filedialog, Tk
from typing import Optional, Any from typing import Optional, Any
@@ -1769,133 +1770,144 @@ class App:
if self._show_ast_inspector: if self._show_ast_inspector:
imgui.open_popup('AST Inspector') imgui.open_popup('AST Inspector')
self._show_ast_inspector = False 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}") #region: AST Inspector
imgui.separator() 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): if not opened:
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)):
self.ui_inspecting_ast_file = None self.ui_inspecting_ast_file = None
imgui.close_current_popup()
imgui.end_popup()
def _render_save_workspace_profile_modal(self) -> None: def _render_save_workspace_profile_modal(self) -> None:
if self._show_save_workspace_profile_modal: if self._show_save_workspace_profile_modal:
@@ -1929,26 +1941,25 @@ class App:
def _render_add_context_files_modal(self) -> None: def _render_add_context_files_modal(self) -> None:
if imgui.begin_popup_modal("Select Context Files", None, imgui.WindowFlags_.always_auto_resize)[0]: 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.text("Select files from project to add to context:")
imgui.begin_child("ctx_picker_list", imgui.ImVec2(600, 300), True) if imgui.begin_child("ctx_picker_list", imgui.ImVec2(600, 300), True):
from src import models
from src import models # Create a temporary selection set if not initialized
# Create a temporary selection set if not initialized if not hasattr(self, '_ui_picker_selected'):
if not hasattr(self, '_ui_picker_selected'): self._ui_picker_selected = set()
self._ui_picker_selected = set()
for f in self.files:
for f in self.files: fpath = f.path if hasattr(f, 'path') else str(f)
fpath = f.path if hasattr(f, 'path') else str(f) # Skip if already in context
# Skip if already in context if any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in self.context_files):
if any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in self.context_files): continue
continue is_sel = fpath in self._ui_picker_selected
is_sel = fpath in self._ui_picker_selected clicked, new_sel = imgui.checkbox(f"{fpath}##picker_{fpath}", is_sel)
clicked, new_sel = imgui.checkbox(f"{fpath}##picker_{fpath}", is_sel) if clicked:
if clicked: if new_sel:
if new_sel: self._ui_picker_selected.add(fpath)
self._ui_picker_selected.add(fpath) else:
else: self._ui_picker_selected.discard(fpath)
self._ui_picker_selected.discard(fpath) imgui.end_child()
imgui.end_child()
imgui.separator() imgui.separator()
if imgui.button("Add Selected", imgui.ImVec2(120, 0)): if imgui.button("Add Selected", imgui.ImVec2(120, 0)):
@@ -2993,11 +3004,11 @@ class App:
self._file_stats_queue = [] self._file_stats_queue = []
if not hasattr(self, '_file_stats_worker_active'): if not hasattr(self, '_file_stats_worker_active'):
self._file_stats_worker_active = False self._file_stats_worker_active = False
if imgui.collapsing_header("Context Composition"): if imgui.collapsing_header("Context Composition"):
total_lines = 0 total_lines = 0
total_ast = 0 total_ast = 0
missing_keys = [] missing_keys = []
for f in self.context_files: for f in self.context_files:
f_path = f.path if hasattr(f, "path") else str(f) f_path = f.path if hasattr(f, "path") else str(f)
@@ -3009,7 +3020,7 @@ class App:
stats = self._file_stats_cache[cache_key] stats = self._file_stats_cache[cache_key]
total_lines += stats.get("lines", 0) total_lines += stats.get("lines", 0)
total_ast += stats.get("ast_elements", 0) total_ast += stats.get("ast_elements", 0)
# Process one missing key per frame or spawn a worker # Process one missing key per frame or spawn a worker
if missing_keys and not self._file_stats_worker_active: if missing_keys and not self._file_stats_worker_active:
def _stats_worker(): def _stats_worker():
@@ -3020,12 +3031,11 @@ class App:
self._file_stats_cache[key] = aggregate.compute_file_stats(path) self._file_stats_cache[key] = aggregate.compute_file_stats(path)
finally: finally:
self._file_stats_worker_active = False self._file_stats_worker_active = False
import threading
threading.Thread(target=_stats_worker, daemon=True).start() threading.Thread(target=_stats_worker, daemon=True).start()
#region: Batch Action Bar imgui.text("Batch:") #region: Batch Action Bar imgui.text("Batch:")
imgui.same_line() # imgui.same_line()
for mode in ["full", "summary", "skeleton", "outline", "masked", "none"]: for mode in ["full", "summary", "skeleton", "outline", "masked", "none"]:
if imgui.button(f"{mode.capitalize()}##batch"): if imgui.button(f"{mode.capitalize()}##batch"):
for f in self.context_files: for f in self.context_files:
@@ -3067,9 +3077,9 @@ class App:
#endregion: Batch Action Bar #endregion: Batch Action Bar
imgui.dummy(imgui.ImVec2(0, 4)) imgui.dummy(imgui.ImVec2(0, 4))
grouped_files = aggregate.group_files_by_dir(self.context_files) grouped_files = aggregate.group_files_by_dir(self.context_files)
if imgui.begin_table("ctx_comp_table", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders): 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("File", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200) imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200)
@@ -3180,6 +3190,7 @@ class App:
imgui.same_line() imgui.same_line()
imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]") imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]")
imgui.tree_pop() imgui.tree_pop()
imgui.end_table() imgui.end_table()
# Context Composition collasping header # Context Composition collasping header