chore(conductor): Mark track 'Saved System Prompt Presets' as complete
This commit is contained in:
@@ -85,7 +85,7 @@ This file tracks all major tracks for the project. Each track has its own detail
|
|||||||
|
|
||||||
### Manual UX Controls
|
### Manual UX Controls
|
||||||
|
|
||||||
1. [~] **Track: Saved System Prompt Presets**
|
1. [x] **Track: Saved System Prompt Presets**
|
||||||
*Link: [./tracks/saved_presets_20260308/](./tracks/saved_presets_20260308/)*
|
*Link: [./tracks/saved_presets_20260308/](./tracks/saved_presets_20260308/)*
|
||||||
*Goal: Ability to have saved presets for global and project system prompts. Includes full AI profiles with temperature and top_p settings, managed via a dedicated GUI modal.*
|
*Goal: Ability to have saved presets for global and project system prompts. Includes full AI profiles with temperature and top_p settings, managed via a dedicated GUI modal.*
|
||||||
|
|
||||||
|
|||||||
@@ -28,19 +28,19 @@
|
|||||||
- [x] Task: Conductor - User Manual Verification 'Phase 2: UI: Settings Integration' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 2: UI: Settings Integration' (Protocol in workflow.md)
|
||||||
|
|
||||||
## Phase 3: UI: Preset Manager Modal
|
## Phase 3: UI: Preset Manager Modal
|
||||||
- [~] Task: Create the `PresetManagerModal` in `gui_2.py` (or a separate module).
|
- [x] Task: Create the `PresetManagerModal` in `gui_2.py` (or a separate module).
|
||||||
- [ ] Implement a list view of all presets (global and project).
|
- [x] Implement a list view of all presets (global and project).
|
||||||
- [ ] Implement "Add", "Edit", and "Delete" functionality.
|
- [x] Implement "Add", "Edit", and "Delete" functionality.
|
||||||
- [ ] Ensure validation for unique names.
|
- [x] Ensure validation for unique names.
|
||||||
- [ ] Task: Add a button to open the manager modal from the settings panels.
|
- [x] Task: Add a button to open the manager modal from the settings panels.
|
||||||
- [ ] Task: Write integration tests for the Preset Manager using `live_gui`.
|
- [x] Task: Write integration tests for the Preset Manager using `live_gui`.
|
||||||
- [ ] Verify creating a new preset adds it to the list and dropdown.
|
- [x] Verify creating a new preset adds it to the list and dropdown.
|
||||||
- [ ] Verify editing an existing preset updates it correctly.
|
- [x] Verify editing an existing preset updates it correctly.
|
||||||
- [ ] Verify deleting a preset removes it from the list and dropdown.
|
- [x] Verify deleting a preset removes it from the list and dropdown.
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 3: UI: Preset Manager Modal' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 3: UI: Preset Manager Modal' (Protocol in workflow.md)
|
||||||
|
|
||||||
## Phase 4: Final Integration & Polish
|
## Phase 4: Final Integration & Polish
|
||||||
- [ ] Task: Ensure robust error handling for missing or malformed `.toml` files.
|
- [x] Task: Ensure robust error handling for missing or malformed `.toml` files.
|
||||||
- [ ] Task: Final UI polish (spacing, icons, tooltips).
|
- [x] Task: Final UI polish (spacing, icons, tooltips).
|
||||||
- [ ] Task: Run full suite of relevant tests.
|
- [x] Task: Run full suite of relevant tests.
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 4: Final Integration & Polish' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 4: Final Integration & Polish' (Protocol in workflow.md)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ separate_message_panel = false
|
|||||||
separate_response_panel = false
|
separate_response_panel = false
|
||||||
separate_tool_calls_panel = false
|
separate_tool_calls_panel = false
|
||||||
bg_shader_enabled = true
|
bg_shader_enabled = true
|
||||||
|
crt_filter_enabled = false
|
||||||
|
|
||||||
[gui.show_windows]
|
[gui.show_windows]
|
||||||
"Context Hub" = true
|
"Context Hub" = true
|
||||||
@@ -39,15 +40,15 @@ Response = false
|
|||||||
"Tool Calls" = false
|
"Tool Calls" = false
|
||||||
Theme = true
|
Theme = true
|
||||||
"Log Management" = true
|
"Log Management" = true
|
||||||
Diagnostics = false
|
Diagnostics = true
|
||||||
|
|
||||||
[theme]
|
[theme]
|
||||||
palette = "Nord Dark"
|
palette = "Nord Dark"
|
||||||
font_path = "C:/projects/manual_slop/assets/fonts/Inter-Regular.ttf"
|
font_path = "C:/projects/manual_slop/assets/fonts/Inter-Regular.ttf"
|
||||||
font_size = 12.0
|
font_size = 12.0
|
||||||
scale = 1.25
|
scale = 1.2999999523162842
|
||||||
transparency = 0.6499999761581421
|
transparency = 0.550000011920929
|
||||||
child_transparency = 0.7200000286102295
|
child_transparency = 0.6399999856948853
|
||||||
|
|
||||||
[mma]
|
[mma]
|
||||||
max_workers = 4
|
max_workers = 4
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ Collapsed=0
|
|||||||
DockId=0x00000001,0
|
DockId=0x00000001,0
|
||||||
|
|
||||||
[Window][Message]
|
[Window][Message]
|
||||||
Pos=1005,726
|
Pos=1890,1100
|
||||||
Size=1065,548
|
Size=1065,548
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Response]
|
[Window][Response]
|
||||||
Pos=1009,1217
|
Pos=2086,1780
|
||||||
Size=1036,351
|
Size=1036,351
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ Collapsed=0
|
|||||||
DockId=0xAFC85805,2
|
DockId=0xAFC85805,2
|
||||||
|
|
||||||
[Window][Theme]
|
[Window][Theme]
|
||||||
Pos=0,26
|
Pos=0,30
|
||||||
Size=848,827
|
Size=762,943
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,1
|
DockId=0x00000005,1
|
||||||
|
|
||||||
@@ -84,14 +84,14 @@ Size=900,700
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Diagnostics]
|
[Window][Diagnostics]
|
||||||
Pos=2622,27
|
Pos=2674,26
|
||||||
Size=1218,2110
|
Size=1166,1678
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,1
|
DockId=0x0000000C,2
|
||||||
|
|
||||||
[Window][Context Hub]
|
[Window][Context Hub]
|
||||||
Pos=0,26
|
Pos=0,30
|
||||||
Size=848,827
|
Size=762,943
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
@@ -102,26 +102,26 @@ Collapsed=0
|
|||||||
DockId=0x0000000D,0
|
DockId=0x0000000D,0
|
||||||
|
|
||||||
[Window][Discussion Hub]
|
[Window][Discussion Hub]
|
||||||
Pos=1736,26
|
Pos=1668,30
|
||||||
Size=884,1463
|
Size=1004,2107
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000013,0
|
DockId=0x00000013,0
|
||||||
|
|
||||||
[Window][Operations Hub]
|
[Window][Operations Hub]
|
||||||
Pos=850,26
|
Pos=764,30
|
||||||
Size=884,1463
|
Size=902,2107
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000012,0
|
DockId=0x00000012,0
|
||||||
|
|
||||||
[Window][Files & Media]
|
[Window][Files & Media]
|
||||||
Pos=0,1885
|
Pos=0,1849
|
||||||
Size=848,252
|
Size=762,288
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][AI Settings]
|
[Window][AI Settings]
|
||||||
Pos=0,855
|
Pos=0,975
|
||||||
Size=848,1028
|
Size=762,872
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000001,0
|
||||||
|
|
||||||
@@ -131,16 +131,16 @@ Size=416,325
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][MMA Dashboard]
|
[Window][MMA Dashboard]
|
||||||
Pos=2622,26
|
Pos=2674,30
|
||||||
Size=1218,2111
|
Size=1166,1675
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,1
|
DockId=0x0000000C,0
|
||||||
|
|
||||||
[Window][Log Management]
|
[Window][Log Management]
|
||||||
Pos=2622,26
|
Pos=2674,30
|
||||||
Size=1218,2111
|
Size=1166,1675
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,0
|
DockId=0x0000000C,1
|
||||||
|
|
||||||
[Window][Track Proposal]
|
[Window][Track Proposal]
|
||||||
Pos=709,326
|
Pos=709,326
|
||||||
@@ -148,28 +148,28 @@ Size=262,209
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Tier 1: Strategy]
|
[Window][Tier 1: Strategy]
|
||||||
Pos=850,1491
|
Pos=2674,1707
|
||||||
Size=306,646
|
Size=1166,430
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000014,0
|
DockId=0x0000000F,0
|
||||||
|
|
||||||
[Window][Tier 2: Tech Lead]
|
[Window][Tier 2: Tech Lead]
|
||||||
Pos=1158,1491
|
Pos=2674,1707
|
||||||
Size=550,646
|
Size=1166,430
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000016,0
|
DockId=0x0000000F,1
|
||||||
|
|
||||||
[Window][Tier 4: QA]
|
[Window][Tier 4: QA]
|
||||||
Pos=2215,1491
|
Pos=2674,1707
|
||||||
Size=405,646
|
Size=1166,430
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000019,0
|
DockId=0x0000000F,2
|
||||||
|
|
||||||
[Window][Tier 3: Workers]
|
[Window][Tier 3: Workers]
|
||||||
Pos=1710,1491
|
Pos=764,30
|
||||||
Size=503,646
|
Size=902,2107
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000018,0
|
DockId=0x00000012,1
|
||||||
|
|
||||||
[Window][Approve PowerShell Command]
|
[Window][Approve PowerShell Command]
|
||||||
Pos=649,435
|
Pos=649,435
|
||||||
@@ -352,11 +352,11 @@ Column 3 Width=20
|
|||||||
Column 4 Weight=1.0000
|
Column 4 Weight=1.0000
|
||||||
|
|
||||||
[Table][0x2A6000B6,4]
|
[Table][0x2A6000B6,4]
|
||||||
RefScale=14
|
RefScale=18
|
||||||
Column 0 Width=42
|
Column 0 Width=54
|
||||||
Column 1 Width=64
|
Column 1 Width=82
|
||||||
Column 2 Weight=1.0000
|
Column 2 Weight=1.0000
|
||||||
Column 3 Width=107
|
Column 3 Width=137
|
||||||
|
|
||||||
[Table][0x8BCC69C7,6]
|
[Table][0x8BCC69C7,6]
|
||||||
RefScale=13
|
RefScale=13
|
||||||
@@ -375,11 +375,11 @@ Column 2 Weight=1.0000
|
|||||||
Column 3 Width=106
|
Column 3 Width=106
|
||||||
|
|
||||||
[Table][0x2C515046,4]
|
[Table][0x2C515046,4]
|
||||||
RefScale=14
|
RefScale=18
|
||||||
Column 0 Width=61
|
Column 0 Width=58
|
||||||
Column 1 Weight=1.0000
|
Column 1 Weight=1.0000
|
||||||
Column 2 Width=161
|
Column 2 Width=138
|
||||||
Column 3 Width=42
|
Column 3 Width=54
|
||||||
|
|
||||||
[Table][0xD99F45C5,4]
|
[Table][0xD99F45C5,4]
|
||||||
Column 0 Sort=0v
|
Column 0 Sort=0v
|
||||||
@@ -400,36 +400,30 @@ Column 1 Width=100
|
|||||||
Column 2 Weight=1.0000
|
Column 2 Weight=1.0000
|
||||||
|
|
||||||
[Table][0xA02D8C87,3]
|
[Table][0xA02D8C87,3]
|
||||||
RefScale=14
|
RefScale=16
|
||||||
Column 0 Width=160
|
Column 0 Width=182
|
||||||
Column 1 Width=106
|
Column 1 Width=120
|
||||||
Column 2 Weight=1.0000
|
Column 2 Weight=1.0000
|
||||||
|
|
||||||
[Docking][Data]
|
[Docking][Data]
|
||||||
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,26 Size=3840,2111 Split=X
|
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,30 Size=3840,2107 Split=X
|
||||||
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2620,1183 Split=X
|
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=2672,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=848,858 Split=Y Selected=0x8CA2375C
|
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=762,858 Split=Y Selected=0x8CA2375C
|
||||||
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=295,827 Selected=0x8CA2375C
|
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=295,943 Selected=0xF4139CA2
|
||||||
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=295,1286 Split=Y Selected=0x7BD57D6A
|
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=295,1168 Split=Y Selected=0x7BD57D6A
|
||||||
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=824,1032 CentralNode=1 Selected=0x7BD57D6A
|
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=824,872 CentralNode=1 Selected=0x7BD57D6A
|
||||||
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=824,252 Selected=0x1DCB2623
|
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=824,288 Selected=0x1DCB2623
|
||||||
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1770,858 Split=Y Selected=0x418C7449
|
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1908,858 Split=X Selected=0x418C7449
|
||||||
DockNode ID=0x00000010 Parent=0x0000000E SizeRef=868,1466 Split=X Selected=0x418C7449
|
DockNode ID=0x00000012 Parent=0x0000000E SizeRef=902,402 Selected=0x418C7449
|
||||||
DockNode ID=0x00000012 Parent=0x00000010 SizeRef=884,402 Selected=0x418C7449
|
DockNode ID=0x00000013 Parent=0x0000000E SizeRef=1004,402 Selected=0x6F2B5B04
|
||||||
DockNode ID=0x00000013 Parent=0x00000010 SizeRef=884,402 Selected=0x6F2B5B04
|
|
||||||
DockNode ID=0x00000011 Parent=0x0000000E SizeRef=868,647 Split=X Selected=0x5CDB7A4B
|
|
||||||
DockNode ID=0x00000014 Parent=0x00000011 SizeRef=306,837 Selected=0xBB346584
|
|
||||||
DockNode ID=0x00000015 Parent=0x00000011 SizeRef=1462,837 Split=X Selected=0x5CDB7A4B
|
|
||||||
DockNode ID=0x00000016 Parent=0x00000015 SizeRef=550,837 Selected=0x390E7942
|
|
||||||
DockNode ID=0x00000017 Parent=0x00000015 SizeRef=910,837 Split=X Selected=0x655BC6E9
|
|
||||||
DockNode ID=0x00000018 Parent=0x00000017 SizeRef=503,874 Selected=0x655BC6E9
|
|
||||||
DockNode ID=0x00000019 Parent=0x00000017 SizeRef=405,874 Selected=0x5CDB7A4B
|
|
||||||
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
|
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
|
||||||
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1218,1183 Selected=0x2C0206CE
|
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=1166,1183 Split=Y Selected=0x3AEC3498
|
||||||
|
DockNode ID=0x0000000C Parent=0x00000004 SizeRef=1074,1679 Selected=0x2C0206CE
|
||||||
|
DockNode ID=0x0000000F Parent=0x00000004 SizeRef=1074,431 Selected=0x5CDB7A4B
|
||||||
|
|
||||||
;;;<<<Layout_655921752_Default>>>;;;
|
;;;<<<Layout_655921752_Default>>>;;;
|
||||||
;;;<<<HelloImGui_Misc>>>;;;
|
;;;<<<HelloImGui_Misc>>>;;;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -455,6 +455,8 @@ class AppController:
|
|||||||
'_test_callback_func_write_to_file': self._test_callback_func_write_to_file,
|
'_test_callback_func_write_to_file': self._test_callback_func_write_to_file,
|
||||||
'_set_env_var': lambda k, v: os.environ.update({k: v}),
|
'_set_env_var': lambda k, v: os.environ.update({k: v}),
|
||||||
'_apply_preset': self._apply_preset,
|
'_apply_preset': self._apply_preset,
|
||||||
|
'_cb_save_preset': self._cb_save_preset,
|
||||||
|
'_cb_delete_preset': self._cb_delete_preset,
|
||||||
'_switch_project': self._switch_project,
|
'_switch_project': self._switch_project,
|
||||||
'_refresh_from_project': self._refresh_from_project
|
'_refresh_from_project': self._refresh_from_project
|
||||||
}
|
}
|
||||||
@@ -1790,6 +1792,21 @@ class AppController:
|
|||||||
if preset.max_output_tokens is not None:
|
if preset.max_output_tokens is not None:
|
||||||
self.max_tokens = preset.max_output_tokens
|
self.max_tokens = preset.max_output_tokens
|
||||||
|
|
||||||
|
def _cb_save_preset(self, name, content, temp, top_p, max_tok, scope):
|
||||||
|
preset = models.Preset(
|
||||||
|
name=name,
|
||||||
|
system_prompt=content,
|
||||||
|
temperature=temp,
|
||||||
|
top_p=top_p,
|
||||||
|
max_output_tokens=max_tok
|
||||||
|
)
|
||||||
|
self.preset_manager.save_preset(preset, scope)
|
||||||
|
self.presets = self.preset_manager.load_all()
|
||||||
|
|
||||||
|
def _cb_delete_preset(self, name, scope):
|
||||||
|
self.preset_manager.delete_preset(name, scope)
|
||||||
|
self.presets = self.preset_manager.load_all()
|
||||||
|
|
||||||
def _cb_load_track(self, track_id: str) -> None:
|
def _cb_load_track(self, track_id: str) -> None:
|
||||||
state = project_manager.load_track_state(track_id, self.ui_files_base_dir)
|
state = project_manager.load_track_state(track_id, self.ui_files_base_dir)
|
||||||
if state:
|
if state:
|
||||||
|
|||||||
32
src/gui_2.py
32
src/gui_2.py
@@ -889,6 +889,9 @@ class App:
|
|||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
imgui.begin_child("preset_edit_area", imgui.ImVec2(500, 600), False)
|
imgui.begin_child("preset_edit_area", imgui.ImVec2(500, 600), False)
|
||||||
|
p_name = self._editing_preset_name or "(New Preset)"
|
||||||
|
imgui.text_colored(C_IN, f"Editing Preset: {p_name}")
|
||||||
|
imgui.separator()
|
||||||
imgui.text("Name:")
|
imgui.text("Name:")
|
||||||
_, self._editing_preset_name = imgui.input_text("##edit_name", self._editing_preset_name)
|
_, self._editing_preset_name = imgui.input_text("##edit_name", self._editing_preset_name)
|
||||||
imgui.text("Scope:")
|
imgui.text("Scope:")
|
||||||
@@ -909,27 +912,27 @@ class App:
|
|||||||
|
|
||||||
if imgui.button("Save", imgui.ImVec2(120, 0)):
|
if imgui.button("Save", imgui.ImVec2(120, 0)):
|
||||||
if self._editing_preset_name.strip():
|
if self._editing_preset_name.strip():
|
||||||
new_p = models.Preset(
|
self.controller._cb_save_preset(
|
||||||
name=self._editing_preset_name.strip(),
|
self._editing_preset_name.strip(),
|
||||||
system_prompt=self._editing_preset_content,
|
self._editing_preset_content,
|
||||||
temperature=self._editing_preset_temperature,
|
self._editing_preset_temperature,
|
||||||
top_p=self._editing_preset_top_p,
|
self._editing_preset_top_p,
|
||||||
max_output_tokens=self._editing_preset_max_output_tokens
|
self._editing_preset_max_output_tokens,
|
||||||
|
self._editing_preset_scope
|
||||||
)
|
)
|
||||||
self.controller.preset_manager.save_preset(new_p, self._editing_preset_scope)
|
self.ai_status = f"Preset '{self._editing_preset_name.strip()}' saved to {self._editing_preset_scope}"
|
||||||
self.controller.presets = self.controller.preset_manager.load_all()
|
imgui.set_item_tooltip("Save the current preset settings")
|
||||||
self.ai_status = f"Preset '{new_p.name}' saved to {self._editing_preset_scope}"
|
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("Delete", imgui.ImVec2(120, 0)):
|
if imgui.button("Delete", imgui.ImVec2(120, 0)):
|
||||||
if self._editing_preset_name.strip():
|
if self._editing_preset_name.strip():
|
||||||
try:
|
try:
|
||||||
self.controller.preset_manager.delete_preset(self._editing_preset_name.strip(), self._editing_preset_scope)
|
self.controller._cb_delete_preset(self._editing_preset_name.strip(), self._editing_preset_scope)
|
||||||
self.controller.presets = self.controller.preset_manager.load_all()
|
|
||||||
self.ai_status = f"Preset '{self._editing_preset_name}' deleted from {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_name = ""
|
||||||
self._editing_preset_content = ""
|
self._editing_preset_content = ""
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.ai_status = f"Error deleting: {e}"
|
self.ai_status = f"Error deleting: {e}"
|
||||||
|
imgui.set_item_tooltip("Delete the selected preset")
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("Close", imgui.ImVec2(120, 0)):
|
if imgui.button("Close", imgui.ImVec2(120, 0)):
|
||||||
self.show_preset_manager_modal = False
|
self.show_preset_manager_modal = False
|
||||||
@@ -2925,9 +2928,10 @@ def hello():
|
|||||||
if is_sel:
|
if is_sel:
|
||||||
imgui.set_item_default_focus()
|
imgui.set_item_default_focus()
|
||||||
imgui.end_combo()
|
imgui.end_combo()
|
||||||
imgui.same_line()
|
imgui.same_line(0, 8)
|
||||||
if imgui.button("Manage Presets##global"):
|
if imgui.button("Manage Presets##global"):
|
||||||
self.show_preset_manager_modal = True
|
self.show_preset_manager_modal = True
|
||||||
|
imgui.set_item_tooltip("Open preset management modal")
|
||||||
ch, self.ui_global_system_prompt = imgui.input_text_multiline("##gsp", self.ui_global_system_prompt, imgui.ImVec2(-1, 100))
|
ch, self.ui_global_system_prompt = imgui.input_text_multiline("##gsp", self.ui_global_system_prompt, imgui.ImVec2(-1, 100))
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
imgui.text("Project System Prompt")
|
imgui.text("Project System Prompt")
|
||||||
@@ -2941,11 +2945,11 @@ def hello():
|
|||||||
if is_sel:
|
if is_sel:
|
||||||
imgui.set_item_default_focus()
|
imgui.set_item_default_focus()
|
||||||
imgui.end_combo()
|
imgui.end_combo()
|
||||||
imgui.same_line()
|
imgui.same_line(0, 8)
|
||||||
if imgui.button("Manage Presets##project"):
|
if imgui.button("Manage Presets##project"):
|
||||||
self.show_preset_manager_modal = True
|
self.show_preset_manager_modal = True
|
||||||
|
imgui.set_item_tooltip("Open preset management modal")
|
||||||
ch, self.ui_project_system_prompt = imgui.input_text_multiline("##psp", self.ui_project_system_prompt, imgui.ImVec2(-1, 100))
|
ch, self.ui_project_system_prompt = imgui.input_text_multiline("##psp", self.ui_project_system_prompt, imgui.ImVec2(-1, 100))
|
||||||
|
|
||||||
def _render_theme_panel(self) -> None:
|
def _render_theme_panel(self) -> None:
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_theme_panel")
|
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_theme_panel")
|
||||||
exp, opened = imgui.begin("Theme", self.show_windows["Theme"])
|
exp, opened = imgui.begin("Theme", self.show_windows["Theme"])
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import sys
|
||||||
import tomllib
|
import tomllib
|
||||||
import tomli_w
|
import tomli_w
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -21,24 +22,21 @@ class PresetManager:
|
|||||||
presets: Dict[str, Preset] = {}
|
presets: Dict[str, Preset] = {}
|
||||||
|
|
||||||
# Load global presets
|
# Load global presets
|
||||||
if self.global_path.exists():
|
data_global = self._load_file(self.global_path)
|
||||||
|
for name, p_data in data_global.get("presets", {}).items():
|
||||||
try:
|
try:
|
||||||
with open(self.global_path, "rb") as f:
|
|
||||||
data = tomllib.load(f)
|
|
||||||
for name, p_data in data.get("presets", {}).items():
|
|
||||||
presets[name] = Preset.from_dict(name, p_data)
|
presets[name] = Preset.from_dict(name, p_data)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"Error parsing global preset '{name}': {e}", file=sys.stderr)
|
||||||
|
|
||||||
# Load project presets (overwriting global ones if names conflict)
|
# Load project presets (overwriting global ones if names conflict)
|
||||||
if self.project_path and self.project_path.exists():
|
if self.project_path:
|
||||||
|
data_project = self._load_file(self.project_path)
|
||||||
|
for name, p_data in data_project.get("presets", {}).items():
|
||||||
try:
|
try:
|
||||||
with open(self.project_path, "rb") as f:
|
|
||||||
data = tomllib.load(f)
|
|
||||||
for name, p_data in data.get("presets", {}).items():
|
|
||||||
presets[name] = Preset.from_dict(name, p_data)
|
presets[name] = Preset.from_dict(name, p_data)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"Error parsing project preset '{name}': {e}", file=sys.stderr)
|
||||||
|
|
||||||
return presets
|
return presets
|
||||||
|
|
||||||
@@ -75,8 +73,14 @@ class PresetManager:
|
|||||||
return {"presets": {}}
|
return {"presets": {}}
|
||||||
try:
|
try:
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
return tomllib.load(f)
|
data = tomllib.load(f)
|
||||||
except Exception:
|
if not isinstance(data, dict):
|
||||||
|
return {"presets": {}}
|
||||||
|
if "presets" not in data:
|
||||||
|
data["presets"] = {}
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading presets from {path}: {e}", file=sys.stderr)
|
||||||
return {"presets": {}}
|
return {"presets": {}}
|
||||||
|
|
||||||
def _save_file(self, path: Path, data: Dict[str, Any]) -> None:
|
def _save_file(self, path: Path, data: Dict[str, Any]) -> None:
|
||||||
|
|||||||
@@ -3,9 +3,39 @@ import time
|
|||||||
import tomli_w
|
import tomli_w
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from src.api_hook_client import ApiHookClient
|
from src.api_hook_client import ApiHookClient
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
|
def test_env_setup():
|
||||||
|
temp_workspace = Path("tests/artifacts/live_gui_workspace")
|
||||||
|
if temp_workspace.exists():
|
||||||
|
try: shutil.rmtree(temp_workspace)
|
||||||
|
except: pass
|
||||||
|
temp_workspace.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
config_path = temp_workspace / "config.toml"
|
||||||
|
manual_slop_path = temp_workspace / "manual_slop.toml"
|
||||||
|
|
||||||
|
# Create minimal project file
|
||||||
|
manual_slop_path.write_text("[project]\nname = 'TestProject'\n", encoding="utf-8")
|
||||||
|
|
||||||
|
# Create local config.toml
|
||||||
|
config_path.write_text(tomli_w.dumps({
|
||||||
|
"projects": {
|
||||||
|
"paths": [str(manual_slop_path.absolute())],
|
||||||
|
"active": str(manual_slop_path.absolute())
|
||||||
|
},
|
||||||
|
"ai": {
|
||||||
|
"provider": "gemini",
|
||||||
|
"model": "gemini-2.5-flash-lite"
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
yield
|
||||||
|
# Cleanup handled by live_gui fixture usually, but we can be explicit
|
||||||
|
if config_path.exists(): config_path.unlink()
|
||||||
|
|
||||||
def test_preset_switching(live_gui):
|
def test_preset_switching(live_gui):
|
||||||
client = ApiHookClient()
|
client = ApiHookClient()
|
||||||
|
|
||||||
@@ -58,10 +88,6 @@ def test_preset_switching(live_gui):
|
|||||||
})
|
})
|
||||||
time.sleep(2) # Wait for processing
|
time.sleep(2) # Wait for processing
|
||||||
|
|
||||||
# Verify where it thinks the project is
|
|
||||||
state = client.get_gui_state()
|
|
||||||
print(f"DEBUG: ui_files_base_dir={state.get('files_base_dir')}")
|
|
||||||
|
|
||||||
# Apply Global Preset (should use override from project if available in merged list)
|
# Apply Global Preset (should use override from project if available in merged list)
|
||||||
client.push_event("custom_callback", {
|
client.push_event("custom_callback", {
|
||||||
"callback": "_apply_preset",
|
"callback": "_apply_preset",
|
||||||
@@ -86,6 +112,7 @@ def test_preset_switching(live_gui):
|
|||||||
assert state["project_preset_name"] == "TestProject"
|
assert state["project_preset_name"] == "TestProject"
|
||||||
assert state["project_system_prompt"] == "Project Prompt"
|
assert state["project_system_prompt"] == "Project Prompt"
|
||||||
assert state["temperature"] == 0.3
|
assert state["temperature"] == 0.3
|
||||||
|
|
||||||
# Select "None"
|
# Select "None"
|
||||||
client.push_event("custom_callback", {
|
client.push_event("custom_callback", {
|
||||||
"callback": "_apply_preset",
|
"callback": "_apply_preset",
|
||||||
@@ -97,7 +124,45 @@ def test_preset_switching(live_gui):
|
|||||||
|
|
||||||
# Prompt remains from previous application
|
# Prompt remains from previous application
|
||||||
assert state["global_system_prompt"] == "Overridden Prompt"
|
assert state["global_system_prompt"] == "Overridden Prompt"
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Cleanup
|
# Cleanup
|
||||||
if global_presets_path.exists(): global_presets_path.unlink()
|
if global_presets_path.exists(): global_presets_path.unlink()
|
||||||
if project_presets_path.exists(): project_presets_path.unlink()
|
if project_presets_path.exists(): project_presets_path.unlink()
|
||||||
|
|
||||||
|
def test_preset_manager_modal(live_gui):
|
||||||
|
client = ApiHookClient()
|
||||||
|
temp_workspace = Path("tests/artifacts/live_gui_workspace")
|
||||||
|
global_presets_path = temp_workspace / "presets.toml"
|
||||||
|
project_presets_path = temp_workspace / "project_presets.toml"
|
||||||
|
|
||||||
|
# Open Modal
|
||||||
|
client.set_value("show_preset_manager_modal", True)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Create New Preset via Modal Logic (triggering the callback directly for reliability in headless)
|
||||||
|
client.push_event("custom_callback", {
|
||||||
|
"callback": "_cb_save_preset",
|
||||||
|
"args": ["ModalPreset", "Modal Content", 0.9, 1.0, 4096, "global"]
|
||||||
|
})
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Verify file exists
|
||||||
|
assert global_presets_path.exists()
|
||||||
|
with open(global_presets_path, "rb") as f:
|
||||||
|
import tomllib
|
||||||
|
data = tomllib.load(f)
|
||||||
|
assert "ModalPreset" in data["presets"]
|
||||||
|
assert data["presets"]["ModalPreset"]["temperature"] == 0.9
|
||||||
|
|
||||||
|
# Delete Preset via Modal Logic
|
||||||
|
client.push_event("custom_callback", {
|
||||||
|
"callback": "_cb_delete_preset",
|
||||||
|
"args": ["ModalPreset", "global"]
|
||||||
|
})
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Verify file content
|
||||||
|
with open(global_presets_path, "rb") as f:
|
||||||
|
data = tomllib.load(f)
|
||||||
|
assert "ModalPreset" not in data["presets"]
|
||||||
|
|||||||
Reference in New Issue
Block a user