Private
Public Access
0
0

docs(workspace_profiles): fix WorkspaceProfile schema (ini_content, show_windows, panel_states)

The 2026-06-05 live_gui_fragility_fixes refactor replaced the old 7-field
WorkspaceProfile (docking_layout: bytes, window_visibility, theme,
theme_fx_enabled, captured_at, description) with a 4-field model:
ini_content: str, show_windows, panel_states. tomli_w rejects bytes,
so the ini_content is now a plain ImGui ini string, not base64.

- Update Data Model class example + field table
- Update Serialization section + TOML example
- Update Profile Activation + Capturing Current State steps
- Update Layout Stability note (binary blob -> raw ini string)
- Replace 'Theme FX State is Global' limitation with 'Theme is Not Captured'
This commit is contained in:
2026-06-10 19:46:46 -04:00
parent 7f58f980c6
commit f973fb275f
+24 -39
View File
@@ -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.