Measure the corner the kitten sits in, not the kitten
The overlap test read the badge's own box, which bobs, shrinks 10% when it yields, and grows 4% on hover — so the mascot's answer to "is a card in my way" depended on what it was currently doing. Against the live build, with three cards up and no bubble, it flipped between drawn widths of 293 and 264 on its own: the 0.9 yield scale, cycling. Measure a probe span instead. It sits exactly where the badge sits, never animates, and moves the size variable to .petal-corner so both are sized from the same number. Nothing the mascot does can now change what it yields to. Cards also settle a pixel from the corner routinely — a rail re-pack, a resize, a browser bar appearing — so hold a yield until the card has retreated 24px rather than deciding on the exact edge. Verified in a real browser over CDP: seven overlap depths against the mascot's top edge, each settling once and holding, drawn width steady where it used to swing. Claude-Session: https://claude.ai/code/session_01GJHNvirh7Hzhc9RL3HAvz7
This commit is contained in:
@@ -88,13 +88,18 @@ export function PetalCompanion({
|
||||
const [pickerOpen, setPickerOpen] = useState(false)
|
||||
const rootRef = useRef<HTMLDivElement>(null)
|
||||
const badgeRef = useRef<HTMLButtonElement>(null)
|
||||
// Stand-in for the badge's corner, used only for measuring — see
|
||||
// useCardOverlap. It sits exactly where the badge sits but never bobs, shrinks
|
||||
// or hovers, so what the mascot yields to can't depend on whether it is
|
||||
// currently yielding.
|
||||
const probeRef = useRef<HTMLSpanElement>(null)
|
||||
|
||||
// When suggestion cards stack down into the corner, the kitten fades to
|
||||
// translucent and shrinks a step so the card stays readable and clickable.
|
||||
// It wakes back up whenever it has something to say (bubble) or is being
|
||||
// interacted with (picker open) — except under an open History or Garden
|
||||
// panel, where even a cheer would cover the controls she just reached for.
|
||||
const crowded = useCardOverlap(badgeRef)
|
||||
const crowded = useCardOverlap(probeRef)
|
||||
const faded = crowded.modal || (crowded.cards && !pickerOpen && !bubble)
|
||||
|
||||
// Awake companions (no sleeping clip) don't visibly nap — when the engine
|
||||
@@ -135,7 +140,7 @@ export function PetalCompanion({
|
||||
return (
|
||||
<div
|
||||
ref={rootRef}
|
||||
className="pointer-events-none fixed bottom-4 right-4 z-40 flex flex-col items-end gap-2"
|
||||
className="petal-corner pointer-events-none fixed bottom-4 right-4 z-40 flex flex-col items-end gap-2"
|
||||
>
|
||||
{pickerOpen && (
|
||||
<div
|
||||
@@ -251,6 +256,16 @@ export function PetalCompanion({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<span
|
||||
ref={probeRef}
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute bottom-0 right-0"
|
||||
style={{
|
||||
width: 'var(--petal-companion-size, 9rem)',
|
||||
height: 'var(--petal-companion-size, 9rem)',
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
ref={badgeRef}
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user