Sync generated provider output

This commit is contained in:
github-actions[bot]
2026-07-08 00:25:53 +00:00
parent 49ae0384b9
commit e813a16d22
13 changed files with 1092 additions and 299 deletions
@@ -153,6 +153,7 @@
let scrollLockRaf = null;
let scrollLockAbort = null;
const SCROLL_ANCHOR_LOCK_ID = 'impeccable-scroll-anchor-lock';
const VARIANT_STATE_STYLE_ID = 'impeccable-variant-state';
// Dedicated key for scroll position - SEPARATE from LS_KEY so that
// saveSession's state updates don't clobber a carefully-captured scrollY.
@@ -3035,16 +3036,26 @@
function applyParamValue(variantEl, param, value) {
if (!variantEl) return;
const attr = 'data-p-' + param.id;
if (param.kind === 'range') {
variantEl.style.setProperty('--p-' + param.id, String(value));
} else if (param.kind === 'toggle') {
if (param.kind === 'toggle') {
const on = !!value;
variantEl.style.setProperty('--p-' + param.id, on ? '1' : '0');
if (on) variantEl.setAttribute(attr, 'on');
else variantEl.removeAttribute(attr);
} else if (param.kind === 'steps') {
variantEl.setAttribute(attr, String(value));
}
// Svelte component variants are client-mounted into
// [data-impeccable-component-mount] with no [data-impeccable-variant="N"]
// wrapper for the state stylesheet to target, and the element is not SSR'd,
// so there is no React hydration to mismatch. Drive range/toggle --p-* inline
// on the mounted element so scoped preview CSS resolves them.
if (svelteComponentSession?.sessionId === currentSessionId) {
if (param.kind === 'range') variantEl.style.setProperty('--p-' + param.id, String(value));
else if (param.kind === 'toggle') variantEl.style.setProperty('--p-' + param.id, value ? '1' : '0');
return;
}
// range/toggle --p-* custom properties are driven through the injected
// variant-state stylesheet so we never mutate inline style on SSR'd divs.
updateVariantStateStylesheet(currentSessionId, visibleVariant);
}
function applyParamDefaults(variantEl, params) {
@@ -4714,6 +4725,7 @@
paramsCurrentValues = {};
tuneOpen = false;
hideParamsPanel();
if (currentSessionId && visibleVariant) updateVariantStateStylesheet(currentSessionId, visibleVariant);
return;
}
applyParamDefaults(variantEl, params);
@@ -4771,20 +4783,7 @@
function isVariantShown(el) {
if (!el) return false;
if (el.hidden) return false;
if (el.style?.display === 'none') return false;
return true;
}
function setVariantShown(el, shown) {
if (!el) return;
if (shown) {
el.removeAttribute('hidden');
el.style.display = '';
} else {
el.setAttribute('hidden', '');
el.style.display = 'none';
}
return getComputedStyle(el).display !== 'none';
}
function scheduleCyclingBarSync(sessionId, variantNum) {
@@ -4823,11 +4822,7 @@
}
const wrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
if (!wrapper) return false;
for (const child of wrapper.children) {
const v = child.dataset ? child.dataset.impeccableVariant : null;
if (!v) continue;
setVariantShown(child, v === String(num));
}
updateVariantStateStylesheet(sessionId, num);
// Unconditional refresh - covers first-reveal (no-op if state isn't
// CYCLING yet, the subsequent CYCLING transition triggers its own
// refresh) and every cycle step.
@@ -5492,6 +5487,7 @@
if (pendingSvelteComponentRetryObserver) { pendingSvelteComponentRetryObserver.disconnect(); pendingSvelteComponentRetryObserver = null; }
if (pendingVariantAnchorRetryObserver) { pendingVariantAnchorRetryObserver.disconnect(); pendingVariantAnchorRetryObserver = null; }
stopScrollLock();
removeVariantStateStylesheet();
clearSession();
clearHandled();
resetSessionFileMeta();
@@ -5806,6 +5802,68 @@
return variantDiv;
}
// Variant visibility and range/toggle params are expressed through ONE
// injected stylesheet, never inline attributes on the variant divs. Those
// divs are scaffolded into page source, so SSR frameworks (Next.js App
// Router) server-render them; toggling their `hidden` / inline `style` /
// `--p-*` client-side trips a React 19 hydration mismatch on the next
// Fast-Refresh re-render — the same failure mode the scroll-anchor (#276)
// and pick-cursor (#286) fixes address. A stylesheet rule has the same
// computed effect without mutating any hydrated element's attributes.
// (steps params keep driving `data-p-*` attributes, matching scoped CSS.)
const VARIANT_HIDE_DECL = 'display: none !important;';
const VARIANT_SHOW_DECL = 'display: block !important;';
// Build a direct-child variant selector for a session. With `num`, targets a
// single variant (`… > [data-impeccable-variant="N"]`); without it, targets
// every variant via the bare `[data-impeccable-variant]` attribute.
function variantStateSelector(sessionId, num) {
const wrapper = '[data-impeccable-variants="' + sessionId + '"]';
const variant = num == null
? '[data-impeccable-variant]'
: '[data-impeccable-variant="' + num + '"]';
return wrapper + ' > ' + variant;
}
// Serialize the visible variant's knob values into `--p-<id>` custom-property
// declarations. Only range (number) and toggle (boolean) values become a
// custom property; steps params drive `data-p-*` attributes instead.
function variantParamDecls(values) {
return Object.entries(values || {})
.map(([id, val]) => {
if (typeof val === 'number') return ' --p-' + id + ': ' + val + ';';
if (typeof val === 'boolean') return ' --p-' + id + ': ' + (val ? '1' : '0') + ';';
return '';
})
.join('');
}
function updateVariantStateStylesheet(sessionId, num) {
if (!sessionId || num == null || num < 1) return;
let styleEl = document.getElementById(VARIANT_STATE_STYLE_ID);
if (!styleEl) {
styleEl = document.createElement('style');
styleEl.id = VARIANT_STATE_STYLE_ID;
(document.head || document.documentElement).appendChild(styleEl);
}
// Hide every variant except the visible one (incl. the SSR'd "original").
const hideOthers = variantStateSelector(sessionId)
+ ':not([data-impeccable-variant="' + num + '"]) { ' + VARIANT_HIDE_DECL + ' }';
// Force-show the visible variant (beats the source inline display:none on
// v2/v3) and apply its knob values as custom properties.
const showVisible = variantStateSelector(sessionId, num)
+ ' { ' + VARIANT_SHOW_DECL + variantParamDecls(paramsCurrentValues) + ' }';
styleEl.textContent = hideOthers + '\n' + showVisible + '\n';
}
function removeVariantStateStylesheet() {
document.getElementById(VARIANT_STATE_STYLE_ID)?.remove();
}
// Hold window.scrollY at a fixed value across DOM mutations inside the
// session's wrapper (HMR patches, variant inserts, cycle swaps).
function startScrollLock(sessionId, initialTargetY) {
@@ -7636,6 +7694,7 @@ void main() {
stopScrollTracking();
if (variantObserver) { variantObserver.disconnect(); variantObserver = null; }
stopScrollLock();
removeVariantStateStylesheet();
clearScrollY();
clearSession();
resetSessionFileMeta();
@@ -7897,6 +7956,7 @@ void main() {
if (variantObserver) { variantObserver.disconnect(); variantObserver = null; }
if (pendingVariantAnchorRetryObserver) { pendingVariantAnchorRetryObserver.disconnect(); pendingVariantAnchorRetryObserver = null; }
stopScrollLock();
removeVariantStateStylesheet();
clearScrollY();
finalizeInsertSession();
clearSession();
@@ -9989,6 +10049,7 @@ void main() {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
setLiveState('IDLE');
document.getElementById(PICK_CURSOR_STYLE_ID)?.remove();
removeVariantStateStylesheet();
window.__IMPECCABLE_LIVE_INIT__ = false;
console.log('[impeccable] Live mode exited.');
}