Build image / build-and-push (push) Successful in 10s
Gadfly on #104: - The handles keyed off `pointer: coarse` but the NudgePad off `md:hidden` (viewport), so a large touchscreen or a narrow mouse window got them disagreeing. Extracted one `isCoarsePointer` in shared.ts that both use — the pad now shows on a coarse pointer, same as the bigger handles. - Wrapped commitLater/nudgeSelected in useCallback([]) — stable identity, so NudgePad doesn't re-render each parent render, and the mount-once keydown effect capturing nudgeSelected is now explicitly safe (a comment spells out the refs-only invariant that makes the empty-deps capture correct). - isCoarsePointer's optional-chained matchMedia keeps it false (mouse defaults) under test/SSR, addressing the constants-file testability note. tsc + build green. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
25 lines
1.5 KiB
TypeScript
25 lines
1.5 KiB
TypeScript
// Shared editor UI constants + tiny helpers used across the object/plop markers
|
|
// and overlays, so colors, handle sizes, and the object-local transform stay in
|
|
// one place instead of drifting between files.
|
|
|
|
export const SELECT_COLOR = '#2f7a3e' // selection stroke/handles
|
|
// Whether the primary pointer is a fingertip rather than a mouse — the one signal
|
|
// the touch affordances key off (bigger handles here, the on-screen nudge pad in
|
|
// the editor), so they can't disagree about what "touch" means. Read once at
|
|
// load; a device doesn't switch its primary pointer mid-session, and the optional
|
|
// chain keeps it false (mouse defaults) under test / SSR where matchMedia is absent.
|
|
export const isCoarsePointer =
|
|
typeof window !== 'undefined' && !!window.matchMedia?.('(pointer: coarse)').matches
|
|
// On-screen size of a drag/resize handle. Bigger on touch so a fingertip can
|
|
// actually grab a resize corner or the rotate knob — 12px is fine for a mouse but
|
|
// frustrating for a thumb (#104).
|
|
export const HANDLE_PX = isCoarsePointer ? 22 : 12
|
|
export const MIN_RADIUS_CM = 1 // smallest plop radius
|
|
export const DIMMED_OPACITY = 0.4 // non-focused objects/plops in focus mode
|
|
|
|
/** SVG transform placing content in an object's local frame: its center point
|
|
* then its clockwise rotation. Plops and overlays render inside this. */
|
|
export function objectTransform(o: { xCm: number; yCm: number; rotationDeg: number }): string {
|
|
return `translate(${o.xCm} ${o.yCm}) rotate(${o.rotationDeg})`
|
|
}
|