From 2b73745cd95d639e429e8fc47a70c0647533d002 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 22 Mar 2026 13:06:15 -0400 Subject: [PATCH] feat(gui): Merge Session Hub into Discussion Hub - Removed Session Hub window from _gui_func - Discussion Hub now has tab bar: Discussion | Context Composition | Snapshot | Takes - _render_discussion_tab: history + message/response tabs - _render_snapshot_tab: Aggregate MD + System Prompt (moved from Session Hub) - _render_context_composition_placeholder: placeholder for Phase 3 - _render_takes_placeholder: placeholder for Phase 4 --- manualslop_layout.ini | 18 ++-- project_history.toml | 2 +- src/gui_2.py | 184 +++++++++++++++++--------------- tests/test_session_hub_merge.py | 42 ++++++++ 4 files changed, 148 insertions(+), 98 deletions(-) create mode 100644 tests/test_session_hub_merge.py diff --git a/manualslop_layout.ini b/manualslop_layout.ini index 3ebac09..a2be322 100644 --- a/manualslop_layout.ini +++ b/manualslop_layout.ini @@ -102,26 +102,26 @@ Collapsed=0 DockId=0x0000000D,0 [Window][Discussion Hub] -Pos=1228,24 -Size=1638,1651 +Pos=1126,24 +Size=1638,1608 Collapsed=0 DockId=0x00000006,0 [Window][Operations Hub] Pos=0,24 -Size=1226,1651 +Size=1124,1608 Collapsed=0 DockId=0x00000005,2 [Window][Files & Media] -Pos=1228,24 -Size=1638,1651 +Pos=1126,24 +Size=1638,1608 Collapsed=0 DockId=0x00000006,1 [Window][AI Settings] Pos=0,24 -Size=1226,1651 +Size=1124,1608 Collapsed=0 DockId=0x00000005,0 @@ -407,7 +407,7 @@ DockId=0x00000006,1 [Window][Project Settings] Pos=0,24 -Size=1226,1651 +Size=1124,1608 Collapsed=0 DockId=0x00000005,1 @@ -524,11 +524,11 @@ Column 1 Weight=1.0000 DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 -DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=2866,1651 Split=X +DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=2764,1608 Split=X DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2175,1183 Split=X DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 DockNode ID=0x00000007 Parent=0x0000000B SizeRef=1512,858 Split=X Selected=0x8CA2375C - DockNode ID=0x00000005 Parent=0x00000007 SizeRef=1226,1681 CentralNode=1 Selected=0x418C7449 + DockNode ID=0x00000005 Parent=0x00000007 SizeRef=1226,1681 CentralNode=1 Selected=0x7BD57D6A DockNode ID=0x00000006 Parent=0x00000007 SizeRef=1638,1681 Selected=0x6F2B5B04 DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1777,858 Selected=0x418C7449 DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 diff --git a/project_history.toml b/project_history.toml index c8f44e3..dd4e15c 100644 --- a/project_history.toml +++ b/project_history.toml @@ -9,5 +9,5 @@ active = "main" [discussions.main] git_commit = "" -last_updated = "2026-03-22T12:57:36" +last_updated = "2026-03-22T12:59:02" history = [] diff --git a/src/gui_2.py b/src/gui_2.py index b9c66be..d7abb6c 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -215,7 +215,6 @@ class App: self.show_windows.setdefault("Tier 4: QA", False) self.show_windows.setdefault('External Tools', False) self.show_windows.setdefault('Shader Editor', False) - self.show_windows.setdefault('Session Hub', False) self.ui_multi_viewport = gui_cfg.get("multi_viewport", False) self.layout_presets = self.config.get("layout_presets", {}) self._new_preset_name = "" @@ -724,50 +723,20 @@ class App: exp, opened = imgui.begin("Discussion Hub", self.show_windows["Discussion Hub"]) self.show_windows["Discussion Hub"] = bool(opened) if exp: - # Top part for the history - imgui.begin_child("HistoryChild", size=(0, -self.ui_discussion_split_h)) - if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel") - self._render_discussion_panel() - if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel") - imgui.end_child() - # Splitter - imgui.button("###discussion_splitter", imgui.ImVec2(-1, 4)) - if imgui.is_item_active(): - self.ui_discussion_split_h = max(150.0, min(imgui.get_window_height() - 150.0, self.ui_discussion_split_h - imgui.get_io().mouse_delta.y)) - # Bottom part with tabs for message and response - # Detach controls - imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4)) - ch1, self.ui_separate_message_panel = imgui.checkbox("Pop Out Message", self.ui_separate_message_panel) - imgui.same_line() - ch2, self.ui_separate_response_panel = imgui.checkbox("Pop Out Response", self.ui_separate_response_panel) - if ch1: self.show_windows["Message"] = self.ui_separate_message_panel - if ch2: self.show_windows["Response"] = self.ui_separate_response_panel - imgui.pop_style_var() - - show_message_tab = not self.ui_separate_message_panel - show_response_tab = not self.ui_separate_response_panel - - if show_message_tab or show_response_tab: - if imgui.begin_tab_bar("discussion_tabs"): - # Task: Auto-focus Response tab when response received - tab_flags = imgui.TabItemFlags_.none - if self._autofocus_response_tab: - tab_flags = imgui.TabItemFlags_.set_selected - self._autofocus_response_tab = False - self.controller._autofocus_response_tab = False - - if show_message_tab: - if imgui.begin_tab_item("Message", None)[0]: - self._render_message_panel() - imgui.end_tab_item() - if show_response_tab: - if imgui.begin_tab_item("Response", None, tab_flags)[0]: - self._render_response_panel() - imgui.end_tab_item() - imgui.end_tab_bar() - else: - imgui.text_disabled("Message & Response panels are detached.") - + if imgui.begin_tab_bar("discussion_hub_tabs"): + if imgui.begin_tab_item("Discussion")[0]: + self._render_discussion_tab() + imgui.end_tab_item() + if imgui.begin_tab_item("Context Composition")[0]: + self._render_context_composition_placeholder() + imgui.end_tab_item() + if imgui.begin_tab_item("Snapshot")[0]: + self._render_snapshot_tab() + imgui.end_tab_item() + if imgui.begin_tab_item("Takes")[0]: + self._render_takes_placeholder() + imgui.end_tab_item() + imgui.end_tab_bar() imgui.end() if self.show_windows.get("Operations Hub", False): exp, opened = imgui.begin("Operations Hub", self.show_windows["Operations Hub"]) @@ -842,8 +811,6 @@ class App: if self.show_windows.get("Diagnostics", False): self._render_diagnostics_panel() - self._render_session_hub() - self.perf_monitor.end_frame() # ---- Modals / Popups with self._pending_dialog_lock: @@ -2081,49 +2048,90 @@ class App: if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_diagnostics_panel") imgui.end() - def _render_session_hub(self) -> None: - if self.show_windows.get('Session Hub', False): - exp, opened = imgui.begin('Session Hub', self.show_windows['Session Hub']) - self.show_windows['Session Hub'] = bool(opened) - if exp: - if imgui.begin_tab_bar('session_hub_tabs'): - if imgui.begin_tab_item('Aggregate MD')[0]: - display_md = self.last_aggregate_markdown - if self.ui_focus_agent: - tier_usage = self.mma_tier_usage.get(self.ui_focus_agent) - if tier_usage: - persona_name = tier_usage.get("persona") - if persona_name: - persona = self.controller.personas.get(persona_name) - if persona and persona.context_preset: - cp_name = persona.context_preset - if cp_name in self._focus_md_cache: - display_md = self._focus_md_cache[cp_name] - else: - # Generate focused aggregate - flat = src.project_manager.flat_config(self.controller.project, self.active_discussion) - cp = self.controller.project.get('context_presets', {}).get(cp_name) - if cp: - flat["files"]["paths"] = cp.get("files", []) - flat["screenshots"]["paths"] = cp.get("screenshots", []) - full_md, _, _ = src.aggregate.run(flat) - self._focus_md_cache[cp_name] = full_md - display_md = full_md - if imgui.button("Copy"): - imgui.set_clipboard_text(display_md) - imgui.begin_child("last_agg_md", imgui.ImVec2(0, 0), True) - markdown_helper.render(display_md, context_id="session_hub_agg") - imgui.end_child() + def _render_discussion_tab(self) -> None: + imgui.begin_child("HistoryChild", size=(0, -self.ui_discussion_split_h)) + if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel") + self._render_discussion_panel() + if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel") + imgui.end_child() + imgui.button("###discussion_splitter", imgui.ImVec2(-1, 4)) + if imgui.is_item_active(): + self.ui_discussion_split_h = max(150.0, min(imgui.get_window_height() - 150.0, self.ui_discussion_split_h - imgui.get_io().mouse_delta.y)) + imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4)) + ch1, self.ui_separate_message_panel = imgui.checkbox("Pop Out Message", self.ui_separate_message_panel) + imgui.same_line() + ch2, self.ui_separate_response_panel = imgui.checkbox("Pop Out Response", self.ui_separate_response_panel) + if ch1: self.show_windows["Message"] = self.ui_separate_message_panel + if ch2: self.show_windows["Response"] = self.ui_separate_response_panel + imgui.pop_style_var() + show_message_tab = not self.ui_separate_message_panel + show_response_tab = not self.ui_separate_response_panel + if show_message_tab or show_response_tab: + if imgui.begin_tab_bar("discussion_tabs"): + tab_flags = imgui.TabItemFlags_.none + if self._autofocus_response_tab: + tab_flags = imgui.TabItemFlags_.set_selected + self._autofocus_response_tab = False + self.controller._autofocus_response_tab = False + if show_message_tab: + if imgui.begin_tab_item("Message", None)[0]: + self._render_message_panel() imgui.end_tab_item() - if imgui.begin_tab_item('System Prompt')[0]: - if imgui.button("Copy"): - imgui.set_clipboard_text(self.last_resolved_system_prompt) - imgui.begin_child("last_sys_prompt", imgui.ImVec2(0, 0), True) - markdown_helper.render(self.last_resolved_system_prompt, context_id="session_hub_sys") - imgui.end_child() + if show_response_tab: + if imgui.begin_tab_item("Response", None, tab_flags)[0]: + self._render_response_panel() imgui.end_tab_item() - imgui.end_tab_bar() - imgui.end() + imgui.end_tab_bar() + else: + imgui.text_disabled("Message & Response panels are detached.") + + def _render_context_composition_placeholder(self) -> None: + imgui.text("Context Composition") + imgui.separator() + imgui.text_colored(imgui.ImColor.IM_COL32(150, 150, 150, 255).Value, "Coming in Phase 3...") + + def _render_snapshot_tab(self) -> None: + if imgui.begin_tab_bar("snapshot_tabs"): + if imgui.begin_tab_item("Aggregate MD")[0]: + display_md = self.last_aggregate_markdown + if self.ui_focus_agent: + tier_usage = self.mma_tier_usage.get(self.ui_focus_agent) + if tier_usage: + persona_name = tier_usage.get("persona") + if persona_name: + persona = self.controller.personas.get(persona_name) + if persona and persona.context_preset: + cp_name = persona.context_preset + if cp_name in self._focus_md_cache: + display_md = self._focus_md_cache[cp_name] + else: + flat = src.project_manager.flat_config(self.controller.project, self.active_discussion) + cp = self.controller.project.get('context_presets', {}).get(cp_name) + if cp: + flat["files"]["paths"] = cp.get("files", []) + flat["screenshots"]["paths"] = cp.get("screenshots", []) + full_md, _, _ = src.aggregate.run(flat) + self._focus_md_cache[cp_name] = full_md + display_md = full_md + if imgui.button("Copy"): + imgui.set_clipboard_text(display_md) + imgui.begin_child("last_agg_md", imgui.ImVec2(0, 0), True) + markdown_helper.render(display_md, context_id="snapshot_agg") + imgui.end_child() + imgui.end_tab_item() + if imgui.begin_tab_item("System Prompt")[0]: + if imgui.button("Copy"): + imgui.set_clipboard_text(self.last_resolved_system_prompt) + imgui.begin_child("last_sys_prompt", imgui.ImVec2(0, 0), True) + markdown_helper.render(self.last_resolved_system_prompt, context_id="snapshot_sys") + imgui.end_child() + imgui.end_tab_item() + imgui.end_tab_bar() + + def _render_takes_placeholder(self) -> None: + imgui.text("Takes & Synthesis") + imgui.separator() + imgui.text_colored(imgui.ImColor.IM_COL32(150, 150, 150, 255).Value, "Coming in Phase 4...") def _render_markdown_test(self) -> None: imgui.text("Markdown Test Panel") diff --git a/tests/test_session_hub_merge.py b/tests/test_session_hub_merge.py new file mode 100644 index 0000000..f5b6b13 --- /dev/null +++ b/tests/test_session_hub_merge.py @@ -0,0 +1,42 @@ +import pytest +import inspect + + +def test_session_hub_window_removed(): + import src.gui_2 as gui_2 + + source = inspect.getsource(gui_2.App._gui_func) + assert "Session Hub" not in source, "Session Hub window should be removed" + + +def test_discussion_hub_has_snapshot_tab(): + import src.gui_2 as gui_2 + + source = inspect.getsource(gui_2.App._gui_func) + assert "Snapshot" in source, "Discussion Hub should have Snapshot tab" + assert "_render_snapshot_tab" in source, "Discussion Hub should call _render_snapshot_tab" + + +def test_discussion_hub_has_context_composition_placeholder(): + import src.gui_2 as gui_2 + + source = inspect.getsource(gui_2.App._gui_func) + assert "Context Composition" in source, ( + "Discussion Hub should have Context Composition tab placeholder" + ) + + +def test_discussion_hub_has_takes_tab(): + import src.gui_2 as gui_2 + + source = inspect.getsource(gui_2.App._gui_func) + assert "Takes" in source, "Discussion Hub should have Takes tab" + + +def test_show_windows_no_session_hub(): + import src.app_controller as app_controller + + source = inspect.getsource(app_controller.AppController) + assert "Session Hub" not in source, ( + "Session Hub should be removed from show_windows" + )