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:
@@ -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
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
184
src/gui_2.py
184
src/gui_2.py
@@ -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()
|
imgui.end_tab_item()
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel")
|
if imgui.begin_tab_item("Context Composition")[0]:
|
||||||
imgui.end_child()
|
self._render_context_composition_placeholder()
|
||||||
# Splitter
|
imgui.end_tab_item()
|
||||||
imgui.button("###discussion_splitter", imgui.ImVec2(-1, 4))
|
if imgui.begin_tab_item("Snapshot")[0]:
|
||||||
if imgui.is_item_active():
|
self._render_snapshot_tab()
|
||||||
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.end_tab_item()
|
||||||
# Bottom part with tabs for message and response
|
if imgui.begin_tab_item("Takes")[0]:
|
||||||
# Detach controls
|
self._render_takes_placeholder()
|
||||||
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
|
imgui.end_tab_item()
|
||||||
ch1, self.ui_separate_message_panel = imgui.checkbox("Pop Out Message", self.ui_separate_message_panel)
|
imgui.end_tab_bar()
|
||||||
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.")
|
|
||||||
|
|
||||||
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,49 +2048,90 @@ 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))
|
||||||
display_md = self.last_aggregate_markdown
|
if imgui.is_item_active():
|
||||||
if self.ui_focus_agent:
|
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))
|
||||||
tier_usage = self.mma_tier_usage.get(self.ui_focus_agent)
|
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
|
||||||
if tier_usage:
|
ch1, self.ui_separate_message_panel = imgui.checkbox("Pop Out Message", self.ui_separate_message_panel)
|
||||||
persona_name = tier_usage.get("persona")
|
imgui.same_line()
|
||||||
if persona_name:
|
ch2, self.ui_separate_response_panel = imgui.checkbox("Pop Out Response", self.ui_separate_response_panel)
|
||||||
persona = self.controller.personas.get(persona_name)
|
if ch1: self.show_windows["Message"] = self.ui_separate_message_panel
|
||||||
if persona and persona.context_preset:
|
if ch2: self.show_windows["Response"] = self.ui_separate_response_panel
|
||||||
cp_name = persona.context_preset
|
imgui.pop_style_var()
|
||||||
if cp_name in self._focus_md_cache:
|
show_message_tab = not self.ui_separate_message_panel
|
||||||
display_md = self._focus_md_cache[cp_name]
|
show_response_tab = not self.ui_separate_response_panel
|
||||||
else:
|
if show_message_tab or show_response_tab:
|
||||||
# Generate focused aggregate
|
if imgui.begin_tab_bar("discussion_tabs"):
|
||||||
flat = src.project_manager.flat_config(self.controller.project, self.active_discussion)
|
tab_flags = imgui.TabItemFlags_.none
|
||||||
cp = self.controller.project.get('context_presets', {}).get(cp_name)
|
if self._autofocus_response_tab:
|
||||||
if cp:
|
tab_flags = imgui.TabItemFlags_.set_selected
|
||||||
flat["files"]["paths"] = cp.get("files", [])
|
self._autofocus_response_tab = False
|
||||||
flat["screenshots"]["paths"] = cp.get("screenshots", [])
|
self.controller._autofocus_response_tab = False
|
||||||
full_md, _, _ = src.aggregate.run(flat)
|
if show_message_tab:
|
||||||
self._focus_md_cache[cp_name] = full_md
|
if imgui.begin_tab_item("Message", None)[0]:
|
||||||
display_md = full_md
|
self._render_message_panel()
|
||||||
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()
|
|
||||||
imgui.end_tab_item()
|
imgui.end_tab_item()
|
||||||
if imgui.begin_tab_item('System Prompt')[0]:
|
if show_response_tab:
|
||||||
if imgui.button("Copy"):
|
if imgui.begin_tab_item("Response", None, tab_flags)[0]:
|
||||||
imgui.set_clipboard_text(self.last_resolved_system_prompt)
|
self._render_response_panel()
|
||||||
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()
|
|
||||||
imgui.end_tab_item()
|
imgui.end_tab_item()
|
||||||
imgui.end_tab_bar()
|
imgui.end_tab_bar()
|
||||||
imgui.end()
|
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:
|
def _render_markdown_test(self) -> None:
|
||||||
imgui.text("Markdown Test Panel")
|
imgui.text("Markdown Test Panel")
|
||||||
|
|||||||
42
tests/test_session_hub_merge.py
Normal file
42
tests/test_session_hub_merge.py
Normal 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"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user