@dfosco/storyboard-react 4.2.0-alpha.13 → 4.2.0-alpha.15

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,11 +1,11 @@
1
1
  {
2
2
  "name": "@dfosco/storyboard-react",
3
- "version": "4.2.0-alpha.13",
3
+ "version": "4.2.0-alpha.15",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@base-ui/react": "^1.4.0",
7
- "@dfosco/storyboard-core": "4.2.0-alpha.13",
8
- "@dfosco/tiny-canvas": "4.2.0-alpha.13",
7
+ "@dfosco/storyboard-core": "4.2.0-alpha.15",
8
+ "@dfosco/tiny-canvas": "4.2.0-alpha.15",
9
9
  "@neodrag/react": "^2.3.1",
10
10
  "glob": "^11.0.0",
11
11
  "jsonc-parser": "^3.3.1",
@@ -68,13 +68,13 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
68
68
  const [expanded, setExpanded] = useState(false)
69
69
  const [filter, setFilter] = useState('')
70
70
  const [canvasTheme, setCanvasTheme] = useState(() => resolveCanvasThemeFromStorage())
71
- const { active: embedActive, activate: activateEmbed, performanceMode, tooMany } = useEmbedActive(widgetId, embedRef)
72
71
  const inputRef = useRef(null)
73
72
  const filterRef = useRef(null)
74
73
  const embedRef = useRef(null)
75
74
  const iframeRef = useRef(null)
76
75
  const inlineContainerRef = useRef(null)
77
76
  const modalContainerRef = useRef(null)
77
+ const { active: embedActive, activate: activateEmbed, performanceMode, tooMany } = useEmbedActive(widgetId, embedRef)
78
78
 
79
79
  const iframeSrc = useMemo(() => {
80
80
  if (!rawSrc) return ''
@@ -323,7 +323,18 @@ export default forwardRef(function TerminalWidget({ id, props, onUpdate, resizab
323
323
 
324
324
  const handleClick = useCallback(() => {
325
325
  if (phase === 'ended') return
326
- if (phase === 'live') termRef.current?.focus()
326
+ if (phase === 'live') {
327
+ // Save canvas scroll position — terminal focus can trigger browser scroll
328
+ const scrollEl = terminalRef.current?.closest('[class*="canvasScroll"]')
329
+ const scrollTop = scrollEl?.scrollTop
330
+ const scrollLeft = scrollEl?.scrollLeft
331
+ termRef.current?.focus({ preventScroll: true })
332
+ // Restore if browser scrolled anyway
333
+ if (scrollEl && (scrollEl.scrollTop !== scrollTop || scrollEl.scrollLeft !== scrollLeft)) {
334
+ scrollEl.scrollTop = scrollTop
335
+ scrollEl.scrollLeft = scrollLeft
336
+ }
337
+ }
327
338
  }, [phase])
328
339
 
329
340
  const handleStartSession = useCallback(() => {
@@ -14,7 +14,7 @@
14
14
  * // Inside a widget:
15
15
  * const { active, activate } = useEmbedActive(widgetId, containerRef)
16
16
  */
17
- import { createContext, useContext, useCallback, useEffect, useRef, useSyncExternalStore } from 'react'
17
+ import { createContext, useContext, useCallback, useEffect, useRef, useState } from 'react'
18
18
 
19
19
  const DEACTIVATE_DELAY = 5000
20
20
  const MAX_VISIBLE_EMBEDS = 7
@@ -147,32 +147,16 @@ export function useEmbedActive(widgetId, containerRef) {
147
147
  const scrollRef = useContext(EmbedControllerContext)
148
148
  const deactivateTimerRef = useRef(null)
149
149
 
150
- // Subscribe to state changes
151
- const snapshot = useSyncExternalStore(subscribe, () => ({
152
- active: isActive(widgetId),
153
- performanceMode: getPerformanceMode(),
154
- tooMany: isTooManyVisible(),
155
- }), () => ({
156
- active: false,
157
- performanceMode: false,
158
- tooMany: false,
159
- }))
160
-
161
- // Need a stable reference check since useSyncExternalStore compares by reference
162
- const activeRef = useRef(false)
163
- const perfRef = useRef(false)
164
- const tooManyRef = useRef(false)
150
+ // Track state with useState + subscribe (avoids useSyncExternalStore object identity issues)
151
+ const [, forceUpdate] = useState(0)
152
+ useEffect(() => {
153
+ return subscribe(() => forceUpdate(c => c + 1))
154
+ }, [])
165
155
 
166
156
  const active = isActive(widgetId)
167
157
  const perf = getPerformanceMode()
168
158
  const tooMany = isTooManyVisible()
169
159
 
170
- if (activeRef.current !== active || perfRef.current !== perf || tooManyRef.current !== tooMany) {
171
- activeRef.current = active
172
- perfRef.current = perf
173
- tooManyRef.current = tooMany
174
- }
175
-
176
160
  // Register/unregister
177
161
  useEffect(() => {
178
162
  registerEmbed(widgetId)