Private
Public Access
0
0

fix(gui): Resolve Markdown squashing, MiniMax compression error, and UI import issues

- Modularize discussion entry rendering to src/discussion_entry_renderer.py to fix layout squashing.
- Fix MiniMax compression routing with robust case-insensitive check and synced base URL.
- Implement src/ui_shared.py to resolve circular imports and consolidate shared UI helpers.
- Finalize Structural File Editor integration and state unification.
This commit is contained in:
2026-06-02 03:28:09 -04:00
parent f116f027cf
commit 9d6fca0e42
4 changed files with 156 additions and 135 deletions
+123
View File
@@ -0,0 +1,123 @@
from __future__ import annotations
from imgui_bundle import imgui
import re
import datetime
from pathlib import Path
from typing import TYPE_CHECKING, Any
from src import imscope, theme, project_manager, mcp_client, ui_shared
if TYPE_CHECKING:
from src.gui_2 import App
def vec4(r: float, g: float, b: float, a: float = 1.0) -> imgui.ImVec4:
return imgui.ImVec4(r/255, g/255, b/255, a)
def render_discussion_entry(app: 'App', entry: dict, index: int) -> None:
with imscope.id(f"disc_{index}"):
role = entry.get("role", "User")
# Simplified header row
collapsed, read_mode = entry.get("collapsed", False), entry.get("read_mode", False)
if imgui.button("+" if collapsed else "-"): entry["collapsed"] = not collapsed
imgui.same_line()
ui_shared.render_text_viewer(app, f"Entry #{index+1}", entry["content"], id_suffix=f"disc_btn_{index}")
imgui.same_line(); imgui.set_next_item_width(120)
if imgui.begin_combo("##role", entry["role"]):
for r in app.disc_roles:
if imgui.selectable(r, r == entry["role"])[0]: entry["role"] = r
imgui.end_combo()
if not collapsed:
imgui.same_line()
if imgui.button("[Edit]" if read_mode else "[Read]"): entry["read_mode"] = not read_mode
ts_str = entry.get("ts", "")
usage = entry.get("usage", {})
if ts_str or usage:
imgui.same_line()
if ts_str: imgui.text_colored(vec4(120, 120, 100), str(ts_str))
if usage:
inp, out, cache = usage.get("input_tokens", 0), usage.get("output_tokens", 0), usage.get("cache_read_input_tokens", 0)
u_str = f" in:{inp} out:{out}" + (f" cache:{cache}" if cache else "")
imgui.same_line(); imgui.text_colored(vec4(100, 150, 180), u_str)
# CRITICAL: Force a newline to ensure any content has full width
imgui.spacing()
imgui.set_cursor_pos_x(imgui.get_cursor_start_pos().x)
if collapsed:
imgui.same_line()
if imgui.button("Ins"): app.disc_entries.insert(index, {"role": "User", "content": "", "collapsed": True, "ts": project_manager.now_ts()})
imgui.same_line()
if imgui.button("Del"):
if entry in app.disc_entries: app.disc_entries.remove(entry)
return
imgui.same_line()
if imgui.button("Branch"): app._branch_discussion(index)
imgui.same_line(); preview = entry["content"].replace("\n", " ")[:60]
if len(entry["content"]) > 60: preview += "..."
imgui.text_colored(vec4(160, 160, 150), preview)
else:
thinking_segments, has_content = entry.get("thinking_segments", []), bool(entry.get("content", "").strip())
if thinking_segments:
# render_thinking_trace is currently in gui_2.py
# We'll just call the App method for now
app.render_thinking_trace(app, entry, thinking_segments, index, is_standalone=not has_content)
imgui.spacing()
if read_mode:
render_discussion_entry_read_mode(app, entry, index)
else:
if not (bool(thinking_segments) and not has_content):
ch, entry["content"] = imgui.input_text_multiline("##content", entry["content"], imgui.ImVec2(-1, 150))
imgui.separator()
def render_discussion_entry_read_mode(app: 'App', entry: dict, index: int) -> None:
with imscope.id(f"read_{index}"):
content = entry["content"]
if not content.strip(): return
# Special RAG check (simplified for now to match main branch)
if '## Retrieved Context' in content:
rag_match = re.search(r'## Retrieved Context\n\n([\s\S]*?)(?=\n\n#|\Z)', content)
if rag_match:
rag_section = rag_match.group(1)
if imgui.collapsing_header('Retrieved Context'):
chunks = re.finditer(r'### Chunk (\d+) \(Source: (.*?)\)\n([\s\S]*?)(?=\n### Chunk|\Z)', rag_section)
for chunk_match in chunks:
idx, path, chunk_content = chunk_match.group(1), chunk_match.group(2), chunk_match.group(3)
if imgui.collapsing_header(f'Chunk {idx}: {path}'):
if imgui.button(f'[Source]##rag_{index}_{idx}'):
res = mcp_client.read_file(path)
if res: app.text_viewer_title, app.text_viewer_content, app.text_viewer_type = path, res, (Path(path).suffix.lstrip('.') if Path(path).suffix else 'text'); app.show_windows["Text Viewer"] = True
imgui.text_unformatted(chunk_content)
content = content[:rag_match.start()] + content[rag_match.end():]
pattern = re.compile(r"\[Definition: (.*?) from (.*?) \(line (\d+)\)\](\s+```[\s\S]*?```)?")
matches = list(pattern.finditer(content))
with theme.ai_text_style():
if not matches:
from src import markdown_helper
markdown_helper.render(content, context_id=f"disc_{index}")
else:
last_idx = 0
for m_idx, match in enumerate(matches):
before = content[last_idx:match.start()]
from src import markdown_helper
if before: markdown_helper.render(before, context_id=f"disc_{index}_b_{m_idx}")
header_text, path, code_block = match.group(0).split("\n")[0].strip(), match.group(2), match.group(4)
if imgui.collapsing_header(header_text):
if imgui.button(f"[Source]##{index}_{match.start()}"):
res = mcp_client.read_file(path)
if res: app.text_viewer_title, app.text_viewer_content, app.text_viewer_type = path, res, (Path(path).suffix.lstrip(".") if Path(path).suffix else "text"); app.show_windows["Text Viewer"] = True
if code_block: markdown_helper.render(code_block, context_id=f"disc_{index}_c_{m_idx}")
last_idx = match.end()
after = content[last_idx:]
if after:
from src import markdown_helper
markdown_helper.render(after, context_id=f"disc_{index}_a")