@dfosco/storyboard-react 4.0.0-beta.31 → 4.0.0-beta.33
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.33",
|
|
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.33",
|
|
7
|
+
"@dfosco/tiny-canvas": "4.0.0-beta.33",
|
|
8
8
|
"@neodrag/react": "^2.3.1",
|
|
9
9
|
"glob": "^11.0.0",
|
|
10
10
|
"jsonc-parser": "^3.3.1",
|
|
@@ -83,13 +83,20 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
83
83
|
const scale = zoom / 100
|
|
84
84
|
|
|
85
85
|
const [editing, setEditing] = useState(false)
|
|
86
|
-
const [interactive,
|
|
87
|
-
const [showIframe,
|
|
88
|
-
const [iframeLoaded,
|
|
86
|
+
const [interactive, _setInteractive] = useState(false)
|
|
87
|
+
const [showIframe, _setShowIframe] = useState(false)
|
|
88
|
+
const [iframeLoaded, _setIframeLoaded] = useState(false)
|
|
89
89
|
const [expanded, setExpanded] = useState(false)
|
|
90
90
|
const [filter, setFilter] = useState('')
|
|
91
|
-
const [canvasTheme,
|
|
92
|
-
const [brokenSnaps,
|
|
91
|
+
const [canvasTheme, _setCanvasTheme] = useState('light')
|
|
92
|
+
const [brokenSnaps, _setBrokenSnaps] = useState({})
|
|
93
|
+
|
|
94
|
+
// ── Debug logging wrappers ──
|
|
95
|
+
const setInteractive = (v) => { console.log(`[embed:${widgetId}] setInteractive →`, v); _setInteractive(v) }
|
|
96
|
+
const setShowIframe = (v) => { if (v) console.trace(`[embed:${widgetId}] setShowIframe → TRUE`); else console.log(`[embed:${widgetId}] setShowIframe → false`); _setShowIframe(v) }
|
|
97
|
+
const setIframeLoaded = (v) => { console.log(`[embed:${widgetId}] iframeLoaded →`, v); _setIframeLoaded(v) }
|
|
98
|
+
const setCanvasTheme = (v) => { console.log(`[embed:${widgetId}] canvasTheme →`, v); _setCanvasTheme(v) }
|
|
99
|
+
const setBrokenSnaps = (fn) => { console.log(`[embed:${widgetId}] setBrokenSnaps`); _setBrokenSnaps(fn) }
|
|
93
100
|
|
|
94
101
|
const inputRef = useRef(null)
|
|
95
102
|
const filterRef = useRef(null)
|
|
@@ -114,6 +121,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
114
121
|
|
|
115
122
|
// Single snapshot — backward compat reads snapshotLight/snapshotDark if snapshot is missing
|
|
116
123
|
const hasSnap = !isExternal && !!(snapshot && snapshot.includes(widgetId) && !brokenSnaps[snapshot])
|
|
124
|
+
console.log(`[embed:${widgetId}] render: hasSnap=${hasSnap}, showIframe=${showIframe}, interactive=${interactive}, canvasTheme=${canvasTheme}, snapshot=${snapshot ? snapshot.slice(0, 50) : 'null'}, broken=${!!brokenSnaps[snapshot]}`)
|
|
117
125
|
|
|
118
126
|
const iframeSrc = useMemo(() => {
|
|
119
127
|
if (!rawSrc) return ''
|
|
@@ -243,6 +251,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
243
251
|
}, [editing, hasPicker])
|
|
244
252
|
|
|
245
253
|
useEffect(() => {
|
|
254
|
+
console.log(`[embed:${widgetId}] effect:showIframe →`, showIframe)
|
|
246
255
|
if (!showIframe) setIframeLoaded(false)
|
|
247
256
|
}, [showIframe])
|
|
248
257
|
|
|
@@ -251,20 +260,24 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
251
260
|
// snapshots in the background with the iframe hidden but still mounted.
|
|
252
261
|
useEffect(() => {
|
|
253
262
|
if (!interactive || expanded) return
|
|
263
|
+
console.log(`[embed:${widgetId}] effect:exit-interactive listener attached`)
|
|
254
264
|
function handlePointerDown(e) {
|
|
255
265
|
if (embedRef.current && !embedRef.current.contains(e.target)) {
|
|
256
266
|
const chromeEl = e.target.closest(`[data-widget-id="${widgetId}"]`)
|
|
257
267
|
if (chromeEl) return
|
|
258
268
|
|
|
269
|
+
console.log(`[embed:${widgetId}] exit-interactive: pointerdown outside, iframeLoaded=${iframeLoaded}, hasContentWindow=${!!iframeRef.current?.contentWindow}`)
|
|
259
270
|
setInteractive(false)
|
|
260
271
|
if (onUpdate && !isExternal && iframeLoaded && iframeRef.current?.contentWindow) {
|
|
261
272
|
if (iframeRef.current) iframeRef.current.style.visibility = 'hidden'
|
|
262
273
|
const session = ++exitSessionRef.current
|
|
274
|
+
console.log(`[embed:${widgetId}] exit-interactive: starting capture, session=${session}`)
|
|
263
275
|
setTimeout(() => {
|
|
264
|
-
if (exitSessionRef.current !== session) return
|
|
276
|
+
if (exitSessionRef.current !== session) { console.log(`[embed:${widgetId}] exit-interactive: stale session ${session}, current=${exitSessionRef.current}`); return }
|
|
265
277
|
requestCapture({ force: true }).then((updates) => {
|
|
266
|
-
if (exitSessionRef.current !== session) return
|
|
278
|
+
if (exitSessionRef.current !== session) { console.log(`[embed:${widgetId}] exit-interactive: stale session after capture`); return }
|
|
267
279
|
const snap = updates?.snapshot
|
|
280
|
+
console.log(`[embed:${widgetId}] exit-interactive: capture done, snap=${snap ? 'yes(' + snap.length + ')' : 'null'}`)
|
|
268
281
|
if (snap) {
|
|
269
282
|
const img = new Image()
|
|
270
283
|
const done = () => {
|
|
@@ -304,13 +317,19 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
304
317
|
|
|
305
318
|
// On canvas theme change, enqueue a background snapshot refresh.
|
|
306
319
|
// Skips the initial render (canvasThemeInitRef tracks first value).
|
|
320
|
+
// Uses a ref to check hasSnap at callback time (not closure time).
|
|
307
321
|
const canvasThemeInitRef = useRef(true)
|
|
308
322
|
const refreshMetaRef = useRef(null)
|
|
323
|
+
const hasSnapRef = useRef(hasSnap)
|
|
324
|
+
hasSnapRef.current = hasSnap
|
|
309
325
|
useEffect(() => {
|
|
310
|
-
if (canvasThemeInitRef.current) { canvasThemeInitRef.current = false; return }
|
|
311
|
-
if (isExternal || !onUpdate || interactive || !hasSnap) return
|
|
326
|
+
if (canvasThemeInitRef.current) { canvasThemeInitRef.current = false; console.log(`[embed:${widgetId}] theme-effect: skip init`); return }
|
|
327
|
+
if (isExternal || !onUpdate || interactive || !hasSnap) { console.log(`[embed:${widgetId}] theme-effect: skip (ext=${isExternal}, noUpdate=${!onUpdate}, interactive=${interactive}, hasSnap=${hasSnap})`); return }
|
|
328
|
+
console.log(`[embed:${widgetId}] theme-effect: enqueue refresh, hasSnap=${hasSnap}, hasSnapRef=${hasSnapRef.current}`)
|
|
312
329
|
const rect = embedRef.current?.getBoundingClientRect()
|
|
313
330
|
enqueueRefresh(widgetId, ({ revealOrder, batchStart }) => {
|
|
331
|
+
console.log(`[embed:${widgetId}] refresh-callback: hasSnapRef=${hasSnapRef.current}`)
|
|
332
|
+
if (!hasSnapRef.current) { console.log(`[embed:${widgetId}] refresh-callback: ABORT, no snap`); return Promise.resolve(false) }
|
|
314
333
|
return new Promise((resolve) => {
|
|
315
334
|
refreshMetaRef.current = { revealOrder, batchStart, resolve }
|
|
316
335
|
captureOnReadyRef.current = true
|
|
@@ -323,8 +342,10 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
323
342
|
|
|
324
343
|
// Capture snapshot on first iframe ready (when no existing snapshot)
|
|
325
344
|
useEffect(() => {
|
|
345
|
+
console.log(`[embed:${widgetId}] effect:iframeReady → ${iframeReady}, onUpdate=${!!onUpdate}, isExternal=${isExternal}, hasSnap=${hasSnap}`)
|
|
326
346
|
if (!iframeReady || !onUpdate || isExternal) return
|
|
327
347
|
if (!hasSnap) {
|
|
348
|
+
console.log(`[embed:${widgetId}] first-ready: requestCapture (no snap)`)
|
|
328
349
|
requestCapture()
|
|
329
350
|
}
|
|
330
351
|
}, [iframeReady]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
@@ -333,8 +354,10 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
333
354
|
useEffect(() => {
|
|
334
355
|
if (iframeReady && captureOnReadyRef.current) {
|
|
335
356
|
captureOnReadyRef.current = false
|
|
357
|
+
console.log(`[embed:${widgetId}] captureOnReady: requestCapture`)
|
|
336
358
|
requestCapture().then((updates) => {
|
|
337
359
|
const meta = refreshMetaRef.current
|
|
360
|
+
console.log(`[embed:${widgetId}] captureOnReady: done, snap=${updates?.snapshot ? 'yes' : 'null'}, hasMeta=${!!meta}`)
|
|
338
361
|
if (meta) {
|
|
339
362
|
refreshMetaRef.current = null
|
|
340
363
|
const snap = updates?.snapshot
|
|
@@ -438,6 +461,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
438
461
|
const chromeVars = useMemo(() => getEmbedChromeVars(canvasTheme), [canvasTheme])
|
|
439
462
|
|
|
440
463
|
const enterInteractive = useCallback(() => {
|
|
464
|
+
console.log(`[embed:${widgetId}] enterInteractive`)
|
|
441
465
|
exitSessionRef.current++
|
|
442
466
|
clearTimeout(teardownTimerRef.current)
|
|
443
467
|
cancelRefresh(widgetId)
|
|
@@ -592,7 +616,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
592
616
|
className={styles.snapshotImage}
|
|
593
617
|
alt={`${prototypeTitle} snapshot`}
|
|
594
618
|
draggable={false}
|
|
595
|
-
onError={() => setBrokenSnaps(prev => ({ ...prev, [snapshot]: true }))}
|
|
619
|
+
onError={() => { console.log(`[embed:${widgetId}] snapshot img onError: ${snapshot?.slice(0, 60)}`); setBrokenSnaps(prev => ({ ...prev, [snapshot]: true })) }}
|
|
596
620
|
/>
|
|
597
621
|
)}
|
|
598
622
|
|
|
@@ -610,7 +634,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
610
634
|
transition: 'opacity 150ms ease',
|
|
611
635
|
...(iframeLoaded ? {} : { opacity: 0 }),
|
|
612
636
|
}}
|
|
613
|
-
onLoad={() => setIframeLoaded(true)}
|
|
637
|
+
onLoad={() => { console.log(`[embed:${widgetId}] iframe onLoad`); setIframeLoaded(true) }}
|
|
614
638
|
title={`${prototypeTitle} prototype`}
|
|
615
639
|
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
|
|
616
640
|
/>
|
|
@@ -31,6 +31,7 @@ export const REVEAL_INTERVAL = 200
|
|
|
31
31
|
* @param {{ x: number, y: number }} [pos] — spatial position for wave ordering
|
|
32
32
|
*/
|
|
33
33
|
export function enqueueRefresh(widgetId, fn, pos) {
|
|
34
|
+
console.log(`[refreshQueue] enqueue: ${widgetId}, queueLen=${queue.length}`)
|
|
34
35
|
const existing = queue.findIndex(item => item.widgetId === widgetId)
|
|
35
36
|
if (existing !== -1) queue.splice(existing, 1)
|
|
36
37
|
|
|
@@ -69,11 +70,13 @@ function scheduleDrain() {
|
|
|
69
70
|
|
|
70
71
|
function onTaskDone(success, item) {
|
|
71
72
|
batchDone++
|
|
73
|
+
console.log(`[refreshQueue] taskDone: ${item.widgetId}, success=${success}, done=${batchDone}/${batchTotal}, retry=${item.isRetry}`)
|
|
72
74
|
if (!success && !item.isRetry) {
|
|
73
75
|
batchFailed.push(item)
|
|
74
76
|
}
|
|
75
77
|
// When batch is complete, re-enqueue failures for one retry
|
|
76
78
|
if (batchDone >= batchTotal && batchFailed.length > 0) {
|
|
79
|
+
console.log(`[refreshQueue] batch complete, retrying ${batchFailed.length} failed`)
|
|
77
80
|
const retries = batchFailed.splice(0)
|
|
78
81
|
for (const failed of retries) {
|
|
79
82
|
failed.isRetry = true
|
|
@@ -95,6 +95,7 @@ export function useSnapshotCapture({
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
if (e.data?.type === 'storyboard:embed:snapshot-ready') {
|
|
98
|
+
console.log(`[snapshot:${widgetId}] iframe ready`)
|
|
98
99
|
setIframeReady(true)
|
|
99
100
|
iframeReadyRef.current = true
|
|
100
101
|
}
|
|
@@ -115,10 +116,11 @@ export function useSnapshotCapture({
|
|
|
115
116
|
* Uploads and saves as `snapshot` prop, overwriting any previous value.
|
|
116
117
|
*/
|
|
117
118
|
const requestCapture = useCallback(async ({ force = false } = {}) => {
|
|
119
|
+
console.log(`[snapshot:${widgetId}] requestCapture: force=${force}, hasContentWindow=${!!iframeRef.current?.contentWindow}, capturing=${capturingRef.current}, ready=${iframeReadyRef.current}`)
|
|
118
120
|
if (!onUpdate) return {}
|
|
119
|
-
if (!iframeRef.current?.contentWindow) return {}
|
|
120
|
-
if (capturingRef.current) return {}
|
|
121
|
-
if (!force && !iframeReadyRef.current) return {}
|
|
121
|
+
if (!iframeRef.current?.contentWindow) { console.log(`[snapshot:${widgetId}] requestCapture: no contentWindow`); return {} }
|
|
122
|
+
if (capturingRef.current) { console.log(`[snapshot:${widgetId}] requestCapture: already capturing`); return {} }
|
|
123
|
+
if (!force && !iframeReadyRef.current) { console.log(`[snapshot:${widgetId}] requestCapture: not ready`); return {} }
|
|
122
124
|
|
|
123
125
|
capturingRef.current = true
|
|
124
126
|
const gen = ++captureGeneration.current
|
|
@@ -129,18 +131,20 @@ export function useSnapshotCapture({
|
|
|
129
131
|
const reqId = ++requestIdCounter.current
|
|
130
132
|
const dataUrl = await captureOnce(cw, reqId, responseHandlers.current)
|
|
131
133
|
|
|
132
|
-
if (gen !== captureGeneration.current) return {}
|
|
133
|
-
if (!dataUrl) return {}
|
|
134
|
+
if (gen !== captureGeneration.current) { console.log(`[snapshot:${widgetId}] stale gen after capture`); return {} }
|
|
135
|
+
if (!dataUrl) { console.log(`[snapshot:${widgetId}] captureOnce returned null`); return {} }
|
|
134
136
|
|
|
135
137
|
const filename = `snapshot-${widgetId}.webp`
|
|
138
|
+
console.log(`[snapshot:${widgetId}] uploading ${filename}`)
|
|
136
139
|
const result = await uploadImage(dataUrl, `snapshot-${widgetId}`, filename)
|
|
137
140
|
|
|
138
|
-
if (gen !== captureGeneration.current) return {}
|
|
141
|
+
if (gen !== captureGeneration.current) { console.log(`[snapshot:${widgetId}] stale gen after upload`); return {} }
|
|
139
142
|
|
|
140
143
|
if (result?.filename) {
|
|
141
144
|
const cacheBust = `?v=${Date.now()}`
|
|
142
145
|
const url = `${base}/_storyboard/canvas/images/${result.filename}${cacheBust}`
|
|
143
146
|
const updates = { snapshot: url }
|
|
147
|
+
console.log(`[snapshot:${widgetId}] saved: ${url.slice(0, 60)}`)
|
|
144
148
|
onUpdate?.(updates)
|
|
145
149
|
return updates
|
|
146
150
|
}
|