fix(gui): Restore discussion tinting and fix Markdown table width
- Implement layered tinting using draw_list channels in modular discussion renderer. - Fix vertical squashing of Markdown tables by forcing full group width with a dummy. - Consolidate color constants into src/ui_shared.py to prevent circular imports. - Update src/theme_2.py with role-based tint helpers. - Successfully verified imports and layout logic.
This commit is contained in:
@@ -4,32 +4,71 @@ import re
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from src import imgui_scopes as imscope, theme_2 as theme, project_manager, mcp_client, ui_shared
|
||||
from src import imscope, theme_2 as 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_thinking_trace(app: 'App', entry: dict, segments: list[dict], entry_index: int, is_standalone: bool = False) -> None:
|
||||
if not segments:
|
||||
return
|
||||
with imscope.style_color(imgui.Col_.child_bg, ui_shared.vec4(40, 35, 25, 180)), \
|
||||
theme.ai_text_style():
|
||||
with imscope.indent():
|
||||
show_content = True
|
||||
if not is_standalone:
|
||||
header_label = f"Monologue ({len(segments)} traces)###thinking_header_{entry_index}"
|
||||
show_content = imgui.collapsing_header(header_label)
|
||||
if show_content:
|
||||
thinking_read_mode = entry.get("thinking_read_mode", True)
|
||||
if imgui.button(f"[Pure]##think_pure_{entry_index}" if thinking_read_mode else f"[Read]##think_read_{entry_index}"):
|
||||
entry["thinking_read_mode"] = not thinking_read_mode
|
||||
imgui.same_line()
|
||||
imgui.text_colored(ui_shared.vec4(180, 150, 80), "Selectable toggle")
|
||||
h = 150 if is_standalone else 100
|
||||
with imscope.child(f"thinking_content_{entry_index}", 0, h, True):
|
||||
for idx, seg in enumerate(segments):
|
||||
content = seg.get("content", "")
|
||||
marker = seg.get("marker", "thinking")
|
||||
with imscope.id(f"think_{entry_index}_{idx}"):
|
||||
imgui.text_colored(ui_shared.vec4(180, 150, 80), f"[{marker}]")
|
||||
if thinking_read_mode:
|
||||
if app.ui_word_wrap:
|
||||
with imscope.text_wrap(imgui.get_content_region_avail().x):
|
||||
imgui.text(content)
|
||||
else:
|
||||
imgui.text(content)
|
||||
else:
|
||||
ui_shared.render_selectable_label(app, f"think_text_{entry_index}_{idx}", content, multiline=True, height=-1)
|
||||
imgui.separator()
|
||||
|
||||
def render_discussion_entry(app: 'App', entry: dict, index: int) -> None:
|
||||
with imscope.id(f"disc_{index}"):
|
||||
role = entry.get("role", "User")
|
||||
bg_col = theme.get_role_tint(role)
|
||||
|
||||
# Simplified header row
|
||||
draw_list = imgui.get_window_draw_list()
|
||||
p_min = imgui.get_cursor_screen_pos()
|
||||
full_width = imgui.get_content_region_avail().x
|
||||
|
||||
# Start Background Layer
|
||||
draw_list.channels_split(2)
|
||||
draw_list.channels_set_current(1) # Foreground
|
||||
|
||||
imgui.begin_group()
|
||||
# Force group to take full width to prevent squashing
|
||||
imgui.dummy(imgui.ImVec2(full_width, 0))
|
||||
|
||||
# Header controls
|
||||
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
|
||||
@@ -38,15 +77,11 @@ def render_discussion_entry(app: 'App', entry: dict, index: int) -> None:
|
||||
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 ts_str: imgui.text_colored(ui_shared.C_SUB, 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)
|
||||
imgui.same_line(); imgui.text_colored(ui_shared.vec4(100, 150, 180), u_str)
|
||||
|
||||
if collapsed:
|
||||
imgui.same_line()
|
||||
@@ -54,18 +89,20 @@ def render_discussion_entry(app: 'App', entry: dict, index: int) -> None:
|
||||
imgui.same_line()
|
||||
if imgui.button("Del"):
|
||||
if entry in app.disc_entries: app.disc_entries.remove(entry)
|
||||
draw_list.channels_merge()
|
||||
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)
|
||||
imgui.text_colored(ui_shared.vec4(160, 160, 150), preview)
|
||||
else:
|
||||
# Body content
|
||||
imgui.spacing()
|
||||
|
||||
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)
|
||||
render_thinking_trace(app, entry, thinking_segments, index, is_standalone=not has_content)
|
||||
imgui.spacing()
|
||||
|
||||
if read_mode:
|
||||
@@ -74,6 +111,16 @@ def render_discussion_entry(app: 'App', entry: dict, index: int) -> None:
|
||||
if not (bool(thinking_segments) and not has_content):
|
||||
ch, entry["content"] = imgui.input_text_multiline("##content", entry["content"], imgui.ImVec2(-1, 150))
|
||||
|
||||
imgui.end_group()
|
||||
|
||||
# 2. Draw Background Rectangle
|
||||
draw_list.channels_set_current(0) # Background
|
||||
p_max = imgui.get_item_rect_max()
|
||||
# Ensure full width coverage
|
||||
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()
|
||||
|
||||
imgui.separator()
|
||||
|
||||
def render_discussion_entry_read_mode(app: 'App', entry: dict, index: int) -> None:
|
||||
@@ -81,7 +128,6 @@ def render_discussion_entry_read_mode(app: 'App', entry: dict, index: int) -> No
|
||||
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:
|
||||
@@ -100,15 +146,14 @@ def render_discussion_entry_read_mode(app: 'App', entry: dict, index: int) -> No
|
||||
pattern = re.compile(r"\[Definition: (.*?) from (.*?) \(line (\d+)\)\](\s+```[\s\S]*?```)?")
|
||||
matches = list(pattern.finditer(content))
|
||||
|
||||
from src import markdown_helper
|
||||
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):
|
||||
@@ -118,6 +163,4 @@ def render_discussion_entry_read_mode(app: 'App', entry: dict, index: int) -> No
|
||||
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")
|
||||
if after: markdown_helper.render(after, context_id=f"disc_{index}_a")
|
||||
|
||||
Reference in New Issue
Block a user