diff --git a/layouts/default.ini b/layouts/default.ini index c581ad27..ec02a784 100644 --- a/layouts/default.ini +++ b/layouts/default.ini @@ -1,94 +1,101 @@ ;;; -;;; Manual Slop default docking layout for live_gui test sessions +;;; Manual Slop default docking layout for live_gui test sessions AND +;;; first-run production launches (when cwd/manualslop_layout.ini is +;;; missing/empty/small). ;;; -;;; Layout strategy: each window entry has Pos + Size + Collapsed=0 set -;;; explicitly so the window is registered at a known absolute position. -;;; No docking data block and no DockId references -- HelloImgui dockspace -;;; IDs are computed dynamically per session (typically a hash of the -;;; dockspace name and creation order), so any DockSpace ID literal baked -;;; into an INI is stale by the next render of a fresh session and its -;;; docking instructions are dropped as orphan. Letting HelloImgui's -;;; auto-dock layer handle the layout (placing windows as tabs in the -;;; central dockspace) is the only session-stable option. +;;; Mechanism: HelloImGui reads this INI at app startup via the +;;; ini_folder_type/ini_filename on the RunnerParams. The DockSpace ID, +;;; DockNode IDs, and per-window DockId lines below tell HelloImgui where +;;; to place each panel. The literal IDs (0xAFC85805, 0x00000001, +;;; 0x00000002) match the runtime-generated MainDockSpace ID (decimal +;;; 2949142533) that HelloImGui computes deterministically per session. ;;; -;;; Window list (matches src/app_controller.py:_default_windows defaults -;;; plus the four Tier panels that the user prefers visible): -;;; Pos=0,29 Size=600,400 : Project Settings, Files and Media, -;;; AI Settings, Operations Hub, Theme -;;; Pos=600,29 Size=600,400 : Discussion Hub, Log Management, -;;; Diagnostics -;;; Pos=0,432 Size=400,300 : Tier 1 Strategy, Tier 2 Tech Lead, -;;; Tier 3 Workers, Tier 4 QA +;;; Window list matches the post-config-merge effective visibility set +;;; (8 default-true windows excluding Response (stale) and the four +;;; Tier panels (disabled in config.toml)): +;;; Project Settings, Files & Media, AI Settings, AI operations, +;;; Discussion Hub, Operations Hub, Theme, Log Management, Diagnostics. +;;; Per-window DockId: +;;; 0x00000001,0..4 = left column tabs (Theme, Project Settings, +;;; AI Settings, Files & Media, Operations Hub) +;;; 0x00000002,0..2 = right column tabs (Discussion Hub, Log Management, +;;; Diagnostics) ;;; ;;; All Collapsed=0 so the windows expand immediately on first render. ;;; -;;; To iterate on this layout: open sloppy.py, arrange windows as -;;; desired, quit (HelloImgui auto-saves), then copy the resulting -;;; cwd/manualslop_layout.ini over this one. Strip the docking data -;;; block from the saved INI before copy (or just keep this default -;;; which auto-docks cleanly). +;;; This INI does NOT include any of the _STALE_WINDOW_NAMES from +;;; src/gui_2.py:603-607 (Projects, Files, Screenshots, Discussion History, +;;; Provider, Message, Response, Tool Calls, Comms History, System Prompts). +;;; _diag_layout_state will not emit a "stale window name" warning. ;;; -;;; Scrubbed entries: no Text Viewer / Tool Script / Inject File / -;;; AST Inspector / Context Preview / Patch modal etc. (transient or -;;; modal-by-default). +;;; To iterate on this layout: open sloppy.py, arrange windows as +;;; desired, quit (HelloImGui auto-saves), then copy the resulting +;;; cwd/manualslop_layout.ini over this one. (HelloImGui adds SplitsIds, +;;; Tables, and other internal sections on save; the bundled default +;;; version is the minimal scaffold needed for first-run visibility.) ;;; [Window][Project Settings] -Pos=0,29 -Size=400,400 +Pos=0,28 +Size=481,1172 Collapsed=0 +DockId=0x00000001,1 [Window][Files & Media] -Pos=0,432 -Size=400,400 +Pos=0,28 +Size=481,1172 Collapsed=0 +DockId=0x00000001,3 [Window][AI Settings] -Pos=410,29 -Size=400,400 +Pos=0,28 +Size=481,1172 Collapsed=0 +DockId=0x00000001,2 [Window][Operations Hub] -Pos=410,432 -Size=400,400 -Collapsed=0 - -[Window][Discussion Hub] -Pos=820,29 -Size=400,600 -Collapsed=0 - -[Window][Log Management] -Pos=820,640 -Size=400,200 -Collapsed=0 - -[Window][Diagnostics] -Pos=820,850 -Size=400,250 +Pos=0,28 +Size=481,1172 Collapsed=0 +DockId=0x00000001,4 [Window][Theme] -Pos=1230,29 -Size=400,300 +Pos=0,28 +Size=481,1172 Collapsed=0 +DockId=0x00000001,0 -[Window][Tier 1: Strategy] -Pos=1230,340 -Size=400,250 +[Window][Discussion Hub] +Pos=483,28 +Size=1197,1172 Collapsed=0 +DockId=0x00000002,0 -[Window][Tier 2: Tech Lead] -Pos=1230,600 -Size=400,250 +[Window][Log Management] +Pos=483,28 +Size=1197,1172 Collapsed=0 +DockId=0x00000002,1 -[Window][Tier 3: Workers] -Pos=1230,860 -Size=400,200 +[Window][Diagnostics] +Pos=483,28 +Size=1197,1172 Collapsed=0 +DockId=0x00000002,2 -[Window][Tier 4: QA] -Pos=1640,29 -Size=400,300 -Collapsed=0 +[Docking][Data] +DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,28 Size=1680,1172 Split=X + DockNode ID=0x00000001 Parent=0xAFC85805 SizeRef=481,1172 CentralNode=1 Selected=0x3F1379AF + DockNode ID=0x00000002 Parent=0xAFC85805 SizeRef=1197,1172 Selected=0xB4CBF21A + +;;;<<>>;;; +[Layout] +Name=Default +[StatusBar] +Show=false +ShowFps=true +[Theme] +Name=ImGuiColorsDark + +;;;<<>>;;; +{"gImGuiSplitIDs":{"MainDockSpace":2949142533}} diff --git a/tests/test_default_layout_install.py b/tests/test_default_layout_install.py index 54357551..7496c66d 100644 --- a/tests/test_default_layout_install.py +++ b/tests/test_default_layout_install.py @@ -98,8 +98,51 @@ def _has_window_with_collapsed_zero(text: str) -> bool: return False -def _has_no_docking_block(text: str) -> bool: - return "[Docking][Data]" not in text +def _has_docking_block_with_docknodes(text: str) -> bool: + if "[Docking][Data]" not in text: + return False + if "DockSpace" not in text: + return False + docknode_count: int = 0 + for line in text.splitlines(): + if line.strip().startswith("DockNode") and "ID=" in line: + docknode_count += 1 + return docknode_count >= 1 + + +def _every_window_has_dockid(text: str) -> bool: + lines: list[str] = text.splitlines() + blocks: dict[str, list[str]] = {} + for idx, line in enumerate(lines): + if line.startswith("[Window][") and line.rstrip().endswith("]"): + block: list[str] = [] + for next_line in lines[idx + 1:]: + if next_line.startswith("[") and "][" in next_line: + break + block.append(next_line) + blocks[line] = block + if not blocks: + return False + has_dockid: bool = True + for header, block in blocks.items(): + if not any("DockId=" in bl for bl in block): + has_dockid = False + break + return has_dockid + + +def _has_no_stale_window_names(text: str) -> bool: + stale: set[str] = { + "Projects", "Files", "Screenshots", "Discussion History", + "Provider", "Message", "Response", "Tool Calls", + "Comms History", "System Prompts", + } + for line in text.splitlines(): + if line.startswith("[Window][") and line.rstrip().endswith("]"): + name: str = line[len("[Window]["):-1] + if name in stale: + return False + return True def _workspace_for(tmp_path: Path, test_name: str) -> Path: @@ -122,8 +165,14 @@ def _assert_installed_default(workspace: Path) -> None: assert _has_window_with_collapsed_zero(text), ( f"installed INI has no [Window][...] entry; got first 400 chars: {text[:400]!r}" ) - assert _has_no_docking_block(text), ( - f"installed INI should not contain a [Docking][Data] block (HelloImgui dockspace IDs are session-specific); got first 400 chars: {text[:400]!r}" + assert _has_docking_block_with_docknodes(text), ( + f"installed INI missing [Docking][Data] block with DockSpace + >=1 DockNode children; got first 400 chars: {text[:400]!r}" + ) + assert _every_window_has_dockid(text), ( + f"installed INI does not have a DockId= line following every [Window][...] header; got first 400 chars: {text[:400]!r}" + ) + assert _has_no_stale_window_names(text), ( + f"installed INI contains a window name from _STALE_WINDOW_NAMES -- _diag_layout_state will emit a stale-name warning; got first 400 chars: {text[:400]!r}" )