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:
+45
-18
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user