feat(models): Implement ContextPreset and ContextFileEntry
This commit is contained in:
+1
-7
@@ -568,7 +568,7 @@ class App:
|
||||
for f in self.context_files:
|
||||
path = f.path if hasattr(f, 'path') else str(f)
|
||||
view_mode = f.view_mode if hasattr(f, 'view_mode') else 'summary'
|
||||
preset_files.append(models.FileViewPreset(path=path, view_mode=view_mode))
|
||||
preset_files.append(models.ContextFileEntry(path=path, view_mode=view_mode))
|
||||
|
||||
preset = models.ContextPreset(name=name, files=preset_files, screenshots=list(self.screenshots))
|
||||
self.controller.project['context_presets'][name] = preset.to_dict()
|
||||
@@ -945,7 +945,6 @@ class App:
|
||||
threading.Thread(target=_stats_worker, daemon=True).start()
|
||||
return total_lines, total_ast
|
||||
|
||||
|
||||
def _close_vscode_diff(self) -> None:
|
||||
if hasattr(self, '_vscode_diff_process') and self._vscode_diff_process:
|
||||
try:
|
||||
@@ -997,7 +996,6 @@ class App:
|
||||
self._vscode_diff_process = result
|
||||
except Exception as e:
|
||||
self._patch_error_message = str(e)
|
||||
|
||||
|
||||
def _reorder_ticket(self, src_idx: int, dst_idx: int) -> None:
|
||||
"""
|
||||
@@ -1117,8 +1115,6 @@ def main() -> None:
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
#region: Main Interface
|
||||
|
||||
def render_main_interface(app: App) -> None:
|
||||
render_error_tint(app)
|
||||
app.perf_monitor.start_frame()
|
||||
@@ -1215,8 +1211,6 @@ def render_custom_title_bar(app: App) -> None:
|
||||
# Controls are now embedded in _show_menus.
|
||||
pass
|
||||
|
||||
#endregion: Main Interface
|
||||
|
||||
#region: Diagnostics & Analytics
|
||||
|
||||
def render_usage_analytics_panel(app: App) -> None:
|
||||
|
||||
+18
-7
@@ -823,22 +823,27 @@ class WorkspaceProfile:
|
||||
)
|
||||
|
||||
@dataclass
|
||||
class FileViewPreset:
|
||||
class ContextFileEntry:
|
||||
path: str
|
||||
view_mode: str = "summary"
|
||||
custom_slices: list = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""
|
||||
[C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_serialization_with_annotations, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_slice_editor_behavior.py:test_add_slice_with_annotations, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
|
||||
"""
|
||||
return {"path": self.path, "view_mode": self.view_mode}
|
||||
return {"path": self.path, "view_mode": self.view_mode, "custom_slices": self.custom_slices}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict[str, Any]) -> "FileViewPreset":
|
||||
def from_dict(cls, data: dict[str, Any]) -> "ContextFileEntry":
|
||||
"""
|
||||
[C: src/personas.py:PersonaManager.load_all, src/presets.py:PresetManager.load_all, src/project_manager.py:load_project, src/project_manager.py:load_track_state, src/tool_presets.py:ToolPresetManager.load_all_bias_profiles, src/tool_presets.py:ToolPresetManager.load_all_presets, src/workspace_manager.py:WorkspaceManager.load_all_profiles, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_context_presets_models.py:test_context_preset_from_dict_legacy, tests/test_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_deserialization_with_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_dict_editors, tests/test_external_editor.py:TestExternalEditorConfig.test_from_dict_with_string_editors, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_with_diff_args, tests/test_external_editor.py:TestTextEditorConfig.test_from_dict_without_diff_args, tests/test_file_item_model.py:test_file_item_from_dict, tests/test_file_item_model.py:test_file_item_from_dict_defaults, tests/test_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_default_on_deserialize, tests/test_per_ticket_model.py:test_model_override_deserialization, tests/test_persona_id.py:test_ticket_persona_id_deserialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_deserialization, tests/test_project_serialization.py:TestProjectSerialization.test_backward_compatibility_strings, tests/test_slice_editor_behavior.py:test_add_slice_with_annotations, tests/test_ticket_queue.py:test_ticket_from_dict_default_priority, tests/test_ticket_queue.py:test_ticket_from_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_from_dict, tests/test_track_state_schema.py:test_track_state_from_dict_empty_and_missing, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
|
||||
"""
|
||||
return cls(path=data.get("path", ""), view_mode=data.get("view_mode", "summary"))
|
||||
return cls(
|
||||
path=data.get("path", ""),
|
||||
view_mode=data.get("view_mode", "summary"),
|
||||
custom_slices=data.get("custom_slices", []),
|
||||
)
|
||||
|
||||
@dataclass
|
||||
class NamedViewPreset:
|
||||
@@ -868,14 +873,19 @@ class NamedViewPreset:
|
||||
@dataclass
|
||||
class ContextPreset:
|
||||
name: str
|
||||
files: list[FileViewPreset] = field(default_factory=list)
|
||||
files: list[ContextFileEntry] = field(default_factory=list)
|
||||
screenshots: list[str] = field(default_factory=list)
|
||||
description: str = ""
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""
|
||||
[C: src/personas.py:PersonaManager.save_persona, src/presets.py:PresetManager.save_preset, src/project_manager.py:save_project, src/project_manager.py:save_track_state, src/tool_presets.py:ToolPresetManager.save_bias_profile, src/tool_presets.py:ToolPresetManager.save_preset, src/workspace_manager.py:WorkspaceManager.save_profile, tests/test_bias_models.py:test_bias_profile_model, tests/test_bias_models.py:test_tool_model, tests/test_bias_models.py:test_tool_preset_extension, tests/test_context_presets_models.py:test_context_preset_serialization, tests/test_context_presets_models.py:test_file_view_preset_serialization, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_round_trip_annotations, tests/test_custom_slices_annotations.py:test_file_item_custom_slices_serialization_with_annotations, tests/test_event_serialization.py:test_user_request_event_serialization, tests/test_external_editor.py:TestExternalEditorConfig.test_to_dict, tests/test_external_editor.py:TestTextEditorConfig.test_to_dict, tests/test_file_item_model.py:test_file_item_to_dict, tests/test_gui_events_v2.py:test_user_request_event_payload, tests/test_history_manager.py:TestHistoryManager.test_snapshot_roundtrip, tests/test_mcp_config.py:test_mcp_configuration_to_from_dict, tests/test_mcp_config.py:test_mcp_server_config_to_from_dict, tests/test_per_ticket_model.py:test_model_override_serialization, tests/test_persona_id.py:test_ticket_persona_id_serialization, tests/test_persona_models.py:test_persona_defaults, tests/test_persona_models.py:test_persona_serialization, tests/test_slice_editor_behavior.py:test_add_slice_with_annotations, tests/test_thinking_gui.py:test_thinking_segment_model_compatibility, tests/test_ticket_queue.py:test_ticket_to_dict_priority, tests/test_tiered_aggregation.py:test_persona_aggregation_strategy, tests/test_track_state_schema.py:test_track_state_to_dict, tests/test_track_state_schema.py:test_track_state_to_dict_with_none, tests/test_ui_summary_only_removal.py:test_file_item_serialization_with_flags]
|
||||
"""
|
||||
return {"files": [f.to_dict() for f in self.files], "screenshots": self.screenshots}
|
||||
return {
|
||||
"files": [f.to_dict() for f in self.files],
|
||||
"screenshots": self.screenshots,
|
||||
"description": self.description,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, name: str, data: dict[str, Any]) -> "ContextPreset":
|
||||
@@ -885,8 +895,9 @@ class ContextPreset:
|
||||
files_data = data.get("files", [])
|
||||
return cls(
|
||||
name=name,
|
||||
files=[FileViewPreset.from_dict(f) if isinstance(f, dict) else FileViewPreset(path=str(f)) for f in files_data],
|
||||
files=[ContextFileEntry.from_dict(f) if isinstance(f, dict) else ContextFileEntry(path=str(f)) for f in files_data],
|
||||
screenshots=data.get("screenshots", []),
|
||||
description=data.get("description", ""),
|
||||
)
|
||||
|
||||
#region: MCP Config
|
||||
|
||||
@@ -1,45 +1,62 @@
|
||||
import pytest
|
||||
from src.models import ContextPreset, FileViewPreset
|
||||
from src.models import ContextPreset, ContextFileEntry
|
||||
|
||||
def test_file_view_preset_serialization():
|
||||
p = FileViewPreset(path="test.py", view_mode="skeleton")
|
||||
d = p.to_dict()
|
||||
assert d == {"path": "test.py", "view_mode": "skeleton"}
|
||||
|
||||
p2 = FileViewPreset.from_dict(d)
|
||||
assert p2.path == "test.py"
|
||||
assert p2.view_mode == "skeleton"
|
||||
def test_context_file_entry_serialization():
|
||||
p = ContextFileEntry(path="test.py", view_mode="skeleton")
|
||||
d = p.to_dict()
|
||||
# Check for default custom_slices
|
||||
assert d == {"path": "test.py", "view_mode": "skeleton", "custom_slices": []}
|
||||
|
||||
p2 = ContextFileEntry.from_dict(d)
|
||||
assert p2.path == "test.py"
|
||||
assert p2.view_mode == "skeleton"
|
||||
assert p2.custom_slices == []
|
||||
|
||||
# Test with custom_slices
|
||||
p3 = ContextFileEntry(path="test.py", view_mode="summary", custom_slices=["1-10"])
|
||||
d3 = p3.to_dict()
|
||||
assert d3["custom_slices"] == ["1-10"]
|
||||
p4 = ContextFileEntry.from_dict(d3)
|
||||
assert p4.custom_slices == ["1-10"]
|
||||
|
||||
def test_context_preset_serialization():
|
||||
f1 = FileViewPreset(path="a.py", view_mode="full")
|
||||
f2 = FileViewPreset(path="b.py", view_mode="summary")
|
||||
|
||||
preset = ContextPreset(
|
||||
name="test_preset",
|
||||
files=[f1, f2],
|
||||
screenshots=["shot1.png"]
|
||||
)
|
||||
|
||||
d = preset.to_dict()
|
||||
assert len(d["files"]) == 2
|
||||
assert d["files"][0]["path"] == "a.py"
|
||||
assert d["screenshots"] == ["shot1.png"]
|
||||
|
||||
p2 = ContextPreset.from_dict("test_preset", d)
|
||||
assert p2.name == "test_preset"
|
||||
assert len(p2.files) == 2
|
||||
assert p2.files[0].view_mode == "full"
|
||||
assert p2.screenshots == ["shot1.png"]
|
||||
f1 = ContextFileEntry(path="a.py", view_mode="full")
|
||||
f2 = ContextFileEntry(path="b.py", view_mode="summary", custom_slices=["5-15"])
|
||||
|
||||
preset = ContextPreset(
|
||||
name="test_preset",
|
||||
files=[f1, f2],
|
||||
screenshots=["shot1.png"],
|
||||
description="Test description"
|
||||
)
|
||||
|
||||
d = preset.to_dict()
|
||||
assert len(d["files"]) == 2
|
||||
assert d["files"][0]["path"] == "a.py"
|
||||
assert d["files"][1]["custom_slices"] == ["5-15"]
|
||||
assert d["screenshots"] == ["shot1.png"]
|
||||
assert d["description"] == "Test description"
|
||||
|
||||
p2 = ContextPreset.from_dict("test_preset", d)
|
||||
assert p2.name == "test_preset"
|
||||
assert p2.description == "Test description"
|
||||
assert len(p2.files) == 2
|
||||
assert p2.files[0].view_mode == "full"
|
||||
assert p2.files[1].custom_slices == ["5-15"]
|
||||
assert p2.screenshots == ["shot1.png"]
|
||||
|
||||
def test_context_preset_from_dict_legacy():
|
||||
# Test handling of legacy string paths in preset files
|
||||
d = {
|
||||
"files": ["legacy.py", {"path": "new.py", "view_mode": "skeleton"}],
|
||||
"screenshots": []
|
||||
}
|
||||
preset = ContextPreset.from_dict("legacy_test", d)
|
||||
assert len(preset.files) == 2
|
||||
assert preset.files[0].path == "legacy.py"
|
||||
assert preset.files[0].view_mode == "summary" # Default
|
||||
assert preset.files[1].path == "new.py"
|
||||
assert preset.files[1].view_mode == "skeleton"
|
||||
# Test handling of legacy string paths in preset files and missing description/custom_slices
|
||||
d = {
|
||||
"files": ["legacy.py", {"path": "new.py", "view_mode": "skeleton"}],
|
||||
"screenshots": []
|
||||
}
|
||||
preset = ContextPreset.from_dict("legacy_test", d)
|
||||
assert len(preset.files) == 2
|
||||
assert preset.files[0].path == "legacy.py"
|
||||
assert preset.files[0].view_mode == "summary" # Default
|
||||
assert preset.files[0].custom_slices == []
|
||||
assert preset.files[1].path == "new.py"
|
||||
assert preset.files[1].view_mode == "skeleton"
|
||||
assert preset.files[1].custom_slices == []
|
||||
assert preset.description == ""
|
||||
|
||||
Reference in New Issue
Block a user