From 6f6af815af7db71602d2fb4bf95f84e677d2414d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 1 Sep 2026 22:02:32 +0000
Subject: [PATCH] Sync generated provider output
---
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.pi/skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
.../skills/impeccable/scripts/live-browser.js | 253 +++++++++---------
16 files changed, 2080 insertions(+), 1968 deletions(-)
diff --git a/.agents/skills/impeccable/scripts/live-browser.js b/.agents/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.agents/skills/impeccable/scripts/live-browser.js
+++ b/.agents/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.claude/skills/impeccable/scripts/live-browser.js b/.claude/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.claude/skills/impeccable/scripts/live-browser.js
+++ b/.claude/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.cursor/skills/impeccable/scripts/live-browser.js b/.cursor/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.cursor/skills/impeccable/scripts/live-browser.js
+++ b/.cursor/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.gemini/skills/impeccable/scripts/live-browser.js b/.gemini/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.gemini/skills/impeccable/scripts/live-browser.js
+++ b/.gemini/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.github/skills/impeccable/scripts/live-browser.js b/.github/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.github/skills/impeccable/scripts/live-browser.js
+++ b/.github/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.grok/skills/impeccable/scripts/live-browser.js b/.grok/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.grok/skills/impeccable/scripts/live-browser.js
+++ b/.grok/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.hermes/skills/impeccable/scripts/live-browser.js b/.hermes/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.hermes/skills/impeccable/scripts/live-browser.js
+++ b/.hermes/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.kiro/skills/impeccable/scripts/live-browser.js b/.kiro/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.kiro/skills/impeccable/scripts/live-browser.js
+++ b/.kiro/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.opencode/skills/impeccable/scripts/live-browser.js b/.opencode/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.opencode/skills/impeccable/scripts/live-browser.js
+++ b/.opencode/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.pi/skills/impeccable/scripts/live-browser.js b/.pi/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.pi/skills/impeccable/scripts/live-browser.js
+++ b/.pi/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.qoder/skills/impeccable/scripts/live-browser.js b/.qoder/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.qoder/skills/impeccable/scripts/live-browser.js
+++ b/.qoder/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.rovodev/skills/impeccable/scripts/live-browser.js b/.rovodev/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.rovodev/skills/impeccable/scripts/live-browser.js
+++ b/.rovodev/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.trae-cn/skills/impeccable/scripts/live-browser.js b/.trae-cn/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.trae-cn/skills/impeccable/scripts/live-browser.js
+++ b/.trae-cn/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.trae/skills/impeccable/scripts/live-browser.js b/.trae/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.trae/skills/impeccable/scripts/live-browser.js
+++ b/.trae/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/.vibe/skills/impeccable/scripts/live-browser.js b/.vibe/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/.vibe/skills/impeccable/scripts/live-browser.js
+++ b/.vibe/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;
diff --git a/plugin/skills/impeccable/scripts/live-browser.js b/plugin/skills/impeccable/scripts/live-browser.js
index 1f373a894..da026e255 100644
--- a/plugin/skills/impeccable/scripts/live-browser.js
+++ b/plugin/skills/impeccable/scripts/live-browser.js
@@ -6212,6 +6212,72 @@
showToast('The previous live session no longer matches the source file, so it was discarded. Pick an element to start fresh.', 6000);
}
+ function isJsxSourceFile(filePath) {
+ return /\.[cm]?[jt]sx$/i.test(String(filePath || ''));
+ }
+
+ function completeSourceInjection(wrapper, sessionId, opts) {
+ recoveryWaitingForAnchor = false;
+ if (pendingVariantAnchorRetryObserver) {
+ pendingVariantAnchorRetryObserver.disconnect();
+ pendingVariantAnchorRetryObserver = null;
+ }
+
+ const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
+ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
+ arrivedVariants = variants.length;
+ expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
+ if (arrivedVariants <= 0) {
+ if (state === 'GENERATING') {
+ // Mid-generation the source legitimately holds a scaffold wrapper
+ // with no variants yet (the server-side preflight wraps before the
+ // agent writes). Tearing the session down here would destroy an
+ // in-flight generation; stay in GENERATING — the variant observer
+ // is armed and the server re-delivers a missed `done`.
+ if (!opts.generationCompleted) {
+ console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
+ return;
+ }
+ // Generation finished, yet the read shows only the scaffold: the
+ // source view is stale and no further event will fire. Re-read a
+ // few times before surfacing recovery — a single silent return
+ // here would strand the tab in GENERATING forever.
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
+ + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ if (arrivedVariants > 0) return;
+ injectVariantsFromSource(opts.filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ }
+ recoverEmptyCycling('source-fallback-empty');
+ return;
+ }
+ const saved = loadSession();
+ const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
+ visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
+ ? previousVisibleVariant
+ : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
+ showVariantInDOM(sessionId, visibleVariant);
+
+ selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+
+ setLiveState('CYCLING');
+ recoveryWaitingForAnchor = false;
+ hideShaderOverlay();
+ showOrUpdateCyclingBar();
+ disableInlineEdit();
+ refreshParamsPanel();
+ positionBar();
+ saveSession();
+ completeParameterGenerationIfReady();
+ console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ }
+
/**
* No-HMR fallback: fetch the raw source file from the live server,
* parse it, extract the variant wrapper, and inject it into the live DOM.
@@ -6229,14 +6295,53 @@
return;
}
rememberSessionFileMeta({ file: filePath });
+ if (isJsxSourceFile(filePath)) {
+ const liveWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ if (liveWrapper && liveWrapper.querySelector('[data-impeccable-variant]:not([data-impeccable-variant="original"])')) {
+ completeSourceInjection(liveWrapper, sessionId, { ...opts, filePath });
+ return;
+ }
+ // #454: never fetch/parse JSX. Missing wrap waits for mount (closed
+ // modal / other route). Insert scaffolds stay for late HMR. A replace
+ // scaffold with no variants after retries is a failed generation.
+ if (opts.generationCompleted && sessionId === currentSessionId) {
+ const attempt = opts.attempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ return;
+ }
+ if (!liveWrapper) {
+ showToast(
+ "Variants ready. If the picked element isn't visible, retrace the path that revealed it - they'll appear automatically.",
+ 15000,
+ );
+ return;
+ }
+ if (liveWrapper.dataset.impeccableMode !== 'insert') {
+ recoverEmptyCycling('source-fallback-empty');
+ }
+ return;
+ }
+ if (opts.orphanDiscard && !liveWrapper && sessionId === currentSessionId) {
+ const attempt = opts._orphanAttempt || 0;
+ if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
+ setTimeout(() => {
+ if (sessionId !== currentSessionId) return;
+ if (state !== 'GENERATING' && state !== 'CYCLING') return;
+ injectVariantsFromSource(filePath, sessionId, { ...opts, _orphanAttempt: attempt + 1 });
+ }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
+ }
+ }
+ return;
+ }
const url = 'http://localhost:' + PORT + '/source?token=' + TOKEN + '&path=' + encodeURIComponent(filePath);
fetch(url)
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
.then(html => {
const parser = new DOMParser();
- let srcWrapper = null;
-
- // Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
const startMark = '';
const endMark = '';
const startIdx = html.indexOf(startMark);
@@ -6244,8 +6349,8 @@
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
? html.slice(startIdx + startMark.length, endIdx).trim()
: html;
- const doc = parser.parseFromString(normalizeSourceFallbackBlock(block, filePath), 'text/html');
- srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
+ const doc = parser.parseFromString(block, 'text/html');
+ const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!srcWrapper) {
console.warn('[impeccable] Variant wrapper not found in source file.');
// A resumed cycling session whose wrapper is gone from source is an
@@ -6270,93 +6375,33 @@
return;
}
- const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
- const wrapper = srcWrapper.cloneNode(true);
-
- // Wrapper already in DOM (wrap HMR landed, variant insert did not).
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (existingWrapper) {
+ const wrapper = srcWrapper.cloneNode(true);
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
- } else {
- const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
- if (!origContent) return;
-
- const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
- if (!liveEl) {
- console.warn('[impeccable] Could not find original element in live DOM.');
- enterRecoveryWaitingForAnchor({
- filePath,
- sessionId,
- srcWrapper,
- checkpointReason: 'variant_anchor_missing',
- trackScroll: false,
- });
- return;
- }
-
- liveEl.parentElement.replaceChild(wrapper, liveEl);
- }
- recoveryWaitingForAnchor = false;
- if (pendingVariantAnchorRetryObserver) {
- pendingVariantAnchorRetryObserver.disconnect();
- pendingVariantAnchorRetryObserver = null;
- }
-
- // Update state: count variants, preserving the user's current variant
- // when a late HMR/source reinjection lands after they have cycled.
- const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])');
- arrivedVariants = variants.length;
- expectedVariants = parseInt(wrapper.dataset.impeccableVariantCount || arrivedVariants);
- if (arrivedVariants <= 0) {
- if (state === 'GENERATING') {
- // Mid-generation the source legitimately holds a scaffold wrapper
- // with no variants yet (the server-side preflight wraps before the
- // agent writes). Tearing the session down here would destroy an
- // in-flight generation; stay in GENERATING — the variant observer
- // is armed and the server re-delivers a missed `done`.
- if (!opts.generationCompleted) {
- console.log('[impeccable] Source has scaffold but no variants yet; still generating.');
- return;
- }
- // Generation finished, yet the read shows only the scaffold: the
- // source view is stale and no further event will fire. Re-read a
- // few times before surfacing recovery — a single silent return
- // here would strand the tab in GENERATING forever.
- const attempt = opts.attempt || 0;
- if (attempt < COMPLETED_SOURCE_FALLBACK_RETRIES) {
- console.log('[impeccable] Generation is done but source shows no variants yet; retrying read ('
- + (attempt + 1) + '/' + COMPLETED_SOURCE_FALLBACK_RETRIES + ').');
- setTimeout(() => {
- if (state !== 'GENERATING' || currentSessionId !== sessionId) return;
- if (arrivedVariants > 0) return;
- injectVariantsFromSource(filePath, sessionId, { ...opts, attempt: attempt + 1 });
- }, COMPLETED_SOURCE_FALLBACK_RETRY_MS);
- return;
- }
- }
- recoverEmptyCycling('source-fallback-empty');
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
return;
}
- const saved = loadSession();
- const savedVisibleVariant = saved && saved.id === sessionId ? saved.visible : 0;
- visibleVariant = previousVisibleVariant > 0 && previousVisibleVariant <= arrivedVariants
- ? previousVisibleVariant
- : (savedVisibleVariant > 0 && savedVisibleVariant <= arrivedVariants ? savedVisibleVariant : 1);
- showVariantInDOM(sessionId, visibleVariant);
- // Update selectedElement to the visible variant's content
- selectedElement = pickVariantContent(wrapper, visibleVariant) || wrapper.parentElement;
+ const wrapper = srcWrapper.cloneNode(true);
+ const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
+ if (!origContent) return;
- setLiveState('CYCLING');
- recoveryWaitingForAnchor = false;
- hideShaderOverlay();
- showOrUpdateCyclingBar();
- disableInlineEdit();
- refreshParamsPanel();
- positionBar();
- saveSession();
- completeParameterGenerationIfReady();
- console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
+ const liveEl = resolveLiveInjectionAnchor(origContent.outerHTML);
+ if (!liveEl) {
+ console.warn('[impeccable] Could not find original element in live DOM.');
+ enterRecoveryWaitingForAnchor({
+ filePath,
+ sessionId,
+ srcWrapper,
+ checkpointReason: 'variant_anchor_missing',
+ trackScroll: false,
+ });
+ return;
+ }
+
+ liveEl.parentElement.replaceChild(wrapper, liveEl);
+ completeSourceInjection(wrapper, sessionId, { ...opts, filePath });
})
.catch(err => {
console.error('[impeccable] Failed to fetch source:', err);
@@ -6364,44 +6409,6 @@
});
}
- function normalizeSourceFallbackBlock(block, filePath) {
- if (!/\.[cm]?[jt]sx$/i.test(String(filePath || ''))) return block;
- return String(block)
- .replace(
- /',
- )
- .replace(/\bclassName\s*=\s*\{\s*`([^`]*?)`\s*\}/g, (_match, value) => {
- const literalClasses = value.replace(/\$\{[^}]*\}/g, ' ').replace(/\s+/g, ' ').trim();
- return literalClasses ? 'class="' + escapeHtml(literalClasses) + '"' : '';
- })
- .replace(/\bclassName\s*=/g, 'class=')
- .replace(/\sstyle=\{\{([\s\S]*?)\}\}/g, (_match, body) => {
- const css = jsxStyleObjectToCss(body);
- return css ? ' style="' + escapeHtml(css) + '"' : '';
- });
- }
-
- function jsxStyleObjectToCss(body) {
- const declarations = [];
- const re = /(["'][^"']+["']|[A-Za-z_$][\w$-]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|(-?\d+(?:\.\d+)?))/g;
- let match;
- while ((match = re.exec(String(body || '')))) {
- const prop = jsxStylePropToCss(match[1]);
- const value = match[2] ?? match[3] ?? match[4] ?? '';
- if (!prop || value === '') continue;
- declarations.push(prop + ': ' + value);
- }
- return declarations.join('; ');
- }
-
- function jsxStylePropToCss(prop) {
- let out = String(prop || '').trim().replace(/^["']|["']$/g, '');
- if (!out) return '';
- if (out.startsWith('--')) return out;
- return out.replace(/[A-Z]/g, (ch) => '-' + ch.toLowerCase()).replace(/^-ms-/, '-ms-');
- }
-
function buildSvelteExpressionTextMap(sourceOriginal, liveOriginal) {
const map = new Map();
if (!sourceOriginal || !liveOriginal) return map;