diff --git a/docs/guide_workspace_profiles.md b/docs/guide_workspace_profiles.md index 6e2a597f..a33ba4d9 100644 --- a/docs/guide_workspace_profiles.md +++ b/docs/guide_workspace_profiles.md @@ -69,46 +69,33 @@ Workspace Profiles live at the boundary between the ImGui render loop and the pe A snapshot of window state at a moment in time. ```python -@dataclass class WorkspaceProfile: name: str - # Window state (ImGui docking layout) - docking_layout: bytes # Serialized ImGui docking configuration - # Window visibility per window name - window_visibility: Dict[str, bool] # {"mma_dashboard": True, "context_panel": True, ...} - # Active theme - theme: str = "dark" # "dark" | "light" | "nerv" | ... - # Active theme FX state - theme_fx_enabled: bool = True - # Capture metadata - captured_at: str = "" # ISO 8601 timestamp - description: str = "" # Optional human description + ini_content: str # ImGui ini settings string (from SaveIniSettingsToMemory) + show_windows: Dict[str, bool] = field(default_factory=dict) # {"mma_dashboard": True, "context_panel": True, ...} + panel_states: Dict[str, Any] = field(default_factory=dict) # Per-panel UI state (collapsed flags, scroll positions, etc.) ``` | Field | Type | Purpose | |---|---|---| | `name` | `str` | Unique identifier within the scope. | -| `docking_layout` | `bytes` | Serialized ImGui docking configuration. Stored as a base64-encoded byte string in TOML. | -| `window_visibility` | `Dict[str, bool]` | Maps window name to visibility. Windows not in the dict use the default (visible). | -| `theme` | `str` | Active theme name. See [guide_nerv_theme.md](guide_nerv_theme.md). | -| `theme_fx_enabled` | `bool` | Whether theme FX (e.g., NERV scanlines) are enabled. | -| `captured_at` | `str` | ISO 8601 timestamp of when the profile was captured. | -| `description` | `str` | Optional human-readable description. | +| `ini_content` | `str` | ImGui ini settings string from `SaveIniSettingsToMemory()`. Plain string in TOML (not base64). | +| `show_windows` | `Dict[str, bool]` | Maps window name to visibility. Windows not in the dict use the default (visible). | +| `panel_states` | `Dict[str, Any]` | Per-panel UI state (collapsed flags, scroll positions, value overrides). See `App._capture_workspace_profile` in `src/gui_2.py` for the captured key set. | ### Serialization -The `docking_layout` is captured via ImGui's `SaveIniSettingsToMemory()` and serialized as bytes. On load, `LoadIniSettingsFromMemory()` restores the layout. +The `ini_content` is captured via `ImGui.SaveIniSettingsToMemory()` as a plain string. On load, `ImGui.LoadIniSettingsFromMemory(ini_data)` restores the layout. The `to_dict`/`from_dict` methods on `WorkspaceProfile` handle the TOML round-trip via the standard `models` registry. -The TOML representation uses base64 for the binary layout: +> **Type contract note:** `ini_content` is `str`, not `bytes`. The previous base64-encoded-bytes design was removed in the 2026-06-05 `live_gui_fragility_fixes_20260605` track. A regression that used `b""` as a sentinel caused `tomli_w` to raise `TypeError: Object of type 'bytes' is not TOML serializable`. The regression test `tests/test_workspace_profile_serialization.py` encodes this contract. See the sentinel type contract rule in [guide_testing.md](guide_testing.md) and [guide_gui_2.md](guide_gui_2.md). + +The TOML representation: ```toml [profiles.focused_work] -docking_layout = "BASE64_ENCODED_BYTES" -window_visibility = { mma_dashboard = false, context_panel = true, ai_settings = false } -theme = "nerv" -theme_fx_enabled = true -captured_at = "2026-05-15T14:32:05" -description = "Minimal layout for focused code writing" +ini_content = "[Window][Project Manager]\nPos=8,8\nSize=400,400\n..." +show_windows = { mma_dashboard = false, context_panel = true, ai_settings = false } +panel_states = { mma_dashboard_collapsed = false, context_panel_scroll_y = 0.0 } ``` --- @@ -150,23 +137,21 @@ def auto_switch(self, context_id: str) -> bool: `activate_profile(name)` performs: 1. Read the profile from the loaded dict. -2. Apply `docking_layout` via `ImGui.LoadIniSettingsFromMemory()`. -3. Apply `window_visibility` to the live windows (hide/show as needed). -4. Apply the theme via the theme system. -5. Set the theme FX state. +2. Apply `ini_content` via `ImGui.LoadIniSettingsFromMemory(ini_content)`. +3. Apply `show_windows` to the live windows (hide/show as needed). +4. Apply `panel_states` to the per-panel settable fields via `App._apply_panel_states`. -Activation is immediate; the next frame renders the new layout. +Activation is immediate; the next frame renders the new layout. Theme is NOT part of the profile (themes are managed by the separate theme system; see [guide_themes.md](guide_themes.md)). ### Capturing Current State `capture_current_as(name, scope)`: -1. Read the current ImGui state via `ImGui.SaveIniSettingsToMemory()`. -2. Snapshot window visibility. -3. Read the active theme from the theme system. -4. Set the timestamp and optional description. -5. Save to the specified scope. +1. Read the current ImGui state via `ImGui.SaveIniSettingsToMemory()` (with the defer-not-catch guard `_ini_capture_ready`). +2. Snapshot window visibility into `show_windows`. +3. Snapshot per-panel state (collapsed flags, scroll positions, value overrides) into `panel_states`. +4. Save to the specified scope. -This is the "Save Profile" action in the GUI. +This is the "Save Profile" action in the GUI. Theme is NOT captured (see `activate_profile` above). --- @@ -288,13 +273,13 @@ def test_profile_save_and_load(tmp_path, live_gui): ### Layout Stability -The `docking_layout` is a binary blob (base64 in TOML). It's not human-readable, so tests typically use a "save, modify, restore" pattern rather than asserting specific layout details. +The `ini_content` is the raw ImGui ini settings string from `SaveIniSettingsToMemory()`. It's machine-generated text (not human-readable, but parseable). Tests typically use a "save, modify, restore" pattern rather than asserting specific layout details. The `panel_states` dict, by contrast, is human-readable and testable. --- ## Limitations -1. **Theme FX State is Global**: NERV FX and other theme effects are global settings, not per-profile. Saving a profile with `theme_fx_enabled = false` and then activating it would change the global setting. +1. **Theme is Not Captured**: Themes are managed by the separate theme system (see [guide_themes.md](guide_themes.md)) and are global. A profile captures docking layout, window visibility, and panel state — but not the active theme. Switching profiles does not change the theme. 2. **No Profile Variants per Monitor**: A single profile applies to all connected monitors. Multi-monitor users with different layouts per monitor must use ImGui's native multi-viewport support, not profiles.