{"record":{"id":"ba4599c3295c147f","repo":"pbakaus/impeccable","slug":"no-painted-ancestor-for-svelte-shader-proxy","errorCode":null,"errorMessage":"No painted ancestor for Svelte shader proxy","messagePattern":"No painted ancestor for Svelte shader proxy","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skill/scripts/live-browser.js","lineNumber":8139,"sourceCode":"        nr.left <= er.left + 0.5 &&\n        nr.top <= er.top + 0.5 &&\n        nr.right >= er.right - 0.5 &&\n        nr.bottom >= er.bottom - 0.5;\n      if (containsElement && paintsShaderProxySurface(node)) return node;\n      node = node.parentElement;\n    }\n    return null;\n  }\n\n  // Capture the element (with current annotations baked in) and return\n  // { blob, paper }: the PNG Blob, plus the representative backdrop tone for the\n  // shader's halftone ground (so capture, upload, and shader all agree on what\n  // sits behind the element). Shared between the Go flow (uploads the blob) and\n  // the shader-resume path.\n  async function captureElementFromRenderedAncestor(ms, el, opts) {\n    const doc = el.ownerDocument || document;\n    const captureRoot = findShaderProxyCaptureRoot(el);\n    if (!captureRoot) throw new Error('No painted ancestor for Svelte shader proxy');\n    const rootCanvas = await ms.domToCanvas(captureRoot, opts);\n    const S = opts.scale;\n    const er = el.getBoundingClientRect();\n    const rr = captureRoot.getBoundingClientRect();\n    const sx = (er.left - rr.left) * S;\n    const sy = (er.top - rr.top) * S;\n    const sw = er.width * S;\n    const sh = er.height * S;\n    if (sw <= 0 || sh <= 0) throw new Error('Selected element has no visible capture rect');\n    const crop = doc.createElement('canvas');\n    crop.width = Math.max(1, Math.round(sw));\n    crop.height = Math.max(1, Math.round(sh));\n    const cctx = crop.getContext('2d', { willReadFrequently: true });\n    cctx.drawImage(rootCanvas, sx, sy, sw, sh, 0, 0, crop.width, crop.height);\n    const paper = dominantRgb01(cctx, crop.width, crop.height) || averageRgb01(cctx, crop.width, crop.height);\n    const blob = await new Promise((res) => crop.toBlob(res, 'image/png'));\n    if (!blob) throw new Error('Ancestor crop failed to produce a PNG blob');\n    return { blob, paper };","sourceCodeStart":8121,"sourceCodeEnd":8157,"githubUrl":"https://github.com/pbakaus/impeccable/blob/2bc2879276c1f321a53c4ca99d3371e411329b52/skill/scripts/live-browser.js#L8121-L8157","documentation":"captureElementFromRenderedAncestor throws this when findShaderProxyCaptureRoot(el) returns null — i.e. it cannot find a suitable painted ancestor element for the Svelte shader proxy to rasterize with html-to-image (ms.domToCanvas). The shader effect composites a background captured from an ancestor; without a painted ancestor, capture, upload, and the shader cannot agree on what sits behind the element.","triggerScenarios":"Requesting a shader/halftone effect on a Svelte component whose element has no ancestor that produces a valid capture root — e.g. the element is position:fixed with no positioned/painted ancestor, is inside a detached or display:none subtree, or the proxy DOM structure differs from what findShaderProxyCaptureRoot expects.","commonSituations":"Applying the effect to an element inside a collapsed/hidden panel; Svelte wrapper components adding extra nesting that breaks the ancestor heuristic; running the effect on an element not yet painted (just mounted, off-DOM).","solutions":["Ensure the target element is attached, visible, and has a painted ancestor with layout (no display:none/hidden subtree)","Apply the effect to an inner, actually-painted element rather than a bare proxy/host wrapper","Check findShaderProxyCaptureRoot's expectations against the Svelte component's rendered DOM and adjust the selector heuristic if custom wrappers intervene","Wrap the target in an element with a background/position so a valid capture root exists"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function hasPaintedAncestor(el) {\n  if (!el.isConnected) return false;\n  let n = el.parentElement;\n  while (n) {\n    const cs = getComputedStyle(n);\n    if (cs.display !== 'none' && cs.visibility !== 'hidden') return true;\n    n = n.parentElement;\n  }\n  return false;\n}\n// call before triggering the shader capture","typeGuard":"function isCaptureReady(el) {\n  return el instanceof Element && el.isConnected\n    && el.getBoundingClientRect().width > 0;\n}","tryCatchPattern":"try {\n  await captureElementFromRenderedAncestor(ms, el, opts);\n} catch (err) {\n  if (err.message.includes('No painted ancestor')) {\n    showToast('Effect needs a visible parent element; select the inner content instead.');\n  }\n}","preventionTips":["Only apply shader effects to attached, visible elements with laid-out ancestors","Avoid targeting bare Svelte host/wrapper components; select the painted inner element","Wait for mount/first paint before triggering capture on freshly rendered components","Check display:none/visibility ancestors before enabling the effect in the UI"],"tags":["svelte","shader","capture","dom"],"backgroundTag":"missing-painted-ancestor","analyzedSha":"2bc2879276c1f321a53c4ca99d3371e411329b52","analyzedAt":"2026-09-08T04:51:14.109Z","contentChangedAt":"2026-09-08T04:51:14.109Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}