Repository navigation
Description
Outcome: panels are dockable the way CS6 panels are — drag to dock, tab groups, and a layout that persists across sessions.
Objective. The CS6 panel set and panel windows exist. What is missing is docking behaviour: a panel that can be dragged between dock areas, grouped into tabs, and whose layout survives a restart. The v1 roadmap's shell-fidelity step depends on the CS6 panel set existing as a real shell, not a fixed list.
Inputs / authority. docs/NEXT.md §22.1; the CS6 panel-docking behaviour observed in the reference application; egui docking support.
Stopping condition. A panel can be moved, tabbed with another panel, and restored to its position on the next launch; the saved layout is part of the user settings.
Verification. A regression that persists a layout, reloads it, and asserts the restored panel positions; the shipped layout must not regress the wasm budget.
Status: LANDED (persistence, the panel set, the movement model and tab groups are done).
Verified against docs/ledger-post-train @ a3fe33b (2026-11-11); source of truth crates/app/src/shell/mod.rs and crates/app/src/shell/panel_windows.rs; the docking and tab-group work is on feat/panel-docking (PR #128, head 11feb22, CI receipt pending).
- Panel set — the CS6 panel roster exists with the fresh-document set open by default (
panel_windows.rs, decision 0069), and each panel toggles from the Window menu. - A real dock, not floating windows — the right dock is a width-reserving
egui::Panel::right(258 pt default) with a scrolling column of collapsible, closable sections (docs/NEXT.md§22.1, LANDED 2026-11-04). - Layout persistence —
ShellLayout(panel set, dock folds, display toggles) is saved and restored throughApp::save— native tostorage_dir, web tolocalStorage— and filtered against the current panel inventory on restore (shell::mod.rs:capture_layout,eframe::get_value::<ShellLayout>(storage, "shell_layout")). - Drag a panel between dock areas — implemented 2026-11-11, PR feat(app): the panels dock left or right, and remember where #128 (CI receipt pending). The panel's title is the drag handle (CS6 moves a panel by its header); both columns are drop targets; the left column appears when a panel is docked there or a drag is in flight, because a drop target has to be there before the drag reaches it; the
«/»arrow in the header moves a panel for anyone who cannot drag. The default arrangement is unchanged — the panel snapshot still records every panel on the right, in CS6's order. - Tab groups — implemented 2026-11-11, PR feat(app): the panels dock left or right, and remember where #128 (CI receipt pending). Dropping a panel on another panel's title tabs the two together, CS6's gesture: the group is one slot with a tab strip in its header row, and the group — not the panel — is what the dock draws, moves and saves, so a move carries the whole group and a drop merges two groups. The group survives a save (its members and the tab it shows round-trip through the layout file); a group naming a panel this build lacks loses that member, a group of one dissolves, and a hidden panel leaves its group. The tab is both the switch and the handle: one widget senses the click and the drag, and egui decides between them by whether the pointer moved — so a click still switches the tab, a drag still moves the group, and a click that never moved merges no panels by accident.
- Panel position persistence regression —
shell::tests::shell_layout_round_trips_through_saveasserts the saved panel set and folds come back, andshell::tests::the_panel_homes_round_trip_through_saveasserts a moved panel's dock survives a RON save while a home naming a panel this build lacks is dropped. - Tab-group regressions —
shell::tests::a_dropped_panel_is_tabbed_with_the_panel_it_landed_onasserts that a dropped panel shares the target's slot, becomes the group's active tab, and that a move carries the whole group;shell::tests::closing_a_panel_dissolves_its_groupasserts that a closed panel leaves its group and that a group of one is no group;shell::tests::the_panel_groups_round_trip_through_saveasserts that the group and its active tab survive a RON save.
Next action (owner: shell).
- Land PR feat(app): the panels dock left or right, and remember where #128 on its receipts (GitHub and GitForge green on the same head SHA).
- Then the shell-parity follow-on: the CS6 chrome pass (Phase 22.5) and the screenshot audit (T22), which now have a real dock to photograph.
CI receipt (2026-11-12). The next red receipt was the coverage gate: the branch adds ~20 functions to crates/app/src/shell/panel_windows.rs, which is not on the coverage exclusion list, and the workspace function coverage fell to 78.6% against the 78.7% floor. The branch now carries targeted unit tests for the spec lookup, the dock roster, the dock sides, tab switching, group splitting, group departure and dock membership, plus a headless egui frame that draws the docks. That frame then failed on CI for two reasons, both fixed in 11feb22: egui's font textures are dropped unapplied in a windowless frame (epaint panics on that), and a panel that left its group fell back to the default dock while the group it was drawn in stayed in the other one — the leaver now keeps the group's dock, like the members it leaves behind.
CI receipt (2026-11-11). The first GitForge run of PR #128 was red at clippy-test: the CI image's clippy (1.99) rejects six lints in crates/app/src/shell/panel_windows.rs that the branch's own authoring pass had not caught — a needless deref on the drag payload, three iter().any() membership scans that contains expresses directly, and two nested ifs that collapse into let-chains. Fixed in 4fb1d58 by using contains, collapsing the ifs, and typing the group lookups with the &'static str panel names every caller already had. The branch head is now 4fb1d58; both receipts must be green on that SHA before the merge.
Activity
docs(tasks): the dockable panel set is in flight