fix(gui): explicit child size for comms_scroll and prior_scroll prevents early clipping
ROOT CAUSE: When child windows used ImVec2(0, 0) for auto-fill, the
child's reported height was unstable inside tab items (especially when
the parent tab was inside a tab_bar inside a window). Result: the
scrollable child rendered with a fixed smaller height, showing only the
first half of the content, with empty space below.
FIX: Use imgui.get_content_region_avail() to compute explicit dimensions
and pass them to begin_child. Now the child fills the full available area
inside the tab content.
- render_comms_history_panel: avail.x, avail.y
- render_prior_session_view: same, plus added entry count indicator next
to the Exit Prior Session button ({N} entries) for at-a-glance info
Tests:
- test_comms_scroll_no_clipping.py: verifies comms_scroll child uses
explicit (non-zero) size
- test_prior_session_no_clipping.py: same for prior_scroll child
- test_log_management_first_open.py: minor cleanup
- 42/42 broad regression pass
This commit is contained in:
+74
-74
@@ -1671,71 +1671,75 @@ def render_log_management(app: App) -> None:
|
||||
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_log_management")
|
||||
with imscope.window("Log Management", app.show_windows["Log Management"]) as (exp, opened):
|
||||
app.show_windows["Log Management"] = bool(opened)
|
||||
|
||||
if app._log_registry is None:
|
||||
from src import log_registry
|
||||
app._log_registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml"))
|
||||
app._log_registry.load_registry()
|
||||
if imgui.button("Refresh Registry"):
|
||||
if app._log_registry is not None: app._log_registry.load_registry()
|
||||
imgui.same_line()
|
||||
if imgui.button("Load Log"): app.cb_load_prior_log()
|
||||
imgui.same_line()
|
||||
if imgui.button("Force Prune Logs"): app.controller.event_queue.put("gui_task", {"action": "click", "item": "btn_prune_logs"})
|
||||
|
||||
registry = app._log_registry
|
||||
sessions = registry.data
|
||||
if imgui.begin_table("sessions_table", 7, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable):
|
||||
imgui.table_setup_column("Session ID")
|
||||
imgui.table_setup_column("Start Time")
|
||||
imgui.table_setup_column("Star")
|
||||
imgui.table_setup_column("Reason")
|
||||
imgui.table_setup_column("Size (KB)")
|
||||
imgui.table_setup_column("Msgs")
|
||||
imgui.table_setup_column("Actions")
|
||||
imgui.table_headers_row()
|
||||
for session_id, s_data in sessions.items():
|
||||
imgui.table_next_row()
|
||||
imgui.table_next_column()
|
||||
imgui.text(session_id)
|
||||
imgui.table_next_column()
|
||||
imgui.text(s_data.get("start_time", ""))
|
||||
imgui.table_next_column()
|
||||
whitelisted = s_data.get("whitelisted", False)
|
||||
if whitelisted:
|
||||
imgui.text_colored(vec4(255, 215, 0), "YES")
|
||||
else:
|
||||
imgui.text("NO")
|
||||
metadata = s_data.get("metadata") or {}
|
||||
imgui.table_next_column()
|
||||
imgui.text(metadata.get("reason", ""))
|
||||
imgui.table_next_column()
|
||||
imgui.text(str(metadata.get("size_kb", "")))
|
||||
imgui.table_next_column()
|
||||
imgui.text(str(metadata.get("message_count", "")))
|
||||
imgui.table_next_column()
|
||||
if imgui.button(f"Load##{session_id}"):
|
||||
app.cb_load_prior_log(s_data.get("path"))
|
||||
imgui.same_line()
|
||||
if whitelisted:
|
||||
if imgui.button(f"Unstar##{session_id}"):
|
||||
registry.update_session_metadata(
|
||||
session_id,
|
||||
message_count=int(metadata.get("message_count") or 0),
|
||||
errors=int(metadata.get("errors") or 0),
|
||||
size_kb=int(metadata.get("size_kb") or 0),
|
||||
whitelisted=False,
|
||||
reason=str(metadata.get("reason") or "")
|
||||
)
|
||||
else:
|
||||
if imgui.button(f"Star##{session_id}"):
|
||||
registry.update_session_metadata(
|
||||
session_id,
|
||||
message_count=int(metadata.get("message_count") or 0),
|
||||
errors=int(metadata.get("errors") or 0),
|
||||
size_kb=int(metadata.get("size_kb") or 0),
|
||||
whitelisted=True,
|
||||
reason="Manually whitelisted"
|
||||
)
|
||||
imgui.end_table()
|
||||
|
||||
if exp:
|
||||
if imgui.button("Refresh Registry"):
|
||||
if app._log_registry is not None: app._log_registry.load_registry()
|
||||
imgui.same_line()
|
||||
if imgui.button("Load Log"): app.cb_load_prior_log()
|
||||
imgui.same_line()
|
||||
if imgui.button("Force Prune Logs"): app.controller.event_queue.put("gui_task", {"action": "click", "item": "btn_prune_logs"})
|
||||
|
||||
registry = app._log_registry
|
||||
sessions = registry.data
|
||||
if imgui.begin_table("sessions_table", 7, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable):
|
||||
imgui.table_setup_column("Session ID")
|
||||
imgui.table_setup_column("Start Time")
|
||||
imgui.table_setup_column("Star")
|
||||
imgui.table_setup_column("Reason")
|
||||
imgui.table_setup_column("Size (KB)")
|
||||
imgui.table_setup_column("Msgs")
|
||||
imgui.table_setup_column("Actions")
|
||||
imgui.table_headers_row()
|
||||
for session_id, s_data in sessions.items():
|
||||
imgui.table_next_row()
|
||||
imgui.table_next_column()
|
||||
imgui.text(session_id)
|
||||
imgui.table_next_column()
|
||||
imgui.text(s_data.get("start_time", ""))
|
||||
imgui.table_next_column()
|
||||
whitelisted = s_data.get("whitelisted", False)
|
||||
if whitelisted:
|
||||
imgui.text_colored(vec4(255, 215, 0), "YES")
|
||||
else:
|
||||
imgui.text("NO")
|
||||
metadata = s_data.get("metadata") or {}
|
||||
imgui.table_next_column()
|
||||
imgui.text(metadata.get("reason", ""))
|
||||
imgui.table_next_column()
|
||||
imgui.text(str(metadata.get("size_kb", "")))
|
||||
imgui.table_next_column()
|
||||
imgui.text(str(metadata.get("message_count", "")))
|
||||
imgui.table_next_column()
|
||||
if imgui.button(f"Load##{session_id}"):
|
||||
app.cb_load_prior_log(s_data.get("path"))
|
||||
imgui.same_line()
|
||||
if whitelisted:
|
||||
if imgui.button(f"Unstar##{session_id}"):
|
||||
registry.update_session_metadata(
|
||||
session_id,
|
||||
message_count=int(metadata.get("message_count") or 0),
|
||||
errors=int(metadata.get("errors") or 0),
|
||||
size_kb=int(metadata.get("size_kb") or 0),
|
||||
whitelisted=False,
|
||||
reason=str(metadata.get("reason") or "")
|
||||
)
|
||||
else:
|
||||
if imgui.button(f"Star##{session_id}"):
|
||||
registry.update_session_metadata(
|
||||
session_id,
|
||||
message_count=int(metadata.get("message_count") or 0),
|
||||
errors=int(metadata.get("errors") or 0),
|
||||
size_kb=int(metadata.get("size_kb") or 0),
|
||||
whitelisted=True,
|
||||
reason="Manually whitelisted"
|
||||
)
|
||||
imgui.end_table()
|
||||
|
||||
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_log_management")
|
||||
|
||||
@@ -3580,19 +3584,18 @@ def render_session_insights_panel(app: App) -> None:
|
||||
imgui.text(f"Session Cost: ${insights.get('session_cost', 0):.4f}")
|
||||
completed = insights.get('completed_tickets', 0)
|
||||
efficiency = insights.get('efficiency', 0)
|
||||
imgui.text(f"Completed: {completed}")
|
||||
imgui.text(f"Tokens/Ticket: {efficiency:.0f}" if efficiency > 0 else "Tokens/Ticket: N/A")
|
||||
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_session_insights_panel")
|
||||
|
||||
def render_prior_session_view(app: App) -> None:
|
||||
with imscope.style_color(imgui.Col_.child_bg, vec4(50, 40, 20)):
|
||||
if imgui.button("Exit Prior Session"): app.controller.cb_exit_prior_session(); app._comms_log_dirty = True
|
||||
imgui.same_line()
|
||||
imgui.text_colored(vec4(200, 180, 100), f"({len(app.prior_disc_entries)} entries)")
|
||||
imgui.separator()
|
||||
with imscope.child("prior_scroll"):
|
||||
avail = imgui.get_content_region_avail()
|
||||
with imscope.child("prior_scroll", size_x=avail.x, size_y=avail.y):
|
||||
clipper = imgui.ListClipper(); clipper.begin(len(app.prior_disc_entries))
|
||||
while clipper.step():
|
||||
for idx in range(clipper.display_start, clipper.display_end):
|
||||
entry = app.prior_disc_entries[idx];
|
||||
entry = app.prior_disc_entries[idx];
|
||||
with imscope.id(f"prior_disc_{idx}"):
|
||||
collapsed = entry.get("collapsed", False)
|
||||
if imgui.button("+" if collapsed else "-"): entry["collapsed"] = not collapsed
|
||||
@@ -3674,12 +3677,9 @@ def render_comms_history_panel(app: App) -> None:
|
||||
imgui.text_colored(C_RES, "response"); imgui.same_line()
|
||||
imgui.text_colored(C_TR, "tool_result")
|
||||
imgui.separator()
|
||||
|
||||
# Use tinted background for prior session
|
||||
if app.is_viewing_prior_session: imgui.push_style_color(imgui.Col_.child_bg, vec4(40, 30, 20))
|
||||
|
||||
imgui.begin_child("comms_scroll", imgui.ImVec2(0, 0), False, imgui.WindowFlags_.horizontal_scrollbar)
|
||||
|
||||
|
||||
avail = imgui.get_content_region_avail()
|
||||
imgui.begin_child("comms_scroll", imgui.ImVec2(avail.x, avail.y), False, imgui.WindowFlags_.horizontal_scrollbar)
|
||||
log_to_render = app._comms_log_cache
|
||||
|
||||
for i, entry in enumerate(log_to_render):
|
||||
|
||||
Reference in New Issue
Block a user