程序员阿江(Relakkes) 8f3a2f092c fix(desktop): let the dragged pet reach the macOS menu bar
The mascot sits at the bottom of a mostly transparent 384x400 window, so
clamping a drag against the mascot rather than against the whole window
deliberately asks for a negative y that pushes the padding above it off-screen.
macOS runs a visible window's frame through
-[NSWindow constrainFrameRect:toScreen:], which rewrites any y above the work
area back down to its top edge -- silently. The request was refused, the
controller never noticed, and the mascot stranded a padding-height below the
menu bar: 208px of dead band on this display, with
~/.claude/cc-haha/pet-window.json recording y=-175 for a window that never
left y=33.

Measured on Electron 42.7.0 / Darwin 25.4: only the top edge is constrained.
Off-screen x and off-screen bottom y are kept verbatim, which is why the other
three edges always worked. Nothing else escapes it -- not window level (all
eight, up to screen-saver), movable, frame, transparent, panel type, and not
positioning the window while hidden, since showInactive re-runs the constraint.
enableLargerThanScreen is the one switch that skips the method, and it leaves
size, getContentBounds and ordinary moves untouched. clampPetWindowPosition is
already a complete four-edge bound, so opting out makes it the only clamp
rather than removing one.

Restoring then exposed a second problem the constraint had been hiding. A saved
position deliberately leaves the padding off-screen, but the window is created
before the renderer reports any region, so getPetWindowBounds clamped it against
the whole window and opened a padding-height lower -- a jump of zero while the
window was pinned, 208px once it is not. Persist the mascot box next to the
position so the first frame lands where the drag left it. State written without
one still restores the way it always did, and an empty box is dropped rather
than trusted.

The fake window accepted every y, so no top-edge assertion could fail and the
suite stayed green through all of this -- the same blind spot that let the
Windows DIP bugs hide. It now applies the constraint the way AppKit does, only
while visible and only when the constructor options did not opt out, so the
assertions prove the fix rather than the platform. Reverting either half turns
the new cases red.
2026-07-26 02:38:03 +08:00
..