@dfosco/storyboard-react 4.0.0-beta.33 → 4.0.0-beta.34
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.
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dfosco/storyboard-react",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.34",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@dfosco/storyboard-core": "4.0.0-beta.
|
|
7
|
-
"@dfosco/tiny-canvas": "4.0.0-beta.
|
|
6
|
+
"@dfosco/storyboard-core": "4.0.0-beta.34",
|
|
7
|
+
"@dfosco/tiny-canvas": "4.0.0-beta.34",
|
|
8
8
|
"@neodrag/react": "^2.3.1",
|
|
9
9
|
"glob": "^11.0.0",
|
|
10
10
|
"jsonc-parser": "^3.3.1",
|
|
@@ -91,7 +91,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
91
91
|
const [canvasTheme, _setCanvasTheme] = useState('light')
|
|
92
92
|
const [brokenSnaps, _setBrokenSnaps] = useState({})
|
|
93
93
|
|
|
94
|
-
// ── Debug logging wrappers ──
|
|
94
|
+
// ── Debug logging wrappers (temporary) ──
|
|
95
95
|
const setInteractive = (v) => { console.log(`[embed:${widgetId}] setInteractive →`, v); _setInteractive(v) }
|
|
96
96
|
const setShowIframe = (v) => { if (v) console.trace(`[embed:${widgetId}] setShowIframe → TRUE`); else console.log(`[embed:${widgetId}] setShowIframe → false`); _setShowIframe(v) }
|
|
97
97
|
const setIframeLoaded = (v) => { console.log(`[embed:${widgetId}] iframeLoaded →`, v); _setIframeLoaded(v) }
|
|
@@ -251,33 +251,27 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
251
251
|
}, [editing, hasPicker])
|
|
252
252
|
|
|
253
253
|
useEffect(() => {
|
|
254
|
-
console.log(`[embed:${widgetId}] effect:showIframe →`, showIframe)
|
|
255
254
|
if (!showIframe) setIframeLoaded(false)
|
|
256
255
|
}, [showIframe])
|
|
257
256
|
|
|
258
257
|
// Exit interactive mode when clicking outside the embed.
|
|
259
|
-
//
|
|
260
|
-
// snapshots in the background with the iframe hidden but still mounted.
|
|
258
|
+
// Captures a snapshot in the background, then unmounts the iframe.
|
|
261
259
|
useEffect(() => {
|
|
262
260
|
if (!interactive || expanded) return
|
|
263
|
-
console.log(`[embed:${widgetId}] effect:exit-interactive listener attached`)
|
|
264
261
|
function handlePointerDown(e) {
|
|
265
262
|
if (embedRef.current && !embedRef.current.contains(e.target)) {
|
|
266
263
|
const chromeEl = e.target.closest(`[data-widget-id="${widgetId}"]`)
|
|
267
264
|
if (chromeEl) return
|
|
268
265
|
|
|
269
|
-
console.log(`[embed:${widgetId}] exit-interactive: pointerdown outside, iframeLoaded=${iframeLoaded}, hasContentWindow=${!!iframeRef.current?.contentWindow}`)
|
|
270
266
|
setInteractive(false)
|
|
271
267
|
if (onUpdate && !isExternal && iframeLoaded && iframeRef.current?.contentWindow) {
|
|
272
268
|
if (iframeRef.current) iframeRef.current.style.visibility = 'hidden'
|
|
273
269
|
const session = ++exitSessionRef.current
|
|
274
|
-
console.log(`[embed:${widgetId}] exit-interactive: starting capture, session=${session}`)
|
|
275
270
|
setTimeout(() => {
|
|
276
|
-
if (exitSessionRef.current !== session)
|
|
271
|
+
if (exitSessionRef.current !== session) return
|
|
277
272
|
requestCapture({ force: true }).then((updates) => {
|
|
278
|
-
if (exitSessionRef.current !== session)
|
|
273
|
+
if (exitSessionRef.current !== session) return
|
|
279
274
|
const snap = updates?.snapshot
|
|
280
|
-
console.log(`[embed:${widgetId}] exit-interactive: capture done, snap=${snap ? 'yes(' + snap.length + ')' : 'null'}`)
|
|
281
275
|
if (snap) {
|
|
282
276
|
const img = new Image()
|
|
283
277
|
const done = () => {
|
|
@@ -293,8 +287,6 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
293
287
|
})
|
|
294
288
|
}, 0)
|
|
295
289
|
} else if (isExternal && showIframe) {
|
|
296
|
-
// External embeds (e.g. Figma) are slow to reload — keep the
|
|
297
|
-
// iframe mounted for 2 min so re-entering is instant.
|
|
298
290
|
const session = ++exitSessionRef.current
|
|
299
291
|
clearTimeout(teardownTimerRef.current)
|
|
300
292
|
teardownTimerRef.current = setTimeout(() => {
|
|
@@ -316,25 +308,27 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
316
308
|
}), [])
|
|
317
309
|
|
|
318
310
|
// On canvas theme change, enqueue a background snapshot refresh.
|
|
319
|
-
//
|
|
320
|
-
// Uses
|
|
321
|
-
|
|
311
|
+
// Only fires for true user-initiated theme changes (not page load).
|
|
312
|
+
// Uses mountTime to ignore the initial theme resolution that happens
|
|
313
|
+
// within the first 3 seconds of mount.
|
|
314
|
+
const mountTimeRef = useRef(Date.now())
|
|
322
315
|
const refreshMetaRef = useRef(null)
|
|
323
316
|
const hasSnapRef = useRef(hasSnap)
|
|
324
317
|
hasSnapRef.current = hasSnap
|
|
325
318
|
useEffect(() => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
console.log(`[embed:${widgetId}] theme-effect:
|
|
319
|
+
// Ignore theme changes during initial page load (first 3 seconds)
|
|
320
|
+
const elapsed = Date.now() - mountTimeRef.current
|
|
321
|
+
if (elapsed < 3000) { console.log(`[embed:${widgetId}] theme-effect: skip page-load (${elapsed}ms)`); return }
|
|
322
|
+
if (isExternal || !onUpdate || interactive) { console.log(`[embed:${widgetId}] theme-effect: skip (ext=${isExternal}, noUpdate=${!onUpdate}, interactive=${interactive})`); return }
|
|
323
|
+
if (!hasSnapRef.current) { console.log(`[embed:${widgetId}] theme-effect: skip (no snap)`); return }
|
|
324
|
+
console.log(`[embed:${widgetId}] theme-effect: enqueue refresh, hasSnapRef=${hasSnapRef.current}`)
|
|
329
325
|
const rect = embedRef.current?.getBoundingClientRect()
|
|
330
326
|
enqueueRefresh(widgetId, ({ revealOrder, batchStart }) => {
|
|
331
|
-
|
|
332
|
-
if (!hasSnapRef.current) { console.log(`[embed:${widgetId}] refresh-callback: ABORT, no snap`); return Promise.resolve(false) }
|
|
327
|
+
if (!hasSnapRef.current) return Promise.resolve(false)
|
|
333
328
|
return new Promise((resolve) => {
|
|
334
329
|
refreshMetaRef.current = { revealOrder, batchStart, resolve }
|
|
335
330
|
captureOnReadyRef.current = true
|
|
336
331
|
setShowIframe(true)
|
|
337
|
-
// Safety timeout — report failure so retry pass picks it up
|
|
338
332
|
setTimeout(() => { refreshMetaRef.current = null; resolve(false) }, 10000)
|
|
339
333
|
})
|
|
340
334
|
}, rect ? { x: rect.left, y: rect.top } : undefined)
|
|
@@ -342,10 +336,8 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
342
336
|
|
|
343
337
|
// Capture snapshot on first iframe ready (when no existing snapshot)
|
|
344
338
|
useEffect(() => {
|
|
345
|
-
console.log(`[embed:${widgetId}] effect:iframeReady → ${iframeReady}, onUpdate=${!!onUpdate}, isExternal=${isExternal}, hasSnap=${hasSnap}`)
|
|
346
339
|
if (!iframeReady || !onUpdate || isExternal) return
|
|
347
340
|
if (!hasSnap) {
|
|
348
|
-
console.log(`[embed:${widgetId}] first-ready: requestCapture (no snap)`)
|
|
349
341
|
requestCapture()
|
|
350
342
|
}
|
|
351
343
|
}, [iframeReady]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
@@ -354,10 +346,8 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
354
346
|
useEffect(() => {
|
|
355
347
|
if (iframeReady && captureOnReadyRef.current) {
|
|
356
348
|
captureOnReadyRef.current = false
|
|
357
|
-
console.log(`[embed:${widgetId}] captureOnReady: requestCapture`)
|
|
358
349
|
requestCapture().then((updates) => {
|
|
359
350
|
const meta = refreshMetaRef.current
|
|
360
|
-
console.log(`[embed:${widgetId}] captureOnReady: done, snap=${updates?.snapshot ? 'yes' : 'null'}, hasMeta=${!!meta}`)
|
|
361
351
|
if (meta) {
|
|
362
352
|
refreshMetaRef.current = null
|
|
363
353
|
const snap = updates?.snapshot
|
|
@@ -374,7 +364,6 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
374
364
|
}
|
|
375
365
|
meta.resolve(!!snap)
|
|
376
366
|
}
|
|
377
|
-
// Wait for our reveal slot in the wave
|
|
378
367
|
const elapsed = Date.now() - meta.batchStart
|
|
379
368
|
const targetTime = meta.revealOrder * REVEAL_INTERVAL
|
|
380
369
|
const wait = Math.max(0, targetTime - elapsed)
|
|
@@ -616,7 +605,12 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
616
605
|
className={styles.snapshotImage}
|
|
617
606
|
alt={`${prototypeTitle} snapshot`}
|
|
618
607
|
draggable={false}
|
|
619
|
-
onError={() => {
|
|
608
|
+
onError={() => {
|
|
609
|
+
console.log(`[embed:${widgetId}] snapshot img onError: ${snapshot?.slice(0, 60)}`)
|
|
610
|
+
setBrokenSnaps(prev => ({ ...prev, [snapshot]: true }))
|
|
611
|
+
// Clear the broken snapshot from widget data so we stop trying
|
|
612
|
+
onUpdate?.({ snapshot: '' })
|
|
613
|
+
}}
|
|
620
614
|
/>
|
|
621
615
|
)}
|
|
622
616
|
|