@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.32",
3
+ "version": "4.0.0-beta.33",
4
4
  "type": "module",
5
5
  "dependencies": {
6
- "@dfosco/storyboard-core": "4.0.0-beta.32",
7
- "@dfosco/tiny-canvas": "4.0.0-beta.32",
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, setInteractive] = useState(false)
87
- const [showIframe, setShowIframe] = useState(false)
88
- const [iframeLoaded, setIframeLoaded] = useState(false)
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, setCanvasTheme] = useState('light')
92
- const [brokenSnaps, setBrokenSnaps] = useState({})
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
- // Re-check hasSnap at callback time — snapshot may have been
318
- // marked broken (404) between enqueue and execution.
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
  }