Fix target-mode Live evidence capture

Capture below-fold selected targets and each progressive variant reliably for portable evidence bundles. AI-assisted implementation under maintainer direction.
This commit is contained in:
Paul Bakaus
2026-07-13 14:13:40 -07:00
parent e0c19eff28
commit e7ed663ecd
4 changed files with 28 additions and 11 deletions
+19 -11
View File
@@ -192,7 +192,7 @@ try {
renderedArtifacts.variants.push(await captureRenderedElement(session.page, {
filePath: join(runArtifactDir, `variant-${acceptVariant}.png`),
variantId: acceptVariant,
selector: renderedContext.captureSelector,
selector: renderedContext.captureMode === 'target' ? null : renderedContext.captureSelector,
}));
}
const browserTiming = await readBrowserTimingProbe(session.page);
@@ -206,7 +206,7 @@ try {
renderedArtifacts.variants.push(await captureRenderedElement(session.page, {
filePath: join(runArtifactDir, `variant-${variantId}.png`),
variantId,
selector: renderedContext.captureSelector,
selector: renderedContext.captureMode === 'target' ? null : renderedContext.captureSelector,
}));
}
await ensureBenchmarkVariant(session.page, 1);
@@ -310,7 +310,8 @@ try {
root: evidenceRoot ? '.' : artifactRoot.startsWith(`${ROOT}${sep}`) ? relative(ROOT, artifactRoot) : null,
externalRoot: evidenceRoot ? false : !artifactRoot.startsWith(`${ROOT}${sep}`),
report: evidenceRoot ? 'report.json' : null,
screenshotScope: renderedContext.captureSelector,
screenshotScope: renderedContext.captureMode === 'target' ? 'selected-target' : renderedContext.captureSelector,
sourceSelector: renderedContext.captureSelector,
};
if (judgeRendered) {
report.renderedQuality = {
@@ -348,7 +349,7 @@ async function readGenerationSnapshot(tmp, eventId) {
}
async function captureRenderedElement(page, { filePath, selector = null, variantId = null }) {
const geometry = await page.evaluate(({ targetSelector, targetVariantId }) => {
const geometry = await page.evaluate(async ({ targetSelector, targetVariantId }) => {
const wrapper = targetVariantId == null ? null : document.querySelector('[data-impeccable-variants]');
const variant = targetVariantId == null
? null
@@ -358,7 +359,8 @@ async function captureRenderedElement(page, { filePath, selector = null, variant
? document.querySelector(targetSelector)
: variant?.firstElementChild;
if (!element) return null;
element.scrollIntoView({ block: 'center', inline: 'nearest' });
element.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'instant' });
await new Promise((resolveFrame) => requestAnimationFrame(() => requestAnimationFrame(resolveFrame)));
const rect = element.getBoundingClientRect();
const style = getComputedStyle(element);
const core = window.__IMPECCABLE_LIVE_CHROME_CORE__;
@@ -371,10 +373,10 @@ async function captureRenderedElement(page, { filePath, selector = null, variant
const padding = 40;
const pageWidth = Math.max(document.documentElement.scrollWidth, document.body?.scrollWidth || 0);
const pageHeight = Math.max(document.documentElement.scrollHeight, document.body?.scrollHeight || 0);
const x = Math.max(0, rect.left + window.scrollX - padding);
const y = Math.max(0, rect.top + window.scrollY - padding);
const width = Math.max(1, Math.min(pageWidth - x, rect.width + padding * 2));
const height = Math.max(1, Math.min(pageHeight - y, rect.height + padding * 2));
const x = Math.floor(Math.max(0, rect.left + window.scrollX - padding));
const y = Math.floor(Math.max(0, rect.top + window.scrollY - padding));
const width = Math.max(1, Math.min(Math.floor(pageWidth - x), Math.ceil(rect.width + padding * 2)));
const height = Math.max(1, Math.min(Math.floor(pageHeight - y), Math.ceil(rect.height + padding * 2)));
return {
x, y, width, height,
elementWidth: rect.width,
@@ -394,9 +396,15 @@ async function captureRenderedElement(page, { filePath, selector = null, variant
await document.fonts?.ready;
await new Promise((resolveFrame) => requestAnimationFrame(() => requestAnimationFrame(resolveFrame)));
});
await page.screenshot({
const screenshotSelector = selector
|| `[data-impeccable-variant="${variantId}"] > :first-child`;
const screenshotTarget = page.locator(screenshotSelector);
const screenshotTargetCount = await screenshotTarget.count();
if (screenshotTargetCount !== 1) {
throw new Error(`rendered capture selector ${screenshotSelector} matched ${screenshotTargetCount} elements`);
}
await screenshotTarget.screenshot({
path: filePath,
clip: { x: geometry.x, y: geometry.y, width: geometry.width, height: geometry.height },
animations: 'disabled',
caret: 'hide',
});
+1
View File
@@ -38,6 +38,7 @@ export function buildRenderedReviewContext({ fixture, fixtureConfig, action, bri
action: selectedAction,
brief: String(brief || configured.brief || `Apply /${selectedAction} to the selected element while preserving its project identity and functional contract.`),
captureSelector: String(configured.captureSelector || fixtureConfig?.runtime?.pickSelector || 'body'),
captureMode: configured.mode === 'target' ? 'target' : 'selector',
safeContext: {
fixture: String(fixture || ''),
reviewFocus: String(configured.reviewFocus || ''),
+6
View File
@@ -143,12 +143,18 @@ An external fixture has the same shape as a directory in this folder:
{
"evidenceCapture": {
"captureSelector": "section.case-study",
"mode": "target",
"viewport": { "width": 1440, "height": 1080 },
"action": "bolder"
}
}
```
Use `"mode": "target"` when `captureSelector` is the picked element itself;
the original resolves through that selector and each variant resolves through
its exact Live wrapper. Omit it when the selector is a stable ancestor used as
shared page context for every capture.
The bundle contains `report.json`, the original capture, each progressively
delivered variant capture, geometry/overflow facts, hashes, and timing data.
It deliberately cannot run `--judge-rendered`; comparative rubrics, private
+2
View File
@@ -54,6 +54,7 @@ describe('Live rendered quality judge', () => {
runtime: { pickSelector: '.picked' },
evidenceCapture: {
captureSelector: '.selected-section',
mode: 'target',
action: 'bolder',
},
renderedQuality: {
@@ -63,6 +64,7 @@ describe('Live rendered quality judge', () => {
},
});
assert.equal(context.captureSelector, '.selected-section');
assert.equal(context.captureMode, 'target');
assert.equal(context.action, 'bolder');
assert.equal(context.safeContext.reviewFocus, '');
});