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
+164 -273
View File
@@ -369,6 +369,7 @@ class App:
self.controller.save_context_preset(preset)
self.ui_new_context_preset_name = ""
self.show_missing_files_modal = False
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._predefined_callbacks['save_context_preset_force'] = _save_context_preset_force
@@ -541,6 +542,7 @@ class App:
self.context_files = [item]
self.screenshots = ['test.png']
self.save_context_preset(name)
def _handle_approve_ask(self) -> None:
"""UI-level wrapper for approving a pending tool execution ask."""
self.controller._handle_approve_ask()
@@ -957,15 +959,12 @@ class App:
"""Re-applies the next snapshot in the history stack (forward navigation).
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.flush()
if not self.history.can_redo:
return
sys.stderr.write(f"[DEBUG History] _handle_redo called. can_redo={self.history.can_redo}\n"); sys.stderr.flush()
if not self.history.can_redo: return
current = self._take_snapshot()
entry = self.history.redo(current, "Redo Action")
if entry:
sys.stderr.write(f"[DEBUG History] Redoing to: {entry.description}\n")
sys.stderr.flush()
sys.stderr.write(f"[DEBUG History] Redoing to: {entry.description}\n"); sys.stderr.flush()
self._apply_snapshot(entry.state)
def shutdown(self) -> None:
@@ -973,6 +972,7 @@ class App:
forces a save of dirty registries/caches, and terminates the active thread pools.
SSDL Shape: `[I:save_ini] -> [I:controller_shutdown]`
"""
#Note(Ed): Exception(Thirdparty)
try:
if hasattr(self, 'runner_params') and 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)
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
except Exception: is_max = False
# 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()
path = filedialog.askdirectory(title='Select Session Directory', initialdir=str(paths.get_logs_dir()))
root.destroy()
if path:
self.controller.cb_load_prior_log(path)
if path: self.controller.cb_load_prior_log(path)
def _set_external_editor_default(self, editor_name: str) -> None:
from src import models
@@ -1326,6 +1325,8 @@ class App:
mcp_client.configure([{"path": abs_path}], [proj_dir] if proj_dir else None)
f_path_lower = f_item.path.lower()
#TODO(Ed): Exception(Review)
try:
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)
@@ -1342,6 +1343,7 @@ class App:
text = f.read()
except Exception:
return
#TODO(Ed): Exception(Review)
try:
from src.fuzzy_anchor import FuzzyAnchor
except ImportError:
@@ -1352,10 +1354,8 @@ class App:
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):
continue
if FuzzyAnchor:
slice_data = FuzzyAnchor.create_slice(text, s_line, e_line)
else:
slice_data = {"start_line": s_line, "end_line": e_line}
if FuzzyAnchor: slice_data = FuzzyAnchor.create_slice(text, s_line, e_line)
else: slice_data = {"start_line": s_line, "end_line": e_line}
slice_data['tag'] = 'auto-ast'
slice_data['comment'] = name
f_item.custom_slices.append(slice_data)
@@ -1402,6 +1402,7 @@ class App:
if not self._pending_patch_text:
self._patch_error_message = "No patch to apply"
return
#TODO(Ed): Exception(Review)
try:
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)
@@ -1549,17 +1550,15 @@ if __name__ == "__main__":
main()
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,
auto-save, comms/tool-log caching, and all dockable windows.
SSDL Shape:
`[I:overlays] -> [I:task_drain] -> [I:modals] -> [I:windows] -> [I:popups]`
SSDL: `[I:overlays] -> [I:task_drain] -> [I:modals] -> [I:windows] -> [I:popups]`
ASCII Layout Map:
Full-screen dockspace (managed by imgui_bundle):
+-------------------+------------------------------------+
+-------------------+-------------------------------------+
| Project Settings | Discussion Hub |
| AI Settings | +--------------------------------+ |
| Files & Media | | History entries (scrollable) | |
@@ -1567,7 +1566,7 @@ def render_main_interface(app: App) -> None:
| MMA Dashboard | [splitter] |
| Task DAG | [Message] [Response] |
| Tier 1..4 streams | |
+-------------------+------------------+-----------------+
+-------------------+------------------+------------------+
| Operations Hub (tab-bar) | Theme window |
| [Comms][Tools][Usage][Ext][Layouts] | |
+---------------------------------------+-----------------+
@@ -1600,6 +1599,7 @@ def render_main_interface(app: App) -> None:
render_tool_preset_manager_window(app)
render_persona_editor_window(app)
#TODO(Ed): Exception(Review)
# Auto-save (every 60s)
now = time.time()
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))
render_text_viewer_window(app)
app.perf_monitor.end_frame()
# Modals / Popups
render_approve_script_modal(app)
render_mma_modals(app)
@@ -1683,12 +1681,10 @@ def render_custom_title_bar(app: App) -> None:
#region: Diagnostics & Analytics
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.
SSDL Shape:
`[I:token_budget] -> [I:cache_panel] -> [I:tool_analytics] -> [I:session_insights] -> [I:rag_status]`
SSDL:`[I:token_budget] -> [I:cache_panel] -> [I:tool_analytics] -> [I:session_insights] -> [I:rag_status]`
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")
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,
optional sparkline graphs, and the diagnostic message log.
SSDL Shape:
`[I:perf_summary_table] -> [B:enable_profiling] => [I:component_timings_table] -> [I:graphs] -> [I:diag_log]`
SSDL: `[I:perf_summary_table] -> [B:enable_profiling] => [I:component_timings_table] -> [I:graphs] -> [I:diag_log]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -1777,16 +1771,11 @@ def render_diagnostics_panel(app: App) -> None:
("CPU %", "cpu_percent", "%.1f"),
("Input Lag (ms)", "input_lag_ms", "%.1f")
]:
imgui.table_next_row()
imgui.table_next_column()
imgui.text(label)
imgui.table_next_column()
if key == "fps":
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()
imgui.table_next_row(); imgui.table_next_column()
imgui.text(label); imgui.table_next_column()
if key == "fps": 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[key] = imgui.checkbox(f"##g_{key}", app.perf_show_graphs[key])
imgui.end_table()
@@ -1809,21 +1798,14 @@ def render_diagnostics_panel(app: App) -> None:
count = int(metrics.get(f"count_{comp_name}", 0))
max_val = metrics.get(f"max_{comp_name}_ms", 0.0)
min_val = metrics.get(f"min_{comp_name}_ms", 0.0)
imgui.table_next_row()
imgui.table_next_column()
imgui.text(comp_name)
imgui.table_next_column()
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_row(); imgui.table_next_column()
imgui.text(comp_name); imgui.table_next_column()
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()
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])
imgui.end_table()
@@ -1850,23 +1832,19 @@ def render_diagnostics_panel(app: App) -> None:
for entry in reversed(app.controller.diagnostic_log):
imgui.table_next_row()
imgui.table_next_column()
imgui.text(entry.get("ts", ""))
imgui.table_next_column()
imgui.text(entry.get("type", ""))
imgui.table_next_column()
imgui.text(entry.get("ts", "")); imgui.table_next_column()
imgui.text(entry.get("type", "")); imgui.table_next_column()
imgui.text_wrapped(entry.get("message", ""))
imgui.end_table()
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_diagnostics_panel")
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.
Skips rendering for non-Gemini providers.
SSDL Shape:
`[I:cache_stats] -> [B:progress_bar] => [B:clear_cache]`
SSDL: `[I:cache_stats] -> [B:progress_bar] => [B:clear_cache]`
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")
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.
SSDL Shape:
`[I:tool_stats] -> [B:stats_table]`
SSDL: `[I:tool_stats] -> [B:stats_table]`
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
fail_pct = (failures / count * 100) if count > 0 else 0
imgui.table_next_row()
imgui.table_set_column_index(0)
imgui.text(tool_name)
imgui.table_set_column_index(1)
imgui.text(str(count))
imgui.table_set_column_index(2)
imgui.text(f"{avg_time:.0f}")
imgui.table_set_column_index(0); imgui.text(tool_name)
imgui.table_set_column_index(1); imgui.text(str(count))
imgui.table_set_column_index(2); imgui.text(f"{avg_time:.0f}")
imgui.table_set_column_index(3)
if fail_pct > 0: imgui.text_colored(theme.get_color("status_error"), f"{fail_pct:.0f}%")
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")
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),
per-tier MMA cost table, trim warnings, and cache activity badge.
SSDL Shape:
`[I:session_tokens] -> [B:utilization_bar] -> [B:breakdown_table] -> [B:tier_costs_table]`
SSDL: `[I:session_tokens] -> [B:utilization_bar] -> [B:breakdown_table] -> [B:tier_costs_table]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -2107,12 +2078,10 @@ def render_token_budget_panel(app: App) -> None:
#region: Logging
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.
SSDL Shape:
`[B:refresh | load | prune] -> [I:sessions_table] => [B:load | star/unstar per row]`
SSDL: `[B:refresh | load | prune] -> [I:sessions_table] => [B:load | star/unstar per row]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -2159,30 +2128,21 @@ def render_log_management(app: App) -> None:
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()
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(theme.get_color("status_warning"), "YES")
else:
imgui.text("NO")
if whitelisted: imgui.text_colored(theme.get_color("status_warning"), "YES")
else: imgui.text("NO")
imgui.table_next_column()
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.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,
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),
@@ -2191,8 +2151,7 @@ def render_log_management(app: App) -> None:
)
else:
if imgui.button(f"Star##{session_id}"):
registry.update_session_metadata(
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),
@@ -2208,11 +2167,9 @@ def render_log_management(app: App) -> None:
#region: Project Management
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:
`[I:tab_bar] => [I:projects_panel] | [I:paths_panel]`
SSDL: `[I:tab_bar] => [I:projects_panel] | [I:paths_panel]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -2229,13 +2186,11 @@ def render_project_settings_hub(app: App) -> None:
if exp: render_paths_panel(app)
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,
and global layout toggles (word-wrap, auto-scroll).
SSDL Shape:
`[I:active_project] -> [B:execution_mode] -> [B:directories] -> [I:project_files] -> [B:project_actions] -> [B:toggles]`
SSDL: `[I:active_project] -> [B:execution_mode] -> [B:directories] -> [I:project_files] -> [B:project_actions] -> [B:toggles]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -2313,8 +2268,7 @@ def render_projects_panel(app: App) -> None:
filetypes = [("TOML", "*.toml"), ("All", "*.*")],
)
r.destroy()
if p and p not in app.project_paths:
app.project_paths.append(p)
if p and p not in app.project_paths: app.project_paths.append(p)
imgui.same_line()
if imgui.button("New Project"):
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")
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.
SSDL Shape:
`[I:path_fields] => [B:apply_reset]`
SSDL: `[I:path_fields] => [B:apply_reset]`
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.
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:
+---------------------------------------------------------+
@@ -2420,12 +2372,10 @@ def render_ai_settings_hub(app: App) -> None:
render_agent_tools_panel(app)
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.
SSDL Shape:
`[B:rag_switch] -> [B:combo_selectors] -> [B:chunking_inputs] => [B:rebuild_index]`
SSDL: `[B:rag_switch] -> [B:combo_selectors] -> [B:chunking_inputs] => [B:rebuild_index]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -2444,23 +2394,23 @@ def render_rag_panel(app: App) -> None:
imgui.text("Vector Store Provider")
providers = ['chroma', 'qdrant', 'mock']
#NOTE(Ed): Exception(Thirdparty)
try:
idx = providers.index(conf.vector_store.provider)
except (ValueError, AttributeError):
idx = 0
ch2, next_idx = imgui.combo("##rag_provider", idx, providers)
if ch2:
conf.vector_store.provider = providers[next_idx]
if ch2: conf.vector_store.provider = providers[next_idx]
imgui.text("Embedding Provider")
emb_providers = ['gemini', 'local']
#NOTE(Ed): Exception(Thirdparty)
try:
idx_e = emb_providers.index(conf.embedding_provider)
except (ValueError, AttributeError):
idx_e = 0
ch3, next_idx_e = imgui.combo("##rag_emb_provider", idx_e, emb_providers)
if ch3:
conf.embedding_provider = emb_providers[next_idx_e]
if ch3: conf.embedding_provider = emb_providers[next_idx_e]
imgui.text("Chunk Size")
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')
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
selector + multiline edit.
SSDL Shape:
`[B:global_preset_combo] -> [I:global_text] -> [B:base_prompt_header] -> [I:base_text] -> [B:project_preset_combo] -> [I:project_text]`
SSDL: `[B:global_preset_combo] -> [I:global_text] -> [B:base_prompt_header] -> [I:base_text] -> [B:project_preset_combo] -> [I:project_text]`
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))
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
tool calling is unsupported by the active provider/model.
SSDL Shape:
`[B:collapsing_header] => [B:preset_combo] -> [B:manage_presets] -> [B:bias_combo]`
SSDL: `[B:collapsing_header] => [B:preset_combo] -> [B:manage_presets] -> [B:bias_combo]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -2580,14 +2526,14 @@ def render_agent_tools_panel(app: App) -> None:
active = getattr(app, "ui_active_tool_preset", "")
if active is None: active = ""
#NOTE(Ed): Exception(Thirdparty)
try:
idx = preset_names.index(active)
except ValueError:
idx = 0
ch, new_idx = imgui.combo("##tool_preset_select", idx, preset_names)
if ch:
app.ui_active_tool_preset = preset_names[new_idx]
if ch: app.ui_active_tool_preset = preset_names[new_idx]
imgui.same_line()
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))
cat_options = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys()))
#NOTE(Ed): Exception(Thirdparty)
try:
f_idx = cat_options.index(app.ui_tool_filter_category)
except ValueError:
f_idx = 0
imgui.set_next_item_width(200)
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]
@@ -2638,17 +2586,10 @@ def render_agent_tools_panel(app: App) -> None:
imgui.tree_pop()
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.
State Mutations:
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]`
SSDL: `[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")
imgui.text("Provider")
@@ -2666,7 +2607,9 @@ def render_provider_panel(app: App) -> None:
if app.current_provider == "llama":
base_url = getattr(ai_client, "_llama_base_url", "")
imgui.set_tooltip(f"Local backend: {base_url or 'unknown'}" if base_url else "Local backend")
_render_v2_capability_badges(caps)
imgui.separator()
imgui.text("Model")
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")
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.
State Mutations:
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]`
SSDL: `[I:personas] -> [B:persona_combo] => [B:manage_personas]`
"""
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_persona_selector_panel")
imgui.text("Persona")
@@ -2768,10 +2700,8 @@ def render_persona_selector_panel(app: App) -> None:
# Apply persona to current state immediately
if persona.preferred_models and len(persona.preferred_models) > 0:
first_model = persona.preferred_models[0]
if 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("provider"): app.current_provider = first_model.get("provider")
if first_model.get("model"): app.current_model = first_model.get("model")
if first_model.get("temperature") is not None:
ai_client.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)
if getattr(persona, 'context_preset', None):
app.ui_active_context_preset = persona.context_preset
#TODO(Ed): Exception(Review)
try:
app.load_context_preset(persona.context_preset)
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")
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.
State Mutations:
app.controller._show_base_prompt_diff_modal (closes/hides modal)
SSDL Shape:
`[I:unified_diff] -> [I:diff_view_box] => [B:close]`
SSDL: `[I:unified_diff] -> [I:diff_view_box] => [B:close]`
"""
if not getattr(app.controller, "_show_base_prompt_diff_modal", False):
return
@@ -2869,16 +2795,9 @@ def render_base_prompt_diff_modal(app: App) -> None:
imgui.end_popup()
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:
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]`
SSDL: `[I:preset_inputs] => [B:save_cancel]`
"""
if not app._show_save_preset_modal: return
imgui.open_popup("Save Layout Preset")
@@ -2904,16 +2823,10 @@ def render_save_preset_modal(app: App) -> None:
imgui.close_current_popup()
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.
State Mutations:
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]`
SSDL: `[I:presets_list] -> [B:new_preset] -> [I:editor_meta] -> [I:editor_textbox] => [B:save_delete]`
"""
avail = imgui.get_content_region_avail()
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()
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:
app.show_preset_manager_window (visibility toggle)
SSDL Shape:
`[I] -> [I:window] => [I:preset_manager_content]`
SSDL: `[I] -> [I:window] => [I:preset_manager_content]`
"""
if not app.show_preset_manager_window and not is_embedded: return
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)
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:
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]`
SSDL: `[I:tool_presets] -> [B:new_tool_preset] -> [I:capability_toggles] -> [I:tools_list] => [B:save_delete]`
"""
avail = imgui.get_content_region_avail()
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)
if True:
if imgui.button("New Profile", imgui.ImVec2(-1, 0)):
app._editing_bias_profile_name = ""; app._editing_bias_profile_tool_weights = {}
app._editing_bias_profile_category_multipliers = {}; app._selected_bias_profile_idx = -1
app._editing_bias_profile_name = ""
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())
for i, bname in enumerate(bnames):
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._editing_bias_profile_tool_weights = copy.deepcopy(prof.tool_weights); app._editing_bias_profile_category_multipliers = copy.deepcopy(prof.category_multipliers)
app._selected_bias_profile_idx = i
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.table_next_column()
imgui.begin_child("bedit_pane", imgui.ImVec2(0, 0), False)
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
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
@@ -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():
if imgui.tree_node(f"{cat_name}##b_list"):
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:
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)
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
@@ -3172,7 +3082,8 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N
imgui.end_child()
if app._bias_cats_open:
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)
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)
if True:
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()):
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)
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
@@ -3202,24 +3115,25 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N
# --- Footer Buttons ---
imgui.separator()
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()
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()
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()
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:
app.show_tool_preset_manager_window (visibility toggle)
SSDL Shape:
`[I] -> [I:window] => [I:tool_preset_manager_content]`
SSDL: `[I] -> [I:window] => [I:tool_preset_manager_content]`
"""
if not app.show_tool_preset_manager_window and not is_embedded: return
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)
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).
State Mutations:
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]`
SSDL: `[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 is_embedded:
@@ -3273,11 +3177,12 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
personas = getattr(app.controller, 'personas', {})
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]:
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 ""
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_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
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)
if True:
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):
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):
if imgui.begin_table("model_settings", 2, imgui.TableFlags_.borders_inner_v):
imgui.table_setup_column("Label", imgui.TableColumnFlags_.width_fixed, 120); imgui.table_setup_column("Control", imgui.TableColumnFlags_.width_stretch)
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column(); imgui.set_next_item_width(-1)
p_idx = providers.index(prov) + 1 if prov in providers else 0; ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers)
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column();
imgui.set_next_item_width(-1)
p_idx = providers.index(prov) + 1 if prov in providers else 0;
ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers)
if ch_p: entry["provider"] = providers[p_idx-1] if p_idx > 0 else ""
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column(); imgui.set_next_item_width(-1)
m_list = app.controller.all_available_models.get(entry.get("provider", ""), []); m_idx = m_list.index(mod) + 1 if mod in m_list else 0
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column();
imgui.set_next_item_width(-1)
m_list = app.controller.all_available_models.get(entry.get("provider", ""), [])
m_idx = m_list.index(mod) + 1 if mod in m_list else 0
ch_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list)
if ch_m: entry["model"] = m_list[m_idx-1] if m_idx > 0 else ""
imgui.table_next_row(); imgui.table_next_column(); imgui.text("Temperature:"); imgui.table_next_column(); cw = imgui.get_content_region_avail().x
@@ -3421,12 +3331,10 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
#region: Context Management
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.
SSDL Shape:
`[I:inventory] -> [B:add_files_folders] -> [I:screenshots] => [B:add_screenshots]`
SSDL: `[I:inventory] -> [B:add_files_folders] -> [I:screenshots] => [B:add_screenshots]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -3482,12 +3390,9 @@ def render_files_and_media(app: App) -> None:
if imgui.is_item_hovered(): imgui.set_tooltip(fpath)
imgui.table_set_column_index(2)
if in_context:
imgui.text_colored(theme.get_color("status_success"), "Active")
elif is_cached:
imgui.text_colored(theme.get_color("status_info"), "Cached")
else:
imgui.text_disabled(" - ")
if in_context: imgui.text_colored(theme.get_color("status_success"), "Active")
elif is_cached: imgui.text_colored(theme.get_color("status_info"), "Cached")
else: imgui.text_disabled(" - ")
imgui.end_table()
if to_remove_idx != -1: app.files.pop(to_remove_idx)
@@ -3530,12 +3435,10 @@ def render_files_and_media(app: App) -> None:
return
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.
SSDL Shape:
`[I:context_files] -> [B:mode_batch_buttons] -> [B:selection_buttons] -> [B:all_add_del] => [B:preview]`
SSDL: `[I:context_files] -> [B:mode_batch_buttons] -> [B:selection_buttons] -> [B:all_add_del] => [B:preview]`
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")
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.
SSDL Shape:
`[I:picker_list] -> [B:checkboxes] -> [B:add_selected_button] => [B:cancel_button]`
SSDL: `[I:picker_list] -> [B:checkboxes] -> [B:add_selected_button] => [B:cancel_button]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -3649,12 +3550,10 @@ def render_add_context_files_modal(app: App) -> None:
imgui.end_popup()
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.
SSDL Shape:
`[I:stats] -> [I:batch_actions] -> [I:files_table] -> [I:presets] -> [I:screenshots]`
SSDL: `[I:stats] -> [I:batch_actions] -> [I:files_table] -> [I:presets] -> [I:screenshots]`
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.")
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
in a 3-column table (ID / Status / Title).
SSDL Shape:
`[I:dep_check] -> [B:refresh] -> [I:beads_table]`
SSDL Shape: `[I:dep_check] -> [B:refresh] -> [I:beads_table]`
ASCII Layout Map:
+---------------------------------------------------------+
@@ -7324,24 +7221,18 @@ def render_beads_tab(app: App) -> None:
imgui.table_setup_column("Title")
imgui.table_headers_row()
for b in beads:
imgui.table_next_row()
imgui.table_next_column()
imgui.text(str(b.id))
imgui.table_next_column()
imgui.text(str(b.status))
imgui.table_next_column()
imgui.text(str(b.title))
imgui.table_next_row(); imgui.table_next_column()
imgui.text(str(b.id)); imgui.table_next_column()
imgui.text(str(b.status)); imgui.table_next_column(); imgui.text(str(b.title))
imgui.end_table()
except Exception as e:
imgui.text_colored(theme.get_color("status_error"), f"Error loading beads: {e}")
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.
SSDL Shape:
`[I:focus_combo] -> [B:clear_x?]`
SSDL: `[I:focus_combo] -> [B:clear_x?]`
ASCII Layout Map:
+---------------------------------------------------------+