From e2279ddab1e7b3b593493fc3d2ac8a76ca9d714d Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Tue, 21 Apr 2026 15:45:46 -0700 Subject: [PATCH] fix(live): restart shader and re-anchor selection on HMR page reload Bun's HTML HMR does a full page reload when the live-wrap.mjs edit lands, so the shader canvas is destroyed and in-memory capture blob is lost. resumeSession rehydrated state from localStorage but never restarted the overlay, so the wait went dead. resumeSession now re-captures the original's content (still in the DOM inside the variant wrapper) and restarts showShaderOverlay when we reload mid-generation. Also swaps the two remaining :first-child selectors in resumeSession for pickVariantContent so the earlier loose-children robustness fix carries across reloads. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .pi/skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- .../skills/impeccable/scripts/live-browser.js | 22 +++++++++++++++++-- 12 files changed, 240 insertions(+), 24 deletions(-) diff --git a/.agents/skills/impeccable/scripts/live-browser.js b/.agents/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.agents/skills/impeccable/scripts/live-browser.js +++ b/.agents/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.claude/skills/impeccable/scripts/live-browser.js b/.claude/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.claude/skills/impeccable/scripts/live-browser.js +++ b/.claude/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.cursor/skills/impeccable/scripts/live-browser.js b/.cursor/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.cursor/skills/impeccable/scripts/live-browser.js +++ b/.cursor/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.gemini/skills/impeccable/scripts/live-browser.js b/.gemini/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.gemini/skills/impeccable/scripts/live-browser.js +++ b/.gemini/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.github/skills/impeccable/scripts/live-browser.js b/.github/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.github/skills/impeccable/scripts/live-browser.js +++ b/.github/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.kiro/skills/impeccable/scripts/live-browser.js b/.kiro/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.kiro/skills/impeccable/scripts/live-browser.js +++ b/.kiro/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.opencode/skills/impeccable/scripts/live-browser.js b/.opencode/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.opencode/skills/impeccable/scripts/live-browser.js +++ b/.opencode/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.pi/skills/impeccable/scripts/live-browser.js b/.pi/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.pi/skills/impeccable/scripts/live-browser.js +++ b/.pi/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.rovodev/skills/impeccable/scripts/live-browser.js b/.rovodev/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.rovodev/skills/impeccable/scripts/live-browser.js +++ b/.rovodev/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.trae-cn/skills/impeccable/scripts/live-browser.js b/.trae-cn/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.trae-cn/skills/impeccable/scripts/live-browser.js +++ b/.trae-cn/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/.trae/skills/impeccable/scripts/live-browser.js b/.trae/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/.trae/skills/impeccable/scripts/live-browser.js +++ b/.trae/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; } diff --git a/source/skills/impeccable/scripts/live-browser.js b/source/skills/impeccable/scripts/live-browser.js index 1ee940530..19a234da2 100644 --- a/source/skills/impeccable/scripts/live-browser.js +++ b/source/skills/impeccable/scripts/live-browser.js @@ -2177,8 +2177,8 @@ void main() { // Find the visible variant's content element for highlight positioning. // Try the visible variant first, fall back to the original's content. - const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + const visEl = visibleVariant > 0 ? pickVariantContent(wrapper, visibleVariant) : null; + const origEl = pickVariantContent(wrapper, 'original'); selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) @@ -2192,6 +2192,24 @@ void main() { // Start observing for more variants AFTER initial setup if (variantObserver) variantObserver.disconnect(); variantObserver = startVariantObserver(currentSessionId); + + // If we reloaded mid-generation (Bun's HTML HMR destroys the shader + // canvas), re-capture the original's content and restart the shader so + // the wait doesn't go dead. + if (state === 'GENERATING' && origEl) { + (async () => { + try { + const rect = origEl.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) return; + const blob = await captureElementToBlob(origEl, null, rect); + if (blob && state === 'GENERATING') { + showShaderOverlay(origEl, blob, rect); + } + } catch (err) { + console.warn('[impeccable] shader resume failed:', err); + } + })(); + } return true; }