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
This commit is contained in:
2026-03-22 13:06:15 -04:00
parent 51d05c15e0
commit 2b73745cd9
4 changed files with 148 additions and 98 deletions

View File

@@ -102,26 +102,26 @@ Collapsed=0
DockId=0x0000000D,0 DockId=0x0000000D,0
[Window][Discussion Hub] [Window][Discussion Hub]
Pos=1228,24 Pos=1126,24
Size=1638,1651 Size=1638,1608
Collapsed=0 Collapsed=0
DockId=0x00000006,0 DockId=0x00000006,0
[Window][Operations Hub] [Window][Operations Hub]
Pos=0,24 Pos=0,24
Size=1226,1651 Size=1124,1608
Collapsed=0 Collapsed=0
DockId=0x00000005,2 DockId=0x00000005,2
[Window][Files & Media] [Window][Files & Media]
Pos=1228,24 Pos=1126,24
Size=1638,1651 Size=1638,1608
Collapsed=0 Collapsed=0
DockId=0x00000006,1 DockId=0x00000006,1
[Window][AI Settings] [Window][AI Settings]
Pos=0,24 Pos=0,24
Size=1226,1651 Size=1124,1608
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
@@ -407,7 +407,7 @@ DockId=0x00000006,1
[Window][Project Settings] [Window][Project Settings]
Pos=0,24 Pos=0,24
Size=1226,1651 Size=1124,1608
Collapsed=0 Collapsed=0
DockId=0x00000005,1 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=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02 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=0x00000003 Parent=0xAFC85805 SizeRef=2175,1183 Split=X
DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2 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=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=0x00000006 Parent=0x00000007 SizeRef=1638,1681 Selected=0x6F2B5B04
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1777,858 Selected=0x418C7449 DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1777,858 Selected=0x418C7449
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6 DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6

View File

@@ -9,5 +9,5 @@ active = "main"
[discussions.main] [discussions.main]
git_commit = "" git_commit = ""
last_updated = "2026-03-22T12:57:36" last_updated = "2026-03-22T12:59:02"
history = [] history = []

View File

@@ -215,7 +215,6 @@ class App:
self.show_windows.setdefault("Tier 4: QA", False) self.show_windows.setdefault("Tier 4: QA", False)
self.show_windows.setdefault('External Tools', False) self.show_windows.setdefault('External Tools', False)
self.show_windows.setdefault('Shader Editor', 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.ui_multi_viewport = gui_cfg.get("multi_viewport", False)
self.layout_presets = self.config.get("layout_presets", {}) self.layout_presets = self.config.get("layout_presets", {})
self._new_preset_name = "" self._new_preset_name = ""
@@ -724,50 +723,20 @@ class App:
exp, opened = imgui.begin("Discussion Hub", self.show_windows["Discussion Hub"]) exp, opened = imgui.begin("Discussion Hub", self.show_windows["Discussion Hub"])
self.show_windows["Discussion Hub"] = bool(opened) self.show_windows["Discussion Hub"] = bool(opened)
if exp: if exp:
# Top part for the history if imgui.begin_tab_bar("discussion_hub_tabs"):
imgui.begin_child("HistoryChild", size=(0, -self.ui_discussion_split_h)) if imgui.begin_tab_item("Discussion")[0]:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel") self._render_discussion_tab()
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() imgui.end_tab_item()
if show_response_tab: if imgui.begin_tab_item("Context Composition")[0]:
if imgui.begin_tab_item("Response", None, tab_flags)[0]: self._render_context_composition_placeholder()
self._render_response_panel() 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_item()
imgui.end_tab_bar() imgui.end_tab_bar()
else:
imgui.text_disabled("Message & Response panels are detached.")
imgui.end() imgui.end()
if self.show_windows.get("Operations Hub", False): if self.show_windows.get("Operations Hub", False):
exp, opened = imgui.begin("Operations Hub", self.show_windows["Operations Hub"]) exp, opened = imgui.begin("Operations Hub", self.show_windows["Operations Hub"])
@@ -842,8 +811,6 @@ class App:
if self.show_windows.get("Diagnostics", False): if self.show_windows.get("Diagnostics", False):
self._render_diagnostics_panel() self._render_diagnostics_panel()
self._render_session_hub()
self.perf_monitor.end_frame() self.perf_monitor.end_frame()
# ---- Modals / Popups # ---- Modals / Popups
with self._pending_dialog_lock: with self._pending_dialog_lock:
@@ -2081,13 +2048,51 @@ class App:
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_diagnostics_panel") if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_diagnostics_panel")
imgui.end() imgui.end()
def _render_session_hub(self) -> None: def _render_discussion_tab(self) -> None:
if self.show_windows.get('Session Hub', False): imgui.begin_child("HistoryChild", size=(0, -self.ui_discussion_split_h))
exp, opened = imgui.begin('Session Hub', self.show_windows['Session Hub']) if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel")
self.show_windows['Session Hub'] = bool(opened) self._render_discussion_panel()
if exp: if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel")
if imgui.begin_tab_bar('session_hub_tabs'): imgui.end_child()
if imgui.begin_tab_item('Aggregate MD')[0]: 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 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.")
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 display_md = self.last_aggregate_markdown
if self.ui_focus_agent: if self.ui_focus_agent:
tier_usage = self.mma_tier_usage.get(self.ui_focus_agent) tier_usage = self.mma_tier_usage.get(self.ui_focus_agent)
@@ -2100,7 +2105,6 @@ class App:
if cp_name in self._focus_md_cache: if cp_name in self._focus_md_cache:
display_md = self._focus_md_cache[cp_name] display_md = self._focus_md_cache[cp_name]
else: else:
# Generate focused aggregate
flat = src.project_manager.flat_config(self.controller.project, self.active_discussion) flat = src.project_manager.flat_config(self.controller.project, self.active_discussion)
cp = self.controller.project.get('context_presets', {}).get(cp_name) cp = self.controller.project.get('context_presets', {}).get(cp_name)
if cp: if cp:
@@ -2112,18 +2116,22 @@ class App:
if imgui.button("Copy"): if imgui.button("Copy"):
imgui.set_clipboard_text(display_md) imgui.set_clipboard_text(display_md)
imgui.begin_child("last_agg_md", imgui.ImVec2(0, 0), True) imgui.begin_child("last_agg_md", imgui.ImVec2(0, 0), True)
markdown_helper.render(display_md, context_id="session_hub_agg") markdown_helper.render(display_md, context_id="snapshot_agg")
imgui.end_child() imgui.end_child()
imgui.end_tab_item() imgui.end_tab_item()
if imgui.begin_tab_item('System Prompt')[0]: if imgui.begin_tab_item("System Prompt")[0]:
if imgui.button("Copy"): if imgui.button("Copy"):
imgui.set_clipboard_text(self.last_resolved_system_prompt) imgui.set_clipboard_text(self.last_resolved_system_prompt)
imgui.begin_child("last_sys_prompt", imgui.ImVec2(0, 0), True) imgui.begin_child("last_sys_prompt", imgui.ImVec2(0, 0), True)
markdown_helper.render(self.last_resolved_system_prompt, context_id="session_hub_sys") markdown_helper.render(self.last_resolved_system_prompt, context_id="snapshot_sys")
imgui.end_child() imgui.end_child()
imgui.end_tab_item() imgui.end_tab_item()
imgui.end_tab_bar() imgui.end_tab_bar()
imgui.end()
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: def _render_markdown_test(self) -> None:
imgui.text("Markdown Test Panel") imgui.text("Markdown Test Panel")

View File

@@ -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"
)