reading throuogh gui_2.py (still reading)

This commit is contained in:
ed
2026-06-13 10:27:48 -04:00
parent 26b1ec77a4
commit 394020e50c
+177 -286
View File
@@ -369,6 +369,7 @@ class App:
self.controller.save_context_preset(preset) self.controller.save_context_preset(preset)
self.ui_new_context_preset_name = "" self.ui_new_context_preset_name = ""
self.show_missing_files_modal = False self.show_missing_files_modal = False
self.controller._predefined_callbacks['get_app_debug_info'] = lambda: self.app_debug_info self.controller._predefined_callbacks['get_app_debug_info'] = lambda: self.app_debug_info
self.controller._gettable_fields ['app_debug_info'] = 'app_debug_info' self.controller._gettable_fields ['app_debug_info'] = 'app_debug_info'
self.controller._predefined_callbacks['save_context_preset_force'] = _save_context_preset_force self.controller._predefined_callbacks['save_context_preset_force'] = _save_context_preset_force
@@ -519,7 +520,7 @@ class App:
self.ui_synthesis_selected_takes: dict[str, bool] = {} self.ui_synthesis_selected_takes: dict[str, bool] = {}
# --- Rendering & Theme State --- # --- Rendering & Theme State ---
self.perf_show_graphs: dict[str, bool] = {} self.perf_show_graphs:dict[str, bool] = {}
self.ui_crt_filter = False self.ui_crt_filter = False
self.ui_tool_filter_category = "All" self.ui_tool_filter_category = "All"
self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8} self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8}
@@ -541,6 +542,7 @@ class App:
self.context_files = [item] self.context_files = [item]
self.screenshots = ['test.png'] self.screenshots = ['test.png']
self.save_context_preset(name) self.save_context_preset(name)
def _handle_approve_ask(self) -> None: def _handle_approve_ask(self) -> None:
"""UI-level wrapper for approving a pending tool execution ask.""" """UI-level wrapper for approving a pending tool execution ask."""
self.controller._handle_approve_ask() self.controller._handle_approve_ask()
@@ -957,15 +959,12 @@ class App:
"""Re-applies the next snapshot in the history stack (forward navigation). """Re-applies the next snapshot in the history stack (forward navigation).
SSDL Shape: `[I:snapshot] -> [B:history] => [I:state]` SSDL Shape: `[I:snapshot] -> [B:history] => [I:state]`
""" """
sys.stderr.write(f"[DEBUG History] _handle_redo called. can_redo={self.history.can_redo}\n") sys.stderr.write(f"[DEBUG History] _handle_redo called. can_redo={self.history.can_redo}\n"); sys.stderr.flush()
sys.stderr.flush() if not self.history.can_redo: return
if not self.history.can_redo:
return
current = self._take_snapshot() current = self._take_snapshot()
entry = self.history.redo(current, "Redo Action") entry = self.history.redo(current, "Redo Action")
if entry: if entry:
sys.stderr.write(f"[DEBUG History] Redoing to: {entry.description}\n") sys.stderr.write(f"[DEBUG History] Redoing to: {entry.description}\n"); sys.stderr.flush()
sys.stderr.flush()
self._apply_snapshot(entry.state) self._apply_snapshot(entry.state)
def shutdown(self) -> None: def shutdown(self) -> None:
@@ -973,6 +972,7 @@ class App:
forces a save of dirty registries/caches, and terminates the active thread pools. forces a save of dirty registries/caches, and terminates the active thread pools.
SSDL Shape: `[I:save_ini] -> [I:controller_shutdown]` SSDL Shape: `[I:save_ini] -> [I:controller_shutdown]`
""" """
#Note(Ed): Exception(Thirdparty)
try: try:
if hasattr(self, 'runner_params') and self.runner_params.ini_filename: if hasattr(self, 'runner_params') and self.runner_params.ini_filename:
imgui.save_ini_settings_to_disk(self.runner_params.ini_filename) imgui.save_ini_settings_to_disk(self.runner_params.ini_filename)
@@ -1218,7 +1218,7 @@ class App:
win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0) win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0)
imgui.push_style_color(imgui.Col_.button, imgui.ImVec4(0, 0, 0, 0)) imgui.push_style_color(imgui.Col_.button, imgui.ImVec4(0, 0, 0, 0))
#Note(Ed): Thirdparty Exception #Note(Ed): Exception(Thirdparty)
try: is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED try: is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED
except Exception: is_max = False except Exception: is_max = False
# Explicitly set Y to 0 and match button height to bar height for perfect alignment # Explicitly set Y to 0 and match button height to bar height for perfect alignment
@@ -1293,8 +1293,7 @@ class App:
root = hide_tk_root() root = hide_tk_root()
path = filedialog.askdirectory(title='Select Session Directory', initialdir=str(paths.get_logs_dir())) path = filedialog.askdirectory(title='Select Session Directory', initialdir=str(paths.get_logs_dir()))
root.destroy() root.destroy()
if path: if path: self.controller.cb_load_prior_log(path)
self.controller.cb_load_prior_log(path)
def _set_external_editor_default(self, editor_name: str) -> None: def _set_external_editor_default(self, editor_name: str) -> None:
from src import models from src import models
@@ -1326,6 +1325,8 @@ class App:
mcp_client.configure([{"path": abs_path}], [proj_dir] if proj_dir else None) mcp_client.configure([{"path": abs_path}], [proj_dir] if proj_dir else None)
f_path_lower = f_item.path.lower() f_path_lower = f_item.path.lower()
#TODO(Ed): Exception(Review)
try: try:
if f_path_lower.endswith('.py'): outline = mcp_client.py_get_code_outline(abs_path) if f_path_lower.endswith('.py'): outline = mcp_client.py_get_code_outline(abs_path)
elif f_path_lower.endswith(('.c', '.h')): outline = mcp_client.ts_c_get_code_outline(abs_path) elif f_path_lower.endswith(('.c', '.h')): outline = mcp_client.ts_c_get_code_outline(abs_path)
@@ -1342,6 +1343,7 @@ class App:
text = f.read() text = f.read()
except Exception: except Exception:
return return
#TODO(Ed): Exception(Review)
try: try:
from src.fuzzy_anchor import FuzzyAnchor from src.fuzzy_anchor import FuzzyAnchor
except ImportError: except ImportError:
@@ -1352,10 +1354,8 @@ class App:
e_line = int(e_str) e_line = int(e_str)
if any(s.get('start_line') == s_line and s.get('end_line') == e_line for s in f_item.custom_slices): if any(s.get('start_line') == s_line and s.get('end_line') == e_line for s in f_item.custom_slices):
continue continue
if FuzzyAnchor: if FuzzyAnchor: slice_data = FuzzyAnchor.create_slice(text, s_line, e_line)
slice_data = FuzzyAnchor.create_slice(text, s_line, e_line) else: slice_data = {"start_line": s_line, "end_line": e_line}
else:
slice_data = {"start_line": s_line, "end_line": e_line}
slice_data['tag'] = 'auto-ast' slice_data['tag'] = 'auto-ast'
slice_data['comment'] = name slice_data['comment'] = name
f_item.custom_slices.append(slice_data) f_item.custom_slices.append(slice_data)
@@ -1402,6 +1402,7 @@ class App:
if not self._pending_patch_text: if not self._pending_patch_text:
self._patch_error_message = "No patch to apply" self._patch_error_message = "No patch to apply"
return return
#TODO(Ed): Exception(Review)
try: try:
base_dir = str(self.controller.current_project_dir) if hasattr(self.controller, 'current_project_dir') else "." base_dir = str(self.controller.current_project_dir) if hasattr(self.controller, 'current_project_dir') else "."
success, msg = apply_patch_to_file(self._pending_patch_text, base_dir) success, msg = apply_patch_to_file(self._pending_patch_text, base_dir)
@@ -1549,17 +1550,15 @@ if __name__ == "__main__":
main() main()
def render_main_interface(app: App) -> None: def render_main_interface(app: App) -> None:
""" """Top-level per-frame orchestrator. Dispatches every subsystem in the correct order:
Top-level per-frame orchestrator. Dispatches every subsystem in the correct order:
error/stale overlay tints, perf bookends, GUI task draining, modal rendering, error/stale overlay tints, perf bookends, GUI task draining, modal rendering,
auto-save, comms/tool-log caching, and all dockable windows. auto-save, comms/tool-log caching, and all dockable windows.
SSDL Shape: SSDL: `[I:overlays] -> [I:task_drain] -> [I:modals] -> [I:windows] -> [I:popups]`
`[I:overlays] -> [I:task_drain] -> [I:modals] -> [I:windows] -> [I:popups]`
ASCII Layout Map: ASCII Layout Map:
Full-screen dockspace (managed by imgui_bundle): Full-screen dockspace (managed by imgui_bundle):
+-------------------+------------------------------------+ +-------------------+-------------------------------------+
| Project Settings | Discussion Hub | | Project Settings | Discussion Hub |
| AI Settings | +--------------------------------+ | | AI Settings | +--------------------------------+ |
| Files & Media | | History entries (scrollable) | | | Files & Media | | History entries (scrollable) | |
@@ -1567,7 +1566,7 @@ def render_main_interface(app: App) -> None:
| MMA Dashboard | [splitter] | | MMA Dashboard | [splitter] |
| Task DAG | [Message] [Response] | | Task DAG | [Message] [Response] |
| Tier 1..4 streams | | | Tier 1..4 streams | |
+-------------------+------------------+-----------------+ +-------------------+------------------+------------------+
| Operations Hub (tab-bar) | Theme window | | Operations Hub (tab-bar) | Theme window |
| [Comms][Tools][Usage][Ext][Layouts] | | | [Comms][Tools][Usage][Ext][Layouts] | |
+---------------------------------------+-----------------+ +---------------------------------------+-----------------+
@@ -1600,6 +1599,7 @@ def render_main_interface(app: App) -> None:
render_tool_preset_manager_window(app) render_tool_preset_manager_window(app)
render_persona_editor_window(app) render_persona_editor_window(app)
#TODO(Ed): Exception(Review)
# Auto-save (every 60s) # Auto-save (every 60s)
now = time.time() now = time.time()
if now - app._last_autosave >= app._autosave_interval: if now - app._last_autosave >= app._autosave_interval:
@@ -1666,10 +1666,8 @@ def render_main_interface(app: App) -> None:
app._render_window_if_open("Context Preview", lambda: render_context_preview_window(app)) app._render_window_if_open("Context Preview", lambda: render_context_preview_window(app))
render_text_viewer_window(app) render_text_viewer_window(app)
app.perf_monitor.end_frame() app.perf_monitor.end_frame()
# Modals / Popups # Modals / Popups
render_approve_script_modal(app) render_approve_script_modal(app)
render_mma_modals(app) render_mma_modals(app)
@@ -1683,12 +1681,10 @@ def render_custom_title_bar(app: App) -> None:
#region: Diagnostics & Analytics #region: Diagnostics & Analytics
def render_usage_analytics_panel(app: App) -> None: def render_usage_analytics_panel(app: App) -> None:
""" """Renders the aggregate dashboard panel for usage, budgeting, cache analytics,
Renders the aggregate dashboard panel for usage, budgeting, cache analytics,
tool performance metrics, session insights, and RAG status. tool performance metrics, session insights, and RAG status.
SSDL Shape: SSDL:`[I:token_budget] -> [I:cache_panel] -> [I:tool_analytics] -> [I:session_insights] -> [I:rag_status]`
`[I:token_budget] -> [I:cache_panel] -> [I:tool_analytics] -> [I:session_insights] -> [I:rag_status]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -1728,13 +1724,11 @@ def render_usage_analytics_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_usage_analytics_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_usage_analytics_panel")
def render_diagnostics_panel(app: App) -> None: def render_diagnostics_panel(app: App) -> None:
""" """Renders the Diagnostics window. Shows FPS, frame time, CPU%, and input lag in a
Renders the Diagnostics window. Shows FPS, frame time, CPU%, and input lag in a
summary table; when profiling is on, shows per-component moving-average timings, summary table; when profiling is on, shows per-component moving-average timings,
optional sparkline graphs, and the diagnostic message log. optional sparkline graphs, and the diagnostic message log.
SSDL Shape: SSDL: `[I:perf_summary_table] -> [B:enable_profiling] => [I:component_timings_table] -> [I:graphs] -> [I:diag_log]`
`[I:perf_summary_table] -> [B:enable_profiling] => [I:component_timings_table] -> [I:graphs] -> [I:diag_log]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -1777,16 +1771,11 @@ def render_diagnostics_panel(app: App) -> None:
("CPU %", "cpu_percent", "%.1f"), ("CPU %", "cpu_percent", "%.1f"),
("Input Lag (ms)", "input_lag_ms", "%.1f") ("Input Lag (ms)", "input_lag_ms", "%.1f")
]: ]:
imgui.table_next_row() imgui.table_next_row(); imgui.table_next_column()
imgui.table_next_column() imgui.text(label); imgui.table_next_column()
imgui.text(label) if key == "fps": avg_val = imgui.get_io().framerate
imgui.table_next_column() else: avg_val = metrics.get(f"{key}_avg", metrics.get(key, 0.0))
if key == "fps": imgui.text(format_str % avg_val); imgui.table_next_column()
avg_val = imgui.get_io().framerate
else:
avg_val = metrics.get(f"{key}_avg", metrics.get(key, 0.0))
imgui.text(format_str % avg_val)
imgui.table_next_column()
app.perf_show_graphs.setdefault(key, False) app.perf_show_graphs.setdefault(key, False)
_, app.perf_show_graphs[key] = imgui.checkbox(f"##g_{key}", app.perf_show_graphs[key]) _, app.perf_show_graphs[key] = imgui.checkbox(f"##g_{key}", app.perf_show_graphs[key])
imgui.end_table() imgui.end_table()
@@ -1809,21 +1798,14 @@ def render_diagnostics_panel(app: App) -> None:
count = int(metrics.get(f"count_{comp_name}", 0)) count = int(metrics.get(f"count_{comp_name}", 0))
max_val = metrics.get(f"max_{comp_name}_ms", 0.0) max_val = metrics.get(f"max_{comp_name}_ms", 0.0)
min_val = metrics.get(f"min_{comp_name}_ms", 0.0) min_val = metrics.get(f"min_{comp_name}_ms", 0.0)
imgui.table_next_row() imgui.table_next_row(); imgui.table_next_column()
imgui.table_next_column() imgui.text(comp_name); imgui.table_next_column()
imgui.text(comp_name) if avg_val > 10.0: imgui.text_colored(theme.get_color("status_error"), f"{avg_val:.2f}")
imgui.table_next_column() else: imgui.text(f"{avg_val:.2f}")
if avg_val > 10.0:
imgui.text_colored(theme.get_color("status_error"), f"{avg_val:.2f}")
else:
imgui.text(f"{avg_val:.2f}")
imgui.table_next_column()
imgui.text(f"{count}")
imgui.table_next_column()
imgui.text(f"{max_val:.2f}")
imgui.table_next_column()
imgui.text(f"{min_val:.2f}")
imgui.table_next_column() imgui.table_next_column()
imgui.text(f"{count}"); imgui.table_next_column()
imgui.text(f"{max_val:.2f}"); imgui.table_next_column()
imgui.text(f"{min_val:.2f}"); imgui.table_next_column()
app.perf_show_graphs.setdefault(comp_name, False) app.perf_show_graphs.setdefault(comp_name, False)
_, app.perf_show_graphs[comp_name] = imgui.checkbox(f"##g_{comp_name}", app.perf_show_graphs[comp_name]) _, app.perf_show_graphs[comp_name] = imgui.checkbox(f"##g_{comp_name}", app.perf_show_graphs[comp_name])
imgui.end_table() imgui.end_table()
@@ -1850,23 +1832,19 @@ def render_diagnostics_panel(app: App) -> None:
for entry in reversed(app.controller.diagnostic_log): for entry in reversed(app.controller.diagnostic_log):
imgui.table_next_row() imgui.table_next_row()
imgui.table_next_column() imgui.table_next_column()
imgui.text(entry.get("ts", "")) imgui.text(entry.get("ts", "")); imgui.table_next_column()
imgui.table_next_column() imgui.text(entry.get("type", "")); imgui.table_next_column()
imgui.text(entry.get("type", ""))
imgui.table_next_column()
imgui.text_wrapped(entry.get("message", "")) imgui.text_wrapped(entry.get("message", ""))
imgui.end_table() imgui.end_table()
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_diagnostics_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_diagnostics_panel")
def render_cache_panel(app: App) -> None: def render_cache_panel(app: App) -> None:
""" """Renders Gemini cache analytics. Shows cache age, TTL remaining as a colour-coded
Renders Gemini cache analytics. Shows cache age, TTL remaining as a colour-coded
progress bar (green > 50%, yellow > 20%, red otherwise), and a [Clear Cache] button. progress bar (green > 50%, yellow > 20%, red otherwise), and a [Clear Cache] button.
Skips rendering for non-Gemini providers. Skips rendering for non-Gemini providers.
SSDL Shape: SSDL: `[I:cache_stats] -> [B:progress_bar] => [B:clear_cache]`
`[I:cache_stats] -> [B:progress_bar] => [B:clear_cache]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -1909,12 +1887,10 @@ def render_cache_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_cache_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_cache_panel")
def render_tool_analytics_panel(app: App) -> None: def render_tool_analytics_panel(app: App) -> None:
""" """Renders a breakdown of tool execution telemetry: calls, average latency ms, and
Renders a breakdown of tool execution telemetry: calls, average latency ms, and
failure rate percentage per tool. Results are sorted by invocation count descending. failure rate percentage per tool. Results are sorted by invocation count descending.
SSDL Shape: SSDL: `[I:tool_stats] -> [B:stats_table]`
`[I:tool_stats] -> [B:stats_table]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -1952,12 +1928,9 @@ def render_tool_analytics_panel(app: App) -> None:
avg_time = total_time / count if count > 0 else 0 avg_time = total_time / count if count > 0 else 0
fail_pct = (failures / count * 100) if count > 0 else 0 fail_pct = (failures / count * 100) if count > 0 else 0
imgui.table_next_row() imgui.table_next_row()
imgui.table_set_column_index(0) imgui.table_set_column_index(0); imgui.text(tool_name)
imgui.text(tool_name) imgui.table_set_column_index(1); imgui.text(str(count))
imgui.table_set_column_index(1) imgui.table_set_column_index(2); imgui.text(f"{avg_time:.0f}")
imgui.text(str(count))
imgui.table_set_column_index(2)
imgui.text(f"{avg_time:.0f}")
imgui.table_set_column_index(3) imgui.table_set_column_index(3)
if fail_pct > 0: imgui.text_colored(theme.get_color("status_error"), f"{fail_pct:.0f}%") if fail_pct > 0: imgui.text_colored(theme.get_color("status_error"), f"{fail_pct:.0f}%")
else: imgui.text("0%") else: imgui.text("0%")
@@ -1965,13 +1938,11 @@ def render_tool_analytics_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tool_analytics_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tool_analytics_panel")
def render_token_budget_panel(app: App) -> None: def render_token_budget_panel(app: App) -> None:
""" """Renders prompt token utilization breakdown: session totals, cache read/creation stats,
Renders prompt token utilization breakdown: session totals, cache read/creation stats,
a colour-coded utilisation progress bar, component breakdown table (System/Tools/History), a colour-coded utilisation progress bar, component breakdown table (System/Tools/History),
per-tier MMA cost table, trim warnings, and cache activity badge. per-tier MMA cost table, trim warnings, and cache activity badge.
SSDL Shape: SSDL: `[I:session_tokens] -> [B:utilization_bar] -> [B:breakdown_table] -> [B:tier_costs_table]`
`[I:session_tokens] -> [B:utilization_bar] -> [B:breakdown_table] -> [B:tier_costs_table]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2107,12 +2078,10 @@ def render_token_budget_panel(app: App) -> None:
#region: Logging #region: Logging
def render_log_management(app: App) -> None: def render_log_management(app: App) -> None:
""" """Renders the Log Management window. Enables browsing, starring (whitelisting), and loading
Renders the Log Management window. Enables browsing, starring (whitelisting), and loading
of prior log sessions from the log registry. Provides one-click prune and refresh actions. of prior log sessions from the log registry. Provides one-click prune and refresh actions.
SSDL Shape: SSDL: `[B:refresh | load | prune] -> [I:sessions_table] => [B:load | star/unstar per row]`
`[B:refresh | load | prune] -> [I:sessions_table] => [B:load | star/unstar per row]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2159,45 +2128,35 @@ def render_log_management(app: App) -> None:
for session_id, s_data in sessions.items(): for session_id, s_data in sessions.items():
imgui.table_next_row() imgui.table_next_row()
imgui.table_next_column() imgui.table_next_column()
imgui.text(session_id) imgui.text(session_id); imgui.table_next_column()
imgui.table_next_column() imgui.text(s_data.get("start_time", "")); imgui.table_next_column()
imgui.text(s_data.get("start_time", ""))
imgui.table_next_column()
whitelisted = s_data.get("whitelisted", False) whitelisted = s_data.get("whitelisted", False)
if whitelisted: if whitelisted: imgui.text_colored(theme.get_color("status_warning"), "YES")
imgui.text_colored(theme.get_color("status_warning"), "YES") else: imgui.text("NO")
else: imgui.table_next_column()
imgui.text("NO")
metadata = s_data.get("metadata") or {} metadata = s_data.get("metadata") or {}
imgui.table_next_column() imgui.text(metadata.get("reason", "")); imgui.table_next_column()
imgui.text(metadata.get("reason", "")) imgui.text(str(metadata.get("size_kb", ""))); imgui.table_next_column()
imgui.table_next_column() imgui.text(str(metadata.get("message_count", ""))); imgui.table_next_column()
imgui.text(str(metadata.get("size_kb", ""))) if imgui.button(f"Load##{session_id}"): app.cb_load_prior_log(s_data.get("path"))
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() imgui.same_line()
if whitelisted: if whitelisted:
if imgui.button(f"Unstar##{session_id}"): if imgui.button(f"Unstar##{session_id}"):
registry.update_session_metadata( registry.update_session_metadata(session_id,
session_id, message_count = int(metadata.get("message_count") or 0),
message_count=int(metadata.get("message_count") or 0), errors = int(metadata.get("errors") or 0),
errors=int(metadata.get("errors") or 0), size_kb = int(metadata.get("size_kb") or 0),
size_kb=int(metadata.get("size_kb") or 0), whitelisted = False,
whitelisted=False, reason = str(metadata.get("reason") or "")
reason=str(metadata.get("reason") or "")
) )
else: else:
if imgui.button(f"Star##{session_id}"): if imgui.button(f"Star##{session_id}"):
registry.update_session_metadata( registry.update_session_metadata(session_id,
session_id, message_count = int(metadata.get("message_count") or 0),
message_count=int(metadata.get("message_count") or 0), errors = int(metadata.get("errors") or 0),
errors=int(metadata.get("errors") or 0), size_kb = int(metadata.get("size_kb") or 0),
size_kb=int(metadata.get("size_kb") or 0), whitelisted = True,
whitelisted=True, reason = "Manually whitelisted"
reason="Manually whitelisted"
) )
imgui.end_table() imgui.end_table()
@@ -2208,11 +2167,9 @@ def render_log_management(app: App) -> None:
#region: Project Management #region: Project Management
def render_project_settings_hub(app: App) -> None: def render_project_settings_hub(app: App) -> None:
""" """Renders the Project Settings Hub: a two-tab container for Projects and Paths configuration.
Renders the Project Settings Hub: a two-tab container for Projects and Paths configuration.
SSDL Shape: SSDL: `[I:tab_bar] => [I:projects_panel] | [I:paths_panel]`
`[I:tab_bar] => [I:projects_panel] | [I:paths_panel]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2229,13 +2186,11 @@ def render_project_settings_hub(app: App) -> None:
if exp: render_paths_panel(app) if exp: render_paths_panel(app)
def render_projects_panel(app: App) -> None: def render_projects_panel(app: App) -> None:
""" """Renders the project configuration panel. Allows setting execution mode,
Renders the project configuration panel. Allows setting execution mode,
repository path, output/conductor directories, managing projects list, repository path, output/conductor directories, managing projects list,
and global layout toggles (word-wrap, auto-scroll). and global layout toggles (word-wrap, auto-scroll).
SSDL Shape: SSDL: `[I:active_project] -> [B:execution_mode] -> [B:directories] -> [I:project_files] -> [B:project_actions] -> [B:toggles]`
`[I:active_project] -> [B:execution_mode] -> [B:directories] -> [I:project_files] -> [B:project_actions] -> [B:toggles]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2309,12 +2264,11 @@ def render_projects_panel(app: App) -> None:
if imgui.button("Add Project"): if imgui.button("Add Project"):
r = hide_tk_root() r = hide_tk_root()
p = filedialog.askopenfilename( p = filedialog.askopenfilename(
title="Select Project .toml", title = "Select Project .toml",
filetypes=[("TOML", "*.toml"), ("All", "*.*")], filetypes = [("TOML", "*.toml"), ("All", "*.*")],
) )
r.destroy() r.destroy()
if p and p not in app.project_paths: if p and p not in app.project_paths: app.project_paths.append(p)
app.project_paths.append(p)
imgui.same_line() imgui.same_line()
if imgui.button("New Project"): if imgui.button("New Project"):
r = hide_tk_root() r = hide_tk_root()
@@ -2338,12 +2292,10 @@ def render_projects_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_projects_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_projects_panel")
def render_paths_panel(app: App) -> None: def render_paths_panel(app: App) -> None:
""" """Renders the System Path Configuration panel. Shows source-tagged logs and scripts
Renders the System Path Configuration panel. Shows source-tagged logs and scripts
directory fields, each with a Browse button, and Apply / Reset buttons. directory fields, each with a Browse button, and Apply / Reset buttons.
SSDL Shape: SSDL: `[I:path_fields] => [B:apply_reset]`
`[I:path_fields] => [B:apply_reset]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2399,7 +2351,7 @@ def render_ai_settings_hub(app: App) -> None:
"""Groups and renders all AI-related configuration panels in a unified hub sidebar. """Groups and renders all AI-related configuration panels in a unified hub sidebar.
Includes persona selection, LLM provider settings, system prompts, RAG config, and tools. Includes persona selection, LLM provider settings, system prompts, RAG config, and tools.
SSDL Shape: `[I:persona_selector] -> [B:provider_header] -> [B:system_prompts_header] -> [B:rag_header] -> [I:agent_tools]` SSDL: `[I:persona_selector] -> [B:provider_header] -> [B:system_prompts_header] -> [B:rag_header] -> [I:agent_tools]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2420,12 +2372,10 @@ def render_ai_settings_hub(app: App) -> None:
render_agent_tools_panel(app) render_agent_tools_panel(app)
def render_rag_panel(app: App) -> None: def render_rag_panel(app: App) -> None:
""" """Renders RAG configuration panel. Exposes enable toggle, vector-store and embedding
Renders RAG configuration panel. Exposes enable toggle, vector-store and embedding
provider selectors, chunk size/overlap inputs, RAG status label, and a Rebuild Index button. provider selectors, chunk size/overlap inputs, RAG status label, and a Rebuild Index button.
SSDL Shape: SSDL: `[B:rag_switch] -> [B:combo_selectors] -> [B:chunking_inputs] => [B:rebuild_index]`
`[B:rag_switch] -> [B:combo_selectors] -> [B:chunking_inputs] => [B:rebuild_index]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2444,23 +2394,23 @@ def render_rag_panel(app: App) -> None:
imgui.text("Vector Store Provider") imgui.text("Vector Store Provider")
providers = ['chroma', 'qdrant', 'mock'] providers = ['chroma', 'qdrant', 'mock']
#NOTE(Ed): Exception(Thirdparty)
try: try:
idx = providers.index(conf.vector_store.provider) idx = providers.index(conf.vector_store.provider)
except (ValueError, AttributeError): except (ValueError, AttributeError):
idx = 0 idx = 0
ch2, next_idx = imgui.combo("##rag_provider", idx, providers) ch2, next_idx = imgui.combo("##rag_provider", idx, providers)
if ch2: if ch2: conf.vector_store.provider = providers[next_idx]
conf.vector_store.provider = providers[next_idx]
imgui.text("Embedding Provider") imgui.text("Embedding Provider")
emb_providers = ['gemini', 'local'] emb_providers = ['gemini', 'local']
#NOTE(Ed): Exception(Thirdparty)
try: try:
idx_e = emb_providers.index(conf.embedding_provider) idx_e = emb_providers.index(conf.embedding_provider)
except (ValueError, AttributeError): except (ValueError, AttributeError):
idx_e = 0 idx_e = 0
ch3, next_idx_e = imgui.combo("##rag_emb_provider", idx_e, emb_providers) ch3, next_idx_e = imgui.combo("##rag_emb_provider", idx_e, emb_providers)
if ch3: if ch3: conf.embedding_provider = emb_providers[next_idx_e]
conf.embedding_provider = emb_providers[next_idx_e]
imgui.text("Chunk Size") imgui.text("Chunk Size")
imgui.set_next_item_width(150) imgui.set_next_item_width(150)
@@ -2475,13 +2425,11 @@ def render_rag_panel(app: App) -> None:
if imgui.button("Rebuild Index"): app.controller.event_queue.put('click', 'btn_rebuild_rag_index') if imgui.button("Rebuild Index"): app.controller.event_queue.put('click', 'btn_rebuild_rag_index')
def render_system_prompts_panel(app: App) -> None: def render_system_prompts_panel(app: App) -> None:
""" """Renders the System Prompts panel. Exposes global preset selector + multiline edit,
Renders the System Prompts panel. Exposes global preset selector + multiline edit,
base prompt toggle (default vs custom) with diff + reset, and project-level preset base prompt toggle (default vs custom) with diff + reset, and project-level preset
selector + multiline edit. selector + multiline edit.
SSDL Shape: SSDL: `[B:global_preset_combo] -> [I:global_text] -> [B:base_prompt_header] -> [I:base_text] -> [B:project_preset_combo] -> [I:project_text]`
`[B:global_preset_combo] -> [I:global_text] -> [B:base_prompt_header] -> [I:base_text] -> [B:project_preset_combo] -> [I:project_text]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2553,13 +2501,11 @@ def render_system_prompts_panel(app: App) -> None:
ch, app.ui_project_system_prompt = imgui.input_text_multiline("##psp", app.ui_project_system_prompt, imgui.ImVec2(-1, 100)) ch, app.ui_project_system_prompt = imgui.input_text_multiline("##psp", app.ui_project_system_prompt, imgui.ImVec2(-1, 100))
def render_agent_tools_panel(app: App) -> None: def render_agent_tools_panel(app: App) -> None:
""" """Renders the Active Tool Presets & Biases collapsible section. Shows a preset combo,
Renders the Active Tool Presets & Biases collapsible section. Shows a preset combo,
a Manage Presets button, and a Bias Profile combo. Displays a disabled notice when a Manage Presets button, and a Bias Profile combo. Displays a disabled notice when
tool calling is unsupported by the active provider/model. tool calling is unsupported by the active provider/model.
SSDL Shape: SSDL: `[B:collapsing_header] => [B:preset_combo] -> [B:manage_presets] -> [B:bias_combo]`
`[B:collapsing_header] => [B:preset_combo] -> [B:manage_presets] -> [B:bias_combo]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -2580,14 +2526,14 @@ def render_agent_tools_panel(app: App) -> None:
active = getattr(app, "ui_active_tool_preset", "") active = getattr(app, "ui_active_tool_preset", "")
if active is None: active = "" if active is None: active = ""
#NOTE(Ed): Exception(Thirdparty)
try: try:
idx = preset_names.index(active) idx = preset_names.index(active)
except ValueError: except ValueError:
idx = 0 idx = 0
ch, new_idx = imgui.combo("##tool_preset_select", idx, preset_names) ch, new_idx = imgui.combo("##tool_preset_select", idx, preset_names)
if ch: if ch: app.ui_active_tool_preset = preset_names[new_idx]
app.ui_active_tool_preset = preset_names[new_idx]
imgui.same_line() imgui.same_line()
if imgui.button("Manage Presets##tools"): app.show_tool_preset_manager_window = True if imgui.button("Manage Presets##tools"): app.show_tool_preset_manager_window = True
@@ -2608,10 +2554,12 @@ def render_agent_tools_panel(app: App) -> None:
imgui.dummy(imgui.ImVec2(0, 8)) imgui.dummy(imgui.ImVec2(0, 8))
cat_options = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys())) cat_options = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys()))
#NOTE(Ed): Exception(Thirdparty)
try: try:
f_idx = cat_options.index(app.ui_tool_filter_category) f_idx = cat_options.index(app.ui_tool_filter_category)
except ValueError: except ValueError:
f_idx = 0 f_idx = 0
imgui.set_next_item_width(200) imgui.set_next_item_width(200)
ch_cat, next_f_idx = imgui.combo("Filter Category##agent", f_idx, cat_options) ch_cat, next_f_idx = imgui.combo("Filter Category##agent", f_idx, cat_options)
if ch_cat: app.ui_tool_filter_category = cat_options[next_f_idx] if ch_cat: app.ui_tool_filter_category = cat_options[next_f_idx]
@@ -2638,17 +2586,10 @@ def render_agent_tools_panel(app: App) -> None:
imgui.tree_pop() imgui.tree_pop()
def render_provider_panel(app: App) -> None: def render_provider_panel(app: App) -> None:
""" """Renders the LLM provider configuration panel. Allows selection of API providers,
Renders the LLM provider configuration panel. Allows selection of API providers,
active models, hyper-parameters (temperature, max tokens, Top-P), history limits, and Gemini CLI binaries. active models, hyper-parameters (temperature, max tokens, Top-P), history limits, and Gemini CLI binaries.
State Mutations: SSDL: `[I:providers] -> [B:provider_combo] -> [B:model_listbox] -> [B:parameters_sliders] => [B:cli_path_browse]`
app.current_provider, app.current_model
app.temperature, app.max_tokens, app.top_p, app.history_trunc_limit
app.ui_gemini_cli_path
SSDL Shape:
`[I:providers] -> [B:provider_combo] -> [B:model_listbox] -> [B:parameters_sliders] => [B:cli_path_browse]`
""" """
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_provider_panel") if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_provider_panel")
imgui.text("Provider") imgui.text("Provider")
@@ -2666,7 +2607,9 @@ def render_provider_panel(app: App) -> None:
if app.current_provider == "llama": if app.current_provider == "llama":
base_url = getattr(ai_client, "_llama_base_url", "") base_url = getattr(ai_client, "_llama_base_url", "")
imgui.set_tooltip(f"Local backend: {base_url or 'unknown'}" if base_url else "Local backend") imgui.set_tooltip(f"Local backend: {base_url or 'unknown'}" if base_url else "Local backend")
_render_v2_capability_badges(caps) _render_v2_capability_badges(caps)
imgui.separator() imgui.separator()
imgui.text("Model") imgui.text("Model")
if imgui.begin_list_box("##models", imgui.ImVec2(-1, 120)): if imgui.begin_list_box("##models", imgui.ImVec2(-1, 120)):
@@ -2728,21 +2671,10 @@ def render_provider_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_provider_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_provider_panel")
def render_persona_selector_panel(app: App) -> None: def render_persona_selector_panel(app: App) -> None:
""" """Renders the Persona Selection panel. Allows selecting profiles, editing persona definitions,
Renders the Persona Selection panel. Allows selecting profiles, editing persona definitions,
overriding system parameters (models, presets, prompts, bias profiles) and loading preset contexts. overriding system parameters (models, presets, prompts, bias profiles) and loading preset contexts.
State Mutations: SSDL: `[I:personas] -> [B:persona_combo] => [B:manage_personas]`
app.ui_active_persona, app._editing_persona_name, app._editing_persona_system_prompt,
app._editing_persona_tool_preset_id, app._editing_persona_bias_profile_id,
app._editing_persona_context_preset_id, app._editing_persona_aggregation_strategy,
app._editing_persona_preferred_models_list, app._editing_persona_is_new,
app.current_provider, app.current_model, app.temperature, app.max_tokens,
app.history_trunc_limit, app.ui_project_system_prompt, app.ui_active_tool_preset,
app.ui_active_bias_profile, app.ui_active_context_preset, app.show_persona_editor_window
SSDL Shape:
`[I:personas] -> [B:persona_combo] => [B:manage_personas]`
""" """
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_persona_selector_panel") if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_persona_selector_panel")
imgui.text("Persona") imgui.text("Persona")
@@ -2768,10 +2700,8 @@ def render_persona_selector_panel(app: App) -> None:
# Apply persona to current state immediately # Apply persona to current state immediately
if persona.preferred_models and len(persona.preferred_models) > 0: if persona.preferred_models and len(persona.preferred_models) > 0:
first_model = persona.preferred_models[0] first_model = persona.preferred_models[0]
if first_model.get("provider"): if first_model.get("provider"): app.current_provider = first_model.get("provider")
app.current_provider = first_model.get("provider") if first_model.get("model"): app.current_model = first_model.get("model")
if first_model.get("model"):
app.current_model = first_model.get("model")
if first_model.get("temperature") is not None: if first_model.get("temperature") is not None:
ai_client.temperature = first_model.get("temperature") ai_client.temperature = first_model.get("temperature")
app.temperature = first_model.get("temperature") app.temperature = first_model.get("temperature")
@@ -2790,6 +2720,7 @@ def render_persona_selector_panel(app: App) -> None:
ai_client.set_bias_profile(persona.bias_profile) ai_client.set_bias_profile(persona.bias_profile)
if getattr(persona, 'context_preset', None): if getattr(persona, 'context_preset', None):
app.ui_active_context_preset = persona.context_preset app.ui_active_context_preset = persona.context_preset
#TODO(Ed): Exception(Review)
try: try:
app.load_context_preset(persona.context_preset) app.load_context_preset(persona.context_preset)
except KeyError as e: except KeyError as e:
@@ -2830,15 +2761,10 @@ def render_persona_selector_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_persona_selector_panel") if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_persona_selector_panel")
def render_base_prompt_diff_modal(app: App) -> None: def render_base_prompt_diff_modal(app: App) -> None:
""" """Renders a popup modal showing a unified diff between the default system prompt
Renders a popup modal showing a unified diff between the default system prompt
and the current custom base prompt. and the current custom base prompt.
State Mutations: SSDL: `[I:unified_diff] -> [I:diff_view_box] => [B:close]`
app.controller._show_base_prompt_diff_modal (closes/hides modal)
SSDL Shape:
`[I:unified_diff] -> [I:diff_view_box] => [B:close]`
""" """
if not getattr(app.controller, "_show_base_prompt_diff_modal", False): if not getattr(app.controller, "_show_base_prompt_diff_modal", False):
return return
@@ -2869,16 +2795,9 @@ def render_base_prompt_diff_modal(app: App) -> None:
imgui.end_popup() imgui.end_popup()
def render_save_preset_modal(app: App) -> None: def render_save_preset_modal(app: App) -> None:
""" """Renders the popup modal for saving the current ImGui window layout preset.
Renders the popup modal for saving the current ImGui window layout preset.
State Mutations: SSDL: `[I:preset_inputs] => [B:save_cancel]`
app._show_save_preset_modal (toggles visibility)
app._new_preset_name (stores input layout name)
app.layout_presets (saves layout definitions to configuration)
SSDL Shape:
`[I:preset_inputs] => [B:save_cancel]`
""" """
if not app._show_save_preset_modal: return if not app._show_save_preset_modal: return
imgui.open_popup("Save Layout Preset") imgui.open_popup("Save Layout Preset")
@@ -2904,16 +2823,10 @@ def render_save_preset_modal(app: App) -> None:
imgui.close_current_popup() imgui.close_current_popup()
def render_preset_manager_content(app: App, is_embedded: bool = False) -> None: def render_preset_manager_content(app: App, is_embedded: bool = False) -> None:
""" """Renders the core prompt preset manager interface including the sidebar preset selector
Renders the core prompt preset manager interface including the sidebar preset selector
and the preset prompt content text editor. and the preset prompt content text editor.
State Mutations: SSDL: `[I:presets_list] -> [B:new_preset] -> [I:editor_meta] -> [I:editor_textbox] => [B:save_delete]`
app._selected_preset_idx
app._editing_preset_name, app._editing_preset_system_prompt, app._editing_preset_scope
SSDL Shape:
`[I:presets_list] -> [B:new_preset] -> [I:editor_meta] -> [I:editor_textbox] => [B:save_delete]`
""" """
avail = imgui.get_content_region_avail() avail = imgui.get_content_region_avail()
if not hasattr(app, "_prompt_md_preview"): app._prompt_md_preview = False if not hasattr(app, "_prompt_md_preview"): app._prompt_md_preview = False
@@ -3004,14 +2917,9 @@ def render_preset_manager_content(app: App, is_embedded: bool = False) -> None:
imgui.end_table() imgui.end_table()
def render_preset_manager_window(app: App, is_embedded: bool = False) -> None: def render_preset_manager_window(app: App, is_embedded: bool = False) -> None:
""" """Renders the window container for the Prompt Presets Manager.
Renders the window container for the Prompt Presets Manager.
State Mutations: SSDL: `[I] -> [I:window] => [I:preset_manager_content]`
app.show_preset_manager_window (visibility toggle)
SSDL Shape:
`[I] -> [I:window] => [I:preset_manager_content]`
""" """
if not app.show_preset_manager_window and not is_embedded: return if not app.show_preset_manager_window and not is_embedded: return
if not is_embedded: if not is_embedded:
@@ -3023,15 +2931,9 @@ def render_preset_manager_window(app: App, is_embedded: bool = False) -> None:
render_preset_manager_content(app, is_embedded=is_embedded) render_preset_manager_content(app, is_embedded=is_embedded)
def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> None: def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> None:
""" """Renders the tool presets and tool capability profiles editor layout.
Renders the tool presets and tool capability profiles editor layout.
State Mutations: SSDL: `[I:tool_presets] -> [B:new_tool_preset] -> [I:capability_toggles] -> [I:tools_list] => [B:save_delete]`
app._editing_tool_preset_name, app._editing_tool_preset_scope, app._selected_tool_preset_idx
app.controller (saves/deletes tool presets)
SSDL Shape:
`[I:tool_presets] -> [B:new_tool_preset] -> [I:capability_toggles] -> [I:tools_list] => [B:save_delete]`
""" """
avail = imgui.get_content_region_avail() avail = imgui.get_content_region_avail()
if not hasattr(app, "_tool_split_v"): app._tool_split_v = 0.4 if not hasattr(app, "_tool_split_v"): app._tool_split_v = 0.4
@@ -3134,19 +3036,25 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N
imgui.begin_child("blist_pane", imgui.ImVec2(0, 0), False) imgui.begin_child("blist_pane", imgui.ImVec2(0, 0), False)
if True: if True:
if imgui.button("New Profile", imgui.ImVec2(-1, 0)): if imgui.button("New Profile", imgui.ImVec2(-1, 0)):
app._editing_bias_profile_name = ""; app._editing_bias_profile_tool_weights = {} app._editing_bias_profile_name = ""
app._editing_bias_profile_category_multipliers = {}; app._selected_bias_profile_idx = -1 app._editing_bias_profile_tool_weights = {}
app._editing_bias_profile_category_multipliers = {}
app._selected_bias_profile_idx = -1
imgui.separator(); bnames = sorted(app.bias_profiles.keys()) imgui.separator(); bnames = sorted(app.bias_profiles.keys())
for i, bname in enumerate(bnames): for i, bname in enumerate(bnames):
if bname and imgui.selectable(f"{bname}##b_{i}", app._selected_bias_profile_idx == i)[0]: if bname and imgui.selectable(f"{bname}##b_{i}", app._selected_bias_profile_idx == i)[0]:
app._selected_bias_profile_idx = i; app._editing_bias_profile_name = bname; prof = app.bias_profiles[bname] app._selected_bias_profile_idx = i
app._editing_bias_profile_tool_weights = copy.deepcopy(prof.tool_weights); app._editing_bias_profile_category_multipliers = copy.deepcopy(prof.category_multipliers) app._editing_bias_profile_name = bname;
prof = app.bias_profiles[bname]
app._editing_bias_profile_tool_weights = copy.deepcopy(prof.tool_weights)
app._editing_bias_profile_category_multipliers = copy.deepcopy(prof.category_multipliers)
imgui.end_child() imgui.end_child()
imgui.table_next_column() imgui.table_next_column()
imgui.begin_child("bedit_pane", imgui.ImVec2(0, 0), False) imgui.begin_child("bedit_pane", imgui.ImVec2(0, 0), False)
if True: if True:
imgui.text("Name:"); imgui.same_line(); imgui.set_next_item_width(-1); _, app._editing_bias_profile_name = imgui.input_text("##bname", app._editing_bias_profile_name) imgui.text("Name:"); imgui.same_line(); imgui.set_next_item_width(-1)
_, app._editing_bias_profile_name = imgui.input_text("##bname", app._editing_bias_profile_name)
rem_bias_y = imgui.get_content_region_avail().y - 45 rem_bias_y = imgui.get_content_region_avail().y - 45
if app._bias_weights_open and app._bias_cats_open: bh1, bh2 = rem_bias_y * app._bias_split_v, rem_bias_y - (rem_bias_y * app._bias_split_v) - 10 if app._bias_weights_open and app._bias_cats_open: bh1, bh2 = rem_bias_y * app._bias_split_v, rem_bias_y - (rem_bias_y * app._bias_split_v) - 10
elif app._bias_weights_open: bh1, bh2 = rem_bias_y, 0 elif app._bias_weights_open: bh1, bh2 = rem_bias_y, 0
@@ -3161,9 +3069,11 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N
for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items(): for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items():
if imgui.tree_node(f"{cat_name}##b_list"): if imgui.tree_node(f"{cat_name}##b_list"):
if imgui.begin_table(f"bt_{cat_name}", 2): if imgui.begin_table(f"bt_{cat_name}", 2):
imgui.table_setup_column("T", imgui.TableColumnFlags_.width_fixed, 220); imgui.table_setup_column("W", imgui.TableColumnFlags_.width_stretch) imgui.table_setup_column("T", imgui.TableColumnFlags_.width_fixed, 220)
imgui.table_setup_column("W", imgui.TableColumnFlags_.width_stretch)
for tn in default_tools: for tn in default_tools:
imgui.table_next_row(); imgui.table_next_column(); imgui.text(tn); imgui.table_next_column() imgui.table_next_row(); imgui.table_next_column()
imgui.text(tn); imgui.table_next_column()
curr_w = app._editing_bias_profile_tool_weights.get(tn, 3); imgui.set_next_item_width(-1) curr_w = app._editing_bias_profile_tool_weights.get(tn, 3); imgui.set_next_item_width(-1)
ch_w, n_w = imgui.slider_int(f"##bw_{tn}", curr_w, 1, 10); ch_w, n_w = imgui.slider_int(f"##bw_{tn}", curr_w, 1, 10);
if ch_w: app._editing_bias_profile_tool_weights[tn] = n_w if ch_w: app._editing_bias_profile_tool_weights[tn] = n_w
@@ -3172,7 +3082,8 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N
imgui.end_child() imgui.end_child()
if app._bias_cats_open: if app._bias_cats_open:
imgui.button("###bias_splitter", imgui.ImVec2(-1, 4)) imgui.button("###bias_splitter", imgui.ImVec2(-1, 4))
if imgui.is_item_active(): app._bias_split_v = max(0.1, min(0.9, app._bias_split_v + imgui.get_io().mouse_delta.y / rem_bias_y)) if imgui.is_item_active():
app._bias_split_v = max(0.1, min(0.9, app._bias_split_v + imgui.get_io().mouse_delta.y / rem_bias_y))
opened_bc = imgui.collapsing_header("Category Multipliers", imgui.TreeNodeFlags_.default_open) opened_bc = imgui.collapsing_header("Category Multipliers", imgui.TreeNodeFlags_.default_open)
if opened_bc != app._bias_cats_open: app._bias_cats_open = opened_bc if opened_bc != app._bias_cats_open: app._bias_cats_open = opened_bc
@@ -3180,9 +3091,11 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N
imgui.begin_child("bcat_scroll", imgui.ImVec2(0, bh2), True) imgui.begin_child("bcat_scroll", imgui.ImVec2(0, bh2), True)
if True: if True:
if imgui.begin_table("bcats", 2): if imgui.begin_table("bcats", 2):
imgui.table_setup_column("C", imgui.TableColumnFlags_.width_fixed, 220); imgui.table_setup_column("M", imgui.TableColumnFlags_.width_stretch) imgui.table_setup_column("C", imgui.TableColumnFlags_.width_fixed, 220)
imgui.table_setup_column("M", imgui.TableColumnFlags_.width_stretch)
for cn in sorted(models.DEFAULT_TOOL_CATEGORIES.keys()): for cn in sorted(models.DEFAULT_TOOL_CATEGORIES.keys()):
imgui.table_next_row(); imgui.table_next_column(); imgui.text(cn); imgui.table_next_column() imgui.table_next_row(); imgui.table_next_column()
imgui.text(cn); imgui.table_next_column()
curr_m = app._editing_bias_profile_category_multipliers.get(cn, 1.0); imgui.set_next_item_width(-1) curr_m = app._editing_bias_profile_category_multipliers.get(cn, 1.0); imgui.set_next_item_width(-1)
ch_m, n_m = imgui.slider_float(f"##cm_{cn}", curr_m, 0.1, 5.0, "%.1fx"); ch_m, n_m = imgui.slider_float(f"##cm_{cn}", curr_m, 0.1, 5.0, "%.1fx");
if ch_m: app._editing_bias_profile_category_multipliers[cn] = n_m if ch_m: app._editing_bias_profile_category_multipliers[cn] = n_m
@@ -3202,24 +3115,25 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N
# --- Footer Buttons --- # --- Footer Buttons ---
imgui.separator() imgui.separator()
if imgui.button("Save##tp", imgui.ImVec2(100, 0)): if imgui.button("Save##tp", imgui.ImVec2(100, 0)):
if app._editing_tool_preset_name.strip(): app.controller._cb_save_tool_preset(app._editing_tool_preset_name.strip(), app._editing_tool_preset_categories, app._editing_tool_preset_scope); app.ai_status = f"Saved: {app._editing_tool_preset_name}" if app._editing_tool_preset_name.strip():
app.controller._cb_save_tool_preset(app._editing_tool_preset_name.strip(), app._editing_tool_preset_categories, app._editing_tool_preset_scope)
app.ai_status = f"Saved: {app._editing_tool_preset_name}"
imgui.same_line() imgui.same_line()
if imgui.button("Delete##tp", imgui.ImVec2(100, 0)): if imgui.button("Delete##tp", imgui.ImVec2(100, 0)):
if app._editing_tool_preset_name: app.controller._cb_delete_tool_preset(app._editing_tool_preset_name, app._editing_tool_preset_scope); app._editing_tool_preset_name = ""; app._selected_tool_preset_idx = -1 if app._editing_tool_preset_name:
app.controller._cb_delete_tool_preset(app._editing_tool_preset_name, app._editing_tool_preset_scope)
app._editing_tool_preset_name = ""
app._selected_tool_preset_idx = -1
imgui.same_line() imgui.same_line()
if not is_embedded: if not is_embedded:
if imgui.button("Close##tp", imgui.ImVec2(100, 0)): app.show_tool_preset_manager_window = False if imgui.button("Close##tp", imgui.ImVec2(100, 0)):
app.show_tool_preset_manager_window = False
imgui.end_table() imgui.end_table()
def render_tool_preset_manager_window(app: App, is_embedded: bool = False) -> None: def render_tool_preset_manager_window(app: App, is_embedded: bool = False) -> None:
""" """Renders the window container for the Tool Preset Manager.
Renders the window container for the Tool Preset Manager.
State Mutations: SSDL: `[I] -> [I:window] => [I:tool_preset_manager_content]`
app.show_tool_preset_manager_window (visibility toggle)
SSDL Shape:
`[I] -> [I:window] => [I:tool_preset_manager_content]`
""" """
if not app.show_tool_preset_manager_window and not is_embedded: return if not app.show_tool_preset_manager_window and not is_embedded: return
if not is_embedded: if not is_embedded:
@@ -3231,20 +3145,10 @@ def render_tool_preset_manager_window(app: App, is_embedded: bool = False) -> No
render_tool_preset_manager_content(app, is_embedded=is_embedded) render_tool_preset_manager_content(app, is_embedded=is_embedded)
def render_persona_editor_window(app: App, is_embedded: bool = False) -> None: def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
""" """Renders the Persona Editor window, allowing creating, deleting, and modifying persona settings
Renders the Persona Editor window, allowing creating, deleting, and modifying persona settings
(prompts, preferred models list, tool presets, aggregation strategy, etc). (prompts, preferred models list, tool presets, aggregation strategy, etc).
State Mutations: SSDL: `[I:personas_list] -> [B:new_persona] -> [I:settings_editor] -> [I:models_list] -> [I:system_prompt_box] => [B:save_delete]`
app.show_persona_editor_window (visibility toggle)
app._editing_persona_name, app._editing_persona_system_prompt
app._editing_persona_tool_preset_id, app._editing_persona_bias_profile_id
app._editing_persona_context_preset_id, app._editing_persona_aggregation_strategy
app._editing_persona_preferred_models_list, app._editing_persona_is_new
app.controller (saves/deletes personas)
SSDL Shape:
`[I:personas_list] -> [B:new_persona] -> [I:settings_editor] -> [I:models_list] -> [I:system_prompt_box] => [B:save_delete]`
""" """
if not app.show_persona_editor_window and not is_embedded: return if not app.show_persona_editor_window and not is_embedded: return
if not is_embedded: if not is_embedded:
@@ -3273,11 +3177,12 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
personas = getattr(app.controller, 'personas', {}) personas = getattr(app.controller, 'personas', {})
for name in sorted(personas.keys()): for name in sorted(personas.keys()):
if name and imgui.selectable(f"{name}##p_list", name == app._editing_persona_name and not getattr(app, '_editing_persona_is_new', False))[0]: if name and imgui.selectable(f"{name}##p_list", name == app._editing_persona_name and not getattr(app, '_editing_persona_is_new', False))[0]:
import copy; #TODO(Ed): Review local import
p = personas[name]; app._editing_persona_name = p.name; app._editing_persona_system_prompt = p.system_prompt or "" p = personas[name]; app._editing_persona_name = p.name; app._editing_persona_system_prompt = p.system_prompt or ""
app._editing_persona_tool_preset_id = p.tool_preset or ""; app._editing_persona_bias_profile_id = p.bias_profile or "" app._editing_persona_tool_preset_id = p.tool_preset or ""; app._editing_persona_bias_profile_id = p.bias_profile or ""
app._editing_persona_context_preset_id = getattr(p, 'context_preset', '') or "" app._editing_persona_context_preset_id = getattr(p, 'context_preset', '') or ""
app._editing_persona_aggregation_strategy = getattr(p, 'aggregation_strategy', '') or "" app._editing_persona_aggregation_strategy = getattr(p, 'aggregation_strategy', '') or ""
import copy; app._editing_persona_preferred_models_list = copy.deepcopy(p.preferred_models) if p.preferred_models else [] app._editing_persona_preferred_models_list = copy.deepcopy(p.preferred_models) if p.preferred_models else []
app._editing_persona_scope = app.controller.persona_manager.get_persona_scope(p.name); app._editing_persona_is_new = False app._editing_persona_scope = app.controller.persona_manager.get_persona_scope(p.name); app._editing_persona_is_new = False
imgui.end_child() imgui.end_child()
@@ -3287,7 +3192,8 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
imgui.begin_child("persona_editor_content", imgui.ImVec2(0, avail.y - 45), False) imgui.begin_child("persona_editor_content", imgui.ImVec2(0, avail.y - 45), False)
if True: if True:
header_text = "New Persona" if getattr(app, '_editing_persona_is_new', True) else f"Editing Persona: {app._editing_persona_name}" header_text = "New Persona" if getattr(app, '_editing_persona_is_new', True) else f"Editing Persona: {app._editing_persona_name}"
imgui.text_colored(C_IN(), header_text); imgui.separator() imgui.text_colored(C_IN(), header_text)
imgui.separator()
if imgui.begin_table("p_meta", 2): if imgui.begin_table("p_meta", 2):
imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 60); imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 60); imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch)
@@ -3328,11 +3234,15 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
with imscope.indent(20): with imscope.indent(20):
if imgui.begin_table("model_settings", 2, imgui.TableFlags_.borders_inner_v): 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_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) imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column();
p_idx = providers.index(prov) + 1 if prov in providers else 0; ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers) 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 "" 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) imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column();
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 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) 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 "" 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.table_next_row(); imgui.table_next_column(); imgui.text("Temperature:"); imgui.table_next_column(); cw = imgui.get_content_region_avail().x
@@ -3421,12 +3331,10 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
#region: Context Management #region: Context Management
def render_files_and_media(app: App) -> None: def render_files_and_media(app: App) -> None:
""" """Renders the inventory of files and screenshots. Allows adding files or directories to the inventory
Renders the inventory of files and screenshots. Allows adding files or directories to the inventory
and attaching files or screenshots to the active context. and attaching files or screenshots to the active context.
SSDL Shape: SSDL: `[I:inventory] -> [B:add_files_folders] -> [I:screenshots] => [B:add_screenshots]`
`[I:inventory] -> [B:add_files_folders] -> [I:screenshots] => [B:add_screenshots]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -3482,12 +3390,9 @@ def render_files_and_media(app: App) -> None:
if imgui.is_item_hovered(): imgui.set_tooltip(fpath) if imgui.is_item_hovered(): imgui.set_tooltip(fpath)
imgui.table_set_column_index(2) imgui.table_set_column_index(2)
if in_context: if in_context: imgui.text_colored(theme.get_color("status_success"), "Active")
imgui.text_colored(theme.get_color("status_success"), "Active") elif is_cached: imgui.text_colored(theme.get_color("status_info"), "Cached")
elif is_cached: else: imgui.text_disabled(" - ")
imgui.text_colored(theme.get_color("status_info"), "Cached")
else:
imgui.text_disabled(" - ")
imgui.end_table() imgui.end_table()
if to_remove_idx != -1: app.files.pop(to_remove_idx) if to_remove_idx != -1: app.files.pop(to_remove_idx)
@@ -3530,12 +3435,10 @@ def render_files_and_media(app: App) -> None:
return return
def render_context_batch_actions(app: App, total_lines: int, total_ast: int) -> None: def render_context_batch_actions(app: App, total_lines: int, total_ast: int) -> None:
""" """Renders a batch actions control bar. Allows batch-changing view modes of selected files,
Renders a batch actions control bar. Allows batch-changing view modes of selected files,
selecting/deselecting all, adding files, and generating context preview markdown. selecting/deselecting all, adding files, and generating context preview markdown.
SSDL Shape: SSDL: `[I:context_files] -> [B:mode_batch_buttons] -> [B:selection_buttons] -> [B:all_add_del] => [B:preview]`
`[I:context_files] -> [B:mode_batch_buttons] -> [B:selection_buttons] -> [B:all_add_del] => [B:preview]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -3593,12 +3496,10 @@ def render_context_batch_actions(app: App, total_lines: int, total_ast: int) ->
imgui.text(f" | Total: {len(app.context_files)} files, {total_lines} lines, {total_ast} AST elements") imgui.text(f" | Total: {len(app.context_files)} files, {total_lines} lines, {total_ast} AST elements")
def render_add_context_files_modal(app: App) -> None: def render_add_context_files_modal(app: App) -> None:
""" """Renders a modal popup listing files in the project inventory that can be batch-added
Renders a modal popup listing files in the project inventory that can be batch-added
to the active context. to the active context.
SSDL Shape: SSDL: `[I:picker_list] -> [B:checkboxes] -> [B:add_selected_button] => [B:cancel_button]`
`[I:picker_list] -> [B:checkboxes] -> [B:add_selected_button] => [B:cancel_button]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -3649,12 +3550,10 @@ def render_add_context_files_modal(app: App) -> None:
imgui.end_popup() imgui.end_popup()
def render_context_composition_panel(app: App) -> None: def render_context_composition_panel(app: App) -> None:
""" """Renders the Context Composition panel containing loaded project files, presets,
Renders the Context Composition panel containing loaded project files, presets,
and visual screenshot files. Displays token stats, batch files actions, and collapsible trees. and visual screenshot files. Displays token stats, batch files actions, and collapsible trees.
SSDL Shape: SSDL: `[I:stats] -> [I:batch_actions] -> [I:files_table] -> [I:presets] -> [I:screenshots]`
`[I:stats] -> [I:batch_actions] -> [I:files_table] -> [I:presets] -> [I:screenshots]`
ASCII Layout Map: ASCII Layout Map:
+-------------------------------------------------------------+ +-------------------------------------------------------------+
@@ -7275,13 +7174,11 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
imgui.text_disabled("No active MMA track or tickets.") imgui.text_disabled("No active MMA track or tickets.")
def render_beads_tab(app: App) -> None: def render_beads_tab(app: App) -> None:
""" """Renders the Beads Graph tab. Checks for `dolt` and `bd` CLI availability,
Renders the Beads Graph tab. Checks for `dolt` and `bd` CLI availability,
shows a warning if missing, then lists beads from the Dolt-backed BeadsClient shows a warning if missing, then lists beads from the Dolt-backed BeadsClient
in a 3-column table (ID / Status / Title). in a 3-column table (ID / Status / Title).
SSDL Shape: SSDL Shape: `[I:dep_check] -> [B:refresh] -> [I:beads_table]`
`[I:dep_check] -> [B:refresh] -> [I:beads_table]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+
@@ -7324,24 +7221,18 @@ def render_beads_tab(app: App) -> None:
imgui.table_setup_column("Title") imgui.table_setup_column("Title")
imgui.table_headers_row() imgui.table_headers_row()
for b in beads: for b in beads:
imgui.table_next_row() imgui.table_next_row(); imgui.table_next_column()
imgui.table_next_column() imgui.text(str(b.id)); imgui.table_next_column()
imgui.text(str(b.id)) imgui.text(str(b.status)); imgui.table_next_column(); imgui.text(str(b.title))
imgui.table_next_column()
imgui.text(str(b.status))
imgui.table_next_column()
imgui.text(str(b.title))
imgui.end_table() imgui.end_table()
except Exception as e: except Exception as e:
imgui.text_colored(theme.get_color("status_error"), f"Error loading beads: {e}") imgui.text_colored(theme.get_color("status_error"), f"Error loading beads: {e}")
def render_mma_focus_selector(app: App) -> None: def render_mma_focus_selector(app: App) -> None:
""" """Renders the Focus Agent selector. Filters the discussion entries list to show
Renders the Focus Agent selector. Filters the discussion entries list to show
only the output of the chosen tier agent (or All). Includes a clear [x] button. only the output of the chosen tier agent (or All). Includes a clear [x] button.
SSDL Shape: SSDL: `[I:focus_combo] -> [B:clear_x?]`
`[I:focus_combo] -> [B:clear_x?]`
ASCII Layout Map: ASCII Layout Map:
+---------------------------------------------------------+ +---------------------------------------------------------+