1. summarizeCanvas() walked only node.nodes, so everything inside Section/ColumnLayout/forms (linkedNodes) was missing and live self-reviews could only name top-level sections. It now walks nodes + linked slots (stable key order) and lists id, type and key props (text first, links, main style values as style.<key>, colour-like props) as key="value", within a documented budget (150 nodes, 6000 chars, 300 chars of props per line; detail is dropped before nodes; cuts happen at line boundaries -- a mid-line cut used to make the server refuse the whole review). HtmlBlock code is no longer summarised. applyPatch no longer counts an op with nothing left to set as a change; ops that did nothing are counted per reason. The self-review note and console say why ops were dropped (server and editor reasons, counts only). Real-browser test 8n builds a nested page, sends the real summary through the real WHP review lockdown (php) and checks the nested edit takes effect while link/text edits, unknown ids and deletes are dropped. 3. Background placeholders use ?text=%E2%80%8B (measured: placehold.co then draws no glyphs; the same-colour label left a 4-colour ghost in its PNG); the capture draws no <text> for a zero-width or same-colour label. Pixel test on the case-4 hero markup in both browsers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0185ukXgk6UunFCKyXqLe2Cq
26 lines
1.6 KiB
TypeScript
26 lines
1.6 KiB
TypeScript
// Bundled by scripts/e2e-capture-fidelity.mjs and run in real Firefox + Chromium.
|
|
import { captureHtml } from '../src/utils/self-review-capture';
|
|
import { sanitizeCaptureHtml } from '../src/utils/sanitize-capture-html';
|
|
import { cleanAiProps } from '../src/sitesmith/clean-ai-response';
|
|
|
|
/** Round 6: the background URL the editor really stores for an AI hero placeholder (apply-time cleaner). */
|
|
(window as any).cleanedBgImage = (url: string) => cleanAiProps({ bgImage: url }, 'BackgroundSection').bgImage;
|
|
|
|
(window as any).sanitizeCaptureHtml = sanitizeCaptureHtml;
|
|
/** Capture through the real path, then decode the JPEG and return RGB samples at the given points. */
|
|
(window as any).captureAndSample = async (html: string, points: Array<[number, number]>) => {
|
|
const t0 = performance.now();
|
|
const r = await captureHtml(html, { settleMs: 200, injectFontAwesome: false });
|
|
const ms = Math.round(performance.now() - t0);
|
|
if (!r.ok) return { ok: false, reason: r.reason };
|
|
const url = URL.createObjectURL(r.blob);
|
|
const img = new Image();
|
|
await new Promise<void>((res, rej) => { img.onload = () => res(); img.onerror = () => rej(new Error('decode')); img.src = url; });
|
|
const c = document.createElement('canvas');
|
|
c.width = img.naturalWidth; c.height = img.naturalHeight;
|
|
const ctx = c.getContext('2d')!;
|
|
ctx.drawImage(img, 0, 0);
|
|
URL.revokeObjectURL(url);
|
|
return { ok: true, w: c.width, h: c.height, strips: r.strips, cropped: r.cropped, pageHeight: r.pageHeight, bytes: r.blob.size, ms, samples: points.map(([x, y]) => Array.from(ctx.getImageData(x, y, 1, 1).data.slice(0, 3))) };
|
|
};
|