From 84b62666100852a337f4e94f235039c024f046da Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 18 Mar 2026 09:04:07 -0400 Subject: [PATCH] feat(gui): Implement Session Hub and context injection visibility --- src/app_controller.py | 7 ++++++ src/gui_2.py | 52 ++++++++++++++++++++++++++++++++++++++++++- src/models.py | 3 +++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/app_controller.py b/src/app_controller.py index c5fa0c9..cf267c9 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -243,6 +243,8 @@ class AppController: self.ai_status: str = 'idle' self.ai_response: str = '' self.last_md: str = '' + self.last_aggregate_markdown: str = '' + self.last_resolved_system_prompt: str = '' self.last_md_path: Optional[Path] = None self.last_file_items: List[Any] = [] self.send_thread: Optional[threading.Thread] = None @@ -2516,6 +2518,11 @@ class AppController: # Build discussion history text separately history = flat.get("discussion", {}).get("history", []) discussion_text = aggregate.build_discussion_text(history) + + csp = filter(bool, [self.ui_global_system_prompt.strip(), self.ui_project_system_prompt.strip()]) + self.last_resolved_system_prompt = "\n\n".join(csp) + self.last_aggregate_markdown = full_md + return full_md, path, file_items, stable_md, discussion_text def _cb_plan_epic(self) -> None: diff --git a/src/gui_2.py b/src/gui_2.py index f07053a..cb51b2f 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -214,6 +214,7 @@ 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 = "" @@ -322,7 +323,15 @@ class App: @ui_file_paths.setter def ui_file_paths(self, paths: list[str]) -> None: - self.files = [models.FileItem(path=p) for p in paths] + old_files = {f.path: f for f in self.files if hasattr(f, 'path')} + new_files = [] + now = time.time() + for p in paths: + if p in old_files: + new_files.append(old_files[p]) + else: + new_files.append(models.FileItem(path=p, injected_at=now)) + self.files = new_files @property def ui_screenshot_paths(self) -> list[str]: @@ -849,6 +858,8 @@ 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: @@ -2087,6 +2098,29 @@ 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]: + if imgui.button("Copy"): + imgui.set_clipboard_text(self.last_aggregate_markdown) + imgui.begin_child("last_agg_md", imgui.ImVec2(0, 0), True) + markdown_helper.render(self.last_aggregate_markdown, context_id="session_hub_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="session_hub_sys") + imgui.end_child() + imgui.end_tab_item() + imgui.end_tab_bar() + imgui.end() + def _render_markdown_test(self) -> None: imgui.text("Markdown Test Panel") imgui.separator() @@ -2397,6 +2431,22 @@ def hello(): if ts_str: imgui.same_line() imgui.text_colored(vec4(120, 120, 100), str(ts_str)) + # Visual indicator for file injections + e_dt = project_manager.parse_ts(ts_str) + if e_dt: + e_unix = e_dt.timestamp() + next_unix = float('inf') + if i + 1 < len(self.disc_entries): + n_ts = self.disc_entries[i+1].get("ts", "") + n_dt = project_manager.parse_ts(n_ts) + if n_dt: next_unix = n_dt.timestamp() + injected_here = [f for f in self.files if hasattr(f, 'injected_at') and f.injected_at and e_unix <= f.injected_at < next_unix] + if injected_here: + imgui.same_line() + imgui.text_colored(vec4(100, 255, 100), f"[{len(injected_here)}+]") + if imgui.is_item_hovered(): + tooltip = "Files injected at this point:\n" + "\n".join([f.path for f in injected_here]) + imgui.set_tooltip(tooltip) if collapsed: imgui.same_line() if imgui.button("Ins"): diff --git a/src/models.py b/src/models.py index b50625c..8f3e980 100644 --- a/src/models.py +++ b/src/models.py @@ -357,12 +357,14 @@ class FileItem: path: str auto_aggregate: bool = True force_full: bool = False + injected_at: Optional[float] = None def to_dict(self) -> Dict[str, Any]: return { "path": self.path, "auto_aggregate": self.auto_aggregate, "force_full": self.force_full, + "injected_at": self.injected_at, } @classmethod @@ -371,6 +373,7 @@ class FileItem: path=data["path"], auto_aggregate=data.get("auto_aggregate", True), force_full=data.get("force_full", False), + injected_at=data.get("injected_at"), ) @dataclass