fix(live): inject inline pre-restore script so scrollY wins vs browser

external live.js is fetched, so by the time it runs the browser has
already queued its reload-scroll animation and history.scrollRestoration
='manual' has no effect. Inject a tiny inline synchronous <script> into
the same block live-inject writes, BEFORE the external live.js tag. It
sets scrollRestoration='manual' and does window.scrollTo(0, savedY)
during HTML parse — before the browser can animate anywhere.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-04-22 10:45:39 -07:00
co-authored by Claude Opus 4.7
parent 5e04a9f25a
commit fb78ec4553
14 changed files with 145 additions and 112 deletions
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);
+24 -112
View File
@@ -724,8 +724,9 @@
<!-- impeccable-variants-start 708145d6 -->
<div data-impeccable-variants="708145d6" data-impeccable-variant-count="3" style="display: contents">
<!-- impeccable-variants-start 32275091 -->
<div data-impeccable-variants="32275091" data-impeccable-variant-count="3" style="display: contents">
<!-- Original -->
<div data-impeccable-variant="original">
<div class="consulting-text">
@@ -734,118 +735,27 @@
</div>
</div>
<!-- Variants: insert below this line -->
<style data-impeccable-css="708145d6">
<style data-impeccable-css="32275091">
@scope ([data-impeccable-variant="1"]) {
.consulting-text {
display: grid;
gap: 14px;
max-width: 56ch;
}
.v1-tag {
font-family: var(--font-mono);
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.25em;
color: var(--color-accent);
}
.v1-title {
font-family: var(--font-display);
font-style: italic;
font-weight: 300;
font-size: clamp(2.75rem, 5.5vw, 4rem);
line-height: 1;
color: var(--color-ink);
margin: 0;
}
.v1-body {
font-family: var(--font-body);
font-size: 1rem;
line-height: 1.6;
color: var(--color-charcoal);
margin: 4px 0 0;
}
.v1-body a {
color: var(--color-ink);
text-decoration: underline;
text-underline-offset: 3px;
text-decoration-color: var(--color-accent);
}
.consulting-text { display: grid; gap: 14px; max-width: 56ch; }
.v1-tag { font-family: var(--font-mono); font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.25em; color: var(--color-accent); }
.v1-title { font-family: var(--font-display); font-style: italic; font-weight: 300; font-size: clamp(2.75rem, 5.5vw, 4rem); line-height: 1; color: var(--color-ink); margin: 0; }
.v1-body { font-family: var(--font-body); font-size: 1rem; line-height: 1.6; color: var(--color-charcoal); margin: 4px 0 0; }
.v1-body a { color: var(--color-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--color-accent); }
}
@scope ([data-impeccable-variant="2"]) {
.consulting-text {
display: flex;
align-items: baseline;
gap: 28px;
padding: 20px 0;
border-block: 1px solid var(--color-ink);
}
.v2-mark {
font-family: var(--font-display);
font-style: italic;
font-weight: 400;
font-size: 3rem;
line-height: 0.9;
color: var(--color-accent);
}
.v2-body {
display: flex;
flex-direction: column;
gap: 8px;
max-width: 52ch;
}
.v2-title {
font-family: var(--font-display);
font-style: italic;
font-size: 1.875rem;
line-height: 1.15;
color: var(--color-ink);
margin: 0;
}
.v2-body p {
font-family: var(--font-body);
font-size: 0.9375rem;
line-height: 1.6;
color: var(--color-charcoal);
margin: 0;
}
.v2-body a {
color: var(--color-ink);
text-decoration: underline;
text-underline-offset: 3px;
text-decoration-color: var(--color-accent);
}
.consulting-text { display: flex; align-items: baseline; gap: 28px; padding: 20px 0; border-block: 1px solid var(--color-ink); }
.v2-mark { font-family: var(--font-display); font-style: italic; font-weight: 400; font-size: 3rem; line-height: 0.9; color: var(--color-accent); }
.v2-body { display: flex; flex-direction: column; gap: 8px; max-width: 52ch; }
.v2-title { font-family: var(--font-display); font-style: italic; font-size: 1.875rem; line-height: 1.15; color: var(--color-ink); margin: 0; }
.v2-body p { font-family: var(--font-body); font-size: 0.9375rem; line-height: 1.6; color: var(--color-charcoal); margin: 0; }
.v2-body a { color: var(--color-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--color-accent); }
}
@scope ([data-impeccable-variant="3"]) {
.consulting-text {
display: grid;
gap: 32px;
max-width: 54ch;
}
.v3-title {
font-family: var(--font-display);
font-style: italic;
font-weight: 300;
font-size: clamp(4rem, 8vw, 6rem);
line-height: 0.9;
color: var(--color-ink);
margin: 0;
letter-spacing: -0.02em;
}
.v3-body {
font-family: var(--font-body);
font-size: 1rem;
line-height: 1.65;
color: var(--color-charcoal);
margin: 0;
padding-left: 20px;
border-left: 1px solid var(--color-accent);
}
.v3-body a {
color: var(--color-ink);
text-decoration: underline;
text-underline-offset: 3px;
text-decoration-color: var(--color-accent);
}
.consulting-text { display: grid; gap: 32px; max-width: 54ch; }
.v3-title { font-family: var(--font-display); font-style: italic; font-weight: 300; font-size: clamp(4rem, 8vw, 6rem); line-height: 0.9; color: var(--color-ink); margin: 0; letter-spacing: -0.02em; }
.v3-body { font-family: var(--font-body); font-size: 1rem; line-height: 1.65; color: var(--color-charcoal); margin: 0; padding-left: 20px; border-left: 1px solid var(--color-accent); }
.v3-body a { color: var(--color-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--color-accent); }
}
</style>
<div data-impeccable-variant="1">
@@ -860,18 +770,19 @@
<span class="v2-mark"></span>
<div class="v2-body">
<h2 class="v2-title">Work with me.</h2>
<p>Impeccable is built by <a href="https://renaissance-geek.ai" target="_blank" rel="noopener">Renaissance Geek</a>. I work with enterprise teams on rollouts, integrations, and training.</p>
<p>Impeccable is built by <a href="https://renaissance-geek.ai" target="_blank" rel="noopener">Renaissance Geek</a>. Rollouts, integrations, training.</p>
</div>
</div>
</div>
<div data-impeccable-variant="3" style="display: none">
<div class="consulting-text">
<h2 class="v3-title">Work <br>with me.</h2>
<p class="v3-body">Impeccable is built by <a href="https://renaissance-geek.ai" target="_blank" rel="noopener">Renaissance Geek</a>. I work with enterprise teams on large-scale rollouts, custom integrations, and training for designers and developers.</p>
<p class="v3-body">Impeccable is built by <a href="https://renaissance-geek.ai" target="_blank" rel="noopener">Renaissance Geek</a>. Large-scale rollouts, custom integrations, and training for teams raising the bar on AI-generated design.</p>
</div>
</div>
</div>
<!-- impeccable-variants-end 708145d6 -->
<!-- impeccable-variants-end 32275091 -->
@@ -921,6 +832,7 @@
<script type="module" src="./app.js"></script>
<!-- impeccable-live-start -->
<script>(function(){try{history.scrollRestoration="manual";var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>
<script src="http://localhost:8400/live.js"></script>
<!-- impeccable-live-end -->
</body>
+1
View File
@@ -78,6 +78,7 @@
<p>Questions about this policy? Open an issue on <a href="https://github.com/pbakaus/impeccable">GitHub</a> or reach out to <a href="https://x.com/pbakaus">@pbakaus</a>.</p>
</main>
<!-- impeccable-live-start -->
<script>(function(){try{history.scrollRestoration="manual";var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>
<script src="http://localhost:8400/live.js"></script>
<!-- impeccable-live-end -->
</body>
@@ -137,8 +137,18 @@ function commentClose(syntax) { return syntax === 'jsx' ? '*/}' : '-->'; }
function buildTagBlock(syntax, port) {
const open = commentOpen(syntax);
const close = commentClose(syntax);
// Inline pre-restore: runs before the external live.js is fetched. Sets
// scrollRestoration='manual' and jumps to the saved scrollY *synchronously*
// during HTML parse, beating the browser's animated reload-restore.
// Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') +
// LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll').
const preRestore =
'<script>(function(){try{history.scrollRestoration="manual";' +
'var y=parseFloat(localStorage.getItem("impeccable-live-session-scroll"));' +
'if(isFinite(y))window.scrollTo(0,y);}catch(e){}})();</script>';
return (
open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' +
preRestore + '\n' +
'<script src="http://localhost:' + port + '/live.js"></script>\n' +
open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n'
);