diff --git a/src/gui_2.py b/src/gui_2.py index 68e22eae..92bf363c 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -4388,6 +4388,23 @@ def render_context_preview_window(app: App) -> None: #region: Discussions def render_discussion_hub(app: App) -> None: + """ + Top-level hub for the Discussion panel. Houses tabs for Discussion, Context Composition, + Context Preview (inline or detached), Snapshot, and Takes. + + SSDL Shape: + `[B:pop_out_toggle] -> [I:tab_bar] => [I:active_tab_content]` + + ASCII Layout Map: + +---------------------------------------------------------+ + | [ ] Pop Out Context Preview | + | [Discussion] [Context Composition] [Context Preview] | + | [Snapshot] [Takes] | + | +-----------------------------------------------------+ | + | | | | + | +-----------------------------------------------------+ | + +---------------------------------------------------------+ + """ _check_auto_refresh_context_preview(app) ch, popped = imgui.checkbox("Pop Out Context Preview", getattr(app, "ui_separate_context_preview", False)) if ch: @@ -4412,6 +4429,24 @@ def render_discussion_hub(app: App) -> None: return def render_thinking_trace(app: App, entry: dict, segments: list[dict], entry_index: int, is_standalone: bool = False) -> None: + """ + Renders a collapsible thinking-trace section within a discussion entry bubble. + Displays each reasoning segment labeled by its marker tag (e.g. [thinking]). + + SSDL Shape: + `[I:segment_list] -> [B:read_pure_toggle] => [I:trace_child]` + + ASCII Layout Map: + +----------------------------------------------------------+ + | > Monologue (3 traces) | + | [Pure] / [Read] Selectable toggle | + | +----------------------------------------------------+ | + | | [thinking] ...reasoning text... | | + | | --- | | + | | [thinking] ...continued... | | + | +----------------------------------------------------+ | + +----------------------------------------------------------+ + """ if not segments: return # Tint thinking trace background slightly differently @@ -4451,15 +4486,10 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None: Supports collapsible states, read/edit mode toggles, role selection, input/output token metrics, and action footers. - State Mutations: - app.disc_entries[index]['collapsed'] (toggled by header button) - app.disc_entries[index]['read_mode'] (toggled by header button) - app.disc_entries[index]['role'] (changed by combo selection) - SSDL Shape: `[I:header] -> [B:collapsed] => [I:preview] | [I:body] -> [I:footer]` - ASCII Layout Preview: + ASCII Layout Map: +-------------------------------------------------------------+ | [-] Entry #3 [Role: AI v] [Read] @2026-06-12 in:12 | +-------------------------------------------------------------+ @@ -4562,12 +4592,21 @@ def render_discussion_entry_read_mode(app: App, entry: dict, index: int) -> None Parses the markdown content, isolates retrieved context sections (RAG chunks), handles custom definition/AST links, and invokes the markdown syntax highlighter. - State Mutations: - app.text_viewer_title, app.text_viewer_content, app.text_viewer_type (populated on [Source] click) - app.show_windows["Text Viewer"] (set to True on [Source] click) - SSDL Shape: `[I:extract_rag] -> [B:definitions?] => [I:markdown]` + + ASCII Layout Map: + +-------------------------------------------------------------+ + | > Retrieved Context (collapsible) | + | > Chunk 1: src/gui_2.py (collapsible) | + | [Source] ...chunk text... | + | --- | + | > [Definition: foo from gui_2.py (line 42)] (collapsible) | + | [Source] ```python...``` | + | --- | + | ## Markdown heading | + | Rendered response text... | + +-------------------------------------------------------------+ """ with imscope.id(f"read_{index}"): content = entry["content"] @@ -4938,6 +4977,31 @@ def render_comms_history_panel(app: App) -> None: if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_comms_history_panel") def render_takes_panel(app: App) -> None: + """ + Renders the Takes & Synthesis panel. Lists all discussion takes in a table, + allows switching or deleting them, and provides a multi-take synthesis workflow. + + SSDL Shape: + `[I:takes_table] -> [B:switch/delete] -> [I:synthesis_config] => [B:generate_synthesis]` + + ASCII Layout Map: + +---------------------------------------------------------+ + | Takes & Synthesis | + | +----------+----------+-----------------------------+ | + | | Name | Entries | Actions | | + | +----------+----------+-----------------------------+ | + | | main | 14 | [Switch] [Delete] | | + | | take_2 | 3 | [Switch] [Delete] | | + | +----------+----------+-----------------------------+ | + | Synthesis | + | [ ] main [ ] take_2 | + | Synthesis Prompt: | + | +-----------------------------------------------------+ | + | | Compare and reconcile these takes... | | + | +-----------------------------------------------------+ | + | [Generate Synthesis] | + +---------------------------------------------------------+ + """ imgui.text("Takes & Synthesis") imgui.separator() discussions = app.project.get('discussion', {}).get('discussions', {}) @@ -4994,6 +5058,23 @@ def render_takes_panel(app: App) -> None: app._handle_generate_send() def render_discussion_entries(app: App) -> None: + """ + Renders the scrollable list of all discussion entry bubbles. When a focus agent is + active (ui_focus_agent), only entries matching that agent's persona (or User) are shown. + + SSDL Shape: + `[I:filter_by_agent?] -> [I:scroll_child] => [I:entry_bubbles]` + + ASCII Layout Map: + +---------------------------------------------------------+ + | +-----------------------------------------------------+ | + | | Entry #1 [User] ... | | + | | Entry #2 [AI] ... | | + | | Entry #3 [User] ... | | + | | <- scroll-to-bottom -> | | + | +-----------------------------------------------------+ | + +---------------------------------------------------------+ + """ with imscope.child("disc_scroll"): display_entries = list(app.disc_entries) if app.ui_focus_agent: @@ -5011,18 +5092,14 @@ def render_discussion_entry_controls(app: App) -> None: Handles adding entries, expanding/collapsing all bubbles, clearing, saving, compressing logs, and configuring auto-history checkpoints. - State Mutations: - app.disc_entries (adds, collapses, clears elements) - app.ui_auto_add_history (toggled via checkbox) - app.ai_status (updated upon saving) - SSDL Shape: `[I:buttons] -> [B:clicks] => [S:entries_or_config]` - ASCII Layout Preview: + ASCII Layout Map: +-------------------------------------------------------------+ | [+ Entry] [-All] [+All] [Clear All] [Save] [Compress] | - | [x] Auto-add message & response to history | + | [ ] Auto-add message & response to history | + | Keep Pairs: [15 ] [Truncate] | +-------------------------------------------------------------+ """ if imgui.button("+ Entry"): app.disc_entries.append({"role": app.disc_roles[0] if app.disc_roles else "User", "content": "", "collapsed": True, "ts": project_manager.now_ts()}) @@ -5048,6 +5125,21 @@ def render_discussion_entry_controls(app: App) -> None: app.ai_status = f"history truncated to {app.ui_disc_truncate_pairs} pairs" def render_discussion_metadata(app: App) -> None: + """ + Renders per-discussion metadata: cumulative token counts, git commit association, + last updated timestamp, and controls for creating, renaming, and deleting discussions. + + SSDL Shape: + `[I:token_totals] -> [I:commit_row] -> [I:timestamp] -> [B:create/rename/delete]` + + ASCII Layout Map: + +---------------------------------------------------------+ + | Discussion Tokens: 1200 In | 800 Out | 0 Cache | + | commit: a1b2c3d4 [Update Commit] | + | updated: 2026-06-12T22:00 | + | [new-name________] [Create] [Rename] [Delete] | + +---------------------------------------------------------+ + """ disc_data = app.project.get("discussion", {}).get("discussions", {}).get(app.active_discussion, {}) git_commit, last_updated = disc_data.get("git_commit", ""), disc_data.get("last_updated", "") @@ -5081,6 +5173,30 @@ def render_discussion_metadata(app: App) -> None: if imgui.button("Delete"): app._delete_discussion(app.active_discussion) def render_discussion_panel(app: App) -> None: + """ + Top-level discussion panel compositor. Renders the thinking indicator, + prior-session read-only view (if active), discussion selector, entry controls, + role list, and scrollable entry bubbles. + + SSDL Shape: + `[I:thinking_indicator] -> [I:prior_view?] | [I:selector] -> [I:controls] -> [I:roles] -> [I:entries]` + + ASCII Layout Map: + +---------------------------------------------------------+ + | [● GENERATING...] (thinking spinner) | + | | + | > Discussions [main v] [Original] [take_2] [Synth] | + | commit: a1b2c3 [Update] | updated: 2026-06-12 | + | [new-name] [Create] [Rename] [Delete] | + | --- | + | [+ Entry] [-All] [+All] [Clear All] [Save] [Compress] | + | --- | + | > Roles (collapsible) | + | --- | + | Entry #1 [User] ... | + | Entry #2 [AI] ... | + +---------------------------------------------------------+ + """ if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_discussion_panel") render_thinking_indicator(app) @@ -5100,6 +5216,22 @@ def render_discussion_panel(app: App) -> None: return def render_discussion_roles(app: App) -> None: + """ + Renders the collapsible Roles section. Lists current roles with an [X] delete button + per entry, and exposes an input field for adding new roles. + + SSDL Shape: + `[B:collapsing_header] => [I:roles_list] -> [B:add_role]` + + ASCII Layout Map: + +---------------------------------------------------------+ + | > Roles | + | +---------------------------------------------------+ | + | | [X] User [X] AI [X] System | | + | +---------------------------------------------------+ | + | [new-role___________] [Add] | + +---------------------------------------------------------+ + """ if imgui.collapsing_header("Roles"): with imscope.child("roles_scroll", size_y=100, flags=True): for i, r in enumerate(list(app.disc_roles)): @@ -5113,6 +5245,24 @@ def render_discussion_roles(app: App) -> None: return def render_discussion_selector(app: App) -> None: + """ + Renders the collapsible Discussions selector. Shows a combo-box for choosing + the active base discussion, a tab-bar for takes, a Synthesis tab, promote/track + controls, and the discussion metadata row. + + SSDL Shape: + `[B:collapsing_header] => [I:combo_base] -> [I:takes_tabs] -> [B:promote?] -> [B:track?] -> [I:metadata]` + + ASCII Layout Map: + +---------------------------------------------------------+ + | > Discussions | + | [main v] | + | [Original] [take_2] [Synthesis] | + | [Promote Take] [ ] Track Discussion | + | commit: a1b2c3 updated: 2026-06-12 | + | [new-name] [Create] [Rename] [Delete] | + +---------------------------------------------------------+ + """ if not imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open): return names = app._get_discussion_names(); grouped = {} @@ -5161,6 +5311,28 @@ def render_discussion_selector(app: App) -> None: return def render_discussion_tab(app: App) -> None: + """ + Renders the Discussion tab content. Comprises a resizable top pane (history/entries) + and a bottom pane with a draggable splitter bar, pop-out checkboxes, and + inline Message/Response tabs. + + SSDL Shape: + `[I:history_child] -> [B:splitter] -> [B:popout_toggles] -> [I:message_response_tabs]` + + ASCII Layout Map: + +---------------------------------------------------------+ + | +-----------------------------------------------------+ | + | | Discussion entries (scrollable)... | | + | | ... | | + | +-----------------------------------------------------+ | + | [=== drag splitter ===========================] | + | [ ] Pop Out Message [ ] Pop Out Response | + | [Message] [Response] | + | +-----------------------------------------------------+ | + | | | | + | +-----------------------------------------------------+ | + +---------------------------------------------------------+ + """ with imscope.child("HistoryChild", size_x=0, size_y=-app.ui_discussion_split_h): if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_discussion_panel") render_discussion_panel(app)