stitch2 0.22.6 → 0.22.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/canvas.html +36 -13
  2. package/package.json +1 -1
package/dist/canvas.html CHANGED
@@ -300,9 +300,13 @@ let reports = { consistency: null, compare: null, states: null }
300
300
  let selected = null
301
301
  const frames = new Map() // path -> { el, device, img, iframe (while live), screen, height }
302
302
  // Heights measured before, so the layout doesn't jump while pictures and pages load.
303
- const HEIGHTS = JSON.parse(localStorage.getItem('stitch2-heights-v2') ?? '{}')
303
+ // Height semantics changed when live frames began loading at their authored
304
+ // viewport. Do not carry a pre-fix cache into this layout: one stale 20k page
305
+ // is enough to create a giant gap before its iframe is visited.
306
+ const HEIGHT_KEY = 'stitch2-heights-v3'
307
+ const HEIGHTS = JSON.parse(localStorage.getItem(HEIGHT_KEY) ?? '{}')
304
308
  let heightsTimer
305
- const saveHeights = () => { clearTimeout(heightsTimer); heightsTimer = setTimeout(() => localStorage.setItem('stitch2-heights-v2', JSON.stringify(HEIGHTS)), 1000) }
309
+ const saveHeights = () => { clearTimeout(heightsTimer); heightsTimer = setTimeout(() => localStorage.setItem(HEIGHT_KEY, JSON.stringify(HEIGHTS)), 1000) }
306
310
  let layoutQueued = false
307
311
  /** Many frames measure themselves at once while loading: one layout per animation frame. */
308
312
  const scheduleLayout = () => { if (!layoutQueued) { layoutQueued = true; requestAnimationFrame(() => { layoutQueued = false; layout() }) } }
@@ -630,21 +634,40 @@ function mount(f) {
630
634
  const iframe = document.createElement('iframe')
631
635
  iframe.setAttribute('scrolling', 'no')
632
636
  f.iframe = iframe
637
+ // A remounted frame can still have a cached full-page height. Start the
638
+ // document at its authored viewport so vh/svh/dvh rules are evaluated
639
+ // against the screen, not against the previous page height.
633
640
  size(f)
641
+ iframe.style.height = `${f.screen.viewportHeight ?? 844}px`
642
+ const waitForStyles = async () => {
643
+ const doc = iframe.contentDocument
644
+ if (!doc || doc.readyState !== 'complete') return false
645
+ try { await doc.fonts?.ready } catch {}
646
+ // Browser-built Tailwind and late stylesheet links can settle just after
647
+ // load. Do not measure or reveal the live page until that layout pass.
648
+ await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))
649
+ // `load` also fires for the initial about:blank document. Compare the
650
+ // loaded document itself with the current iframe target (which may have
651
+ // changed for a language/theme reload while this mount was pending).
652
+ return f.iframe === iframe && iframe.contentDocument === doc && doc.readyState === 'complete' && doc.location.href === iframe.src
653
+ }
634
654
  iframe.addEventListener('load', () => {
635
655
  if (f.iframe !== iframe) return
636
- applyTheme(f)
637
- measure(f)
638
- clearTimeout(f.reveal)
639
- // Tailwind's browser build and the components settle just after load: the picture stays until then.
640
- f.reveal = setTimeout(() => {
641
- if (f.iframe !== iframe) return
656
+ void (async () => {
657
+ if (!(await waitForStyles()) || f.iframe !== iframe) return
658
+ applyTheme(f)
642
659
  measure(f)
643
- f.el.classList.add('ready')
644
- for (const go of f.waiters.splice(0)) go()
645
- }, 350)
646
- setTimeout(() => { if (f.iframe === iframe) measure(f) }, 1200)
647
- setTimeout(() => { if (f.iframe === iframe) measure(f) }, 2500)
660
+ clearTimeout(f.reveal)
661
+ // The picture stays until the complete, styled page has been measured.
662
+ f.reveal = setTimeout(() => {
663
+ if (f.iframe !== iframe) return
664
+ measure(f)
665
+ f.el.classList.add('ready')
666
+ for (const go of f.waiters.splice(0)) go()
667
+ }, 350)
668
+ setTimeout(() => { if (f.iframe === iframe) measure(f) }, 1200)
669
+ setTimeout(() => { if (f.iframe === iframe) measure(f) }, 2500)
670
+ })()
648
671
  })
649
672
  iframe.src = frameSrc(f.screen)
650
673
  f.device.insertBefore(iframe, f.img.nextSibling)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitch2",
3
- "version": "0.22.6",
3
+ "version": "0.22.7",
4
4
  "description": "A design canvas, layout and type checks, component consistency and screen versioning for agent-built UI, on Google's DESIGN.md format.",
5
5
  "license": "MIT",
6
6
  "author": "ifokeev",