From bd86147d70305cc83bc5c96ed08d177937324299 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Wed, 22 Apr 2026 10:55:32 -0700 Subject: [PATCH] refactor(live): move scroll-restore out of injected inline back into live.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inline pre-restore wasn't actually fixing a timing issue — the fix was the fonts.ready + load retries. Since live.js's own top-level block runs before DOMContentLoaded and we can do the same retries there, we don't need an inline script injected into every user page. Simpler HTML, single source of truth. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .pi/skills/impeccable/scripts/live-browser.js | 20 ++++-- .pi/skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ public/index.html | 64 ++----------------- public/privacy.html | 1 - .../skills/impeccable/scripts/live-browser.js | 20 ++++-- .../skills/impeccable/scripts/live-inject.mjs | 19 ------ 26 files changed, 175 insertions(+), 358 deletions(-) diff --git a/.agents/skills/impeccable/scripts/live-browser.js b/.agents/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.agents/skills/impeccable/scripts/live-browser.js +++ b/.agents/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.agents/skills/impeccable/scripts/live-inject.mjs b/.agents/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.agents/skills/impeccable/scripts/live-inject.mjs +++ b/.agents/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.claude/skills/impeccable/scripts/live-browser.js b/.claude/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.claude/skills/impeccable/scripts/live-browser.js +++ b/.claude/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.claude/skills/impeccable/scripts/live-inject.mjs b/.claude/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.claude/skills/impeccable/scripts/live-inject.mjs +++ b/.claude/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.cursor/skills/impeccable/scripts/live-browser.js b/.cursor/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.cursor/skills/impeccable/scripts/live-browser.js +++ b/.cursor/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.cursor/skills/impeccable/scripts/live-inject.mjs b/.cursor/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.cursor/skills/impeccable/scripts/live-inject.mjs +++ b/.cursor/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.gemini/skills/impeccable/scripts/live-browser.js b/.gemini/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.gemini/skills/impeccable/scripts/live-browser.js +++ b/.gemini/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.gemini/skills/impeccable/scripts/live-inject.mjs b/.gemini/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.gemini/skills/impeccable/scripts/live-inject.mjs +++ b/.gemini/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.github/skills/impeccable/scripts/live-browser.js b/.github/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.github/skills/impeccable/scripts/live-browser.js +++ b/.github/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.github/skills/impeccable/scripts/live-inject.mjs b/.github/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.github/skills/impeccable/scripts/live-inject.mjs +++ b/.github/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.kiro/skills/impeccable/scripts/live-browser.js b/.kiro/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.kiro/skills/impeccable/scripts/live-browser.js +++ b/.kiro/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.kiro/skills/impeccable/scripts/live-inject.mjs b/.kiro/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.kiro/skills/impeccable/scripts/live-inject.mjs +++ b/.kiro/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.opencode/skills/impeccable/scripts/live-browser.js b/.opencode/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.opencode/skills/impeccable/scripts/live-browser.js +++ b/.opencode/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.opencode/skills/impeccable/scripts/live-inject.mjs b/.opencode/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.opencode/skills/impeccable/scripts/live-inject.mjs +++ b/.opencode/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.pi/skills/impeccable/scripts/live-browser.js b/.pi/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.pi/skills/impeccable/scripts/live-browser.js +++ b/.pi/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.pi/skills/impeccable/scripts/live-inject.mjs b/.pi/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.pi/skills/impeccable/scripts/live-inject.mjs +++ b/.pi/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.rovodev/skills/impeccable/scripts/live-browser.js b/.rovodev/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.rovodev/skills/impeccable/scripts/live-browser.js +++ b/.rovodev/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.rovodev/skills/impeccable/scripts/live-inject.mjs b/.rovodev/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.rovodev/skills/impeccable/scripts/live-inject.mjs +++ b/.rovodev/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.trae-cn/skills/impeccable/scripts/live-browser.js b/.trae-cn/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.trae-cn/skills/impeccable/scripts/live-browser.js +++ b/.trae-cn/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.trae-cn/skills/impeccable/scripts/live-inject.mjs b/.trae-cn/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.trae-cn/skills/impeccable/scripts/live-inject.mjs +++ b/.trae-cn/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/.trae/skills/impeccable/scripts/live-browser.js b/.trae/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/.trae/skills/impeccable/scripts/live-browser.js +++ b/.trae/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/.trae/skills/impeccable/scripts/live-inject.mjs b/.trae/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/.trae/skills/impeccable/scripts/live-inject.mjs +++ b/.trae/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' ); diff --git a/public/index.html b/public/index.html index 06423fd5f..68f4a7283 100644 --- a/public/index.html +++ b/public/index.html @@ -726,63 +726,14 @@ - -
- -
-
-

Work with me

-

Impeccable is built by Renaissance Geek. I work with enterprise teams on large-scale rollouts, custom integrations, and training for designers and developers. If you're a frontier lab, design tool company, or enterprise looking to raise the bar on AI-generated design, let's talk.

-
-
- - -
-
- Renaissance Geek · Consulting -

Work with me.

-

Enterprise rollouts, custom integrations, and training for designers and developers. Get in touch.

-
-
-
-
- -
-

Work with me.

-

Impeccable is built by Renaissance Geek. Rollouts, integrations, training.

-
-
-
-
-
-

Work
with me.

-

Impeccable is built by Renaissance Geek. Large-scale rollouts, custom integrations, and training for teams raising the bar on AI-generated design.

-
-
+ + +
+

Work with me

+

Impeccable is built by Renaissance Geek. I work with enterprise teams on large-scale rollouts, custom integrations, and training for designers and developers. If you're a frontier lab, design tool company, or enterprise looking to raise the bar on AI-generated design, let's talk.

- + + @@ -834,7 +785,6 @@ - diff --git a/public/privacy.html b/public/privacy.html index 817b152e4..4139e711e 100644 --- a/public/privacy.html +++ b/public/privacy.html @@ -78,7 +78,6 @@

Questions about this policy? Open an issue on GitHub or reach out to @pbakaus.

- diff --git a/source/skills/impeccable/scripts/live-browser.js b/source/skills/impeccable/scripts/live-browser.js index 71ec5ab75..109d0d58e 100644 --- a/source/skills/impeccable/scripts/live-browser.js +++ b/source/skills/impeccable/scripts/live-browser.js @@ -121,15 +121,23 @@ } // Pre-empt the browser: apply manual scroll restoration and jump to the - // saved scrollY at script-parse time (before DOMContentLoaded). If we - // wait until init(), the browser has already begun animating its own - // restore — especially bad when `scroll-behavior: smooth` is set on html. + // saved scrollY at script-parse time. Retries on fonts.ready and load + // are essential: scrollTo(y) clamps to the current document.scrollHeight, + // which is often hundreds of pixels short of the final value until + // async-loaded fonts swap in and reflow. try { history.scrollRestoration = 'manual'; const savedY = readScrollY(); - if (savedY != null && Math.abs(window.scrollY - savedY) > 0.5) { - console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); - window.scrollTo({ top: savedY, left: 0, behavior: 'instant' }); + if (savedY != null) { + const apply = () => { + if (Math.abs(window.scrollY - savedY) > 0.5) { + console.log('[impeccable.scroll] early restore', { from: window.scrollY, to: savedY }); + window.scrollTo(0, savedY); + } + }; + apply(); + if (document.fonts?.ready) document.fonts.ready.then(apply).catch(() => {}); + window.addEventListener('load', apply, { once: true }); } } catch {} diff --git a/source/skills/impeccable/scripts/live-inject.mjs b/source/skills/impeccable/scripts/live-inject.mjs index 37a16f1ea..9bbc345ac 100644 --- a/source/skills/impeccable/scripts/live-inject.mjs +++ b/source/skills/impeccable/scripts/live-inject.mjs @@ -137,27 +137,8 @@ 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. - // - // Retries on fonts.ready and load are essential: scrollTo(y) clamps to - // the document's current scrollHeight, which is often hundreds of - // pixels short of the final value until async-loaded fonts swap in. - // Hardcoded key matches live-browser.js: PREFIX ('impeccable-live') + - // LS_KEY suffix ('-session') + SCROLL_KEY_SUFFIX ('-scroll'). - const preRestore = - ''; return ( open + ' ' + MARKER_OPEN_TEXT + ' ' + close + '\n' + - preRestore + '\n' + '\n' + open + ' ' + MARKER_CLOSE_TEXT + ' ' + close + '\n' );