feat(ui): Implement selectable text across primary GUI panels
This commit is contained in:
114
src/gui_2.py
114
src/gui_2.py
@@ -211,33 +211,34 @@ class App:
|
||||
if len(content) > COMMS_CLAMP_CHARS:
|
||||
# Use a fixed-height child window with unformatted text for large text to avoid expensive frame-by-frame wrapping or input_text_multiline overhead
|
||||
imgui.begin_child(f"heavy_text_child_{label}_{hash(content)}", imgui.ImVec2(0, 80), True)
|
||||
imgui.text_unformatted(content)
|
||||
self._render_selectable_label(f'heavy_val_{label}_{hash(content)}', content, width=-1, multiline=True, height=80)
|
||||
imgui.end_child()
|
||||
else:
|
||||
if self.ui_word_wrap:
|
||||
imgui.push_text_wrap_pos(0.0)
|
||||
imgui.text_unformatted(content)
|
||||
imgui.pop_text_wrap_pos()
|
||||
else:
|
||||
imgui.text_unformatted(content)
|
||||
self._render_selectable_label(f'heavy_val_{label}_{hash(content)}', content, width=-1, multiline=self.ui_word_wrap, height=0)
|
||||
# ---------------------------------------------------------------- gui
|
||||
|
||||
|
||||
def _render_selectable_label(self, label: str, value: str, width: float = 0.0, multiline: bool = False, height: float = 0.0, color: Optional[imgui.ImVec4] = None) -> None:
|
||||
imgui.push_id(label + str(hash(value)))
|
||||
pops = 2
|
||||
pops = 4
|
||||
imgui.push_style_color(imgui.Col_.frame_bg, vec4(0, 0, 0, 0))
|
||||
imgui.push_style_color(imgui.Col_.frame_bg_hovered, vec4(0, 0, 0, 0))
|
||||
imgui.push_style_color(imgui.Col_.frame_bg_active, vec4(0, 0, 0, 0))
|
||||
imgui.push_style_color(imgui.Col_.border, vec4(0, 0, 0, 0))
|
||||
if color:
|
||||
imgui.push_style_color(imgui.Col_.text, color)
|
||||
pops += 1
|
||||
imgui.push_style_var(imgui.StyleVar_.frame_border_size, 0.0)
|
||||
imgui.push_style_var(imgui.StyleVar_.frame_padding, imgui.ImVec2(0, 0))
|
||||
if multiline:
|
||||
imgui.input_text_multiline("##" + label, value, imgui.ImVec2(width, height), imgui.InputTextFlags_.read_only)
|
||||
else:
|
||||
if width > 0: imgui.set_next_item_width(width)
|
||||
imgui.input_text("##" + label, value, imgui.InputTextFlags_.read_only)
|
||||
imgui.pop_style_color(pops)
|
||||
imgui.pop_style_var(2)
|
||||
imgui.pop_id()
|
||||
|
||||
def _show_menus(self) -> None:
|
||||
if imgui.begin_menu("manual slop"):
|
||||
if imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
|
||||
@@ -1305,14 +1306,7 @@ class App:
|
||||
if len(content) > 80: preview += "..."
|
||||
imgui.text_colored(vec4(180, 180, 180), preview)
|
||||
else:
|
||||
imgui.begin_child(f"prior_content_{idx}", imgui.ImVec2(0, 150), True)
|
||||
if self.ui_word_wrap:
|
||||
imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
|
||||
imgui.text_unformatted(content)
|
||||
imgui.pop_text_wrap_pos()
|
||||
else:
|
||||
imgui.text_unformatted(content)
|
||||
imgui.end_child()
|
||||
self._render_selectable_label(f'prior_content_val_{idx}', content, width=-1, multiline=True, height=150)
|
||||
|
||||
imgui.separator()
|
||||
imgui.pop_id()
|
||||
@@ -1349,7 +1343,7 @@ class App:
|
||||
last_updated = disc_data.get("last_updated", "")
|
||||
imgui.text_colored(C_LBL, "commit:")
|
||||
imgui.same_line()
|
||||
imgui.text_colored(C_IN if git_commit else C_LBL, git_commit[:12] if git_commit else "(none)")
|
||||
self._render_selectable_label('git_commit_val', git_commit[:12] if git_commit else '(none)', width=100, color=(C_IN if git_commit else C_LBL))
|
||||
imgui.same_line()
|
||||
if imgui.button("Update Commit"):
|
||||
git_dir = self.ui_project_git_dir
|
||||
@@ -1467,37 +1461,41 @@ class App:
|
||||
imgui.text_colored(vec4(160, 160, 150), preview)
|
||||
if not collapsed:
|
||||
if read_mode:
|
||||
imgui.begin_child("read_content", imgui.ImVec2(0, 150), True)
|
||||
if self.ui_word_wrap: imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
|
||||
content = entry["content"]
|
||||
last_idx = 0
|
||||
pattern = re.compile(r"\[Definition: (.*?) from (.*?) \(line (\d+)\)\](\s+```[\s\S]*?```)?")
|
||||
for match in pattern.finditer(content):
|
||||
before = content[last_idx:match.start()]
|
||||
if before: imgui.text(before)
|
||||
header_text = match.group(0).split("\n")[0].strip()
|
||||
path = match.group(2)
|
||||
code_block = match.group(4)
|
||||
if imgui.collapsing_header(header_text):
|
||||
if imgui.button(f"[Source]##{i}_{match.start()}"):
|
||||
res = mcp_client.read_file(path)
|
||||
if res:
|
||||
self.text_viewer_title = path
|
||||
self.text_viewer_content = res
|
||||
self.show_text_viewer = True
|
||||
if code_block:
|
||||
code_content = code_block.strip()
|
||||
if code_content.count("\n") + 1 > 50:
|
||||
imgui.begin_child(f"code_{i}_{match.start()}", imgui.ImVec2(0, 200), True)
|
||||
imgui.text(code_content)
|
||||
imgui.end_child()
|
||||
else:
|
||||
imgui.text(code_content)
|
||||
last_idx = match.end()
|
||||
after = content[last_idx:]
|
||||
if after: imgui.text(after)
|
||||
if self.ui_word_wrap: imgui.pop_text_wrap_pos()
|
||||
imgui.end_child()
|
||||
matches = list(pattern.finditer(content))
|
||||
if not matches:
|
||||
self._render_selectable_label(f'read_content_{i}', content, width=-1, multiline=True, height=150)
|
||||
else:
|
||||
imgui.begin_child("read_content", imgui.ImVec2(0, 150), True)
|
||||
if self.ui_word_wrap: imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
|
||||
last_idx = 0
|
||||
for m_idx, match in enumerate(matches):
|
||||
before = content[last_idx:match.start()]
|
||||
if before: self._render_selectable_label(f'read_before_{i}_{m_idx}', before, width=-1, multiline=True, height=0)
|
||||
header_text = match.group(0).split("\n")[0].strip()
|
||||
path = match.group(2)
|
||||
code_block = match.group(4)
|
||||
if imgui.collapsing_header(header_text):
|
||||
if imgui.button(f"[Source]##{i}_{match.start()}"):
|
||||
res = mcp_client.read_file(path)
|
||||
if res:
|
||||
self.text_viewer_title = path
|
||||
self.text_viewer_content = res
|
||||
self.show_text_viewer = True
|
||||
if code_block:
|
||||
code_content = code_block.strip()
|
||||
if code_content.count("\n") + 1 > 50:
|
||||
imgui.begin_child(f"code_{i}_{match.start()}", imgui.ImVec2(0, 200), True)
|
||||
imgui.text(code_content)
|
||||
imgui.end_child()
|
||||
else:
|
||||
imgui.text(code_content)
|
||||
last_idx = match.end()
|
||||
after = content[last_idx:]
|
||||
if after: self._render_selectable_label(f'read_after_{i}_{last_idx}', after, width=-1, multiline=True, height=0)
|
||||
if self.ui_word_wrap: imgui.pop_text_wrap_pos()
|
||||
imgui.end_child()
|
||||
else:
|
||||
ch, entry["content"] = imgui.input_text_multiline("##content", entry["content"], imgui.ImVec2(-1, 150))
|
||||
imgui.separator()
|
||||
@@ -1536,7 +1534,7 @@ class App:
|
||||
sid = "None"
|
||||
if hasattr(ai_client, "_gemini_cli_adapter") and ai_client._gemini_cli_adapter:
|
||||
sid = ai_client._gemini_cli_adapter.session_id or "None"
|
||||
imgui.text(f"Session ID: {sid}")
|
||||
imgui.text("Session ID:"); imgui.same_line(); self._render_selectable_label("gemini_cli_sid", sid, width=200)
|
||||
if imgui.button("Reset CLI Session"):
|
||||
ai_client.reset_session()
|
||||
imgui.text("Binary Path")
|
||||
@@ -1560,7 +1558,7 @@ class App:
|
||||
total = usage["input_tokens"] + usage["output_tokens"]
|
||||
if total == 0 and usage.get("total_tokens", 0) > 0:
|
||||
total = usage["total_tokens"]
|
||||
imgui.text_colored(C_RES, f"Tokens: {total:,} (In: {usage['input_tokens']:,} Out: {usage['output_tokens']:,})")
|
||||
self._render_selectable_label("session_telemetry_tokens", f"Tokens: {total:,} (In: {usage['input_tokens']:,} Out: {usage['output_tokens']:,})", width=-1, color=C_RES)
|
||||
if usage.get("last_latency", 0.0) > 0:
|
||||
imgui.text_colored(C_LBL, f" Last Latency: {usage['last_latency']:.2f}s")
|
||||
if usage["cache_read_input_tokens"]:
|
||||
@@ -1623,13 +1621,13 @@ class App:
|
||||
tokens = in_t + out_t
|
||||
cost = cost_tracker.estimate_cost(model, in_t, out_t)
|
||||
imgui.table_next_row()
|
||||
imgui.table_set_column_index(0); imgui.text(tier)
|
||||
imgui.table_set_column_index(1); imgui.text(model.split('-')[0])
|
||||
imgui.table_set_column_index(2); imgui.text(f"{tokens:,}")
|
||||
imgui.table_set_column_index(3); imgui.text_colored(imgui.ImVec4(0.2, 0.9, 0.2, 1), f"${cost:.4f}")
|
||||
imgui.table_set_column_index(0); self._render_selectable_label(f"tier_{tier}", tier, width=-1)
|
||||
imgui.table_set_column_index(1); self._render_selectable_label(f"model_{tier}", model.split("-")[0], width=-1)
|
||||
imgui.table_set_column_index(2); self._render_selectable_label(f"tokens_{tier}", f"{tokens:,}", width=-1)
|
||||
imgui.table_set_column_index(3); self._render_selectable_label(f"cost_{tier}", f"${cost:.4f}", width=-1, color=imgui.ImVec4(0.2, 0.9, 0.2, 1))
|
||||
imgui.end_table()
|
||||
tier_total = sum(cost_tracker.estimate_cost(stats.get('model', ''), stats.get('input', 0), stats.get('output', 0)) for stats in self.mma_tier_usage.values())
|
||||
imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), f"Session Total: ${tier_total:.4f}")
|
||||
self._render_selectable_label("session_total_cost", f"Session Total: ${tier_total:.4f}", width=-1, color=imgui.ImVec4(0, 1, 0, 1))
|
||||
else:
|
||||
imgui.text_disabled("No MMA tier usage data")
|
||||
if stats.get("would_trim"):
|
||||
@@ -1974,7 +1972,8 @@ class App:
|
||||
|
||||
script_preview = script.replace("\n", " ")[:150]
|
||||
if len(script) > 150: script_preview += "..."
|
||||
if imgui.selectable(f"{script_preview}##tc_{i}", False, imgui.SelectableFlags_.span_all_columns)[0]:
|
||||
self._render_selectable_label(f'tc_script_{i}', script_preview, width=-1)
|
||||
if imgui.is_item_clicked():
|
||||
self.text_viewer_title = f"Tool Call #{i+1} Details"
|
||||
self.text_viewer_content = combined
|
||||
self.show_text_viewer = True
|
||||
@@ -1982,7 +1981,8 @@ class App:
|
||||
imgui.table_next_column()
|
||||
res_preview = res.replace("\n", " ")[:30]
|
||||
if len(res) > 30: res_preview += "..."
|
||||
if imgui.button(f"View##res_{i}"):
|
||||
self._render_selectable_label(f'tc_res_{i}', res_preview, width=-1)
|
||||
if imgui.is_item_clicked():
|
||||
self.text_viewer_title = f"Tool Call #{i+1} Details"
|
||||
self.text_viewer_content = combined
|
||||
self.show_text_viewer = True
|
||||
@@ -2648,7 +2648,7 @@ class App:
|
||||
if stream_key is not None:
|
||||
content = self.mma_streams.get(stream_key, "")
|
||||
imgui.begin_child(f"##stream_content_{tier_key}", imgui.ImVec2(-1, -1))
|
||||
imgui.text_wrapped(content)
|
||||
self._render_selectable_label(f'stream_{tier_key}', content, width=-1, multiline=True, height=0)
|
||||
try:
|
||||
if len(content) != self._tier_stream_last_len.get(stream_key, -1):
|
||||
imgui.set_scroll_here_y(1.0)
|
||||
@@ -2674,7 +2674,7 @@ class App:
|
||||
else:
|
||||
imgui.text(f"{ticket_id} [{status}]")
|
||||
imgui.begin_child(f"##tier3_{ticket_id}_scroll", imgui.ImVec2(-1, 150), True)
|
||||
imgui.text_wrapped(self.mma_streams[key])
|
||||
self._render_selectable_label(f'stream_t3_{ticket_id}', self.mma_streams[key], width=-1, multiline=True, height=0)
|
||||
try:
|
||||
if len(self.mma_streams[key]) != self._tier_stream_last_len.get(key, -1):
|
||||
imgui.set_scroll_here_y(1.0)
|
||||
|
||||
Reference in New Issue
Block a user