diff --git a/packages/imhotep-solver/src/proofs.ts b/packages/imhotep-solver/src/proofs.ts index aac024b..8676f57 100644 --- a/packages/imhotep-solver/src/proofs.ts +++ b/packages/imhotep-solver/src/proofs.ts @@ -59,9 +59,20 @@ function buildFailedPredicate( // --- Directional gap (leftOf / rightOf / above / below) --- if (hasGap) { - const gap = metrics.observedGap ?? metrics.gap ?? 0; + const gap = metrics.observedGap ?? metrics.gap; + if (gap === undefined) return undefined; const min = metrics.minGap ?? 0; const max = metrics.maxGap ?? Infinity; + const hasSubjectRect = + metrics.subjectLeft !== undefined && + metrics.subjectTop !== undefined && + metrics.subjectRight !== undefined && + metrics.subjectBottom !== undefined; + const hasRefRect = + metrics.refLeft !== undefined && + metrics.refTop !== undefined && + metrics.refRight !== undefined && + metrics.refBottom !== undefined; return { ...base, op: gap < min ? '<' : '>', @@ -70,68 +81,71 @@ function buildFailedPredicate( measuredGap: gap, expectedMinGap: Number.isFinite(min) ? min : undefined, expectedMaxGap: Number.isFinite(max) ? max : undefined, - subjectRect: - metrics.subjectLeft !== undefined - ? { - left: metrics.subjectLeft, - top: metrics.subjectTop ?? 0, - right: metrics.subjectRight ?? 0, - bottom: metrics.subjectBottom ?? 0, - } - : undefined, - referenceRect: - metrics.refLeft !== undefined - ? { - left: metrics.refLeft, - top: metrics.refTop ?? 0, - right: metrics.refRight ?? 0, - bottom: metrics.refBottom ?? 0, - } - : undefined, + subjectRect: hasSubjectRect + ? { + left: metrics.subjectLeft, + top: metrics.subjectTop, + right: metrics.subjectRight, + bottom: metrics.subjectBottom, + } + : undefined, + referenceRect: hasRefRect + ? { + left: metrics.refLeft, + top: metrics.refTop, + right: metrics.refRight, + bottom: metrics.refBottom, + } + : undefined, }; } // --- Containment / inside (unique overflow metrics) --- if (kind === 'inside') { - const overflowLeft = metrics.overflowLeft ?? 0; - const overflowTop = metrics.overflowTop ?? 0; - const overflowRight = metrics.overflowRight ?? 0; - const overflowBottom = metrics.overflowBottom ?? 0; + const hasSubjectRect = + metrics.subjectLeft !== undefined && + metrics.subjectTop !== undefined && + metrics.subjectRight !== undefined && + metrics.subjectBottom !== undefined; + const hasRefRect = + metrics.refLeft !== undefined && + metrics.refTop !== undefined && + metrics.refRight !== undefined && + metrics.refBottom !== undefined; return { ...base, op: 'not-contained', left: 0, right: 0, overflowEdges: { - left: overflowLeft, - top: overflowTop, - right: overflowRight, - bottom: overflowBottom, + left: metrics.overflowLeft ?? 0, + top: metrics.overflowTop ?? 0, + right: metrics.overflowRight ?? 0, + bottom: metrics.overflowBottom ?? 0, }, - subjectRect: - metrics.subjectLeft !== undefined - ? { - left: metrics.subjectLeft, - top: metrics.subjectTop ?? 0, - right: metrics.subjectRight ?? 0, - bottom: metrics.subjectBottom ?? 0, - } - : undefined, - referenceRect: - metrics.refLeft !== undefined - ? { - left: metrics.refLeft, - top: metrics.refTop ?? 0, - right: metrics.refRight ?? 0, - bottom: metrics.refBottom ?? 0, - } - : undefined, + subjectRect: hasSubjectRect + ? { + left: metrics.subjectLeft, + top: metrics.subjectTop, + right: metrics.subjectRight, + bottom: metrics.subjectBottom, + } + : undefined, + referenceRect: hasRefRect + ? { + left: metrics.refLeft, + top: metrics.refTop, + right: metrics.refRight, + bottom: metrics.refBottom, + } + : undefined, }; } // --- Size threshold (atLeast / atMost / between) --- if (isSize && kind !== 'aspectRatio') { - const observed = metrics.observed ?? metrics.value ?? 0; + const observed = metrics.observed ?? metrics.value; + if (observed === undefined) return undefined; const min = metrics.min ?? -Infinity; const max = metrics.max ?? Infinity; return { @@ -147,7 +161,8 @@ function buildFailedPredicate( // --- Aspect ratio --- if (kind === 'aspectRatio') { - const observed = metrics.observed ?? 0; + const observed = metrics.observed; + if (observed === undefined) return undefined; const minRatio = metrics.minRatio ?? -Infinity; const maxRatio = metrics.maxRatio ?? Infinity; return { @@ -163,7 +178,8 @@ function buildFailedPredicate( // --- Alignment (alignedWith / centeredWithin) --- if (hasAxis || kind === 'centeredWithin') { - const delta = metrics.delta ?? metrics.deltaX ?? metrics.deltaY ?? 0; + const delta = metrics.delta ?? metrics.deltaX ?? metrics.deltaY; + if (delta === undefined) return undefined; const tolerance = metrics.tolerance ?? 0; return { ...base, @@ -188,16 +204,21 @@ function synthesizeGenericFailedPredicate( ): Proof['failedPredicate'] { const keys = Object.keys(metrics); if (keys.length >= 2) { + const left = metrics[keys[0]]; + const right = metrics[keys[1]]; + if (left === undefined || right === undefined) return undefined; return { op: '<', - left: metrics[keys[0]] ?? 0, - right: metrics[keys[1]] ?? 0, + left, + right, }; } if (keys.length === 1) { + const val = metrics[keys[0]]; + if (val === undefined) return undefined; return { op: '<', - left: metrics[keys[0]] ?? 0, + left: val, right: 0, }; }