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:
@@ -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.
|
A snapshot of window state at a moment in time.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@dataclass
|
|
||||||
class WorkspaceProfile:
|
class WorkspaceProfile:
|
||||||
name: str
|
name: str
|
||||||
# Window state (ImGui docking layout)
|
ini_content: str # ImGui ini settings string (from SaveIniSettingsToMemory)
|
||||||
docking_layout: bytes # Serialized ImGui docking configuration
|
show_windows: Dict[str, bool] = field(default_factory=dict) # {"mma_dashboard": True, "context_panel": True, ...}
|
||||||
# Window visibility per window name
|
panel_states: Dict[str, Any] = field(default_factory=dict) # Per-panel UI state (collapsed flags, scroll positions, etc.)
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
| Field | Type | Purpose |
|
| Field | Type | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `name` | `str` | Unique identifier within the scope. |
|
| `name` | `str` | Unique identifier within the scope. |
|
||||||
| `docking_layout` | `bytes` | Serialized ImGui docking configuration. Stored as a base64-encoded byte string in TOML. |
|
| `ini_content` | `str` | ImGui ini settings string from `SaveIniSettingsToMemory()`. Plain string in TOML (not base64). |
|
||||||
| `window_visibility` | `Dict[str, bool]` | Maps window name to visibility. Windows not in the dict use the default (visible). |
|
| `show_windows` | `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). |
|
| `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. |
|
||||||
| `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. |
|
|
||||||
|
|
||||||
### Serialization
|
### 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
|
```toml
|
||||||
[profiles.focused_work]
|
[profiles.focused_work]
|
||||||
docking_layout = "BASE64_ENCODED_BYTES"
|
ini_content = "[Window][Project Manager]\nPos=8,8\nSize=400,400\n..."
|
||||||
window_visibility = { mma_dashboard = false, context_panel = true, ai_settings = false }
|
show_windows = { mma_dashboard = false, context_panel = true, ai_settings = false }
|
||||||
theme = "nerv"
|
panel_states = { mma_dashboard_collapsed = false, context_panel_scroll_y = 0.0 }
|
||||||
theme_fx_enabled = true
|
|
||||||
captured_at = "2026-05-15T14:32:05"
|
|
||||||
description = "Minimal layout for focused code writing"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -150,23 +137,21 @@ def auto_switch(self, context_id: str) -> bool:
|
|||||||
|
|
||||||
`activate_profile(name)` performs:
|
`activate_profile(name)` performs:
|
||||||
1. Read the profile from the loaded dict.
|
1. Read the profile from the loaded dict.
|
||||||
2. Apply `docking_layout` via `ImGui.LoadIniSettingsFromMemory()`.
|
2. Apply `ini_content` via `ImGui.LoadIniSettingsFromMemory(ini_content)`.
|
||||||
3. Apply `window_visibility` to the live windows (hide/show as needed).
|
3. Apply `show_windows` to the live windows (hide/show as needed).
|
||||||
4. Apply the theme via the theme system.
|
4. Apply `panel_states` to the per-panel settable fields via `App._apply_panel_states`.
|
||||||
5. Set the theme FX state.
|
|
||||||
|
|
||||||
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
|
### Capturing Current State
|
||||||
|
|
||||||
`capture_current_as(name, scope)`:
|
`capture_current_as(name, scope)`:
|
||||||
1. Read the current ImGui state via `ImGui.SaveIniSettingsToMemory()`.
|
1. Read the current ImGui state via `ImGui.SaveIniSettingsToMemory()` (with the defer-not-catch guard `_ini_capture_ready`).
|
||||||
2. Snapshot window visibility.
|
2. Snapshot window visibility into `show_windows`.
|
||||||
3. Read the active theme from the theme system.
|
3. Snapshot per-panel state (collapsed flags, scroll positions, value overrides) into `panel_states`.
|
||||||
4. Set the timestamp and optional description.
|
4. Save to the specified scope.
|
||||||
5. 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
|
### 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
|
## 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.
|
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.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user