@dfosco/storyboard-react 4.0.0-beta.32 → 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 = () => {
|
|
@@ -310,13 +323,13 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
310
323
|
const hasSnapRef = useRef(hasSnap)
|
|
311
324
|
hasSnapRef.current = hasSnap
|
|
312
325
|
useEffect(() => {
|
|
313
|
-
if (canvasThemeInitRef.current) { canvasThemeInitRef.current = false; return }
|
|
314
|
-
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}`)
|
|
315
329
|
const rect = embedRef.current?.getBoundingClientRect()
|
|
316
330
|
enqueueRefresh(widgetId, ({ revealOrder, batchStart }) => {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (!hasSnapRef.current) return Promise.resolve(false)
|
|
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) }
|
|
320
333
|
return new Promise((resolve) => {
|
|
321
334
|
refreshMetaRef.current = { revealOrder, batchStart, resolve }
|
|
322
335
|
captureOnReadyRef.current = true
|
|
@@ -329,8 +342,10 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
329
342
|
|
|
330
343
|
// Capture snapshot on first iframe ready (when no existing snapshot)
|
|
331
344
|
useEffect(() => {
|
|
345
|
+
console.log(`[embed:${widgetId}] effect:iframeReady → ${iframeReady}, onUpdate=${!!onUpdate}, isExternal=${isExternal}, hasSnap=${hasSnap}`)
|
|
332
346
|
if (!iframeReady || !onUpdate || isExternal) return
|
|
333
347
|
if (!hasSnap) {
|
|
348
|
+
console.log(`[embed:${widgetId}] first-ready: requestCapture (no snap)`)
|
|
334
349
|
requestCapture()
|
|
335
350
|
}
|
|
336
351
|
}, [iframeReady]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
@@ -339,8 +354,10 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
339
354
|
useEffect(() => {
|
|
340
355
|
if (iframeReady && captureOnReadyRef.current) {
|
|
341
356
|
captureOnReadyRef.current = false
|
|
357
|
+
console.log(`[embed:${widgetId}] captureOnReady: requestCapture`)
|
|
342
358
|
requestCapture().then((updates) => {
|
|
343
359
|
const meta = refreshMetaRef.current
|
|
360
|
+
console.log(`[embed:${widgetId}] captureOnReady: done, snap=${updates?.snapshot ? 'yes' : 'null'}, hasMeta=${!!meta}`)
|
|
344
361
|
if (meta) {
|
|
345
362
|
refreshMetaRef.current = null
|
|
346
363
|
const snap = updates?.snapshot
|
|
@@ -444,6 +461,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
444
461
|
const chromeVars = useMemo(() => getEmbedChromeVars(canvasTheme), [canvasTheme])
|
|
445
462
|
|
|
446
463
|
const enterInteractive = useCallback(() => {
|
|
464
|
+
console.log(`[embed:${widgetId}] enterInteractive`)
|
|
447
465
|
exitSessionRef.current++
|
|
448
466
|
clearTimeout(teardownTimerRef.current)
|
|
449
467
|
cancelRefresh(widgetId)
|
|
@@ -598,7 +616,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
598
616
|
className={styles.snapshotImage}
|
|
599
617
|
alt={`${prototypeTitle} snapshot`}
|
|
600
618
|
draggable={false}
|
|
601
|
-
onError={() => setBrokenSnaps(prev => ({ ...prev, [snapshot]: true }))}
|
|
619
|
+
onError={() => { console.log(`[embed:${widgetId}] snapshot img onError: ${snapshot?.slice(0, 60)}`); setBrokenSnaps(prev => ({ ...prev, [snapshot]: true })) }}
|
|
602
620
|
/>
|
|
603
621
|
)}
|
|
604
622
|
|
|
@@ -616,7 +634,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
616
634
|
transition: 'opacity 150ms ease',
|
|
617
635
|
...(iframeLoaded ? {} : { opacity: 0 }),
|
|
618
636
|
}}
|
|
619
|
-
onLoad={() => setIframeLoaded(true)}
|
|
637
|
+
onLoad={() => { console.log(`[embed:${widgetId}] iframe onLoad`); setIframeLoaded(true) }}
|
|
620
638
|
title={`${prototypeTitle} prototype`}
|
|
621
639
|
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
|
|
622
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
|
}
|