Skip to content

Phase 22: the dockable panel set — docking, tab groups and layout persistence #91

Open

Description

@aliasfoxkde

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 through App::save — native to storage_dir, web to localStorage — 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_save asserts the saved panel set and folds come back, and shell::tests::the_panel_homes_round_trip_through_save asserts 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_on asserts 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_group asserts that a closed panel leaves its group and that a group of one is no group; shell::tests::the_panel_groups_round_trip_through_save asserts that the group and its active tab survive a RON save.

Next action (owner: shell).

  1. 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).
  2. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmilestoneMilestone and phase trackingparityCS6 parity work

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions