mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-31 16:33:34 +08:00
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.