Private
Public Access
0
0

fix(gui): Final monolithic stabilization and UI polish

- Restore monolithic architecture in gui_2.py to fix test compatibility.
- Implement full-width horizontal expansion for Markdown tables in discussion entries.
- Re-implement layered role-based tints using draw_list channels.
- Standardize Text Viewer docking ID to '###Text_Viewer_Unified'.
- Fix MiniMax compression routing and base URL.
- Fully restore missing theme_2.py definitions.
This commit is contained in:
2026-06-02 18:04:49 -04:00
parent 7eb8f9eed4
commit 8733528f67
5 changed files with 226 additions and 109 deletions
+45 -18
View File
@@ -221,6 +221,9 @@ class App:
self._pending_save_anyway_click = False
self.show_missing_files_modal = False
self.show_structural_editor_modal = False
self.show_command_palette = False
self.ui_command_search = ""
self.ui_command_idx = 0
self.missing_context_files = []
self._new_preset_name = ""
self._editing_preset_name = ""
@@ -322,6 +325,10 @@ class App:
self.ui_crt_filter = False
self.ui_tool_filter_category = "All"
self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8}
self._is_generating_preview = False
self._pending_preview_refresh = False
self._preview_refresh_timer: float = 0.0
self._preview_refresh_debounce: float = 0.5
self._hot_reload_error: Optional[str] = None
def _set_context_files(self, paths: list[str]) -> None:
@@ -3371,12 +3378,12 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
p_min = imgui.get_cursor_screen_pos()
full_width = imgui.get_content_region_avail().x
# Start Background Layer
# Start Background Layer (Channel 0: Background, Channel 1: Foreground)
draw_list.channels_split(2)
draw_list.channels_set_current(1) # Foreground
draw_list.channels_set_current(1)
imgui.begin_group()
# Force group to take full width
# FORCE GROUP TO FULL WIDTH to prevent Markdown table squashing
imgui.dummy(imgui.ImVec2(full_width, 0))
# Header controls
@@ -3417,9 +3424,9 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
if len(entry["content"]) > 60: preview += "..."
imgui.text_colored(C_SUB, preview)
else:
# Body content - FORCE START ON NEW LINE
# Body content - FORCE START ON NEW LINE to prevent horizontal squashing
imgui.new_line()
imgui.set_cursor_pos_x(imgui.get_cursor_start_pos().x)
imgui.spacing()
thinking_segments, has_content = entry.get("thinking_segments", []), bool(entry.get("content", "").strip())
if thinking_segments:
@@ -3430,6 +3437,8 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
render_discussion_entry_read_mode(app, entry, index)
else:
if not (bool(thinking_segments) and not has_content):
# Ensure multiline editor uses full width
imgui.set_next_item_width(-1)
ch, entry["content"] = imgui.input_text_multiline("##content", entry["content"], imgui.ImVec2(-1, 150))
imgui.end_group()
@@ -3437,7 +3446,7 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
# Finalize Background Tint
draw_list.channels_set_current(0)
p_max = imgui.get_item_rect_max()
# Ensure full width coverage
# Ensure full width coverage of the panel
p_max.x = p_min.x + full_width + imgui.get_style().window_padding.x
draw_list.add_rect_filled(p_min, p_max, imgui.get_color_u32(bg_col), 4.0)
draw_list.channels_merge()
@@ -3467,6 +3476,7 @@ def render_discussion_entry_read_mode(app: App, entry: dict, index: int) -> None
pattern = re.compile(r"\[Definition: (.*?) from (.*?) \(line (\d+)\)\](\s+```[\s\S]*?```)?")
matches = list(pattern.finditer(content))
# Provide a stable width by using a group and ensuring it starts on a new line
imgui.begin_group()
with theme.ai_text_style():
if not matches:
@@ -4173,7 +4183,7 @@ def render_text_viewer_window(app: App) -> None:
"""Renders the standalone text/code/markdown viewer window."""
if not app.show_windows.get("Text Viewer", False): return
imgui.set_next_window_size(imgui.ImVec2(900, 700), imgui.Cond_.first_use_ever)
# Use a unique stable ID string to clear any legacy docking conflicts
# Force a unique ID to clear legacy docking corruption
expanded, opened = imgui.begin(f"{app.text_viewer_title or 'Text Viewer'}###Text_Viewer_Unified", True, imgui.WindowFlags_.no_collapse)
app.show_windows["Text Viewer"] = bool(opened)
if not opened:
@@ -4217,7 +4227,6 @@ def render_text_viewer_window(app: App) -> None:
if imgui.button("Close"): imgui.close_current_popup()
imgui.end_popup()
imgui.separator()
to_remove = -1
tags = app.controller.project.get("context_tags", ["auto-ast", "bug", "feature", "important"])
for idx, slc in enumerate(app.ui_editing_slices_file.custom_slices):
@@ -4225,13 +4234,13 @@ def render_text_viewer_window(app: App) -> None:
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(150)
ch_tag, new_tag_idx = imgui.combo("Category/Tag", tag_idx, tags)
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(300); changed_comm, new_comm = imgui.input_text("Note/Comment", slc.get('comment', ''))
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("Remove"): to_remove = idx
if imgui.button("X"): to_remove = idx
imgui.pop_id()
if to_remove != -1: app.ui_editing_slices_file.custom_slices.pop(to_remove)
imgui.separator()
@@ -5375,12 +5384,30 @@ def _check_auto_refresh_context_preview(app: App) -> None:
if not any(getattr(f, 'auto_aggregate', False) for f in app.context_files) and not app.screenshots:
app.context_preview_text = "# Context Composition Empty\n\nNo files or screenshots have been selected for aggregation."
return
try:
app.controller.context_files = app.context_files
res = app.controller._do_generate()
app.context_preview_text = res[0]
except Exception:
app.context_preview_text = "Error generating context preview."
if getattr(app, "_is_generating_preview", False):
app._pending_preview_refresh = True
return
app._is_generating_preview = True
def worker():
try:
app.controller.context_files = app.context_files
res = app.controller._do_generate()
app.context_preview_text = res[0]
except Exception:
app.context_preview_text = "Error generating context preview."
finally:
app._is_generating_preview = False
if getattr(app, "_pending_preview_refresh", False):
app._pending_preview_refresh = False
# This will trigger again on next GUI frame because _last_context_preview_state
# will be slightly behind if another change happened during the thread.
# Or we just clear the state so it re-triggers.
app._last_context_preview_state = None
import threading
threading.Thread(target=worker, daemon=True).start()
def render_context_preview_window(app: App) -> None:
_check_auto_refresh_context_preview(app)