diff --git a/src/gui_2.py b/src/gui_2.py index 82b26ae..d457c4a 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -940,100 +940,90 @@ class App: def _render_preset_manager_content(self, is_embedded: bool = False) -> None: avail = imgui.get_content_region_avail() - if not hasattr(self, "_prompt_md_preview"): self._prompt_md_preview = False if imgui.begin_table("prompt_main_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): - try: - imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) - imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) - imgui.table_next_row() - - # Left Column: Presets - imgui.table_next_column() - imgui.begin_child("prompt_list_pane", imgui.ImVec2(0, 0), False) - try: - if imgui.button("New Preset", imgui.ImVec2(-1, 0)): - self._editing_preset_name = "" - self._editing_preset_system_prompt = "" - self._editing_preset_scope = "project" - self._selected_preset_idx = -1 - imgui.separator() - preset_names = sorted(self.controller.presets.keys()) - for i, name in enumerate(preset_names): - if name and imgui.selectable(f"{name}##p_{i}", self._selected_preset_idx == i)[0]: - self._selected_preset_idx = i - self._editing_preset_name = name - p = self.controller.presets[name] - self._editing_preset_system_prompt = p.system_prompt - self._editing_preset_scope = self.controller.preset_manager.get_preset_scope(name) - finally: - imgui.end_child() - - # Right Column: Editor - imgui.table_next_column() - avail_r = imgui.get_content_region_avail() - # Subtract space for bottom buttons - imgui.begin_child("prompt_edit_pane", imgui.ImVec2(0, avail_r.y - 45), False) - try: - p_disp = self._editing_preset_name or "(New Preset)" - imgui.text_colored(C_IN, f"Editing Prompt Preset: {p_disp}") - imgui.separator() - - if imgui.begin_table("p_meta", 2): - try: - imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 80) - imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) - imgui.table_next_row() - imgui.table_next_column(); imgui.text("Name:") - imgui.table_next_column(); imgui.set_next_item_width(-1) - _, self._editing_preset_name = imgui.input_text("##epn", self._editing_preset_name) - imgui.table_next_row() - imgui.table_next_column(); imgui.text("Scope:") - imgui.table_next_column() - if imgui.radio_button("Global", self._editing_preset_scope == "global"): self._editing_preset_scope = "global" - imgui.same_line() - if imgui.radio_button("Project", self._editing_preset_scope == "project"): self._editing_preset_scope = "project" - finally: - imgui.end_table() - - imgui.dummy(imgui.ImVec2(0, 4)) - imgui.separator() - imgui.text("Prompt Content:") - imgui.same_line() - if imgui.button("MD Preview" if not self._prompt_md_preview else "Edit Mode"): - self._prompt_md_preview = not self._prompt_md_preview - - if self._prompt_md_preview: - imgui.begin_child("prompt_preview", imgui.ImVec2(-1, imgui.get_content_region_avail().y), True) - try: - markdown_helper.render(self._editing_preset_system_prompt, context_id="prompt_preset_preview") - finally: - imgui.end_child() - else: - _, self._editing_preset_system_prompt = imgui.input_text_multiline("##pcont", self._editing_preset_system_prompt, imgui.ImVec2(-1, imgui.get_content_region_avail().y)) - finally: - imgui.end_child() - - # Bottom Buttons + imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) + imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + + # Left Sidebar + imgui.table_next_column() + if imgui.begin_child("prompt_list_pane", imgui.ImVec2(0, 0), False): + if imgui.button("New Preset", imgui.ImVec2(-1, 0)): + self._editing_preset_name = "" + self._editing_preset_system_prompt = "" + self._editing_preset_scope = "project" + self._selected_preset_idx = -1 imgui.separator() - imgui.dummy(imgui.ImVec2(0, 4)) - if imgui.button("Save", imgui.ImVec2(100, 0)): - if self._editing_preset_name.strip(): - p = models.Preset(name=self._editing_preset_name.strip(), system_prompt=self._editing_preset_system_prompt) - self.controller._cb_save_preset(p, self._editing_preset_scope) - self.ai_status = f"Saved: {p.name}" - imgui.same_line() - if imgui.button("Delete", imgui.ImVec2(100, 0)): - if self._editing_preset_name: - self.controller._cb_delete_preset(self._editing_preset_name, self._editing_preset_scope) - self._editing_preset_name = "" - self._selected_preset_idx = -1 - if not is_embedded: + preset_names = sorted(self.controller.presets.keys()) + for i, name in enumerate(preset_names): + if name and imgui.selectable(f"{name}##p_{i}", self._selected_preset_idx == i)[0]: + self._selected_preset_idx = i + self._editing_preset_name = name + p = self.controller.presets[name] + self._editing_preset_system_prompt = p.system_prompt + self._editing_preset_scope = self.controller.preset_manager.get_preset_scope(name) + imgui.end_child() + + # Right Editor + imgui.table_next_column() + avail_r = imgui.get_content_region_avail() + # Reserve 45px for footer buttons + if imgui.begin_child("prompt_edit_pane", imgui.ImVec2(0, avail_r.y - 45), False): + p_disp = self._editing_preset_name or "(New Preset)" + imgui.text_colored(C_IN, f"Editing Prompt Preset: {p_disp}") + imgui.separator() + + if imgui.begin_table("p_meta", 2): + imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 80) + imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + imgui.table_next_column(); imgui.text("Name:") + imgui.table_next_column(); imgui.set_next_item_width(-1) + _, self._editing_preset_name = imgui.input_text("##epn", self._editing_preset_name) + imgui.table_next_row() + imgui.table_next_column(); imgui.text("Scope:") + imgui.table_next_column() + if imgui.radio_button("Global##ps", self._editing_preset_scope == "global"): self._editing_preset_scope = "global" imgui.same_line() - if imgui.button("Close", imgui.ImVec2(100, 0)): - self.show_preset_manager_window = False - finally: - imgui.end_table() + if imgui.radio_button("Project##ps", self._editing_preset_scope == "project"): self._editing_preset_scope = "project" + imgui.end_table() + + imgui.dummy(imgui.ImVec2(0, 4)) + imgui.separator() + imgui.text("Prompt Content:") + imgui.same_line() + if imgui.button("MD Preview" if not self._prompt_md_preview else "Edit Mode"): + self._prompt_md_preview = not self._prompt_md_preview + + rem_y = imgui.get_content_region_avail().y + if self._prompt_md_preview: + if imgui.begin_child("prompt_preview", imgui.ImVec2(-1, rem_y), True): + markdown_helper.render(self._editing_preset_system_prompt, context_id="prompt_preset_preview") + imgui.end_child() + else: + _, self._editing_preset_system_prompt = imgui.input_text_multiline("##pcont", self._editing_preset_system_prompt, imgui.ImVec2(-1, rem_y)) + imgui.end_child() + + # Footer Buttons + imgui.separator() + imgui.dummy(imgui.ImVec2(0, 4)) + if imgui.button("Save", imgui.ImVec2(100, 0)): + if self._editing_preset_name.strip(): + p = models.Preset(name=self._editing_preset_name.strip(), system_prompt=self._editing_preset_system_prompt) + self.controller._cb_save_preset(p, self._editing_preset_scope) + self.ai_status = f"Saved: {p.name}" + imgui.same_line() + if imgui.button("Delete", imgui.ImVec2(100, 0)): + if self._editing_preset_name: + self.controller._cb_delete_preset(self._editing_preset_name, self._editing_preset_scope) + self._editing_preset_name = "" + self._selected_preset_idx = -1 + if not is_embedded: + imgui.same_line() + if imgui.button("Close", imgui.ImVec2(100, 0)): + self.show_preset_manager_window = False + imgui.end_table() def _render_preset_manager_window(self, is_embedded: bool = False) -> None: if not self.show_preset_manager_window and not is_embedded: return @@ -1057,214 +1047,173 @@ class App: def _render_tool_preset_manager_content(self, is_embedded: bool = False) -> None: avail = imgui.get_content_region_avail() - if not hasattr(self, "_tool_split_v"): self._tool_split_v = 0.4 - if not hasattr(self, "_bias_split_v"): self._bias_split_v = 0.6 - if not hasattr(self, "_tool_list_open"): self._tool_list_open = True - if not hasattr(self, "_bias_list_open"): self._bias_list_open = True - if not hasattr(self, "_bias_weights_open"): self._bias_weights_open = True - if not hasattr(self, "_bias_cats_open"): self._bias_cats_open = True - + if imgui.begin_table("tp_main_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): - try: - imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) - imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) - imgui.table_next_row() - - # Left Sidebar - imgui.table_next_column() - imgui.begin_child("tp_list_pane", imgui.ImVec2(0, 0), False) - try: - if imgui.button("New Preset", imgui.ImVec2(-1, 0)): - self._editing_tool_preset_name = ""; self._editing_tool_preset_categories = {cat: {} for cat in models.DEFAULT_TOOL_CATEGORIES} - self._editing_tool_preset_scope = "project"; self._selected_tool_preset_idx = -1 - imgui.separator() - preset_names = sorted(self.controller.tool_presets.keys()) - for i, name in enumerate(preset_names): - if imgui.selectable(f"{name}##tp_{i}", self._selected_tool_preset_idx == i)[0]: - self._selected_tool_preset_idx = i; self._editing_tool_preset_name = name - preset = self.controller.tool_presets[name] - self._editing_tool_preset_categories = {cat: copy.deepcopy(tools) for cat, tools in preset.categories.items()} - finally: - imgui.end_child() - - # Right Editor - imgui.table_next_column() - avail_r = imgui.get_content_region_avail() - # Bottom buttons reserved space - imgui.begin_child("tp_editor_content", imgui.ImVec2(0, avail_r.y - 45), False) - try: - p_name = self._editing_tool_preset_name or "(New Tool Preset)" - imgui.text_colored(C_IN, f"Editing Tool Preset: {p_name}"); imgui.separator() - - if imgui.begin_table("tp_meta", 2): - try: - imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 80); imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Name:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, self._editing_tool_preset_name = imgui.input_text("##etpn", self._editing_tool_preset_name) - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Scope:"); imgui.table_next_column() - if imgui.radio_button("Global", self._editing_tool_preset_scope == "global"): self._editing_tool_preset_scope = "global" - imgui.same_line(); - if imgui.radio_button("Project", self._editing_tool_preset_scope == "project"): self._editing_tool_preset_scope = "project" - finally: - imgui.end_table() - - # Vertical split calculation - rem_y = imgui.get_content_region_avail().y - 80 - if self._tool_list_open and self._bias_list_open: - h1 = rem_y * self._tool_split_v; h2 = rem_y - h1 - 10 - elif self._tool_list_open: h1, h2 = rem_y, 0 - elif self._bias_list_open: h1, h2 = 0, rem_y - else: h1, h2 = 0, 0 - - imgui.dummy(imgui.ImVec2(0, 4)) - opened_tools = imgui.collapsing_header("Categories & Tools", imgui.TreeNodeFlags_.default_open) - if opened_tools != self._tool_list_open: self._tool_list_open = opened_tools - - if self._tool_list_open: - imgui.text("Filter:"); imgui.same_line() - cat_opts = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys())) - f_idx = cat_opts.index(self.ui_tool_filter_category) if self.ui_tool_filter_category in cat_opts else 0 - imgui.set_next_item_width(200); ch_cat, next_f_idx = imgui.combo("##tp_filter", f_idx, cat_opts) - if ch_cat: self.ui_tool_filter_category = cat_opts[next_f_idx] - - imgui.begin_child("tp_scroll", imgui.ImVec2(0, h1), True) - try: - for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items(): - if self.ui_tool_filter_category != "All" and self.ui_tool_filter_category != cat_name: continue - if imgui.tree_node(cat_name): - if cat_name not in self._editing_tool_preset_categories: self._editing_tool_preset_categories[cat_name] = [] - curr_cat_tools = self._editing_tool_preset_categories[cat_name] - if imgui.begin_table(f"tt_{cat_name}", 2, imgui.TableFlags_.borders_inner_v): - try: - imgui.table_setup_column("Tool", imgui.TableColumnFlags_.width_fixed, 250); imgui.table_setup_column("Ctrls", imgui.TableColumnFlags_.width_stretch) - for tool_name in default_tools: - tool = next((t for t in curr_cat_tools if t.name == tool_name), None) - mode = "disabled" if tool is None else tool.approval - imgui.table_next_row(); imgui.table_next_column(); imgui.text(tool_name); imgui.table_next_column() - if imgui.radio_button(f"Off##{cat_name}_{tool_name}", mode == "disabled"): - if tool: curr_cat_tools.remove(tool) - imgui.same_line(); - if imgui.radio_button(f"Auto##{cat_name}_{tool_name}", mode == "auto"): - if not tool: tool = models.Tool(name=tool_name, approval="auto"); current_cat_tools.append(tool) - else: tool.approval = "auto" - imgui.same_line(); - if imgui.radio_button(f"Ask##{cat_name}_{tool_name}", mode == "ask"): - if not tool: tool = models.Tool(name=tool_name, approval="ask"); current_cat_tools.append(tool) - else: tool.approval = "ask" - finally: - imgui.end_table() - imgui.tree_pop() - finally: - imgui.end_child() - if self._bias_list_open: - imgui.button("###tool_splitter", imgui.ImVec2(-1, 4)) - if imgui.is_item_active(): self._tool_split_v = max(0.1, min(0.9, self._tool_split_v + imgui.get_io().mouse_delta.y / rem_y)) - - imgui.dummy(imgui.ImVec2(0, 4)) - opened_bias = imgui.collapsing_header("Bias Profiles", imgui.TreeNodeFlags_.default_open) - if opened_bias != self._bias_list_open: self._bias_list_open = opened_bias - - if self._bias_list_open: - imgui.begin_child("bias_area", imgui.ImVec2(0, h2), True) - try: - if imgui.begin_table("bias_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): - try: - imgui.table_setup_column("BList", imgui.TableColumnFlags_.width_fixed, 150); imgui.table_setup_column("BEdit", imgui.TableColumnFlags_.width_stretch) - imgui.table_next_row(); imgui.table_next_column() - imgui.begin_child("blist_pane", imgui.ImVec2(0, 0), False) - try: - if imgui.button("New Profile", imgui.ImVec2(-1, 0)): - self._editing_bias_profile_name = ""; self._editing_bias_profile_tool_weights = {} - self._editing_bias_profile_category_multipliers = {}; self._selected_bias_profile_idx = -1 - imgui.separator(); bnames = sorted(self.bias_profiles.keys()) - for i, bname in enumerate(bnames): - if imgui.selectable(f"{bname}##b_{i}", self._selected_bias_profile_idx == i)[0]: - self._selected_bias_profile_idx = i; self._editing_bias_profile_name = bname; prof = self.bias_profiles[bname] - self._editing_bias_profile_tool_weights = copy.deepcopy(prof.tool_weights); self._editing_bias_profile_category_multipliers = copy.deepcopy(prof.category_multipliers) - finally: - imgui.end_child() - imgui.table_next_column() - imgui.begin_child("bedit_pane", imgui.ImVec2(0, 0), False) - try: - imgui.text("Name:"); imgui.same_line(); imgui.set_next_item_width(-1); _, self._editing_bias_profile_name = imgui.input_text("##bname", self._editing_bias_profile_name) - - rem_bias_y = imgui.get_content_region_avail().y - 45 # Save button space - if self._bias_weights_open and self._bias_cats_open: - bh1 = rem_bias_y * self._bias_split_v; bh2 = rem_bias_y - bh1 - 10 - elif self._bias_weights_open: bh1, bh2 = rem_bias_y, 0 - elif self._bias_cats_open: bh1, bh2 = 0, rem_bias_y - else: bh1, bh2 = 0, 0 - - opened_bw = imgui.collapsing_header("Tool Weights", imgui.TreeNodeFlags_.default_open) - if opened_bw != self._bias_weights_open: self._bias_weights_open = opened_bw - if self._bias_weights_open: - imgui.begin_child("btool_scroll", imgui.ImVec2(0, bh1), True) - try: - for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items(): - if imgui.tree_node(f"{cat_name}##b"): - if imgui.begin_table(f"bt_{cat_name}", 2): - try: - imgui.table_setup_column("T", imgui.TableColumnFlags_.width_fixed, 220); imgui.table_setup_column("W", imgui.TableColumnFlags_.width_stretch) - for tn in default_tools: - imgui.table_next_row(); imgui.table_next_column(); imgui.text(tn); imgui.table_next_column() - curr_w = self._editing_bias_profile_tool_weights.get(tn, 3); imgui.set_next_item_width(-1) - ch_w, n_w = imgui.slider_int(f"##bw_{tn}", curr_w, 1, 10); - if ch_w: self._editing_bias_profile_tool_weights[tn] = n_w - finally: - imgui.end_table() - imgui.tree_pop() - finally: - imgui.end_child() - if self._bias_cats_open: - imgui.button("###bias_splitter", imgui.ImVec2(-1, 4)) - if imgui.is_item_active(): self._bias_split_v = max(0.1, min(0.9, self._bias_split_v + imgui.get_io().mouse_delta.y / rem_bias_y)) - - opened_bc = imgui.collapsing_header("Category Multipliers", imgui.TreeNodeFlags_.default_open) - if opened_bc != self._bias_cats_open: self._bias_cats_open = opened_bc - if self._bias_cats_open: - imgui.begin_child("bcat_scroll", imgui.ImVec2(0, bh2), True) - try: - if imgui.begin_table("bcats", 2): - try: - imgui.table_setup_column("C", imgui.TableColumnFlags_.width_fixed, 220); imgui.table_setup_column("M", imgui.TableColumnFlags_.width_stretch) - for cn in sorted(models.DEFAULT_TOOL_CATEGORIES.keys()): - imgui.table_next_row(); imgui.table_next_column(); imgui.text(cn); imgui.table_next_column() - curr_m = self._editing_bias_profile_category_multipliers.get(cn, 1.0); imgui.set_next_item_width(-1) - ch_m, n_m = imgui.slider_float(f"##cm_{cn}", curr_m, 0.1, 5.0, "%.1fx"); - if ch_m: self._editing_bias_profile_category_multipliers[cn] = n_m - finally: - imgui.end_table() - finally: - imgui.end_child() - - if imgui.button("Save Profile", imgui.ImVec2(-1, 0)): - try: - p = models.BiasProfile(name=self._editing_bias_profile_name, tool_weights=self._editing_bias_profile_tool_weights, category_multipliers=self._editing_bias_profile_category_multipliers) - self.controller._cb_save_bias_profile(p, self._editing_tool_preset_scope); self.ai_status = f"Saved: {p.name}" - except Exception as e: self.ai_status = f"Error: {e}" - finally: - imgui.end_child() - finally: - imgui.end_table() - finally: - imgui.end_child() - finally: - imgui.end_child() - - # --- Footer Buttons --- + imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) + imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + + # Left Sidebar + imgui.table_next_column() + if imgui.begin_child("tp_list_pane", imgui.ImVec2(0, 0), False): + if imgui.button("New Preset", imgui.ImVec2(-1, 0)): + self._editing_tool_preset_name = ""; self._editing_tool_preset_categories = {cat: {} for cat in models.DEFAULT_TOOL_CATEGORIES} + self._editing_tool_preset_scope = "project"; self._selected_tool_preset_idx = -1 imgui.separator() - if imgui.button("Save", imgui.ImVec2(100, 0)): - if self._editing_tool_preset_name.strip(): self.controller._cb_save_tool_preset(self._editing_tool_preset_name.strip(), self._editing_tool_preset_categories, self._editing_tool_preset_scope); self.ai_status = f"Saved: {self._editing_tool_preset_name}" - imgui.same_line(); - if imgui.button("Delete", imgui.ImVec2(100, 0)): - if self._editing_tool_preset_name: self.controller._cb_delete_tool_preset(self._editing_tool_preset_name, self._editing_tool_preset_scope); self._editing_tool_preset_name = ""; self._selected_tool_preset_idx = -1 - imgui.same_line(); - if not is_embedded: - if imgui.button("Close", imgui.ImVec2(100, 0)): self.show_tool_preset_manager_window = False - finally: - imgui.end_table() - finally: - pass + preset_names = sorted(self.controller.tool_presets.keys()) + for i, name in enumerate(preset_names): + if name and imgui.selectable(f"{name}##tp_{i}", self._selected_tool_preset_idx == i)[0]: + self._selected_tool_preset_idx = i; self._editing_tool_preset_name = name + preset = self.controller.tool_presets[name] + self._editing_tool_preset_categories = {cat: copy.deepcopy(tools) for cat, tools in preset.categories.items()} + imgui.end_child() + # Right Editor + imgui.table_next_column() + avail_r = imgui.get_content_region_avail() + if imgui.begin_child("tp_editor_content", imgui.ImVec2(0, avail_r.y - 45), False): + p_name = self._editing_tool_preset_name or "(New Tool Preset)" + imgui.text_colored(C_IN, f"Editing Tool Preset: {p_name}"); imgui.separator() + + if imgui.begin_table("tp_meta", 2): + imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 80); imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Name:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, self._editing_tool_preset_name = imgui.input_text("##etpn", self._editing_tool_preset_name) + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Scope:"); imgui.table_next_column() + if imgui.radio_button("Global##tps", self._editing_tool_preset_scope == "global"): self._editing_tool_preset_scope = "global" + imgui.same_line(); + if imgui.radio_button("Project##tps", self._editing_tool_preset_scope == "project"): self._editing_tool_preset_scope = "project" + imgui.end_table() + + rem_y = imgui.get_content_region_avail().y - 80 + if self._tool_list_open and self._bias_list_open: + h1 = rem_y * self._tool_split_v; h2 = rem_y - h1 - 10 + elif self._tool_list_open: h1, h2 = rem_y, 0 + elif self._bias_list_open: h1, h2 = 0, rem_y + else: h1, h2 = 0, 0 + + imgui.dummy(imgui.ImVec2(0, 4)) + opened_t = imgui.collapsing_header("Categories & Tools", imgui.TreeNodeFlags_.default_open) + if opened_t != self._tool_list_open: self._tool_list_open = opened_t + if self._tool_list_open: + imgui.text("Filter:"); imgui.same_line() + cat_opts = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys())) + f_idx = cat_opts.index(self.ui_tool_filter_category) if self.ui_tool_filter_category in cat_opts else 0 + imgui.set_next_item_width(200); ch_cat, next_f_idx = imgui.combo("##tp_filter", f_idx, cat_opts) + if ch_cat: self.ui_tool_filter_category = cat_opts[next_f_idx] + + if imgui.begin_child("tp_scroll", imgui.ImVec2(0, h1), True): + for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items(): + if self.ui_tool_filter_category != "All" and self.ui_tool_filter_category != cat_name: continue + if imgui.tree_node(cat_name): + if cat_name not in self._editing_tool_preset_categories: self._editing_tool_preset_categories[cat_name] = [] + curr_cat_tools = self._editing_tool_preset_categories[cat_name] + if imgui.begin_table(f"tt_{cat_name}", 2, imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("Tool", imgui.TableColumnFlags_.width_fixed, 250); imgui.table_setup_column("Ctrls", imgui.TableColumnFlags_.width_stretch) + for tool_name in default_tools: + tool = next((t for t in curr_cat_tools if t.name == tool_name), None) + mode = "disabled" if tool is None else tool.approval + imgui.table_next_row(); imgui.table_next_column(); imgui.text(tool_name); imgui.table_next_column() + if imgui.radio_button(f"Off##{cat_name}_{tool_name}", mode == "disabled"): + if tool: curr_cat_tools.remove(tool) + imgui.same_line(); + if imgui.radio_button(f"Auto##{cat_name}_{tool_name}", mode == "auto"): + if not tool: tool = models.Tool(name=tool_name, approval="auto"); current_cat_tools.append(tool) + else: tool.approval = "auto" + imgui.same_line(); + if imgui.radio_button(f"Ask##{cat_name}_{tool_name}", mode == "ask"): + if not tool: tool = models.Tool(name=tool_name, approval="ask"); current_cat_tools.append(tool) + else: tool.approval = "ask" + imgui.end_table() + imgui.tree_pop() + imgui.end_child() + if self._bias_list_open: + imgui.button("###tool_splitter", imgui.ImVec2(-1, 4)) + if imgui.is_item_active(): self._tool_split_v = max(0.1, min(0.9, self._tool_split_v + imgui.get_io().mouse_delta.y / rem_y)) + + imgui.dummy(imgui.ImVec2(0, 4)) + opened_b = imgui.collapsing_header("Bias Profiles", imgui.TreeNodeFlags_.default_open) + if opened_b != self._bias_list_open: self._bias_list_open = opened_b + if self._bias_list_open: + if imgui.begin_child("bias_area", imgui.ImVec2(0, h2), True): + if imgui.begin_table("bias_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("BList", imgui.TableColumnFlags_.width_fixed, 150); imgui.table_setup_column("BEdit", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row(); imgui.table_next_column() + if imgui.begin_child("blist_pane", imgui.ImVec2(0, 0), False): + if imgui.button("New Profile", imgui.ImVec2(-1, 0)): + self._editing_bias_profile_name = ""; self._editing_bias_profile_tool_weights = {} + self._editing_bias_profile_category_multipliers = {}; self._selected_bias_profile_idx = -1 + imgui.separator(); bnames = sorted(self.bias_profiles.keys()) + for i, bname in enumerate(bnames): + if bname and imgui.selectable(f"{bname}##b_{i}", self._selected_bias_profile_idx == i)[0]: + self._selected_bias_profile_idx = i; self._editing_bias_profile_name = bname; prof = self.bias_profiles[bname] + self._editing_bias_profile_tool_weights = copy.deepcopy(prof.tool_weights); self._editing_bias_profile_category_multipliers = copy.deepcopy(prof.category_multipliers) + imgui.end_child() + + imgui.table_next_column() + if imgui.begin_child("bedit_pane", imgui.ImVec2(0, 0), False): + imgui.text("Name:"); imgui.same_line(); imgui.set_next_item_width(-1); _, self._editing_bias_profile_name = imgui.input_text("##bname", self._editing_bias_profile_name) + rem_bias_y = imgui.get_content_region_avail().y - 45 + if self._bias_weights_open and self._bias_cats_open: + bh1 = rem_bias_y * self._bias_split_v; bh2 = rem_bias_y - bh1 - 10 + elif self._bias_weights_open: bh1, bh2 = rem_bias_y, 0 + elif self._bias_cats_open: bh1, bh2 = 0, rem_bias_y + else: bh1, bh2 = 0, 0 + + opened_bw = imgui.collapsing_header("Tool Weights", imgui.TreeNodeFlags_.default_open) + if opened_bw != self._bias_weights_open: self._bias_weights_open = opened_bw + if self._bias_weights_open: + if imgui.begin_child("btool_scroll", imgui.ImVec2(0, bh1), True): + for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items(): + if imgui.tree_node(f"{cat_name}##b_list"): + if imgui.begin_table(f"bt_{cat_name}", 2): + imgui.table_setup_column("T", imgui.TableColumnFlags_.width_fixed, 220); imgui.table_setup_column("W", imgui.TableColumnFlags_.width_stretch) + for tn in default_tools: + imgui.table_next_row(); imgui.table_next_column(); imgui.text(tn); imgui.table_next_column() + curr_w = self._editing_bias_profile_tool_weights.get(tn, 3); imgui.set_next_item_width(-1) + ch_w, n_w = imgui.slider_int(f"##bw_{tn}", curr_w, 1, 10); + if ch_w: self._editing_bias_profile_tool_weights[tn] = n_w + imgui.end_table() + imgui.tree_pop() + imgui.end_child() + if self._bias_cats_open: + imgui.button("###bias_splitter", imgui.ImVec2(-1, 4)) + if imgui.is_item_active(): self._bias_split_v = max(0.1, min(0.9, self._bias_split_v + imgui.get_io().mouse_delta.y / rem_bias_y)) + + opened_bc = imgui.collapsing_header("Category Multipliers", imgui.TreeNodeFlags_.default_open) + if opened_bc != self._bias_cats_open: self._bias_cats_open = opened_bc + if self._bias_cats_open: + if imgui.begin_child("bcat_scroll", imgui.ImVec2(0, bh2), True): + if imgui.begin_table("bcats", 2): + imgui.table_setup_column("C", imgui.TableColumnFlags_.width_fixed, 220); imgui.table_setup_column("M", imgui.TableColumnFlags_.width_stretch) + for cn in sorted(models.DEFAULT_TOOL_CATEGORIES.keys()): + imgui.table_next_row(); imgui.table_next_column(); imgui.text(cn); imgui.table_next_column() + curr_m = self._editing_bias_profile_category_multipliers.get(cn, 1.0); imgui.set_next_item_width(-1) + ch_m, n_m = imgui.slider_float(f"##cm_{cn}", curr_m, 0.1, 5.0, "%.1fx"); + if ch_m: self._editing_bias_profile_category_multipliers[cn] = n_m + imgui.end_table() + imgui.end_child() + + if imgui.button("Save Profile", imgui.ImVec2(-1, 0)): + try: + p = models.BiasProfile(name=self._editing_bias_profile_name, tool_weights=self._editing_bias_profile_tool_weights, category_multipliers=self._editing_bias_profile_category_multipliers) + self.controller._cb_save_bias_profile(p, self._editing_tool_preset_scope); self.ai_status = f"Saved: {p.name}" + except Exception as e: self.ai_status = f"Error: {e}" + imgui.end_child() + imgui.end_table() + imgui.end_child() + imgui.end_child() + + # --- Footer Buttons --- + imgui.separator() + if imgui.button("Save", imgui.ImVec2(100, 0)): + if self._editing_tool_preset_name.strip(): self.controller._cb_save_tool_preset(self._editing_tool_preset_name.strip(), self._editing_tool_preset_categories, self._editing_tool_preset_scope); self.ai_status = f"Saved: {self._editing_tool_preset_name}" + imgui.same_line() + if imgui.button("Delete", imgui.ImVec2(100, 0)): + if self._editing_tool_preset_name: self.controller._cb_delete_tool_preset(self._editing_tool_preset_name, self._editing_tool_preset_scope); self._editing_tool_preset_name = ""; self._selected_tool_preset_idx = -1 + imgui.same_line() + if not is_embedded: + if imgui.button("Close", imgui.ImVec2(100, 0)): self.show_tool_preset_manager_window = False + imgui.end_table() def _render_tool_preset_manager_window(self, is_embedded: bool = False) -> None: if not self.show_tool_preset_manager_window and not is_embedded: return @@ -1292,15 +1241,14 @@ class App: if not opened: imgui.end(); return - try: - if imgui.begin_table("persona_main_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): - imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) - imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) - imgui.table_next_row() - - # --- Left Sidebar --- - imgui.table_next_column() - imgui.begin_child("persona_list_pane", imgui.ImVec2(0, 0), False) + if imgui.begin_table("persona_main_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): + # --- Left Sidebar --- + imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) + imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + + imgui.table_next_column() + if imgui.begin_child("persona_list_pane", imgui.ImVec2(0, 0), False): if imgui.button("New Persona", imgui.ImVec2(-1, 0)): self._editing_persona_name = ""; self._editing_persona_system_prompt = "" self._editing_persona_tool_preset_id = ""; self._editing_persona_bias_profile_id = "" @@ -1309,19 +1257,17 @@ class App: imgui.separator() personas = getattr(self.controller, 'personas', {}) for name in sorted(personas.keys()): - if imgui.selectable(name, name == self._editing_persona_name and not getattr(self, '_editing_persona_is_new', False))[0]: + if name and imgui.selectable(f"{name}##p_list", name == self._editing_persona_name and not getattr(self, '_editing_persona_is_new', False))[0]: p = personas[name]; self._editing_persona_name = p.name; self._editing_persona_system_prompt = p.system_prompt or "" self._editing_persona_tool_preset_id = p.tool_preset or ""; self._editing_persona_bias_profile_id = p.bias_profile or "" import copy; self._editing_persona_preferred_models_list = copy.deepcopy(p.preferred_models) if p.preferred_models else [] self._editing_persona_scope = self.controller.persona_manager.get_persona_scope(p.name); self._editing_persona_is_new = False imgui.end_child() - # --- Right Editor --- - imgui.table_next_column() - avail = imgui.get_content_region_avail() - # Bottom buttons reserved space - imgui.begin_child("persona_editor_content", imgui.ImVec2(0, avail.y - 45), False) - + # --- Right Editor --- + imgui.table_next_column() + avail = imgui.get_content_region_avail() + if imgui.begin_child("persona_editor_content", imgui.ImVec2(0, avail.y - 45), False): header_text = "New Persona" if getattr(self, '_editing_persona_is_new', True) else f"Editing Persona: {self._editing_persona_name}" imgui.text_colored(C_IN, header_text); imgui.separator() @@ -1334,7 +1280,6 @@ class App: if imgui.radio_button("Project##pscope", getattr(self, '_editing_persona_scope', 'project') == "project"): self._editing_persona_scope = "project" imgui.end_table() - # Vertical Distribution Logic rem_y = imgui.get_content_region_avail().y - 100 if self._persona_models_open and self._persona_prompt_open: h1 = rem_y * self._persona_split_v; h2 = rem_y - h1 - 10 @@ -1347,44 +1292,44 @@ class App: if opened_models != self._persona_models_open: self._persona_models_open = opened_models if self._persona_models_open: - imgui.begin_child("pref_models_scroll", imgui.ImVec2(0, h1), True) - to_remove = [] - providers = self.controller.PROVIDERS - if not hasattr(self, '_persona_pref_models_expanded'): self._persona_pref_models_expanded = {} - for i, entry in enumerate(self._editing_persona_preferred_models_list): - imgui.push_id(f"pref_model_{i}") - prov, mod, is_expanded = entry.get("provider", "Unknown"), entry.get("model", "Unknown"), self._persona_pref_models_expanded.get(i, False) - if imgui.button("-" if is_expanded else "+"): self._persona_pref_models_expanded[i] = not is_expanded - imgui.same_line(); imgui.text(f"{i+1}."); imgui.same_line(); imgui.text_colored(C_LBL, f"{prov}"); imgui.same_line(); imgui.text("-"); imgui.same_line(); imgui.text_colored(C_IN, f"{mod}") - if not is_expanded: - imgui.same_line(); summary = f" (T:{entry.get('temperature', 0.7):.1f}, P:{entry.get('top_p', 1.0):.2f}, M:{entry.get('max_output_tokens', 0)})" - imgui.text_colored(C_SUB, summary) - imgui.same_line(imgui.get_content_region_avail().x - 30); - if imgui.button("x"): to_remove.append(i) - if is_expanded: - imgui.indent(20) - if imgui.begin_table("model_settings", 2, imgui.TableFlags_.borders_inner_v): - imgui.table_setup_column("Label", imgui.TableColumnFlags_.width_fixed, 120); imgui.table_setup_column("Control", imgui.TableColumnFlags_.width_stretch) - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column(); imgui.set_next_item_width(-1) - p_idx = providers.index(prov) + 1 if prov in providers else 0; ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers) - if ch_p: entry["provider"] = providers[p_idx-1] if p_idx > 0 else "" - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column(); imgui.set_next_item_width(-1) - m_list = self.controller.all_available_models.get(entry.get("provider", ""), []); m_idx = m_list.index(mod) + 1 if mod in m_list else 0 - ch_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list) - if ch_m: entry["model"] = m_list[m_idx-1] if m_idx > 0 else "" - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Temperature:"); imgui.table_next_column(); cw = imgui.get_content_region_avail().x - imgui.set_next_item_width(cw * 0.7); _, entry["temperature"] = imgui.slider_float("##ts", entry.get("temperature", 0.7), 0.0, 2.0, "%.1f") - imgui.same_line(); imgui.set_next_item_width(-1); _, entry["temperature"] = imgui.input_float("##ti", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f") - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Top-P:"); imgui.table_next_column() - imgui.set_next_item_width(cw * 0.7); _, entry["top_p"] = imgui.slider_float("##tp_s", entry.get("top_p", 1.0), 0.0, 1.0, "%.2f") - imgui.same_line(); imgui.set_next_item_width(-1); _, entry["top_p"] = imgui.input_float("##tp_i", entry.get("top_p", 1.0), 0.05, 0.05, "%.2f") - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Max Tokens:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096)) - imgui.table_next_row(); imgui.table_next_column(); imgui.text("History Limit:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["history_trunc_limit"] = imgui.input_int("##hist", entry.get("history_trunc_limit", 900000)) - imgui.end_table() - imgui.unindent(20) - imgui.pop_id() - for i in reversed(to_remove): self._editing_persona_preferred_models_list.pop(i) - imgui.end_child() + if imgui.begin_child("pref_models_scroll", imgui.ImVec2(0, h1), True): + to_remove = [] + providers = self.controller.PROVIDERS + if not hasattr(self, '_persona_pref_models_expanded'): self._persona_pref_models_expanded = {} + for i, entry in enumerate(self._editing_persona_preferred_models_list): + imgui.push_id(f"pref_model_{i}") + prov, mod, is_expanded = entry.get("provider", "Unknown"), entry.get("model", "Unknown"), self._persona_pref_models_expanded.get(i, False) + if imgui.button("-" if is_expanded else "+"): self._persona_pref_models_expanded[i] = not is_expanded + imgui.same_line(); imgui.text(f"{i+1}."); imgui.same_line(); imgui.text_colored(C_LBL, f"{prov}"); imgui.same_line(); imgui.text("-"); imgui.same_line(); imgui.text_colored(C_IN, f"{mod}") + if not is_expanded: + imgui.same_line(); summary = f" (T:{entry.get('temperature', 0.7):.1f}, P:{entry.get('top_p', 1.0):.2f}, M:{entry.get('max_output_tokens', 0)})" + imgui.text_colored(C_SUB, summary) + imgui.same_line(imgui.get_content_region_avail().x - 30); + if imgui.button("x"): to_remove.append(i) + if is_expanded: + imgui.indent(20) + if imgui.begin_table("model_settings", 2, imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("Label", imgui.TableColumnFlags_.width_fixed, 120); imgui.table_setup_column("Control", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column(); imgui.set_next_item_width(-1) + p_idx = providers.index(prov) + 1 if prov in providers else 0; ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers) + if ch_p: entry["provider"] = providers[p_idx-1] if p_idx > 0 else "" + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column(); imgui.set_next_item_width(-1) + m_list = self.controller.all_available_models.get(entry.get("provider", ""), []); m_idx = m_list.index(mod) + 1 if mod in m_list else 0 + ch_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list) + if ch_m: entry["model"] = m_list[m_idx-1] if m_idx > 0 else "" + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Temperature:"); imgui.table_next_column(); cw = imgui.get_content_region_avail().x + imgui.set_next_item_width(cw * 0.7); _, entry["temperature"] = imgui.slider_float("##ts", entry.get("temperature", 0.7), 0.0, 2.0, "%.1f") + imgui.same_line(); imgui.set_next_item_width(-1); _, entry["temperature"] = imgui.input_float("##ti", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f") + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Top-P:"); imgui.table_next_column() + imgui.set_next_item_width(cw * 0.7); _, entry["top_p"] = imgui.slider_float("##tp_s", entry.get("top_p", 1.0), 0.0, 1.0, "%.2f") + imgui.same_line(); imgui.set_next_item_width(-1); _, entry["top_p"] = imgui.input_float("##tp_i", entry.get("top_p", 1.0), 0.05, 0.05, "%.2f") + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Max Tokens:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096)) + imgui.table_next_row(); imgui.table_next_column(); imgui.text("History Limit:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["history_trunc_limit"] = imgui.input_int("##hist", entry.get("history_trunc_limit", 900000)) + imgui.end_table() + imgui.unindent(20) + imgui.pop_id() + for i in reversed(to_remove): self._editing_persona_preferred_models_list.pop(i) + imgui.end_child() if self._persona_prompt_open: imgui.button("###persona_splitter", imgui.ImVec2(-1, 4)) if imgui.is_item_active(): self._persona_split_v = max(0.1, min(0.9, self._persona_split_v + imgui.get_io().mouse_delta.y / rem_y)) @@ -1410,42 +1355,38 @@ class App: if opened_prompt != self._persona_prompt_open: self._persona_prompt_open = opened_prompt if self._persona_prompt_open: - imgui.begin_child("p_prompt_header", imgui.ImVec2(0, 30), False) - imgui.text("Template:"); imgui.same_line(); p_pre = ["Select..."] + sorted(self.controller.presets.keys()) - if not hasattr(self, "_load_preset_idx"): self._load_preset_idx = 0 - imgui.set_next_item_width(200); _, self._load_preset_idx = imgui.combo("##load_p", self._load_preset_idx, p_pre) - imgui.same_line(); - if imgui.button("Apply"): - if self._load_preset_idx > 0: self._editing_persona_system_prompt = self.controller.presets[p_pre[self._load_preset_idx]].system_prompt - imgui.same_line(); - if imgui.button("Manage"): self.show_preset_manager_window = True - imgui.end_child() + if imgui.begin_child("p_prompt_header_pane", imgui.ImVec2(0, 30), False): + imgui.text("Template:"); imgui.same_line(); p_pre = ["Select..."] + sorted(self.controller.presets.keys()) + if not hasattr(self, "_load_preset_idx"): self._load_preset_idx = 0 + imgui.set_next_item_width(200); _, self._load_preset_idx = imgui.combo("##load_p", self._load_preset_idx, p_pre) + imgui.same_line(); + if imgui.button("Apply"): + if self._load_preset_idx > 0: self._editing_persona_system_prompt = self.controller.presets[p_pre[self._load_preset_idx]].system_prompt + imgui.same_line(); + if imgui.button("Manage"): self.show_preset_manager_window = True + imgui.end_child() _, self._editing_persona_system_prompt = imgui.input_text_multiline("##pprompt", self._editing_persona_system_prompt, imgui.ImVec2(-1, h2)) imgui.end_child() - # --- Footer Buttons --- - imgui.separator() - if imgui.button("Save", imgui.ImVec2(100, 0)): - if self._editing_persona_name.strip(): - try: - import copy; persona = models.Persona(name=self._editing_persona_name.strip(), system_prompt=self._editing_persona_system_prompt, tool_preset=self._editing_persona_tool_preset_id or None, bias_profile=self._editing_persona_bias_profile_id or None, preferred_models=copy.deepcopy(self._editing_persona_preferred_models_list)) - self.controller._cb_save_persona(persona, getattr(self, '_editing_persona_scope', 'project')); self.ai_status = f"Saved: {persona.name}" - except Exception as e: self.ai_status = f"Error: {e}" - else: self.ai_status = "Name required" + # --- Footer Buttons --- + imgui.separator() + if imgui.button("Save", imgui.ImVec2(100, 0)): + if self._editing_persona_name.strip(): + try: + import copy; persona = models.Persona(name=self._editing_persona_name.strip(), system_prompt=self._editing_persona_system_prompt, tool_preset=self._editing_persona_tool_preset_id or None, bias_profile=self._editing_persona_bias_profile_id or None, preferred_models=copy.deepcopy(self._editing_persona_preferred_models_list)) + self.controller._cb_save_persona(persona, getattr(self, '_editing_persona_scope', 'project')); self.ai_status = f"Saved: {persona.name}" + except Exception as e: self.ai_status = f"Error: {e}" + else: self.ai_status = "Name required" + imgui.same_line(); + if imgui.button("Delete", imgui.ImVec2(100, 0)): + if not getattr(self, '_editing_persona_is_new', True) and self._editing_persona_name: + self.controller._cb_delete_persona(self._editing_persona_name, getattr(self, '_editing_persona_scope', 'project')) + self._editing_persona_name = ""; self._editing_persona_is_new = True + if not is_embedded: imgui.same_line(); - if imgui.button("Delete", imgui.ImVec2(100, 0)): - if not getattr(self, '_editing_persona_is_new', True) and self._editing_persona_name: - self.controller._cb_delete_persona(self._editing_persona_name, getattr(self, '_editing_persona_scope', 'project')) - self._editing_persona_name = ""; self._editing_persona_is_new = True - if not is_embedded: - imgui.same_line(); - if imgui.button("Close", imgui.ImVec2(100, 0)): self.show_persona_editor_window = False - imgui.end_table() - except Exception as e: - import traceback; traceback.print_exc() - imgui.text_colored(vec4(255, 0, 0), f"Error in Persona Editor: {e}") - finally: - if not is_embedded: imgui.end() + if imgui.button("Close", imgui.ImVec2(100, 0)): self.show_persona_editor_window = False + imgui.end_table() + if not is_embedded: imgui.end() def _render_projects_panel(self) -> None: if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_projects_panel") proj_name = self.project.get("project", {}).get("name", Path(self.active_project_path).stem)