From cc6a651664f759a96ad1b772bd3901f73b1b24b3 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 22 Mar 2026 13:27:41 -0400 Subject: [PATCH] feat(gui): Implement Takes panel (Phase 4) - Replaced _render_takes_placeholder with _render_takes_panel - Shows list of takes with entry count and switch/delete actions - Includes synthesis UI with take selection and prompt - Uses existing synthesis_formatter for diff generation --- src/gui_2.py | 58 ++++++++++++++++++++++++++++++++++++--- tests/test_takes_panel.py | 16 +++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 tests/test_takes_panel.py diff --git a/src/gui_2.py b/src/gui_2.py index 0b0e7e4..e6d273d 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -734,7 +734,7 @@ class App: self._render_snapshot_tab() imgui.end_tab_item() if imgui.begin_tab_item("Takes")[0]: - self._render_takes_placeholder() + self._render_takes_panel() imgui.end_tab_item() imgui.end_tab_bar() imgui.end() @@ -2175,11 +2175,61 @@ class App: imgui.end_tab_item() imgui.end_tab_bar() - def _render_takes_placeholder(self) -> None: + def _render_takes_panel(self) -> None: imgui.text("Takes & Synthesis") imgui.separator() - imgui.text_colored(C_LBL, "Coming in Phase 4...") - + discussions = self.project.get('discussion', {}).get('discussions', {}) + if not hasattr(self, 'ui_synthesis_selected_takes'): + self.ui_synthesis_selected_takes = {name: False for name in discussions} + if not hasattr(self, 'ui_synthesis_prompt'): + self.ui_synthesis_prompt = "" + if imgui.begin_table("takes_table", 3, imgui.TableFlags_.resizable | imgui.TableFlags_.borders): + imgui.table_setup_column("Name", imgui.TableColumnFlags_.width_stretch) + imgui.table_setup_column("Entries", imgui.TableColumnFlags_.width_fixed, 80) + imgui.table_setup_column("Actions", imgui.TableColumnFlags_.width_fixed, 150) + imgui.table_headers_row() + for name, disc in discussions.items(): + imgui.table_next_row() + imgui.table_set_column_index(0) + is_active = name == self.active_discussion + if is_active: + imgui.text_colored(C_IN, name) + else: + imgui.text(name) + imgui.table_set_column_index(1) + history = disc.get('history', []) + imgui.text(f"{len(history)}") + imgui.table_set_column_index(2) + if imgui.button(f"Switch##{name}"): + self._switch_discussion(name) + imgui.same_line() + if name != "main" and imgui.button(f"Delete##{name}"): + del discussions[name] + imgui.end_table() + imgui.separator() + imgui.text("Synthesis") + imgui.text("Select takes to synthesize:") + for name in discussions: + _, self.ui_synthesis_selected_takes[name] = imgui.checkbox(name, self.ui_synthesis_selected_takes.get(name, False)) + imgui.spacing() + imgui.text("Synthesis Prompt:") + _, self.ui_synthesis_prompt = imgui.input_text_multiline("##synthesis_prompt", self.ui_synthesis_prompt, imgui.ImVec2(-1, 100)) + if imgui.button("Generate Synthesis"): + selected = [name for name, sel in self.ui_synthesis_selected_takes.items() if sel] + if len(selected) > 1: + from src import synthesis_formatter + takes_dict = {name: discussions.get(name, {}).get('history', []) for name in selected} + diff_text = synthesis_formatter.format_takes_diff(takes_dict) + prompt = f"{self.ui_synthesis_prompt}\n\nHere are the variations:\n{diff_text}" + new_name = "synthesis_take" + counter = 1 + while new_name in discussions: + new_name = f"synthesis_take_{counter}" + counter += 1 + self._create_discussion(new_name) + with self._disc_entries_lock: + self.disc_entries.append({"role": "user", "content": prompt, "collapsed": False, "ts": project_manager.now_ts()}) + self._handle_generate_send() def _render_markdown_test(self) -> None: imgui.text("Markdown Test Panel") imgui.separator() diff --git a/tests/test_takes_panel.py b/tests/test_takes_panel.py new file mode 100644 index 0000000..dc1d1a3 --- /dev/null +++ b/tests/test_takes_panel.py @@ -0,0 +1,16 @@ +import pytest +import inspect + + +def test_takes_tab_replaces_placeholder(): + import src.gui_2 as gui_2 + + source = inspect.getsource(gui_2.App._gui_func) + assert "_render_takes_placeholder" not in source, "Placeholder should be replaced" + + +def test_takes_panel_has_synthesis(): + import src.gui_2 as gui_2 + + source = inspect.getsource(gui_2.App._render_takes_panel) + assert "synthesis" in source.lower(), "Should have synthesis functionality"