@dfosco/storyboard-react 4.2.2 → 4.2.3

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.2",
3
+ "version": "4.2.3",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@base-ui/react": "^1.4.0",
7
- "@dfosco/storyboard-core": "4.2.2",
8
- "@dfosco/tiny-canvas": "4.2.2",
7
+ "@dfosco/storyboard-core": "4.2.3",
8
+ "@dfosco/tiny-canvas": "4.2.3",
9
9
  "@neodrag/react": "^2.3.1",
10
10
  "@radix-ui/react-dialog": "^1.1.15",
11
11
  "@radix-ui/react-visually-hidden": "^1.2.4",
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AuthModal — Global PAT entry dialog for comments authentication.
3
3
  * Mounted at app root, triggered by:
4
- * - Svelte CoreUIBar (comments tool / "C" shortcut) via 'storyboard:open-auth-modal' event
4
+ * - CoreUIBar (comments tool / "C" shortcut) via 'storyboard:open-auth-modal' event
5
5
  * - ViewfinderNew sidebar login button via same event
6
6
  */
7
7
  import { useState, useEffect, useCallback } from 'react'
@@ -15,6 +15,15 @@ function checkLocalDev() {
15
15
  return window.__SB_LOCAL_DEV__ === true
16
16
  }
17
17
 
18
+ /** Read the dev domain color injected by the server plugin, validated via CSS.supports. */
19
+ function getDevDomainColor() {
20
+ if (typeof window === 'undefined') return null
21
+ const color = window.__SB_DEV_DOMAIN_COLOR__
22
+ if (!color) return null
23
+ if (typeof CSS !== 'undefined' && CSS.supports && !CSS.supports('color', color)) return null
24
+ return color
25
+ }
26
+
18
27
  export default function BranchBar({ basePath }) {
19
28
  const [hidden, setHidden] = useState(
20
29
  () => typeof document !== 'undefined' && document.documentElement.classList.contains('storyboard-chrome-hidden')
@@ -33,6 +42,7 @@ export default function BranchBar({ basePath }) {
33
42
 
34
43
  const isLocalDev = checkLocalDev()
35
44
  const isOnBranch = currentBranch !== 'main'
45
+ const domainColor = isLocalDev ? getDevDomainColor() : null
36
46
 
37
47
  useEffect(() => {
38
48
  const observer = new MutationObserver(() => {
@@ -52,8 +62,15 @@ export default function BranchBar({ basePath }) {
52
62
 
53
63
  return (
54
64
  <div className={css.bar} data-branch-bar>
55
- <div className={`${css.barInner}${isLocalDev ? '' : ` ${css.barProd}`}`}>
65
+ <div
66
+ className={`${css.barInner}${isLocalDev ? '' : ` ${css.barProd}`}`}
67
+ style={domainColor ? { '--sb-branch-bar-bg': domainColor } : undefined}
68
+ >
56
69
  <span className={css.barLabel}>
70
+ {isLocalDev && window.__SB_DEV_DOMAIN__ && <>
71
+ <span className={css.barDomainName}>⌘ {window.__SB_DEV_DOMAIN__}</span>
72
+ <span className={css.barSeparator}>·</span>
73
+ </>}
57
74
  <GitBranchIcon size={12} />
58
75
  <span className={css.barBranchName}>{currentBranch}</span>
59
76
  {isLocalDev && <>
@@ -16,7 +16,7 @@
16
16
  justify-content: center;
17
17
  gap: 8px;
18
18
  height: 32px;
19
- background: hsl(212, 92%, 45%);
19
+ background: var(--sb-branch-bar-bg, hsl(212, 92%, 45%));
20
20
  color: #fff;
21
21
  padding: 4px 12px;
22
22
  position: relative;
@@ -72,6 +72,14 @@
72
72
  white-space: nowrap;
73
73
  }
74
74
 
75
+ .barDomainName {
76
+ font-weight: 400;
77
+ max-width: 200px;
78
+ overflow: hidden;
79
+ text-overflow: ellipsis;
80
+ white-space: nowrap;
81
+ }
82
+
75
83
  .barSeparator {
76
84
  opacity: 0.6;
77
85
  margin: 0 2px;
@@ -880,7 +880,7 @@ function buildPaletteItems(basePath, onCreateAction, onNavigateToPage) {
880
880
 
881
881
  /**
882
882
  * StoryboardCommandPalette — React command palette using react-cmdk.
883
- * Mounted at app root, listens for custom events from Svelte CoreUIBar.
883
+ * Mounted at app root, listens for custom events from CoreUIBar.
884
884
  */
885
885
  export default function StoryboardCommandPalette({ basePath }) {
886
886
  const [open, setOpen] = useState(false)
@@ -2075,7 +2075,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
2075
2075
  return () => document.removeEventListener('storyboard:canvas:toggle-snap', handleSnapToggle)
2076
2076
  }, [canvasId])
2077
2077
 
2078
- // Broadcast snap state to Svelte toolbar
2078
+ // Broadcast snap state to toolbar
2079
2079
  useEffect(() => {
2080
2080
  document.dispatchEvent(new CustomEvent('storyboard:canvas:snap-state', {
2081
2081
  detail: { snapEnabled }
@@ -2083,7 +2083,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
2083
2083
  snapEnabledRef.current = snapEnabled
2084
2084
  }, [snapEnabled])
2085
2085
 
2086
- // Respond to snap-state requests from Svelte toolbar (handles mount-order race)
2086
+ // Respond to snap-state requests from toolbar (handles mount-order race)
2087
2087
  useEffect(() => {
2088
2088
  function handleRequest() {
2089
2089
  document.dispatchEvent(new CustomEvent('storyboard:canvas:snap-state', {
@@ -2094,7 +2094,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
2094
2094
  return () => document.removeEventListener('storyboard:canvas:snap-state-request', handleRequest)
2095
2095
  }, [])
2096
2096
 
2097
- // Listen for gridSize from Svelte toolbar config
2097
+ // Listen for gridSize from toolbar config
2098
2098
  useEffect(() => {
2099
2099
  function handleGridSize(e) {
2100
2100
  const size = e.detail?.gridSize
@@ -2638,7 +2638,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
2638
2638
  return () => document.removeEventListener('keydown', handleKeyDown)
2639
2639
  }, [handleUndo, handleRedo, handleDuplicateSelected, handleDuplicateWithConnectors, handleSelectAll])
2640
2640
 
2641
- // Listen for undo/redo from CoreUIBar (Svelte toolbar)
2641
+ // Listen for undo/redo from CoreUIBar
2642
2642
  useEffect(() => {
2643
2643
  function handleUndoEvent() { handleUndo() }
2644
2644
  function handleRedoEvent() { handleRedo() }
@@ -2650,7 +2650,7 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
2650
2650
  }
2651
2651
  }, [handleUndo, handleRedo])
2652
2652
 
2653
- // Broadcast undo/redo availability to Svelte toolbar
2653
+ // Broadcast undo/redo availability to toolbar
2654
2654
  useEffect(() => {
2655
2655
  document.dispatchEvent(new CustomEvent('storyboard:canvas:undo-redo-state', {
2656
2656
  detail: { canUndo: undoRedo.canUndo, canRedo: undoRedo.canRedo }
@@ -73,15 +73,24 @@ export default forwardRef(function ComponentSetWidget({ id: widgetId, props, onU
73
73
  useEffect(() => {
74
74
  function handleMessage(e) {
75
75
  if (e.source !== iframeRef.current?.contentWindow) return
76
- if (e.data?.type !== 'storyboard:component-set:select') return
77
- const newSelected = e.data.exportName || ''
78
- if (newSelected !== selected) {
79
- onUpdate?.({ selected: newSelected })
76
+ if (e.data?.type === 'storyboard:component-set:select') {
77
+ const newSelected = e.data.exportName || ''
78
+ if (newSelected !== selected) {
79
+ onUpdate?.({ selected: newSelected })
80
+ }
81
+ } else if (e.data?.type === 'storyboard:component-set:resize') {
82
+ // Auto-size widget to fit the grid content (+ header height)
83
+ const headerH = 32
84
+ const newW = Math.max(200, Math.ceil(e.data.width))
85
+ const newH = Math.max(60, Math.ceil(e.data.height) + headerH)
86
+ if (newW !== width || newH !== height) {
87
+ onUpdate?.({ width: newW, height: newH })
88
+ }
80
89
  }
81
90
  }
82
91
  window.addEventListener('message', handleMessage)
83
92
  return () => window.removeEventListener('message', handleMessage)
84
- }, [selected, onUpdate])
93
+ }, [selected, width, height, onUpdate])
85
94
 
86
95
  const handleResize = useCallback((w, h) => {
87
96
  onUpdate?.({ width: w, height: h })
package/src/context.jsx CHANGED
@@ -263,7 +263,7 @@ function StoryboardProviderInner({ flowName, sceneName, recordName, recordParam,
263
263
  cleanup = mountDesignModes()
264
264
  })
265
265
  .catch(() => {
266
- // Svelte UI not available — degrade gracefully
266
+ // UI not available — degrade gracefully
267
267
  })
268
268
 
269
269
  return () => cleanup?.()
package/src/index.js CHANGED
@@ -34,7 +34,7 @@ export { installHashPreserver } from './hashPreserver.js'
34
34
  export { FormContext } from './context/FormContext.js'
35
35
 
36
36
  // Design mode hook (keep — React apps may still read mode state)
37
- // ModeSwitch and ToolbarShell UI moved to @dfosco/storyboard-svelte-ui
37
+
38
38
 
39
39
  // Workspace dashboard
40
40
  export { default as Workspace } from './Workspace.jsx'
@@ -84,7 +84,8 @@ export default function ComponentSetPage({ name }) {
84
84
 
85
85
  const gridRef = useRef(null)
86
86
 
87
- // Measure all cell content elements and snap cells to the largest
87
+ // Measure all cell content elements and snap cells to the largest.
88
+ // Posts the total grid size to the parent widget so it can auto-size.
88
89
  useLayoutEffect(() => {
89
90
  const grid = gridRef.current
90
91
  if (!grid || !exports) return
@@ -101,6 +102,17 @@ export default function ComponentSetPage({ name }) {
101
102
  }
102
103
  grid.style.setProperty('--cell-snap-w', `${maxW}px`)
103
104
  grid.style.setProperty('--cell-snap-h', `${maxH}px`)
105
+
106
+ // Post total grid size to parent widget
107
+ if (isEmbed && window.parent !== window) {
108
+ requestAnimationFrame(() => {
109
+ window.parent.postMessage({
110
+ type: 'storyboard:component-set:resize',
111
+ width: grid.scrollWidth,
112
+ height: grid.scrollHeight,
113
+ }, '*')
114
+ })
115
+ }
104
116
  }
105
117
 
106
118
  // Measure after fonts load and initial paint
@@ -110,7 +122,7 @@ export default function ComponentSetPage({ name }) {
110
122
  const ro = new ResizeObserver(measure)
111
123
  for (const el of cells) ro.observe(el)
112
124
  return () => ro.disconnect()
113
- }, [exports, layout])
125
+ }, [exports, layout, isEmbed])
114
126
 
115
127
  const handleSelect = useCallback((exportName) => {
116
128
  const params = new URLSearchParams(location.search)
@@ -1,12 +1,11 @@
1
1
  /* ComponentSetPage — grid layout for all exports of a story */
2
2
 
3
3
  .grid {
4
- background-color: var(--bgColor-muted, #f6f8fa);
4
+ background-color: var(--bgColor-default, #ffffff);
5
5
  display: flex;
6
6
  flex-wrap: nowrap;
7
- gap: 12px;
8
- padding: 12px;
9
- min-height: 100vh;
7
+ gap: 0;
8
+ padding: 0;
10
9
  width: max-content;
11
10
  min-width: 100%;
12
11
  box-sizing: border-box;
@@ -24,20 +23,30 @@
24
23
  flex: 0 0 auto;
25
24
  display: flex;
26
25
  flex-direction: column;
27
- border: 2px solid var(--borderColor-muted, #d8dee4);
28
- border-radius: 2px;
26
+ border: 1px solid var(--borderColor-muted, #d8dee4);
27
+ border-radius: 0;
29
28
  overflow: hidden;
30
29
  transition: border-color 120ms ease, box-shadow 120ms ease;
31
30
  position: relative;
32
- background: var(--bgColor-default, #ffffff);
31
+ background: var(--bgColor-muted, #f6f8fa);
32
+ }
33
+
34
+ /* Collapse double borders between adjacent cells */
35
+ .grid[data-layout="horizontal"] .cell + .cell {
36
+ border-left: none;
33
37
  }
34
38
 
35
- /* In horizontal layout, each cell snaps to the widest component */
39
+ .grid[data-layout="vertical"] .cell + .cell {
40
+ border-top: none;
41
+ }
42
+
43
+ /* In horizontal layout, cells snap to widest and tallest component */
36
44
  .grid[data-layout="horizontal"] .cell {
37
45
  min-width: var(--cell-snap-w, 200px);
46
+ min-height: var(--cell-snap-h, 60px);
38
47
  }
39
48
 
40
- /* In vertical layout, each cell snaps to the tallest component */
49
+ /* In vertical layout, cells snap to widest and tallest component */
41
50
  .grid[data-layout="vertical"] .cell {
42
51
  min-height: var(--cell-snap-h, 120px);
43
52
  min-width: 100%;
@@ -58,14 +67,12 @@
58
67
  font-size: 11px;
59
68
  font-weight: 600;
60
69
  color: var(--fgColor-muted, #656d76);
61
- background: var(--bgColor-muted, #f6f8fa);
70
+ background: var(--bgColor-default, #ffffff);
62
71
  border-bottom: 1px solid var(--borderColor-muted, #d8dee4);
63
72
  cursor: pointer;
64
73
  user-select: none;
65
74
  transition: background 100ms ease, color 100ms ease;
66
75
  flex-shrink: 0;
67
- /* Round top corners to match cell */
68
- border-radius: 6px 6px 0 0;
69
76
  }
70
77
 
71
78
  .cellLabel:hover {
@@ -99,6 +106,7 @@
99
106
  flex: 1;
100
107
  overflow: visible;
101
108
  position: relative;
109
+ padding: 12px;
102
110
  }
103
111
 
104
112
  .error {