feat(presets): Implement NamedViewPresets for per-file view settings

This commit is contained in:
2026-05-11 18:31:56 -04:00
parent 6e53906715
commit cb0fa89730
4 changed files with 213 additions and 0 deletions
+43
View File
@@ -226,6 +226,7 @@ class AppController:
self._pending_actions: Dict[str, ConfirmDialog] = {}
self._pending_ask_dialog: bool = False
self.mcp_config: models.MCPConfiguration = models.MCPConfiguration()
self.view_presets: list[models.NamedViewPreset] = []
self.rag_config: Optional[models.RAGConfig] = None
self.rag_engine: Optional[rag_engine.RAGEngine] = None
self.rag_status: str = 'idle'
@@ -2449,6 +2450,12 @@ class AppController:
self.tool_presets = self.tool_preset_manager.load_all_presets()
self.bias_profiles = self.tool_preset_manager.load_all_bias_profiles()
raw_presets = proj.get("view_presets", [])
if isinstance(raw_presets, dict):
self.view_presets = [models.NamedViewPreset.from_dict({"name": name, **data}) for name, data in raw_presets.items()]
else:
self.view_presets = [models.NamedViewPreset.from_dict(p) for p in raw_presets if isinstance(p, dict)]
if self.rag_config and self.rag_config.enabled:
self._rebuild_rag_index()
@@ -2561,6 +2568,41 @@ class AppController:
self.persona_manager.delete_persona(name, scope)
self.personas = self.persona_manager.load_all()
def _cb_save_view_preset(self, name: str, f_item: models.FileItem) -> None:
"""
[C: src/gui_2.py:App._render_files_panel]
"""
preset = models.NamedViewPreset(
name=name,
view_mode=f_item.view_mode,
ast_mask=copy.deepcopy(f_item.ast_mask) if hasattr(f_item, "ast_mask") else {},
custom_slices=copy.deepcopy(f_item.custom_slices) if hasattr(f_item, "custom_slices") else []
)
for i, vp in enumerate(self.view_presets):
if vp.name == name:
self.view_presets[i] = preset
break
else:
self.view_presets.append(preset)
self._flush_to_project()
def _cb_apply_view_preset(self, name: str, f_item: models.FileItem) -> None:
"""
[C: src/gui_2.py:App._render_files_panel]
"""
preset = next((vp for vp in self.view_presets if vp.name == name), None)
if preset:
f_item.view_mode = preset.view_mode
f_item.ast_mask = copy.deepcopy(preset.ast_mask)
f_item.custom_slices = copy.deepcopy(preset.custom_slices)
def _cb_delete_view_preset(self, name: str) -> None:
"""
[C: src/gui_2.py:App._render_files_panel]
"""
self.view_presets = [vp for vp in self.view_presets if vp.name != name]
self._flush_to_project()
def _cb_load_track(self, track_id: str) -> None:
"""
@@ -3011,6 +3053,7 @@ class AppController:
disc_sec["roles"] = self.disc_roles
disc_sec["active"] = self.active_discussion
disc_sec["auto_add"] = self.ui_auto_add_history
proj["view_presets"] = [vp.to_dict() for vp in self.view_presets]
# Save MMA State
mma_sec = proj.setdefault("mma", {})
mma_sec["epic"] = self.ui_epic_input
+29
View File
@@ -238,6 +238,7 @@ class App:
self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8}
self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8}
self.ui_new_context_preset_name = ""
self.ui_new_vp_name = ""
self._focus_md_cache: dict[str, str] = {}
self.ui_inspecting_ast_file = None
self._show_ast_inspector = False
@@ -3048,6 +3049,34 @@ class App:
changed_vm, new_idx = imgui.combo(f"##vm{i}", current_idx, view_modes)
if changed_vm:
f_item.view_mode = view_modes[new_idx]
imgui.same_line()
if imgui.button(f"[Save]##vpsave{i}"):
imgui.open_popup(f"save_vp_popup{i}")
if imgui.begin_popup(f"save_vp_popup{i}"):
imgui.text("Preset Name:")
changed_pname, self.ui_new_vp_name = imgui.input_text(f"##pname{i}", self.ui_new_vp_name)
if imgui.button("OK"):
if self.ui_new_vp_name.strip():
self.controller._cb_save_view_preset(self.ui_new_vp_name.strip(), f_item)
self.ui_new_vp_name = ""
imgui.close_current_popup()
imgui.end_popup()
imgui.same_line()
if imgui.button(f"[Load]##vpload{i}"):
imgui.open_popup(f"load_vp_popup{i}")
if imgui.begin_popup(f"load_vp_popup{i}"):
vp_names = sorted([vp.name for vp in self.controller.view_presets])
if not vp_names:
imgui.text("No presets saved.")
for vp_name in vp_names:
if imgui.selectable(vp_name):
self.controller._cb_apply_view_preset(vp_name, f_item)
imgui.close_current_popup()
imgui.end_popup()
if hasattr(f_item, "custom_slices") and f_item.custom_slices:
imgui.same_line()
imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]")
+24
View File
@@ -939,6 +939,30 @@ class FileViewPreset:
view_mode=data.get("view_mode", "summary")
)
@dataclass
class NamedViewPreset:
name: str
view_mode: str
ast_mask: dict = field(default_factory=dict)
custom_slices: list = field(default_factory=list)
def to_dict(self) -> dict[str, Any]:
return {
"name": self.name,
"view_mode": self.view_mode,
"ast_mask": self.ast_mask,
"custom_slices": self.custom_slices
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "NamedViewPreset":
return cls(
name=data.get("name", ""),
view_mode=data.get("view_mode", "summary"),
ast_mask=data.get("ast_mask", {}),
custom_slices=data.get("custom_slices", [])
)
@dataclass
class ContextPreset:
name: str