/** * Pure clamp/flip math for the floating label that follows the cursor while * the user is picking an element in the in-app browser. Lives in its own * electron-free module so the geometry can be unit-tested without spinning * up an Electron preload context (`PickPreload.ts` itself imports * `electron` and `react-grab/primitives`, which can't load under vitest). * * - Horizontally pins the label to `targetLeft`, clamped into * `[VIEWPORT_MARGIN, viewportWidth - labelWidth - VIEWPORT_MARGIN]`. * - Vertically prefers above the target. If the label would overflow the * top, flips below; if THAT also overflows the bottom, pins to the * bottom margin (better to overlap the highlight than disappear). *//** Distance in CSS pixels between the highlight and the floating label. */export const LABEL_GAP = 4;/** Minimum padding the label keeps from any viewport edge. */export const VIEWPORT_MARGIN = 4;export function computeLabelPosition(input: { targetLeft: number; targetTop: number; targetBottom: number; labelWidth: number; labelHeight: number; viewportWidth: number; viewportHeight: number;}): { x: number; y: number } { const { targetLeft, targetTop, targetBottom, labelWidth, labelHeight } = input; const { viewportWidth, viewportHeight } = input; let x = targetLeft; const maxX = viewportWidth - labelWidth - VIEWPORT_MARGIN; if (x > maxX) x = maxX; if (x < VIEWPORT_MARGIN) x = VIEWPORT_MARGIN; let y = targetTop - labelHeight - LABEL_GAP; if (y < VIEWPORT_MARGIN) { y = targetBottom + LABEL_GAP; if (y + labelHeight > viewportHeight - VIEWPORT_MARGIN) { y = Math.max(VIEWPORT_MARGIN, viewportHeight - labelHeight - VIEWPORT_MARGIN); } } return { x, y };}
/**
* Pure clamp/flip math for the floating label that follows the cursor while
* the user is picking an element in the in-app browser. Lives in its own
* electron-free module so the geometry can be unit-tested without spinning
* up an Electron preload context (`PickPreload.ts` itself imports
* `electron` and `react-grab/primitives`, which can't load under vitest).
*
* - Horizontally pins the label to `targetLeft`, clamped into
* `[VIEWPORT_MARGIN, viewportWidth - labelWidth - VIEWPORT_MARGIN]`.
* - Vertically prefers above the target. If the label would overflow the
* top, flips below; if THAT also overflows the bottom, pins to the
* bottom margin (better to overlap the highlight than disappear).
*/
/** Distance in CSS pixels between the highlight and the floating label. */
export const LABEL_GAP = 4;
/** Minimum padding the label keeps from any viewport edge. */
export const VIEWPORT_MARGIN = 4;
export function computeLabelPosition(input: {
targetLeft: number;
targetTop: number;
targetBottom: number;
labelWidth: number;
labelHeight: number;
viewportWidth: number;
viewportHeight: number;
}): { x: number; y: number } {
const { targetLeft, targetTop, targetBottom, labelWidth, labelHeight } = input;
const { viewportWidth, viewportHeight } = input;
let x = targetLeft;
const maxX = viewportWidth - labelWidth - VIEWPORT_MARGIN;
if (x > maxX) x = maxX;
if (x < VIEWPORT_MARGIN) x = VIEWPORT_MARGIN;
let y = targetTop - labelHeight - LABEL_GAP;
if (y < VIEWPORT_MARGIN) {
y = targetBottom + LABEL_GAP;
if (y + labelHeight > viewportHeight - VIEWPORT_MARGIN) {
y = Math.max(VIEWPORT_MARGIN, viewportHeight - labelHeight - VIEWPORT_MARGIN);
}
}
return { x, y };
}