Skip to content

fix(layout): pin shell side panels to px so a window resize can't wobble them / 侧栏与终端固定像素尺寸,消除缩放时分割线抖动 - #897

Merged
xintaofei merged 2 commits into
spacering-net:mainfrom
sumanx:fix/shell-panels-px-sizing
Oct 9, 2026

Conversation

@sumanx

@sumanx sumanx commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

What / 改动内容

The divider between the sidebar and the main area jittered left/right while the window was being resized. Cause: react-resizable-panels writes each panel's size as flex-grow rounded to 3 significant digits (20.83% → "20.8", ~0.5px at 1440px wide), and that rounding error changes with every window width. A sidebar sized through it wobbled sub-pixel on each resize step, however precisely the layout was fed in.

  • Size the sidebar and aux panels (and the terminal, vertically) with a px flex-basis through the Panel style prop, which overrides the library's computed style, and let the center / workspace pane take the remainder. The library's layout is still kept in step — it drives handle drags and min/max clamping, and a drag reaches the px values through the existing onLayout → setWidth / setHeight path.
  • Convert px ↔ percent against the space left after the 1px dividers rather than the container width, and compare re-applied layouts at sub-pixel precision (the old 0.25% tolerance skipped small steps until they added up, then snapped).
  • Apply the layout in layout effects, and ignore the onLayout calls a container resize itself causes so they are not persisted as a user resize.
  • Keep the pane contents (sidebar, aux panel, terminal, workspace) as stable elements, so the shell re-rendering on each resize step skips their subtrees.
  • The panel slide transition now covers flex-basis too, since that is what changes when a side panel opens or closes.

Only src/app/workspace/layout.tsx and one rule in globals.css change.

Testing / 测试

  • pnpm lint ., pnpm test (560 files / 8390 tests) and pnpm build pass.
  • Checked by hand in the desktop app: dragging the window edges no longer moves the sidebar divider. Layout is not covered by the jsdom tests, so dragging the sidebar / aux / terminal dividers and the panel slide animation (which now transitions flex-basis) deserve a quick look from a reviewer.
  • Tested on Linux only (GNOME / Wayland, WebKitGTK desktop build); not tried on macOS or Windows.

中文说明

调整窗口大小时,左侧栏与主框架之间的分割线会左右抖动。原因:react-resizable-panels 把每个面板的尺寸写成保留 3 位有效数字的 flex-grow(20.83% 变成 "20.8",1440px 宽时约差 0.5px),而这个舍入误差会随窗口宽度不断变化,所以即使传入的布局再精确,侧栏也会在亚像素级别来回晃动。

  • 侧栏、右侧面板(竖向的终端同理)通过 Panel 的 style 用像素的 flex-basis 定宽(它会覆盖库计算出的样式),中间 / 工作区面板占满剩余空间。库的布局仍保持同步,用于拖动分割线和最小/最大宽度限制;拖动结果经由现有的 onLayout → setWidth / setHeight 写回像素值。
  • 像素与百分比的换算改为基于“扣除 1px 分割线后的剩余空间”,重新应用布局时的比较精度提高到亚像素(旧的 0.25% 容差会跳过小幅变化,累积后再一次性校正)。
  • 在 layout effect 中应用布局,并忽略由容器尺寸变化自身引起的 onLayout,避免被当作用户拖动而持久化。
  • 侧栏、右侧面板、终端、工作区内容改为稳定的元素,外壳在每一步缩放时重渲染会跳过它们的子树。
  • 面板滑动动画的过渡同时覆盖 flex-basis,因为打开/收起侧栏时变化的就是它。

只改动 src/app/workspace/layout.tsx 和 globals.css 中的一条规则。

测试:pnpm lint .、pnpm test(560 个文件 / 8390 个用例)、pnpm build 均通过。在桌面端手动验证:拖动窗口边缘时侧栏分割线不再移动。布局部分没有被 jsdom 测试覆盖,拖动侧栏 / 右侧面板 / 终端分割线以及面板的滑动动画(现在过渡的是 flex-basis)建议审查时再看一下。仅在 Linux(GNOME / Wayland、WebKitGTK 桌面版)测试,未在 macOS、Windows 上试过。

🤖 Generated with Claude Code

sumanx and others added 2 commits October 8, 2026 23:50
…ble them

react-resizable-panels writes each panel's size as `flex-grow` rounded
to 3 significant digits (20.83% -> "20.8", ~0.5px at 1440px wide), by an
error that changes with every window width. A sidebar sized through it
wobbled sub-pixel on each resize step, however precisely the layout was
fed in, so the divider jittered while the window was dragged.

- Size the sidebar and aux panels (and the terminal, vertically) with a
  px `flex-basis` through the Panel `style` prop, which overrides the
  library's computed style, and let the center / workspace pane take the
  remainder. The library layout stays in step for handle drags and
  min/max clamping, and a drag reaches the px values through the
  existing onLayout -> setWidth / setHeight path.
- Convert px <-> percent against the space left after the 1px dividers,
  not the container width, and compare re-applied layouts at sub-pixel
  precision (the old 0.25% tolerance skipped small steps until they
  added up, then snapped).
- Apply the layout in layout effects, and ignore the onLayout calls a
  container resize itself causes, so they are not persisted as a user
  resize.
- Keep the pane contents (sidebar, aux panel, terminal, workspace) as
  stable elements so the shell re-rendering on each resize step skips
  their subtrees.
- The slide transition on the panels now covers `flex-basis` as well,
  since that is what changes when a side panel opens or closes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…he resize guard

- The side columns scaled down for a narrow window could drop below
  their minimum widths (and the terminal below its minimum height),
  where react-resizable-panels clamps its own layout. The drawn panels
  then disagreed with the layout a handle drag starts from by up to
  ~100px, and the aux panel could get narrower than the width that keeps
  the Windows/Linux title-bar overlay off the center column. Raise each
  open column back to its minimum and take the difference the way the
  library's clamp does: from the sidebar down to its own minimum, then
  from the center.
- The container-resize guard was a flag raised in the ResizeObserver and
  lowered by the layout effect that applies the new layout. Two
  observations that cancel out before React renders make that render
  bail out with no layout effect, leaving the flag raised, so every
  later drag was dropped until the next window resize. Compare the last
  observed container size with the size the layout was last applied for
  instead. The first measurement now counts as a resize too, so applying
  it no longer persists scaled-down widths.
- Update the comments that still described percent-sized panels and a
  flex-grow-only slide.
@xintaofei

Copy link
Copy Markdown
Collaborator

codeg work task 294 is done — #897 (2 files, +79/-39).

@xintaofei
xintaofei merged commit 3cc0603 into spacering-net:main Oct 9, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants