feat(ui): Implement walkthrough refinements (Diagnostics, Tabs, Selectable text, Session Loading)
This commit is contained in:
144
gui.py
144
gui.py
@@ -128,7 +128,8 @@ def _add_text_field(parent: str, label: str, value: str):
|
||||
if len(value) > COMMS_CLAMP_CHARS:
|
||||
if wrap:
|
||||
with dpg.child_window(height=80, border=True):
|
||||
dpg.add_text(value, wrap=0, color=_VALUE_COLOR)
|
||||
# add_input_text for selection
|
||||
dpg.add_input_text(default_value=value, multiline=True, readonly=True, width=-1, height=-1, border=False)
|
||||
else:
|
||||
dpg.add_input_text(
|
||||
default_value=value,
|
||||
@@ -138,15 +139,15 @@ def _add_text_field(parent: str, label: str, value: str):
|
||||
height=80,
|
||||
)
|
||||
else:
|
||||
dpg.add_text(value if value else "(empty)", wrap=0, color=_VALUE_COLOR)
|
||||
# Short selectable text
|
||||
dpg.add_input_text(default_value=value if value else "(empty)", readonly=True, width=-1, border=False)
|
||||
|
||||
|
||||
def _add_kv_row(parent: str, key: str, val, val_color=None):
|
||||
"""Single key: value row, horizontally laid out."""
|
||||
vc = val_color or _VALUE_COLOR
|
||||
with dpg.group(horizontal=True, parent=parent):
|
||||
dpg.add_text(f"{key}:", color=_LABEL_COLOR)
|
||||
dpg.add_text(str(val), color=vc)
|
||||
dpg.add_input_text(default_value=str(val), readonly=True, width=-1, border=False)
|
||||
|
||||
|
||||
def _render_usage(parent: str, usage: dict):
|
||||
@@ -451,6 +452,7 @@ class App:
|
||||
"AI Settings Hub": "win_ai_settings_hub",
|
||||
"Discussion Hub": "win_discussion_hub",
|
||||
"Operations Hub": "win_operations_hub",
|
||||
"Diagnostics": "win_diagnostics",
|
||||
"Theme": "win_theme",
|
||||
"Last Script Output": "win_script_output",
|
||||
"Text Viewer": "win_text_viewer",
|
||||
@@ -489,6 +491,8 @@ class App:
|
||||
self._is_script_blinking = False
|
||||
self._script_blink_start_time = 0.0
|
||||
|
||||
self.is_viewing_prior_session = False
|
||||
|
||||
# Subscribe to API lifecycle events
|
||||
ai_client.events.on("request_start", self._on_api_event)
|
||||
ai_client.events.on("response_received", self._on_api_event)
|
||||
@@ -863,7 +867,7 @@ class App:
|
||||
# Update Diagnostics panel (throttled for smoothness)
|
||||
if now - self._last_perf_update_time > 0.5:
|
||||
self._last_perf_update_time = now
|
||||
if dpg.is_item_shown("win_operations_hub"):
|
||||
if dpg.is_item_shown("win_diagnostics"):
|
||||
metrics = self.perf_monitor.get_metrics()
|
||||
|
||||
# Update history
|
||||
@@ -1310,6 +1314,65 @@ class App:
|
||||
except Exception as e:
|
||||
self._update_status(f"error: {e}")
|
||||
|
||||
def cb_load_prior_log(self):
|
||||
root = hide_tk_root()
|
||||
path = filedialog.askopenfilename(
|
||||
title="Load Session Log",
|
||||
initialdir="logs",
|
||||
filetypes=[("Log Files", "*.log"), ("JSONL Files", "*.jsonl"), ("All Files", "*.*")]
|
||||
)
|
||||
root.destroy()
|
||||
if not path:
|
||||
return
|
||||
|
||||
try:
|
||||
import json
|
||||
entries = []
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if line.strip():
|
||||
entries.append(json.loads(line))
|
||||
|
||||
if not entries:
|
||||
return
|
||||
|
||||
self.is_viewing_prior_session = True
|
||||
dpg.configure_item("prior_session_indicator", show=True)
|
||||
dpg.configure_item("exit_prior_btn", show=True)
|
||||
|
||||
# Apply Tinted Mode Theme
|
||||
if not dpg.does_item_exist("prior_session_theme"):
|
||||
with dpg.theme(tag="prior_session_theme"):
|
||||
with dpg.theme_component(dpg.mvAll):
|
||||
# Tint everything slightly amber/sepia
|
||||
dpg.add_theme_color(dpg.mvThemeCol_WindowBg, (40, 30, 20, 255))
|
||||
dpg.add_theme_color(dpg.mvThemeCol_ChildBg, (50, 40, 30, 255))
|
||||
|
||||
for hub in ["win_context_hub", "win_ai_settings_hub", "win_discussion_hub", "win_operations_hub", "win_diagnostics"]:
|
||||
if dpg.does_item_exist(hub):
|
||||
dpg.bind_item_theme(hub, "prior_session_theme")
|
||||
|
||||
# Clear and render old entries
|
||||
dpg.delete_item("comms_scroll", children_only=True)
|
||||
for i, entry in enumerate(entries):
|
||||
_render_comms_entry("comms_scroll", entry, i + 1)
|
||||
|
||||
except Exception as e:
|
||||
self._update_status(f"Load error: {e}")
|
||||
|
||||
def cb_exit_prior_session(self):
|
||||
self.is_viewing_prior_session = False
|
||||
dpg.configure_item("prior_session_indicator", show=False)
|
||||
dpg.configure_item("exit_prior_btn", show=False)
|
||||
|
||||
# Unbind theme
|
||||
for hub in ["win_context_hub", "win_ai_settings_hub", "win_discussion_hub", "win_operations_hub", "win_diagnostics"]:
|
||||
if dpg.does_item_exist(hub):
|
||||
dpg.bind_item_theme(hub, 0)
|
||||
|
||||
# Restore current session comms
|
||||
self._rebuild_comms_log()
|
||||
|
||||
def cb_reset_session(self):
|
||||
ai_client.reset_session()
|
||||
ai_client.clear_comms_log()
|
||||
@@ -1586,8 +1649,14 @@ class App:
|
||||
# ---- disc entry list ----
|
||||
|
||||
def _render_disc_entry(self, i: int, entry: dict):
|
||||
collapsed = entry.get("collapsed", False)
|
||||
read_mode = entry.get("read_mode", False)
|
||||
# Default to collapsed and read-mode if not specified
|
||||
if "collapsed" not in entry:
|
||||
entry["collapsed"] = True
|
||||
if "read_mode" not in entry:
|
||||
entry["read_mode"] = True
|
||||
|
||||
collapsed = entry.get("collapsed", True)
|
||||
read_mode = entry.get("read_mode", True)
|
||||
ts_str = entry.get("ts", "")
|
||||
|
||||
preview = entry["content"].replace("\n", " ")[:60]
|
||||
@@ -1602,6 +1671,11 @@ class App:
|
||||
width=24,
|
||||
callback=self._make_disc_toggle_cb(i),
|
||||
)
|
||||
dpg.add_button(
|
||||
label="[+ Max]",
|
||||
user_data=i,
|
||||
callback=lambda s, a, u: _show_text_viewer(f"Entry #{u+1}", self.disc_entries[u]["content"])
|
||||
)
|
||||
dpg.add_combo(
|
||||
tag=f"disc_role_{i}",
|
||||
items=self.disc_roles,
|
||||
@@ -1623,11 +1697,6 @@ class App:
|
||||
width=36,
|
||||
callback=self._make_disc_insert_cb(i),
|
||||
)
|
||||
dpg.add_button(
|
||||
label="[+ Max]",
|
||||
user_data=i,
|
||||
callback=lambda s, a, u: _show_text_viewer(f"Entry #{u+1}", self.disc_entries[u]["content"])
|
||||
)
|
||||
dpg.add_button(
|
||||
label="Del",
|
||||
width=36,
|
||||
@@ -1637,8 +1706,14 @@ class App:
|
||||
|
||||
with dpg.group(tag=f"disc_body_{i}", show=not collapsed):
|
||||
if read_mode:
|
||||
with dpg.child_window(height=150, border=True):
|
||||
dpg.add_text(entry["content"], wrap=0, color=(200, 200, 200))
|
||||
# Use a read-only input_text instead of dpg.add_text to allow selection
|
||||
dpg.add_input_text(
|
||||
default_value=entry["content"],
|
||||
multiline=True,
|
||||
readonly=True,
|
||||
width=-1,
|
||||
height=150,
|
||||
)
|
||||
else:
|
||||
dpg.add_input_text(
|
||||
tag=f"disc_content_{i}",
|
||||
@@ -1972,6 +2047,11 @@ class App:
|
||||
no_close=False,
|
||||
no_collapse=True,
|
||||
):
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_text("DISCUSSION", color=_SUBHDR_COLOR)
|
||||
dpg.add_spacer(width=20)
|
||||
dpg.add_text("THINKING...", tag="thinking_indicator", color=(255, 100, 100), show=False)
|
||||
|
||||
# History at Top
|
||||
with dpg.child_window(tag="disc_history_section", height=-400, border=True):
|
||||
# Discussion selector section
|
||||
@@ -2008,17 +2088,16 @@ class App:
|
||||
with dpg.child_window(tag="disc_scroll", height=-1, border=False):
|
||||
pass
|
||||
|
||||
# Message Composer in Middle
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_text("Message", color=_SUBHDR_COLOR)
|
||||
dpg.add_spacer(width=20)
|
||||
dpg.add_text("THINKING...", tag="thinking_indicator", color=(255, 100, 100), show=False)
|
||||
dpg.add_separator()
|
||||
|
||||
# Interaction Tabs at Bottom
|
||||
with dpg.tab_bar():
|
||||
with dpg.tab(label="Message"):
|
||||
dpg.add_input_text(
|
||||
tag="ai_input",
|
||||
multiline=True,
|
||||
width=-1,
|
||||
height=120,
|
||||
height=200,
|
||||
)
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_button(label="Gen + Send", callback=self.cb_generate_send)
|
||||
@@ -2026,10 +2105,7 @@ class App:
|
||||
dpg.add_button(label="Reset", callback=self.cb_reset_session)
|
||||
dpg.add_button(label="-> History", callback=self.cb_append_message_to_history)
|
||||
|
||||
dpg.add_separator()
|
||||
|
||||
# AI Response at Bottom
|
||||
dpg.add_text("AI Response", color=_SUBHDR_COLOR)
|
||||
with dpg.tab(label="AI Response"):
|
||||
dpg.add_input_text(
|
||||
tag="ai_response",
|
||||
multiline=True,
|
||||
@@ -2063,6 +2139,10 @@ class App:
|
||||
dpg.add_text("Status: idle", tag="ai_status", color=(200, 220, 160))
|
||||
dpg.add_spacer(width=16)
|
||||
dpg.add_button(label="Clear", callback=self.cb_clear_comms)
|
||||
dpg.add_button(label="Load Log", callback=self.cb_load_prior_log)
|
||||
dpg.add_button(label="Exit Prior", tag="exit_prior_btn", callback=self.cb_exit_prior_session, show=False)
|
||||
|
||||
dpg.add_text("PRIOR SESSION VIEW", tag="prior_session_indicator", color=(255, 100, 100), show=False)
|
||||
dpg.add_text("Tokens: 0 (In: 0 Out: 0)", tag="ai_token_usage", color=(180, 255, 180))
|
||||
dpg.add_separator()
|
||||
with dpg.child_window(tag="comms_scroll", height=-1, border=False, horizontal_scrollbar=True):
|
||||
@@ -2076,7 +2156,16 @@ class App:
|
||||
with dpg.child_window(tag="tool_log_scroll", height=-1, border=False):
|
||||
pass
|
||||
|
||||
with dpg.tab(label="Diagnostics"):
|
||||
def _build_diagnostics_window(self):
|
||||
with dpg.window(
|
||||
label="Diagnostics",
|
||||
tag="win_diagnostics",
|
||||
pos=(1244, 804),
|
||||
width=428,
|
||||
height=360,
|
||||
no_close=False,
|
||||
no_collapse=True,
|
||||
):
|
||||
dpg.add_text("Performance Telemetry")
|
||||
with dpg.table(header_row=False, borders_innerH=True, borders_outerH=True, borders_innerV=True, borders_outerV=True):
|
||||
dpg.add_table_column()
|
||||
@@ -2095,13 +2184,13 @@ class App:
|
||||
dpg.add_text("0.0ms", tag="perf_lag_text", color=(255, 180, 80))
|
||||
|
||||
dpg.add_spacer(height=4)
|
||||
dpg.add_plot(label="Frame Time (ms)", tag="plot_frame", height=120, width=-1, no_mouse_pos=True)
|
||||
dpg.add_plot(label="Frame Time (ms)", tag="plot_frame", height=140, width=-1, no_mouse_pos=True)
|
||||
dpg.add_plot_axis(dpg.mvXAxis, label="samples", no_tick_labels=True, parent="plot_frame")
|
||||
with dpg.plot_axis(dpg.mvYAxis, label="ms", tag="axis_frame_y", parent="plot_frame"):
|
||||
dpg.add_line_series(list(range(100)), self.perf_history["frame_time"], label="frame time", tag="perf_frame_plot")
|
||||
dpg.set_axis_limits("axis_frame_y", 0, 50)
|
||||
|
||||
dpg.add_plot(label="CPU Usage (%)", tag="plot_cpu", height=120, width=-1, no_mouse_pos=True)
|
||||
dpg.add_plot(label="CPU Usage (%)", tag="plot_cpu", height=140, width=-1, no_mouse_pos=True)
|
||||
dpg.add_plot_axis(dpg.mvXAxis, label="samples", no_tick_labels=True, parent="plot_cpu")
|
||||
with dpg.plot_axis(dpg.mvYAxis, label="%", tag="axis_cpu_y", parent="plot_cpu"):
|
||||
dpg.add_line_series(list(range(100)), self.perf_history["cpu"], label="cpu usage", tag="perf_cpu_plot")
|
||||
@@ -2127,6 +2216,7 @@ class App:
|
||||
self._build_ai_settings_hub()
|
||||
self._build_discussion_hub()
|
||||
self._build_operations_hub()
|
||||
self._build_diagnostics_window()
|
||||
|
||||
self._build_theme_window()
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ def test_old_windows_removed_from_window_info(app_instance_simple):
|
||||
"win_projects", "win_files", "win_screenshots",
|
||||
"win_provider", "win_system_prompts",
|
||||
"win_discussion", "win_message", "win_response",
|
||||
"win_comms", "win_tool_log", "win_diagnostics"
|
||||
"win_comms", "win_tool_log"
|
||||
]
|
||||
|
||||
for tag in old_tags:
|
||||
|
||||
Reference in New Issue
Block a user