Private
Public Access
0
0

UX UX UX UX UX

This commit is contained in:
2026-06-02 02:58:33 -04:00
parent 964b5c5aa4
commit e9ff6efe20
6 changed files with 57 additions and 325 deletions
+34 -304
View File
@@ -2632,24 +2632,22 @@ def render_files_and_media(app: App) -> None:
"""
[C: tests/test_gui_fast_render.py:test_render_files_and_media_fast]
"""
avail = imgui.get_content_region_avail().y
if not hasattr(app, 'files_screenshots_split'): app.files_screenshots_split = 0.65
split_y = int(avail * app.files_screenshots_split)
if imgui.collapsing_header("Files", imgui.TreeNodeFlags_.default_open):
with imscope.child("Files_child", -1, split_y, True):
if not hasattr(app, 'files_last_selected'): app.files_last_selected = -1
with imscope.table("files_table", 3, imgui.TableFlags_.resizable | imgui.TableFlags_.borders):
imgui.table_setup_column("", imgui.TableColumnFlags_.width_fixed, 25)
with imscope.group():
if imgui.begin_table("files_table", 3, imgui.TableFlags_.resizable | imgui.TableFlags_.borders | imgui.TableFlags_.row_bg):
imgui.table_setup_column("Act", imgui.TableColumnFlags_.width_fixed, 60)
imgui.table_setup_column("Path", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Status", imgui.TableColumnFlags_.width_fixed, 60)
imgui.table_setup_column("Status", imgui.TableColumnFlags_.width_fixed, 70)
imgui.table_headers_row()
to_remove_idx = -1
app.files.sort(key=lambda f: f.path.lower() if hasattr(f, 'path') else str(f).lower())
for i, f_item in enumerate(app.files):
imgui.table_next_row(); imgui.table_set_column_index(0)
imgui.table_next_row()
imgui.table_set_column_index(0)
fpath = f_item.path if hasattr(f_item, 'path') else str(f_item)
in_context = any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in app.context_files)
is_cached = any(fpath in c for c in getattr(app, '_cached_files', []))
if imgui.button(f"+##add_f_{i}"):
if not in_context:
@@ -2657,157 +2655,48 @@ def render_files_and_media(app: App) -> None:
new_item = models.FileItem(path=fpath)
app.context_files.append(new_item)
app._populate_auto_slices(new_item)
imgui.same_line()
if imgui.button(f"x##rem_f_{i}"):
to_remove_idx = i
imgui.table_set_column_index(1); imgui.text(fpath)
imgui.table_set_column_index(1)
imgui.text(fpath)
if imgui.is_item_hovered(): imgui.set_tooltip(fpath)
imgui.table_set_column_index(2)
if in_context:
imgui.text_colored(imgui.ImVec4(0.3, 0.8, 0.3, 1), "Active")
elif is_cached:
imgui.text_colored(imgui.ImVec4(0.3, 0.8, 1, 1), "Cached")
else:
imgui.text_disabled(" - ")
if is_cached: imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), "Y")
else: imgui.text_colored(imgui.ImVec4(0.5, 0.5, 0.5, 1), "-")
if imgui.button("Add Files to Inventory"):
r = hide_tk_root(); paths = filedialog.askopenfilenames(); r.destroy()
for p in paths:
if p not in [f.path if hasattr(f, 'path') else f for f in app.files]: app.files.append(models.FileItem(path=p))
imgui.same_line()
if imgui.button("Clear Selection##inv"):
for f in app.files: f.selected = False
imgui.same_line()
if imgui.button("Remove from Inventory"):
app.files = [f for f in app.files if not f.selected]
imgui.end_table()
if to_remove_idx != -1: app.files.pop(to_remove_idx)
imgui.dummy(imgui.ImVec2(0, 5))
if imgui.button("Add Files to Inventory"):
r = hide_tk_root(); paths = filedialog.askopenfilenames(); r.destroy()
for p in paths:
if p not in [f.path if hasattr(f, 'path') else f for f in app.files]: app.files.append(models.FileItem(path=p))
imgui.separator()
if imgui.collapsing_header("Screenshots", imgui.TreeNodeFlags_.default_open):
with imscope.child("Shots_child", -1, -1, True):
with imscope.child("Shots_child", -1, 150, True):
to_rem_shot = -1
for i, s in enumerate(app.screenshots):
if imgui.button(f"x##s{i}"):
app.screenshots.pop(i)
break
if imgui.button(f"x##s{i}"): to_rem_shot = i
imgui.same_line(); imgui.text(s)
if to_rem_shot != -1: app.screenshots.pop(to_rem_shot)
if imgui.button("Add Screenshots##adds"):
r = hide_tk_root(); paths = filedialog.askopenfilenames(filetypes=[("Images", "*.png *.jpg *.jpeg *.gif *.bmp *.webp"), ("All", "*.*")]); r.destroy()
for p in paths:
if p not in app.screenshots: app.screenshots.append(p)
return
def render_files_panel(app: App, height_override: float = 0) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_files_panel")
imgui.text("Paths")
imgui.same_line()
imgui.text("| Base Dir:")
imgui.same_line()
imgui.set_next_item_width(-100)
ch, app.ui_files_base_dir = imgui.input_text("##f_base", app.ui_files_base_dir)
imgui.same_line()
if imgui.button("Browse##fb"):
r = hide_tk_root()
d = filedialog.askdirectory()
r.destroy()
if d: app.ui_files_base_dir = d
imgui.separator()
# Calculate content-based height: use override if provided, else content-based
if height_override > 0:
child_h = height_override
else:
row_count = max(len(app.files), 1)
child_h = min(row_count * 28 + 40, 300)
# BEGIN f_paths child window
imgui.begin_child("f_paths", imgui.ImVec2(0, child_h), True)
if imgui.begin_table("files_table", 4, imgui.TableFlags_.resizable | imgui.TableFlags_.borders):
imgui.table_setup_column("Actions", imgui.TableColumnFlags_.width_fixed, 40)
imgui.table_setup_column("File Path", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 150)
imgui.table_setup_column("Cache", imgui.TableColumnFlags_.width_fixed, 40)
imgui.table_headers_row()
for i, f_item in enumerate(app.files):
imgui.table_next_row()
# Actions
imgui.table_set_column_index(0)
if imgui.button(f"x##f{i}"):
app.files.pop(i)
break
# File Path
imgui.table_set_column_index(1)
imgui.text(f_item.path if hasattr(f_item, "path") else str(f_item))
# Flags
imgui.table_set_column_index(2)
if hasattr(f_item, "auto_aggregate"):
changed_agg, f_item.auto_aggregate = imgui.checkbox(f"Agg##a{i}", f_item.auto_aggregate)
imgui.same_line()
changed_full, f_item.force_full = imgui.checkbox(f"Full##f{i}", f_item.force_full)
# Cache
imgui.table_set_column_index(3)
path = f_item.path if hasattr(f_item, "path") else str(f_item)
is_cached = any(path in c for c in getattr(app, "_cached_files", []))
if is_cached:
imgui.text_colored("", imgui.ImVec4(0, 1, 0, 1)) # Green dot
else:
imgui.text_disabled("")
imgui.end_table()
imgui.end_child()
if imgui.button("Add File(s)"):
r = hide_tk_root()
paths = filedialog.askopenfilenames()
r.destroy()
for p in paths:
if p not in [f.path if hasattr(f, "path") else f for f in app.files]:
app.files.append(models.FileItem(path=p))
imgui.same_line()
if imgui.button("Add Wildcard"):
r = hide_tk_root()
d = filedialog.askdirectory()
r.destroy()
if d: app.files.append(models.FileItem(path=str(Path(d) / "**" / "*")))
imgui.separator()
from src import summarize
stats = summarize._summary_cache.get_stats()
imgui.text_disabled(f"Summary Cache: {stats['entries']} entries ({stats['size_bytes']} bytes)")
imgui.same_line()
if imgui.button("Clear Summary Cache##btn_clear_summary_cache"):
app.controller._cb_clear_summary_cache()
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_files_panel")
def render_screenshots_panel(app: App, height_override: float = 0) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_screenshots_panel")
imgui.text("Paths"); imgui.same_line(); imgui.text("| Base Dir:"); imgui.same_line();
imgui.set_next_item_width(-100)
ch, app.ui_shots_base_dir = imgui.input_text("##s_base", app.ui_shots_base_dir)
imgui.same_line()
if imgui.button("Browse##sb"):
r = hide_tk_root(); d = filedialog.askdirectory(); r.destroy()
if d: app.ui_shots_base_dir = d
imgui.separator()
# Calculate content-based height: use override if provided, else content-based
if height_override > 0: shot_h = height_override
else:
shot_count = max(len(app.screenshots), 1)
shot_h = min(shot_count * 28 + 40, 200)
# BEGIN s_paths child window
imgui.begin_child("s_paths", imgui.ImVec2(0, shot_h), True)
for i, s in enumerate(app.screenshots):
if imgui.button(f"x##s{i}"):
app.screenshots.pop(i)
break
imgui.same_line(); imgui.text(s)
imgui.end_child()
if imgui.button("Add Screenshot(s)"):
r = hide_tk_root()
paths = filedialog.askopenfilenames(
title="Select Screenshots",
filetypes=[("Images", "*.png *.jpg *.jpeg *.gif *.bmp *.webp"), ("All", "*.*")],
)
r.destroy()
for p in paths:
if p not in app.screenshots: app.screenshots.append(p)
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_screenshots_panel")
#endregion: Context Management
def render_context_batch_actions(app: App, total_lines: int, total_ast: int) -> None:
imgui.text("Batch:")
@@ -2914,166 +2803,7 @@ def render_context_composition_panel(app: App) -> None:
render_context_screenshots(app)
def render_ast_inspector_modal(app: App) -> None:
"""
[C: tests/test_ast_inspector_extended.py:test_ast_inspector_line_range_parsing]
"""
if app._show_ast_inspector:
imgui.open_popup('AST Inspector')
app._show_ast_inspector = False
#region: AST Inspector
imgui.set_next_window_size(imgui.ImVec2(1200, 800), imgui.Cond_.first_use_ever)
expanded, opened = imgui.begin_popup_modal('AST Inspector', True, imgui.WindowFlags_.none)
if opened:
if expanded:
if app.ui_inspecting_ast_file is None:
imgui.close_current_popup()
else:
f_item = app.ui_inspecting_ast_file
f_path = f_item.path if hasattr(f_item, "path") else str(f_item)
if f_path != app._cached_ast_file_path:
outline = ""
try:
from src import mcp_client
from pathlib import Path
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)
else: 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
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])
app._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)
app._cached_ast_file_lines = content.splitlines()
except Exception:
app._cached_ast_file_lines = ["Error loading file content."]
app._cached_ast_file_path = f_path
imgui.text(f"Inspecting AST: {f_path}")
imgui.separator()
avail = imgui.get_content_region_avail()
table_height = max(100.0, avail.y - imgui.get_frame_height_with_spacing() - 10)
#region: ast_dual_pane
if imgui.begin_table('ast_dual_pane', 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v, imgui.ImVec2(0, table_height)):
imgui.table_next_column()
#region: LEFT COLUMN (Tree) ---
imgui.begin_child("ast_tree_scroll", imgui.ImVec2(0, 0), True)
if True:
if not app._cached_ast_nodes: imgui.text("No AST nodes found or error fetching outline.")
else:
for node in app._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}")
if imgui.is_item_hovered():
app._hovered_ast_node = full_path
btn_width = 150
avail_width = imgui.get_content_region_avail().x
try:
do_align = avail_width > btn_width
except TypeError:
do_align = False
if do_align:
imgui.same_line(imgui.get_window_width() - btn_width)
else:
imgui.same_line()
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) ---
imgui.begin_child("ast_content_scroll", imgui.ImVec2(0, 0), True)
if True:
if not hasattr(app, '_cached_ast_file_lines') or not app._cached_ast_file_lines:
imgui.text("No file content loaded.")
else:
draw_list = imgui.get_window_draw_list()
for i, line_text in enumerate(app._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 app._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)))
elif deepest_node and deepest_node['full_path'] == getattr(app, '_hovered_ast_node', None):
# Yellow, alpha 0.3 for hover
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(255, 255, 0, 0.3)))
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)):
app.ui_inspecting_ast_file = None
imgui.close_current_popup()
imgui.end_popup()
#endregion: AST Inspector
if not opened: app.ui_inspecting_ast_file = None
pass
def render_save_workspace_profile_modal(app: App) -> None:
if app._show_save_workspace_profile_modal: