Skip to content

fix(desktop): keep macOS drag rows draggable over scrolled content - #1613

Merged
xipian1216 merged 1 commit into
masterfrom
fix/desktop/scrolled-content-window-drag
Nov 6, 2026
Merged

xipian1216 merged 1 commit into
masterfrom
fix/desktop/scrolled-content-window-drag

Conversation

@xipian1216

Copy link
Copy Markdown
Collaborator

Summary

On macOS the empty part of the conversation header stopped dragging the window while an image sat near the top of the transcript or partly beneath the header. Chromium 152 (Electron 44) collects each element's app-region box without clipping it to its scroller, and app-region is now a surrogate of the inherited window-drag property. The header precedes the transcript in document order, so any control scrolled beneath it (the image button and its inherited img, a pre.shiki[tabindex=0], tool rows) subtracts its whole box from the header's drag. The same happens to the sidebar top strip and logo row over a long workspace list, to the right panel's tab strip over its pane, and to the Plugins page head over its cards. Upstream master (fcc0ad4621), the npm release (0.2.2-rc.1) and the installed official app (0.2.1-rc.5) carry the same rules with no guard; discussion #9112 measured the hollowed rows without finding the scroll trigger.

  • src/client/styles.ts (Beta and Next) resets the region of the content of the scrollers below chrome rows ([data-conversation-scroll], [data-plugin-body], [role='tree'], dock pane bodies) through @scope, stopping at raised surfaces (dialog, [popover], dialog/menu/listbox/tooltip roles) and at drag rows. The reset on each root also cuts a region inherited from a no-drag pane body.
  • The collapsed Plugins head keeps its 128px resting box but paints 64px; a no-drag ::after trims the drag to the painted part, which also stops the invisible band from dragging the window over visible cards.
  • verify:window-controls checks the conversation header, both sidebar rows, the Plugins head and the dock tab strip: a control placed in each scroller beneath the row must not change the row's drag surface, and a control run without the Desktop sheet must show the official rules alone let it punch the row, so the reset can be retired once upstream guards it. The check reads the surface only after the official recall mark has stayed off for 12 frames.
  • tests/window-styles.spec.ts compares the reset's control list with the official interactive no-drag rule in the frontend CSS and checks the hooks the reset targets.
  • Next README (both languages) notes the one exception to "Next adds no drag override".

Performance

  • The region list no longer changes while content scrolls, so Electron stops re-sending it on every scroll frame (up to 4027 boxes per frame in a 23k-element session, 6056 with the Files pane open) and the browser stops rebuilding its hit-test region.
  • The official recall pulse no longer restyles the transcript: a sidebar collapse and expand in a 16k-element session drops from 2114 to 1266 ms of style work and from 202 to 43 ms of layout, and its longest task from 366 to 178 ms.
  • Cost: about 15% more style time while a reply streams into a 7k-9k element session (task time +6%). A per-control selector list would cut that to 4% with the same coverage; the @scope form is kept.

Validation

  • corepack yarn check, check:desktop-variants and check:bilingual-docs pass; the Next and Beta package checks run 1241 tests each (9 skipped).
  • verify:window-controls in Beta and Next, three parallel rounds.
  • Headless sweep of every drag row over scrolled content (pure npm dsh --profile web, Chromium, darwin mark, Chromium 152's collection rule): 5401 hole points across 19 states without the reset; 0 holes, 0 swallowed controls and 0 invisible drag points across 22 states with it, and the same in the Beta and Next compositions.
  • Real presses on Electron 44.7.0 under Xvfb (Docker, official frontend, xdotool press-and-drag read through the window bounds): with the reset 20 of 20 presses moved the window, without it 11 did not; the image still opens its lightbox and a card in the trimmed band still opens its detail.
  • Not automated: native macOS window movement.

🤖 Generated with Claude Code

Chromium 152 (Electron 44) collects app-region boxes without clipping
them to their scroller, and app-region is now inherited (window-drag).
A control scrolled beneath a chrome row therefore subtracts its whole
box from the row's drag: the conversation header stopped dragging while
an image or code block sat beneath it, and the sidebar rows, the right
panel's tab strip and the Plugins page head lose drag the same way.
Upstream master, the npm release and the official app carry the same
rules with no guard.

The Desktop window styles now reset the region of the content of the
scrollers below chrome rows (transcript, Plugins body, workspace tree,
dock pane bodies), keeping raised surfaces and drag rows, and trim the
collapsed Plugins head to its painted part. verify:window-controls
checks each row with a control run on the official rules alone, and a
spec ties the reset to the official interactive no-drag list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xipian1216
xipian1216 merged commit 233b76c into master Nov 6, 2026
5 checks passed
@xipian1216
xipian1216 deleted the fix/desktop/scrolled-content-window-drag branch November 6, 2026 16:29
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.

1 participant