refactor(gui): redesign persona modal as non-blocking window and embed sub-managers

This commit is contained in:
2026-03-10 23:28:20 -04:00
parent 6da3d95c0e
commit 26e03d2c9f

View File

@@ -923,6 +923,70 @@ class App:
imgui.close_current_popup()
imgui.end_popup()
def _render_preset_manager_content(self, is_embedded: bool = False) -> None:
avail = imgui.get_content_region_avail()
imgui.begin_child("preset_list_area", imgui.ImVec2(250, avail.y), True)
try:
preset_names = sorted(self.controller.presets.keys())
if imgui.button("New Preset", imgui.ImVec2(-1, 0)):
self._editing_preset_name = ""
self._editing_preset_content = ""
self._editing_preset_scope = "project"
self._editing_preset_is_new = True
imgui.separator()
for name in preset_names:
p = self.controller.presets[name]
is_sel = (name == self._editing_preset_name)
if imgui.selectable(name, is_sel)[0]:
self._editing_preset_name = name
self._editing_preset_content = p.system_prompt
self._editing_preset_is_new = False
finally:
imgui.end_child()
imgui.same_line()
imgui.begin_child("preset_edit_area", imgui.ImVec2(0, avail.y), False)
try:
p_name = self._editing_preset_name or "(New Preset)"
imgui.text_colored(C_IN, f"Editing Preset: {p_name}")
imgui.separator()
imgui.text("Name:")
_, self._editing_preset_name = imgui.input_text("##edit_name", self._editing_preset_name)
imgui.text("Scope:")
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"
imgui.text("Content:")
_, self._editing_preset_content = imgui.input_text_multiline("##edit_content", self._editing_preset_content, imgui.ImVec2(-1, -40))
if imgui.button("Save", imgui.ImVec2(120, 0)):
if self._editing_preset_name.strip():
self.controller._cb_save_preset(
self._editing_preset_name.strip(),
self._editing_preset_content,
self._editing_preset_scope
)
self.ai_status = f"Preset '{self._editing_preset_name.strip()}' saved to {self._editing_preset_scope}"
imgui.set_item_tooltip("Save the current preset settings")
imgui.same_line()
if imgui.button("Delete", imgui.ImVec2(120, 0)):
if self._editing_preset_name.strip():
try:
self.controller._cb_delete_preset(self._editing_preset_name.strip(), self._editing_preset_scope)
self.ai_status = f"Preset '{self._editing_preset_name}' deleted from {self._editing_preset_scope}"
self._editing_preset_name = ""
self._editing_preset_content = ""
except Exception as e:
self.ai_status = f"Error deleting: {e}"
imgui.set_item_tooltip("Delete the selected preset")
if not is_embedded:
imgui.same_line()
if imgui.button("Close", imgui.ImVec2(120, 0)):
self.show_preset_manager_window = False
finally:
imgui.end_child()
def _render_preset_manager_window(self, is_embedded: bool = False) -> None:
if not self.show_preset_manager_window and not is_embedded: return
@@ -934,72 +998,240 @@ class App:
return
try:
avail = imgui.get_content_region_avail()
imgui.begin_child("preset_list_area", imgui.ImVec2(250, avail.y), True)
try:
preset_names = sorted(self.controller.presets.keys())
if imgui.button("New Preset", imgui.ImVec2(-1, 0)):
self._editing_preset_name = ""
self._editing_preset_content = ""
self._editing_preset_scope = "project"
self._editing_preset_is_new = True
imgui.separator()
for name in preset_names:
p = self.controller.presets[name]
is_sel = (name == self._editing_preset_name)
if imgui.selectable(name, is_sel)[0]:
self._editing_preset_name = name
self._editing_preset_content = p.system_prompt
self._editing_preset_is_new = False
finally:
imgui.end_child()
imgui.same_line()
imgui.begin_child("preset_edit_area", imgui.ImVec2(0, avail.y), False)
try:
p_name = self._editing_preset_name or "(New Preset)"
imgui.text_colored(C_IN, f"Editing Preset: {p_name}")
imgui.separator()
imgui.text("Name:")
_, self._editing_preset_name = imgui.input_text("##edit_name", self._editing_preset_name)
imgui.text("Scope:")
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"
imgui.text("Content:")
_, self._editing_preset_content = imgui.input_text_multiline("##edit_content", self._editing_preset_content, imgui.ImVec2(-1, -40))
if imgui.button("Save", imgui.ImVec2(120, 0)):
if self._editing_preset_name.strip():
self.controller._cb_save_preset(
self._editing_preset_name.strip(),
self._editing_preset_content,
self._editing_preset_scope
)
self.ai_status = f"Preset '{self._editing_preset_name.strip()}' saved to {self._editing_preset_scope}"
imgui.set_item_tooltip("Save the current preset settings")
imgui.same_line()
if imgui.button("Delete", imgui.ImVec2(120, 0)):
if self._editing_preset_name.strip():
try:
self.controller._cb_delete_preset(self._editing_preset_name.strip(), self._editing_preset_scope)
self.ai_status = f"Preset '{self._editing_preset_name}' deleted from {self._editing_preset_scope}"
self._editing_preset_name = ""
self._editing_preset_content = ""
except Exception as e:
self.ai_status = f"Error deleting: {e}"
imgui.set_item_tooltip("Delete the selected preset")
if not is_embedded:
imgui.same_line()
if imgui.button("Close", imgui.ImVec2(120, 0)):
self.show_preset_manager_window = False
finally:
imgui.end_child()
self._render_preset_manager_content(is_embedded=is_embedded)
finally:
if not is_embedded:
imgui.end()
def _render_tool_preset_manager_content(self, is_embedded: bool = False) -> None:
avail = imgui.get_content_region_avail()
# Left Column: Listbox
imgui.begin_child("tool_preset_list_area", imgui.ImVec2(250, avail.y), True)
try:
if imgui.button("New Tool 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
if imgui.is_item_hovered():
imgui.set_tooltip("Create a new tool preset configuration.")
imgui.separator()
preset_names = sorted(self.controller.tool_presets.keys())
for i, name in enumerate(preset_names):
is_selected = (self._selected_tool_preset_idx == i)
if imgui.selectable(name, is_selected)[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: {} for cat in models.DEFAULT_TOOL_CATEGORIES}
for cat, tools in preset.categories.items():
self._editing_tool_preset_categories[cat] = copy.deepcopy(tools)
finally:
imgui.end_child()
imgui.same_line()
# Right Column: Edit Area
imgui.begin_child("tool_preset_edit_area", imgui.ImVec2(0, avail.y), 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()
imgui.dummy(imgui.ImVec2(0, 8))
imgui.text("Name:")
_, self._editing_tool_preset_name = imgui.input_text("##edit_tp_name", self._editing_tool_preset_name)
imgui.dummy(imgui.ImVec2(0, 8))
imgui.text("Scope:")
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"
imgui.dummy(imgui.ImVec2(0, 8))
imgui.text("Categories & Tools:")
imgui.begin_child("tp_categories_scroll", imgui.ImVec2(0, 300), True)
try:
for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items():
if imgui.tree_node(cat_name):
if cat_name not in self._editing_tool_preset_categories:
self._editing_tool_preset_categories[cat_name] = []
current_cat_tools = self._editing_tool_preset_categories[cat_name] # List of Tool
for tool_name in default_tools:
# Find existing Tool object in list
tool = next((t for t in current_cat_tools if t.name == tool_name), None)
mode = "disabled" if tool is None else tool.approval
if imgui.radio_button(f"Off##{cat_name}_{tool_name}", mode == "disabled"):
if tool: current_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.same_line()
imgui.text(tool_name)
if tool:
imgui.same_line(250)
imgui.set_next_item_width(100)
_, tool.weight = imgui.slider_int(f"Weight##{cat_name}_{tool_name}", tool.weight, 1, 5)
imgui.same_line()
pb_str = json.dumps(tool.parameter_bias)
imgui.set_next_item_width(150)
ch_pb, pb_new = imgui.input_text(f"Params##{cat_name}_{tool_name}", pb_str)
if ch_pb:
try: tool.parameter_bias = json.loads(pb_new)
except: pass
imgui.tree_pop()
finally:
imgui.end_child()
imgui.separator()
imgui.text_colored(C_SUB, "Bias Profiles")
imgui.begin_child("bias_profiles_area", imgui.ImVec2(0, 300), True)
try:
avail_bias = imgui.get_content_region_avail()
imgui.begin_child("bias_list", imgui.ImVec2(200, avail_bias.y), 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):
is_sel = (self._selected_bias_profile_idx == i)
if imgui.selectable(bname, is_sel)[0]:
self._selected_bias_profile_idx = i
self._editing_bias_profile_name = bname
profile = self.bias_profiles[bname]
self._editing_bias_profile_tool_weights = copy.deepcopy(profile.tool_weights)
self._editing_bias_profile_category_multipliers = copy.deepcopy(profile.category_multipliers)
imgui.end_child()
imgui.same_line()
imgui.begin_child("bias_edit", imgui.ImVec2(0, avail_bias.y), False)
imgui.text("Name:")
_, self._editing_bias_profile_name = imgui.input_text("##b_name", self._editing_bias_profile_name)
imgui.text_colored(C_KEY, "Tool Weights:")
to_remove_tw = []
for tw_name, tw_val in list(self._editing_bias_profile_tool_weights.items()):
imgui.text(f" {tw_name}:")
imgui.same_line(150)
imgui.set_next_item_width(100)
changed, new_val = imgui.slider_int(f"##tw_{tw_name}", tw_val, 1, 10)
if changed: self._editing_bias_profile_tool_weights[tw_name] = new_val
imgui.same_line()
if imgui.button(f"x##rem_tw_{tw_name}"):
to_remove_tw.append(tw_name)
for r in to_remove_tw: del self._editing_bias_profile_tool_weights[r]
# Add Tool Override
imgui.set_next_item_width(150)
if imgui.begin_combo("##add_tw_combo", self._new_bias_tool_name):
for tn in models.AGENT_TOOL_NAMES:
if tn not in self._editing_bias_profile_tool_weights:
if imgui.selectable(tn, tn == self._new_bias_tool_name)[0]:
self._new_bias_tool_name = tn
imgui.end_combo()
imgui.same_line()
if imgui.button("Add Tool Override"):
self._editing_bias_profile_tool_weights[self._new_bias_tool_name] = 5
imgui.dummy(imgui.ImVec2(0, 4))
imgui.text_colored(C_KEY, "Category Multipliers:")
to_remove_cm = []
for cm_name, cm_val in list(self._editing_bias_profile_category_multipliers.items()):
imgui.text(f" {cm_name}:")
imgui.same_line(150)
imgui.set_next_item_width(100)
changed, new_val = imgui.slider_float(f"##cm_{cm_name}", cm_val, 0.1, 5.0, "%.1fx")
if changed: self._editing_bias_profile_category_multipliers[cm_name] = new_val
imgui.same_line()
if imgui.button(f"x##rem_cm_{cm_name}"):
to_remove_cm.append(cm_name)
for r in to_remove_cm: del self._editing_bias_profile_category_multipliers[r]
# Add Category Override
imgui.set_next_item_width(150)
cat_names = sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys()))
if imgui.begin_combo("##add_cm_combo", self._new_bias_category_name):
for cn in cat_names:
if cn not in self._editing_bias_profile_category_multipliers:
if imgui.selectable(cn, cn == self._new_bias_category_name)[0]:
self._new_bias_category_name = cn
imgui.end_combo()
imgui.same_line()
if imgui.button("Add Category Override"):
self._editing_bias_profile_category_multipliers[self._new_bias_category_name] = 1.0
imgui.dummy(imgui.ImVec2(0, 8))
if imgui.button("Save Profile"):
try:
prof = 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(prof, self._editing_tool_preset_scope)
self.ai_status = f"Bias profile '{prof.name}' saved"
except Exception as e:
self.ai_status = f"Error: {e}"
imgui.same_line()
if imgui.button("Delete Profile"):
self.controller._cb_delete_bias_profile(self._editing_bias_profile_name, self._editing_tool_preset_scope)
self.ai_status = f"Bias profile deleted"
imgui.end_child()
finally:
imgui.end_child()
imgui.dummy(imgui.ImVec2(0, 8))
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"Tool preset '{self._editing_tool_preset_name}' saved"
if imgui.is_item_hovered():
imgui.set_tooltip("Save the current tool preset configuration.")
imgui.same_line()
if imgui.button("Delete", imgui.ImVec2(100, 0)):
if self._editing_tool_preset_name.strip():
self.controller._cb_delete_tool_preset(
self._editing_tool_preset_name.strip(),
self._editing_tool_preset_scope
)
self.ai_status = f"Tool preset '{self._editing_tool_preset_name}' deleted"
self._editing_tool_preset_name = ""
self._editing_tool_preset_categories = {}
self._selected_tool_preset_idx = -1
if imgui.is_item_hovered():
imgui.set_tooltip("Delete this tool preset permanently.")
if not is_embedded:
imgui.same_line()
if imgui.button("Close", imgui.ImVec2(100, 0)):
self.show_tool_preset_manager_window = False
finally:
imgui.end_child()
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
@@ -1011,233 +1243,7 @@ class App:
return
try:
avail = imgui.get_content_region_avail()
# Left Column: Listbox
imgui.begin_child("tool_preset_list_area", imgui.ImVec2(250, avail.y), True)
try:
if imgui.button("New Tool 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
if imgui.is_item_hovered():
imgui.set_tooltip("Create a new tool preset configuration.")
imgui.separator()
preset_names = sorted(self.controller.tool_presets.keys())
for i, name in enumerate(preset_names):
is_selected = (self._selected_tool_preset_idx == i)
if imgui.selectable(name, is_selected)[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: {} for cat in models.DEFAULT_TOOL_CATEGORIES}
for cat, tools in preset.categories.items():
self._editing_tool_preset_categories[cat] = copy.deepcopy(tools)
finally:
imgui.end_child()
imgui.same_line()
# Right Column: Edit Area
imgui.begin_child("tool_preset_edit_area", imgui.ImVec2(0, avail.y), 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()
imgui.dummy(imgui.ImVec2(0, 8))
imgui.text("Name:")
_, self._editing_tool_preset_name = imgui.input_text("##edit_tp_name", self._editing_tool_preset_name)
imgui.dummy(imgui.ImVec2(0, 8))
imgui.text("Scope:")
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"
imgui.dummy(imgui.ImVec2(0, 8))
imgui.text("Categories & Tools:")
imgui.begin_child("tp_categories_scroll", imgui.ImVec2(0, 300), True)
try:
for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items():
if imgui.tree_node(cat_name):
if cat_name not in self._editing_tool_preset_categories:
self._editing_tool_preset_categories[cat_name] = []
current_cat_tools = self._editing_tool_preset_categories[cat_name] # List of Tool
for tool_name in default_tools:
# Find existing Tool object in list
tool = next((t for t in current_cat_tools if t.name == tool_name), None)
mode = "disabled" if tool is None else tool.approval
if imgui.radio_button(f"Off##{cat_name}_{tool_name}", mode == "disabled"):
if tool: current_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.same_line()
imgui.text(tool_name)
if tool:
imgui.same_line(250)
imgui.set_next_item_width(100)
_, tool.weight = imgui.slider_int(f"Weight##{cat_name}_{tool_name}", tool.weight, 1, 5)
imgui.same_line()
pb_str = json.dumps(tool.parameter_bias)
imgui.set_next_item_width(150)
ch_pb, pb_new = imgui.input_text(f"Params##{cat_name}_{tool_name}", pb_str)
if ch_pb:
try: tool.parameter_bias = json.loads(pb_new)
except: pass
imgui.tree_pop()
finally:
imgui.end_child()
imgui.separator()
imgui.text_colored(C_SUB, "Bias Profiles")
imgui.begin_child("bias_profiles_area", imgui.ImVec2(0, 300), True)
try:
avail_bias = imgui.get_content_region_avail()
imgui.begin_child("bias_list", imgui.ImVec2(200, avail_bias.y), 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):
is_sel = (self._selected_bias_profile_idx == i)
if imgui.selectable(bname, is_sel)[0]:
self._selected_bias_profile_idx = i
self._editing_bias_profile_name = bname
profile = self.bias_profiles[bname]
self._editing_bias_profile_tool_weights = copy.deepcopy(profile.tool_weights)
self._editing_bias_profile_category_multipliers = copy.deepcopy(profile.category_multipliers)
imgui.end_child()
imgui.same_line()
imgui.begin_child("bias_edit", imgui.ImVec2(0, avail_bias.y), False)
imgui.text("Name:")
_, self._editing_bias_profile_name = imgui.input_text("##b_name", self._editing_bias_profile_name)
imgui.text_colored(C_KEY, "Tool Weights:")
to_remove_tw = []
for tw_name, tw_val in list(self._editing_bias_profile_tool_weights.items()):
imgui.text(f" {tw_name}:")
imgui.same_line(150)
imgui.set_next_item_width(100)
changed, new_val = imgui.slider_int(f"##tw_{tw_name}", tw_val, 1, 10)
if changed: self._editing_bias_profile_tool_weights[tw_name] = new_val
imgui.same_line()
if imgui.button(f"x##rem_tw_{tw_name}"):
to_remove_tw.append(tw_name)
for r in to_remove_tw: del self._editing_bias_profile_tool_weights[r]
# Add Tool Override
imgui.set_next_item_width(150)
if imgui.begin_combo("##add_tw_combo", self._new_bias_tool_name):
for tn in models.AGENT_TOOL_NAMES:
if tn not in self._editing_bias_profile_tool_weights:
if imgui.selectable(tn, tn == self._new_bias_tool_name)[0]:
self._new_bias_tool_name = tn
imgui.end_combo()
imgui.same_line()
if imgui.button("Add Tool Override"):
self._editing_bias_profile_tool_weights[self._new_bias_tool_name] = 5
imgui.dummy(imgui.ImVec2(0, 4))
imgui.text_colored(C_KEY, "Category Multipliers:")
to_remove_cm = []
for cm_name, cm_val in list(self._editing_bias_profile_category_multipliers.items()):
imgui.text(f" {cm_name}:")
imgui.same_line(150)
imgui.set_next_item_width(100)
changed, new_val = imgui.slider_float(f"##cm_{cm_name}", cm_val, 0.1, 5.0, "%.1fx")
if changed: self._editing_bias_profile_category_multipliers[cm_name] = new_val
imgui.same_line()
if imgui.button(f"x##rem_cm_{cm_name}"):
to_remove_cm.append(cm_name)
for r in to_remove_cm: del self._editing_bias_profile_category_multipliers[r]
# Add Category Override
imgui.set_next_item_width(150)
cat_names = sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys()))
if imgui.begin_combo("##add_cm_combo", self._new_bias_category_name):
for cn in cat_names:
if cn not in self._editing_bias_profile_category_multipliers:
if imgui.selectable(cn, cn == self._new_bias_category_name)[0]:
self._new_bias_category_name = cn
imgui.end_combo()
imgui.same_line()
if imgui.button("Add Category Override"):
self._editing_bias_profile_category_multipliers[self._new_bias_category_name] = 1.0
imgui.dummy(imgui.ImVec2(0, 8))
if imgui.button("Save Profile"):
try:
prof = 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(prof, self._editing_tool_preset_scope)
self.ai_status = f"Bias profile '{prof.name}' saved"
except Exception as e:
self.ai_status = f"Error: {e}"
imgui.same_line()
if imgui.button("Delete Profile"):
self.controller._cb_delete_bias_profile(self._editing_bias_profile_name, self._editing_tool_preset_scope)
self.ai_status = f"Bias profile deleted"
imgui.end_child()
finally:
imgui.end_child()
imgui.dummy(imgui.ImVec2(0, 8))
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"Tool preset '{self._editing_tool_preset_name}' saved"
if imgui.is_item_hovered():
imgui.set_tooltip("Save the current tool preset configuration.")
imgui.same_line()
if imgui.button("Delete", imgui.ImVec2(100, 0)):
if self._editing_tool_preset_name.strip():
self.controller._cb_delete_tool_preset(
self._editing_tool_preset_name.strip(),
self._editing_tool_preset_scope
)
self.ai_status = f"Tool preset '{self._editing_tool_preset_name}' deleted"
self._editing_tool_preset_name = ""
self._editing_tool_preset_categories = {}
self._selected_tool_preset_idx = -1
if imgui.is_item_hovered():
imgui.set_tooltip("Delete this tool preset permanently.")
if not is_embedded:
imgui.same_line()
if imgui.button("Close", imgui.ImVec2(100, 0)):
self.show_tool_preset_manager_window = False
finally:
imgui.end_child()
self._render_tool_preset_manager_content(is_embedded=is_embedded)
finally:
if not is_embedded:
imgui.end()
@@ -1258,14 +1264,15 @@ class App:
try:
if imgui.button("New Persona", imgui.ImVec2(-1, 0)):
self._editing_persona_name = ""
self._editing_persona_provider = self.current_provider
self._editing_persona_model = self.current_model
self._editing_persona_system_prompt = ""
self._editing_persona_temperature = 0.7
self._editing_persona_max_tokens = 4096
self._editing_persona_tool_preset_id = ""
self._editing_persona_bias_profile_id = ""
self._editing_persona_preferred_models_list = []
self._editing_persona_preferred_models_list = [{
"provider": self.current_provider,
"model": self.current_model,
"temperature": 0.7,
"max_output_tokens": 4096
}]
self._editing_persona_scope = "project"
self._editing_persona_is_new = True
imgui.separator()
@@ -1275,13 +1282,10 @@ class App:
if imgui.selectable(name, is_sel)[0]:
p = personas[name]
self._editing_persona_name = p.name
self._editing_persona_provider = p.provider or ""
self._editing_persona_model = p.model or ""
self._editing_persona_system_prompt = p.system_prompt or ""
self._editing_persona_temperature = p.temperature if p.temperature is not None else 0.7
self._editing_persona_max_tokens = p.max_output_tokens if p.max_output_tokens is not None else 4096
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
@@ -1293,7 +1297,7 @@ class App:
# Right Pane: Editor
imgui.begin_child("persona_edit_area", imgui.ImVec2(0, avail.y), False)
try:
header = "New Persona" if self._editing_persona_is_new else f"Editing Persona: {self._editing_persona_name}"
header = "New Persona" if getattr(self, '_editing_persona_is_new', True) else f"Editing Persona: {self._editing_persona_name}"
imgui.text_colored(C_IN, header)
imgui.separator()
@@ -1310,38 +1314,59 @@ class App:
imgui.separator()
imgui.text("Default Provider/Model (used if Preferred Models list is empty):")
imgui.text("Provider:")
imgui.same_line()
imgui.text("Preferred Models:")
providers = self.controller.PROVIDERS
p_idx = providers.index(self._editing_persona_provider) + 1 if self._editing_persona_provider in providers else 0
imgui.push_item_width(150)
_, p_idx = imgui.combo("##pprov", p_idx, ["None"] + providers)
self._editing_persona_provider = providers[p_idx - 1] if p_idx > 0 else ""
imgui.pop_item_width()
imgui.begin_child("pref_models_list", imgui.ImVec2(0, 150), True)
to_remove = []
for i, entry in enumerate(self._editing_persona_preferred_models_list):
imgui.push_id(f"pref_model_{i}")
imgui.text(f"{i+1}.")
imgui.same_line()
imgui.same_line()
imgui.text("Model:")
imgui.same_line()
all_models = self.controller.all_available_models.get(self._editing_persona_provider, [])
if not all_models and self._editing_persona_model:
all_models = [self._editing_persona_model]
m_idx = all_models.index(self._editing_persona_model) + 1 if self._editing_persona_model in all_models else 0
imgui.push_item_width(200)
_, m_idx = imgui.combo("##pmodel", m_idx, ["None"] + all_models)
self._editing_persona_model = all_models[m_idx - 1] if m_idx > 0 else ""
imgui.pop_item_width()
imgui.set_next_item_width(120)
prov = entry.get("provider", "")
p_idx = providers.index(prov) + 1 if prov in providers else 0
changed_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers)
if changed_p:
entry["provider"] = providers[p_idx-1] if p_idx > 0 else ""
imgui.text("Temp:")
imgui.same_line()
imgui.set_next_item_width(100)
_, self._editing_persona_temperature = imgui.slider_float("##ptemp", self._editing_persona_temperature, 0.0, 2.0)
imgui.same_line()
imgui.set_next_item_width(200)
curr_prov = entry.get("provider", "")
m_list = self.controller.all_available_models.get(curr_prov, [])
model = entry.get("model", "")
m_idx = m_list.index(model) + 1 if model in m_list else 0
changed_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list)
if changed_m:
entry["model"] = m_list[m_idx-1] if m_idx > 0 else ""
imgui.same_line()
imgui.text("Max Tok:")
imgui.same_line()
imgui.set_next_item_width(100)
_, self._editing_persona_max_tokens = imgui.input_int("##pmaxt", self._editing_persona_max_tokens)
imgui.same_line()
imgui.text("T:")
imgui.same_line()
imgui.set_next_item_width(60)
_, entry["temperature"] = imgui.input_float("##temp", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f")
imgui.same_line()
imgui.text("Max:")
imgui.same_line()
imgui.set_next_item_width(80)
_, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096))
imgui.same_line()
if imgui.button("x"):
to_remove.append(i)
imgui.pop_id()
for i in reversed(to_remove):
self._editing_persona_preferred_models_list.pop(i)
imgui.end_child()
if imgui.button("Add Preferred Model"):
self._editing_persona_preferred_models_list.append({
"provider": self.current_provider,
"model": self.current_model,
"temperature": 0.7,
"max_output_tokens": 4096
})
imgui.separator()
imgui.text("Tool Preset:")
@@ -1363,68 +1388,14 @@ class App:
self._editing_persona_bias_profile_id = bias_names[b_idx] if b_idx > 0 else ""
imgui.pop_item_width()
imgui.separator()
imgui.text("Preferred Models List:")
imgui.begin_child("pref_models_list", imgui.ImVec2(0, 200), True)
to_remove = []
for i, entry in enumerate(self._editing_persona_preferred_models_list):
imgui.push_id(f"pref_model_{i}")
imgui.text(f"{i+1}.")
imgui.same_line()
# Provider
imgui.set_next_item_width(120)
prov = entry.get("provider", "")
p_idx = providers.index(prov) + 1 if prov in providers else 0
changed_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers)
if changed_p:
entry["provider"] = providers[p_idx-1] if p_idx > 0 else ""
imgui.same_line()
# Model
imgui.set_next_item_width(200)
curr_prov = entry.get("provider", "")
m_list = self.controller.all_available_models.get(curr_prov, [])
model = entry.get("model", "")
m_idx = m_list.index(model) + 1 if model in m_list else 0
changed_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list)
if changed_m:
entry["model"] = m_list[m_idx-1] if m_idx > 0 else ""
imgui.same_line()
imgui.text("T:")
imgui.same_line()
# Temp
imgui.set_next_item_width(60)
_, entry["temperature"] = imgui.input_float("##temp", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f")
imgui.same_line()
imgui.text("M:")
imgui.same_line()
# MaxTok
imgui.set_next_item_width(80)
_, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096))
imgui.same_line()
if imgui.button("x"):
to_remove.append(i)
imgui.pop_id()
for i in reversed(to_remove):
self._editing_persona_preferred_models_list.pop(i)
imgui.end_child()
if imgui.button("Add Preferred Model"):
self._editing_persona_preferred_models_list.append({
"provider": self._editing_persona_provider or self.current_provider,
"model": self._editing_persona_model or self.current_model,
"temperature": self._editing_persona_temperature,
"max_output_tokens": self._editing_persona_max_tokens
})
if imgui.collapsing_header("Manage Tool Presets & Biases"):
imgui.begin_child("tool_preset_embedded", imgui.ImVec2(0, 300), True)
self._render_tool_preset_manager_content(is_embedded=True)
imgui.end_child()
imgui.separator()
imgui.text("System Prompt:")
# Load Prompt Preset
imgui.text("Load from Preset:")
imgui.same_line()
prompt_presets = ["Select..."] + sorted(self.controller.presets.keys())
@@ -1439,27 +1410,21 @@ class App:
if pname in self.controller.presets:
p = self.controller.presets[pname]
self._editing_persona_system_prompt = p.system_prompt
if p.temperature is not None: self._editing_persona_temperature = p.temperature
if p.max_output_tokens is not None: self._editing_persona_max_tokens = p.max_output_tokens
self._load_preset_idx = 0
if imgui.collapsing_header("Manage Prompt Presets"):
imgui.begin_child("prompt_preset_embedded", imgui.ImVec2(0, 200), True)
self._render_preset_manager_content(is_embedded=True)
imgui.end_child()
_, self._editing_persona_system_prompt = imgui.input_text_multiline("##pprompt", self._editing_persona_system_prompt, imgui.ImVec2(-1, 150))
imgui.separator()
if imgui.button("Save Persona", imgui.ImVec2(120, 0)):
if self._editing_persona_name.strip():
try:
# If preferred list is empty, maybe we should add the default one?
# The models.Persona properties already handle returning None/default if list is empty.
# But for saving, we should probably ensure the list isn't empty if we have values.
import copy
save_models = copy.deepcopy(self._editing_persona_preferred_models_list)
if not save_models and self._editing_persona_model:
save_models.append({
"provider": self._editing_persona_provider,
"model": self._editing_persona_model,
"temperature": self._editing_persona_temperature,
"max_output_tokens": self._editing_persona_max_tokens
})
persona = models.Persona(
name=self._editing_persona_name.strip(),
@@ -1479,7 +1444,7 @@ class App:
imgui.same_line()
if imgui.button("Delete", imgui.ImVec2(100, 0)):
if not self._editing_persona_is_new and self._editing_persona_name:
if not getattr(self, '_editing_persona_is_new', True) and self._editing_persona_name:
self.controller._cb_delete_persona(self._editing_persona_name, self._editing_persona_scope)
self.ai_status = f"Deleted Persona: {self._editing_persona_name}"
self._editing_persona_name = ""
@@ -1494,8 +1459,6 @@ class App:
finally:
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)