fix(gui): Resolve AttributeError in imscope.indent and stabilize render loop
- Add 'indent' context manager to src/imgui_scopes.py. - Refactor manual imgui.indent/unindent calls in src/gui_2.py to use imscope.indent. - Fix cascading ImGui assertion failures caused by Open/Close mismatches during exceptions. - Finalize 'Selectable Thinking Monologs' track.
This commit is contained in:
+1
-1
@@ -278,5 +278,5 @@ This file tracks all major tracks for the project. Each track has its own detail
|
||||
|
||||
---
|
||||
|
||||
- [~] **Track: Selectable Thinking Monologs**
|
||||
- [x] **Track: Selectable Thinking Monologs**
|
||||
*Link: [./tracks/selectable_thinking_monologs_20260601/](./tracks/selectable_thinking_monologs_20260601/)*
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
- [x] If `thinking_read_mode` is `False` (Pure mode), use `render_selectable_label(app, f"think_text_...", content, multiline=True, height=-1)` to make the text selectable and copyable.
|
||||
|
||||
## Phase 2: Verification
|
||||
- [ ] Task: Verification
|
||||
- [ ] Verify that thinking traces in the Discussion Hub can be toggled between Pure and Read modes.
|
||||
- [ ] Verify that text can be selected and copied via Ctrl+C in Pure mode.
|
||||
- [ ] Verify that the change does not crash other areas rendering thinking traces (like Comms History).
|
||||
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Verification' (Protocol in workflow.md)
|
||||
- [x] Task: Verification
|
||||
- [x] Verify that thinking traces in the Discussion Hub can be toggled between Pure and Read modes.
|
||||
- [x] Verify that text can be selected and copied via Ctrl+C in Pure mode.
|
||||
- [x] Verify that the change does not crash other areas rendering thinking traces (like Comms History).
|
||||
- [x] Task: Conductor - User Manual Verification 'Phase 2: Verification' (Protocol in workflow.md)
|
||||
+44
-46
@@ -2517,26 +2517,25 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
|
||||
imgui.same_line(imgui.get_content_region_avail().x - 30);
|
||||
if imgui.button("x"): to_remove.append(i)
|
||||
if is_expanded:
|
||||
imgui.indent(20)
|
||||
if imgui.begin_table("model_settings", 2, imgui.TableFlags_.borders_inner_v):
|
||||
imgui.table_setup_column("Label", imgui.TableColumnFlags_.width_fixed, 120); imgui.table_setup_column("Control", imgui.TableColumnFlags_.width_stretch)
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column(); imgui.set_next_item_width(-1)
|
||||
p_idx = providers.index(prov) + 1 if prov in providers else 0; ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers)
|
||||
if ch_p: entry["provider"] = providers[p_idx-1] if p_idx > 0 else ""
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column(); imgui.set_next_item_width(-1)
|
||||
m_list = app.controller.all_available_models.get(entry.get("provider", ""), []); m_idx = m_list.index(mod) + 1 if mod in m_list else 0
|
||||
ch_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list)
|
||||
if ch_m: entry["model"] = m_list[m_idx-1] if m_idx > 0 else ""
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Temperature:"); imgui.table_next_column(); cw = imgui.get_content_region_avail().x
|
||||
imgui.set_next_item_width(cw * 0.7); _, entry["temperature"] = imgui.slider_float("##ts", entry.get("temperature", 0.7), 0.0, 2.0, "%.1f")
|
||||
imgui.same_line(); imgui.set_next_item_width(-1); _, entry["temperature"] = imgui.input_float("##ti", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f")
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Top-P:"); imgui.table_next_column()
|
||||
imgui.set_next_item_width(cw * 0.7); _, entry["top_p"] = imgui.slider_float("##tp_s", entry.get("top_p", 1.0), 0.0, 1.0, "%.2f")
|
||||
imgui.same_line(); imgui.set_next_item_width(-1); _, entry["top_p"] = imgui.input_float("##tp_i", entry.get("top_p", 1.0), 0.05, 0.05, "%.2f")
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Max Tokens:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096))
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("History Limit:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["history_trunc_limit"] = imgui.input_int("##hist", entry.get("history_trunc_limit", 900000))
|
||||
imgui.end_table()
|
||||
imgui.unindent(20)
|
||||
with imscope.indent(20):
|
||||
if imgui.begin_table("model_settings", 2, imgui.TableFlags_.borders_inner_v):
|
||||
imgui.table_setup_column("Label", imgui.TableColumnFlags_.width_fixed, 120); imgui.table_setup_column("Control", imgui.TableColumnFlags_.width_stretch)
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column(); imgui.set_next_item_width(-1)
|
||||
p_idx = providers.index(prov) + 1 if prov in providers else 0; ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers)
|
||||
if ch_p: entry["provider"] = providers[p_idx-1] if p_idx > 0 else ""
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column(); imgui.set_next_item_width(-1)
|
||||
m_list = app.controller.all_available_models.get(entry.get("provider", ""), []); m_idx = m_list.index(mod) + 1 if mod in m_list else 0
|
||||
ch_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list)
|
||||
if ch_m: entry["model"] = m_list[m_idx-1] if m_idx > 0 else ""
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Temperature:"); imgui.table_next_column(); cw = imgui.get_content_region_avail().x
|
||||
imgui.set_next_item_width(cw * 0.7); _, entry["temperature"] = imgui.slider_float("##ts", entry.get("temperature", 0.7), 0.0, 2.0, "%.1f")
|
||||
imgui.same_line(); imgui.set_next_item_width(-1); _, entry["temperature"] = imgui.input_float("##ti", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f")
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Top-P:"); imgui.table_next_column()
|
||||
imgui.set_next_item_width(cw * 0.7); _, entry["top_p"] = imgui.slider_float("##tp_s", entry.get("top_p", 1.0), 0.0, 1.0, "%.2f")
|
||||
imgui.same_line(); imgui.set_next_item_width(-1); _, entry["top_p"] = imgui.input_float("##tp_i", entry.get("top_p", 1.0), 0.05, 0.05, "%.2f")
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Max Tokens:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096))
|
||||
imgui.table_next_row(); imgui.table_next_column(); imgui.text("History Limit:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["history_trunc_limit"] = imgui.input_int("##hist", entry.get("history_trunc_limit", 900000))
|
||||
imgui.end_table()
|
||||
imgui.pop_id()
|
||||
for i in reversed(to_remove): app._editing_persona_preferred_models_list.pop(i)
|
||||
imgui.end_child()
|
||||
@@ -4654,34 +4653,33 @@ def render_thinking_trace(app: App, entry: dict, segments: list[dict], entry_ind
|
||||
return
|
||||
with imscope.style_color(imgui.Col_.child_bg, vec4(40, 35, 25, 180)), \
|
||||
theme.ai_text_style():
|
||||
imgui.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(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(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):
|
||||
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(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(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:
|
||||
imgui.text(content)
|
||||
else:
|
||||
render_selectable_label(app, f"think_text_{entry_index}_{idx}", content, multiline=True, height=-1)
|
||||
imgui.separator()
|
||||
imgui.unindent()
|
||||
render_selectable_label(app, f"think_text_{entry_index}_{idx}", content, multiline=True, height=-1)
|
||||
imgui.separator()
|
||||
|
||||
def render_selectable_label(app: App, label: str, value: str, width: float = 0.0, multiline: bool = False, height: float = 0.0, color: Optional[imgui.ImVec4] = None) -> None:
|
||||
with imscope.id(label + str(hash(value))):
|
||||
|
||||
+12
-1
@@ -1,4 +1,5 @@
|
||||
from __future__ import annotations
|
||||
from typing import Any
|
||||
from imgui_bundle import imgui
|
||||
from imgui_bundle import imgui_node_editor
|
||||
|
||||
@@ -43,6 +44,16 @@ class _ScopeId:
|
||||
imgui.pop_id()
|
||||
return False
|
||||
|
||||
def indent(width: float = 0.0): return _ScopeIndent(width)
|
||||
class _ScopeIndent:
|
||||
def __init__(self, width: float):
|
||||
self._width = width
|
||||
def __enter__(self):
|
||||
imgui.indent(self._width)
|
||||
def __exit__(self, *args):
|
||||
imgui.unindent(self._width)
|
||||
return False
|
||||
|
||||
def menu(label: str): return _ScopeMenu(label)
|
||||
class _ScopeMenu:
|
||||
def __init__(self, label: str):
|
||||
@@ -256,4 +267,4 @@ class _ScopeWindow:
|
||||
return self._result
|
||||
def __exit__(self, *args):
|
||||
imgui.end()
|
||||
return False
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user