LearnNewsExamplesServices
Frontmatter
id17857
titleEdge-band resize: boot-pinned caps and a dead floor
stateClosed
labels
bugdesignai
assigneesneo-fable
createdAtAug 29, 2026, 8:13 PM
updatedAtAug 29, 2026, 10:42 PM
githubUrlhttps://github.com/neomjs/neo/issues/17857
authorneo-fable
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 29, 2026, 10:40 PM

Edge-band resize: boot-pinned caps and a dead floor

neo-fable
neo-fable commented on Aug 29, 2026, 8:13 PM

Context

Operator report on apps/workstation (2026-08-29): the left edge splitter can be dragged left, but on drop the side pane snaps back to its initial width; dragging right does not move at all. Both symptoms reproduced first-hand with a real pointer against the dev server, and both trace to the workstation theme's edge-band bounds — with one engine-tier parity hole underneath that turns a mis-themed floor into a silent restore instead of a clamped commit.

Live latest-open sweep: checked latest 20 open issues at 2026-08-29T18:10Z; no equivalent found. A2A claim sweep over the herd window: no competing claim on this scope.

The Problem

Two defects, one theme block, and they contradict each other:

1. The right-drag freeze (dead ceiling). resources/scss/src/apps/workstation/Workspace.scss sets --dock-edge-band-left-max-inline-size: 16.25rem (260px). The committed document boots the left edge at extent: 0.20, which the projection paints as inline width: 20% — 315px in a 1576px row. CSS clamps the render to 260px, so the band boots already pinned at its own max. Neo.main.draggable.Resize#createState folds computed CSS min/max into the clamp window, giving maxSize === startSize → every rightward frame clamps to the start value → zero movement. Measured: a +228px rightward drag stops at exactly 260px.

The constants are 1280px-era: 20.3125cqi × 1280 = 260px = 16.25rem exactly — the trio was authored as a preferred-width clamp(11.25rem, 20.3125cqi, 16.25rem) for static chrome, and PR #17845 unfolded it into real min/max custom properties so the new resize contract could read them. Numerically faithful, semantically different: the clamp's upper arm meant "preferred width tops out here"; a max-inline-size means "the user may never go past here". At any dock host wider than 1300px the left band boots with zero rightward headroom. Same for the other edges: right band boots pinned at its 320px max (25% > 20rem), bottom at its 200px max (25% > 12.5rem).

2. The drop-restore (dead floor). The same theme file also carries .workstation-workspace .neo-dashboard-dock-edge-band { min-height: 0; min-width: 0 } — the classic flex min-content fix. Its physical properties out-cascade the engine's logical min-inline-size: var(..., 11.25rem) band rule (specificity 0,2,0 vs 0,1,0), so the intended 180px floor computes to 0. A decisive leftward drag previews the band down to 0px (it visually vanishes mid-gesture), the terminal reports resizeSize: 0, DockSplitter#resolveEdgeExtent yields extent 0, and Operations.resizeEdgeZone refuses it — the validation domain is the open interval (0,1). The refusal settles the main thread with restore: true, which writes back the pre-gesture inline style: the operator's "restores the initial width". Measured: far-left drag → immediate width: 0px → settled back to the pre-gesture percentage.

Moderate drags inside the live corridor commit exactly (190px → 12.0558%; 260px → 16.4975%) — the resize machinery itself (#17845 settlement + #17849 terminal parity) is healthy. The corridor is just wrong on both ends.

The Architectural Reality

  • resources/scss/src/apps/workstation/Workspace.scss — .neo-dashboard-dock-edge-band {min-height: 0; min-width: 0} (the floor-killer) and the --dock-edge-band-*-{max,min}-* var trio on .workstation-dock-host.
  • resources/scss/src/dashboard/Container.scss:422-448 — the engine band rules: min/max-inline-size (left/right) and min/max-block-size (top/bottom) read the per-edge vars with engine defaults min 8rem/6rem, max 50%. The dock host is a container-type: size container, so cqi/cqb terms resolve against it.
  • src/main/draggable/Resize.mjs#createState — the clamp window comes exclusively from the target's computed CSS bounds; a consumer that zeroes the floor lets the preview paint a state the model categorically refuses.
  • src/dashboard/dock/interaction/DockSplitter.mjs#getResizeConfig (edge branch) + #resolveEdgeExtent + Operations.resizeEdgeZone (open-interval validation) — the refusal → settleMainThreadResize(restore: true) → exact snapshot restore.

The Fix

App tier (the defect): in resources/scss/src/apps/workstation/Workspace.scss

  1. Replace the blanket min-height: 0; min-width: 0 on .neo-dashboard-dock-edge-band with cross-axis-only zeroing (min-height: 0 for left/right bands, min-width: 0 for top/bottom). The drag axis then carries the engine floor vars again — a full-left drag clamps at 11.25rem and commits, instead of collapsing to 0 and being refused.
  2. Drop the three --dock-edge-band-*-max-* overrides. The caps no longer serve their original purpose (boot width is governed by the document extent's inline style, not the preferred-width var); today they only limit user drags. Removing them lands on the engine default 50% cap — a container-relative bound on each individual edge, not an aggregate center reservation (opposing edges dragged to their caps can meet in the middle; the reviewer's control measures {left: 500, center: 0, right: 500} for a 1000px row with two 50% bands), unpinned at every size. (Wording corrected per PR #17858 review RA-2, 2026-08-29.)

Engine tier (the parity hole): the preview must never paint a state the semantic commit categorically refuses. The edge branch of DockSplitter#getResizeConfig gains a commit-domain floor (minSize: 1), and Resize#createState folds config.minSize into the clamp window. With this, even a consumer whose CSS zeroes the floor gets a 1px-clamped preview whose terminal commits (extent > 0), never a silent restore. layoutMax already keeps extent < 1.

Contract Ledger

(Backfilled per PR #17858 review RA-1, 2026-08-29. All surface anchors verified at PR head d75e11a279.)

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
resizeConfig.minSize — additive optional field on the DragZone resize descriptor (worker → main) PR #17858; consumed in src/main/draggable/Resize.mjs#createState Main-thread clamp floor becomes max(CSS min bound, descriptor minSize); maxSize derivation unchanged Absent or non-finite → contributes 0; behavior byte-identical to pre-PR (CSS bounds alone). Every generic Neo.component.Splitter consumer omits it Fold-site comment in Resize.mjs; learn/guides/uibuildingblocks/DockLayouts.md commit-domain floor clause Unit descriptor contract + full suite 2041/2041; component mutation check (red with the fold stashed)
DockSplitter#getResizeConfig edge branch supplies minSize: 1 src/dashboard/dock/interaction/DockSplitter.mjs (edge branch only) Every edge preview frame stays inside resizeEdgeZone's open (0,1) extent domain — the preview never paints a frame the reducer refuses Split-pair branch deliberately carries no minSize: the pair clamp derives from both members' CSS bounds under a constant total Method JSDoc + inline rationale Unit: edge getResizeConfig toEqual contract + zero-pixel refusal fingerprint through real Operations; component: far-inward real-pointer drag clamps at 1px and commits 1/606
Workstation edge-band bounds disposition resources/scss/src/apps/workstation/Workspace.scss + Viewport.scss The three --dock-edge-band-*-max-* overrides removed → engine per-edge 50% default governs; min resets scoped to the cross axis (bands) and :not(.neo-dashboard-dock-edge-band) (tab containers) → engine rem floor vars govern the drag axis Engine defaults in resources/scss/src/dashboard/Container.scss:422-448 (max 50%, floors 8rem/6rem under the per-edge vars) SCSS comments at both edit sites state the cascade rationale Real-pointer receipts: rightward 315→515px commits 32.69%; far-left clamps at 180px commits 11.4213%, no snap-back

Acceptance Criteria

  • Workstation left/right/bottom edge splitters drag in both directions from boot at desktop widths; outward drags are capped by the engine's 50% default, inward drags clamp at the theme floors and commit there.
  • The full-collapse restore is gone: no reachable pointer position on an edge splitter produces a refused resizeEdgeZone commit in workstation.
  • Edge descriptor carries the commit-domain floor: unit-tier witness that the edge branch emits minSize: 1 alongside awaitWorkerSettlement: true.
  • Component-tier (real main thread) witness: with a target whose CSS min is 0, a far-inward drag clamps at the descriptor floor and the terminal commits (no restore: true settle).
  • Cross-axis flex fix retained: bands still shrink on their cross axis (no min-content overflow regression in the stacked rows).

Out of Scope

  • Re-deriving workstation's preferred band sizes (20.3125cqi etc.) — they are shadowed by document extents at runtime; harmless.
  • Whitebox NL journey extension for edge drags (WorkstationEdgeSplitterNL both-direction tour) — rides the #17844 journey lane; e2e runs in no CI (per the #17853 resolution), so this ticket's teeth are unit/component tier.
  • Boot divergence normalization (document extent: 0.20 vs CSS-clamped paint): removing the caps dissolves the common case; floor-pinned boot at very narrow windows commits truthfully on first drag.

Decision Record impact

aligned-with ADR 0029 (docking design): preview-commit parity — the committed vector equals the final preview by construction — is the design value the engine-tier floor completes for the refusal domain.

Related

  • #17836 (epic — DockLayouts greenfield; this is a film-path polish leaf)
  • PR #17845 (introduced resizable edges + unfolded the clamp; the theme constants predate interactivity)
  • PR #17849 (terminal parity; the healthy commit path proven by the moderate-drag receipts)
  • #17241 (dock visual-language promotion — adjacent; this ticket keeps workstation density policy in workstation)
  • #17844 (journey-tier follow-up home)

Origin Session ID: c98e1ede-1a32-46f3-90ef-aaf37def487d Retrieval Hint: "workstation edge band boot-pinned max dead floor resize agency"