@dfosco/storyboard-react 4.2.0-beta.4 → 4.2.1

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.
Files changed (89) hide show
  1. package/package.json +10 -11
  2. package/src/AuthModal/AuthModal.jsx +6 -8
  3. package/src/BranchBar/BranchBar.jsx +20 -6
  4. package/src/BranchBar/BranchBar.module.css +13 -4
  5. package/src/BranchBar/useBranches.js +20 -6
  6. package/src/BranchBar/useBranches.test.js +68 -0
  7. package/src/CommandPalette/CommandPalette.jsx +480 -187
  8. package/src/CommandPalette/command-palette.css +142 -78
  9. package/src/Icon.jsx +157 -58
  10. package/src/Viewfinder.jsx +562 -207
  11. package/src/Viewfinder.module.css +434 -93
  12. package/src/Workspace.jsx +7 -0
  13. package/src/canvas/CanvasPage.bridge.test.jsx +14 -6
  14. package/src/canvas/CanvasPage.dragdrop.test.jsx +11 -7
  15. package/src/canvas/CanvasPage.jsx +739 -219
  16. package/src/canvas/CanvasPage.module.css +13 -15
  17. package/src/canvas/CanvasPage.multiselect.test.jsx +17 -6
  18. package/src/canvas/ConnectorLayer.jsx +121 -165
  19. package/src/canvas/ConnectorLayer.module.css +69 -0
  20. package/src/canvas/PageSelector.test.jsx +15 -6
  21. package/src/canvas/canvasApi.js +68 -2
  22. package/src/canvas/canvasReloadGuard.test.js +1 -1
  23. package/src/canvas/connectorGeometry.js +132 -0
  24. package/src/canvas/hotPoolDevLogs.js +25 -0
  25. package/src/canvas/useCanvas.js +1 -1
  26. package/src/canvas/useMarqueeSelect.js +30 -4
  27. package/src/canvas/widgets/CodePenEmbed.jsx +1 -0
  28. package/src/canvas/widgets/ComponentSetWidget.jsx +199 -0
  29. package/src/canvas/widgets/ComponentSetWidget.module.css +89 -0
  30. package/src/canvas/widgets/ComponentWidget.jsx +1 -0
  31. package/src/canvas/widgets/CropOverlay.jsx +219 -0
  32. package/src/canvas/widgets/CropOverlay.module.css +118 -0
  33. package/src/canvas/widgets/ExpandedPane.jsx +474 -0
  34. package/src/canvas/widgets/ExpandedPane.module.css +179 -0
  35. package/src/canvas/widgets/ExpandedPane.test.jsx +240 -0
  36. package/src/canvas/widgets/ExpandedPaneTopBar.jsx +111 -0
  37. package/src/canvas/widgets/ExpandedPaneTopBar.module.css +59 -0
  38. package/src/canvas/widgets/ExpandedPaneTopBar.test.jsx +45 -0
  39. package/src/canvas/widgets/FigmaEmbed.jsx +62 -47
  40. package/src/canvas/widgets/FigmaEmbed.module.css +61 -0
  41. package/src/canvas/widgets/ImageWidget.jsx +130 -9
  42. package/src/canvas/widgets/ImageWidget.module.css +30 -0
  43. package/src/canvas/widgets/LinkPreview.jsx +113 -5
  44. package/src/canvas/widgets/LinkPreview.module.css +127 -0
  45. package/src/canvas/widgets/MarkdownBlock.jsx +167 -17
  46. package/src/canvas/widgets/MarkdownBlock.module.css +148 -0
  47. package/src/canvas/widgets/PromptWidget.jsx +414 -0
  48. package/src/canvas/widgets/PromptWidget.module.css +273 -0
  49. package/src/canvas/widgets/PrototypeEmbed.jsx +77 -39
  50. package/src/canvas/widgets/PrototypeEmbed.module.css +117 -0
  51. package/src/canvas/widgets/PrototypeEmbed.test.jsx +2 -2
  52. package/src/canvas/widgets/ResizeHandle.jsx +17 -6
  53. package/src/canvas/widgets/StoryWidget.jsx +73 -15
  54. package/src/canvas/widgets/TerminalReadWidget.jsx +146 -0
  55. package/src/canvas/widgets/TerminalReadWidget.module.css +94 -0
  56. package/src/canvas/widgets/TerminalWidget.jsx +445 -67
  57. package/src/canvas/widgets/TerminalWidget.module.css +271 -8
  58. package/src/canvas/widgets/TilesWidget.jsx +300 -0
  59. package/src/canvas/widgets/TilesWidget.module.css +133 -0
  60. package/src/canvas/widgets/WidgetChrome.jsx +74 -153
  61. package/src/canvas/widgets/WidgetChrome.module.css +30 -1
  62. package/src/canvas/widgets/embedInteraction.test.jsx +24 -26
  63. package/src/canvas/widgets/expandUtils.js +560 -0
  64. package/src/canvas/widgets/expandUtils.test.js +155 -0
  65. package/src/canvas/widgets/index.js +9 -0
  66. package/src/canvas/widgets/snapshotDisplay.test.jsx +23 -71
  67. package/src/canvas/widgets/tilePool.js +23 -0
  68. package/src/canvas/widgets/tiles/diagonal-bl.png +0 -0
  69. package/src/canvas/widgets/tiles/diagonal-br.png +0 -0
  70. package/src/canvas/widgets/tiles/diagonal-tl.png +0 -0
  71. package/src/canvas/widgets/tiles/leaf.png +0 -0
  72. package/src/canvas/widgets/tiles/quarter-tl.png +0 -0
  73. package/src/canvas/widgets/tiles/quarter-tr.png +0 -0
  74. package/src/canvas/widgets/tiles/solid-a.png +0 -0
  75. package/src/canvas/widgets/tiles/solid-b.png +0 -0
  76. package/src/canvas/widgets/widgetConfig.js +55 -4
  77. package/src/canvas/widgets/widgetIcons.jsx +190 -0
  78. package/src/canvas/widgets/widgetProps.js +1 -0
  79. package/src/context.jsx +48 -20
  80. package/src/hooks/useConfig.js +14 -0
  81. package/src/hooks/usePrototypeReloadGuard.js +64 -0
  82. package/src/hooks/useSceneData.js +1 -0
  83. package/src/hooks/useThemeState.test.js +1 -1
  84. package/src/index.js +8 -2
  85. package/src/story/ComponentSetPage.jsx +186 -0
  86. package/src/story/ComponentSetPage.module.css +121 -0
  87. package/src/story/StoryPage.jsx +32 -2
  88. package/src/vite/data-plugin.js +407 -67
  89. package/src/vite/data-plugin.test.js +1 -1
@@ -0,0 +1,121 @@
1
+ /* ComponentSetPage — grid layout for all exports of a story */
2
+
3
+ .grid {
4
+ background-color: var(--bgColor-muted, #f6f8fa);
5
+ display: flex;
6
+ flex-wrap: nowrap;
7
+ gap: 12px;
8
+ padding: 12px;
9
+ min-height: 100vh;
10
+ width: max-content;
11
+ min-width: 100%;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ .grid[data-layout="horizontal"] {
16
+ flex-direction: row;
17
+ }
18
+
19
+ .grid[data-layout="vertical"] {
20
+ flex-direction: column;
21
+ }
22
+
23
+ .cell {
24
+ flex: 0 0 auto;
25
+ display: flex;
26
+ flex-direction: column;
27
+ border: 2px solid var(--borderColor-muted, #d8dee4);
28
+ border-radius: 2px;
29
+ overflow: hidden;
30
+ transition: border-color 120ms ease, box-shadow 120ms ease;
31
+ position: relative;
32
+ background: var(--bgColor-default, #ffffff);
33
+ }
34
+
35
+ /* In horizontal layout, each cell snaps to the widest component */
36
+ .grid[data-layout="horizontal"] .cell {
37
+ min-width: var(--cell-snap-w, 200px);
38
+ }
39
+
40
+ /* In vertical layout, each cell snaps to the tallest component */
41
+ .grid[data-layout="vertical"] .cell {
42
+ min-height: var(--cell-snap-h, 120px);
43
+ min-width: 100%;
44
+ }
45
+
46
+ .cell[data-selected] {
47
+ border-color: var(--fgColor-accent, #0969da);
48
+ box-shadow: 0 0 0 1px var(--fgColor-accent, #0969da);
49
+ }
50
+
51
+ .cellLabel {
52
+ all: unset;
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 6px;
56
+ padding: 6px 10px;
57
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
58
+ font-size: 11px;
59
+ font-weight: 600;
60
+ color: var(--fgColor-muted, #656d76);
61
+ background: var(--bgColor-muted, #f6f8fa);
62
+ border-bottom: 1px solid var(--borderColor-muted, #d8dee4);
63
+ cursor: pointer;
64
+ user-select: none;
65
+ transition: background 100ms ease, color 100ms ease;
66
+ flex-shrink: 0;
67
+ /* Round top corners to match cell */
68
+ border-radius: 6px 6px 0 0;
69
+ }
70
+
71
+ .cellLabel:hover {
72
+ background: var(--bgColor-neutral-muted, #eaeef2);
73
+ color: var(--fgColor-default, #1f2328);
74
+ }
75
+
76
+ .cellLabel[data-selected] {
77
+ color: var(--fgColor-accent, #0969da);
78
+ background: var(--bgColor-accent-muted, #ddf4ff);
79
+ }
80
+
81
+ .cellRadio {
82
+ display: inline-flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ width: 12px;
86
+ height: 12px;
87
+ border-radius: 50%;
88
+ border: 2px solid var(--borderColor-accent, #d8dee4);
89
+ flex-shrink: 0;
90
+ transition: border-color 100ms ease, background 100ms ease;
91
+ }
92
+
93
+ .cellRadio[data-selected] {
94
+ border-color: var(--fgColor-accent, #0969da);
95
+ background: var(--fgColor-accent, #0969da);
96
+ }
97
+
98
+ .cellContent {
99
+ flex: 1;
100
+ overflow: visible;
101
+ position: relative;
102
+ }
103
+
104
+ .error {
105
+ display: flex;
106
+ flex-direction: column;
107
+ gap: 4px;
108
+ padding: 1rem;
109
+ color: var(--fgColor-danger, #cf222e);
110
+ font-size: 0.875rem;
111
+ line-height: 1.5;
112
+ }
113
+
114
+ .loading {
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ padding: 3rem;
119
+ color: var(--fgColor-muted, #656d76);
120
+ font-size: 0.875rem;
121
+ }
@@ -5,12 +5,14 @@
5
5
  * When ?export=ExportName is present, renders that single export.
6
6
  * Without ?export, renders all named exports stacked.
7
7
  */
8
- import { useState, useEffect, useMemo } from 'react'
8
+ import { useState, useEffect, useMemo, lazy, Suspense } from 'react'
9
9
  import { useLocation } from 'react-router-dom'
10
10
  import { getStoryData } from '@dfosco/storyboard-core'
11
11
  import { ThemeProvider, BaseStyles } from '@primer/react'
12
12
  import styles from './StoryPage.module.css'
13
13
 
14
+ const ComponentSetPageLazy = lazy(() => import('./ComponentSetPage.jsx'))
15
+
14
16
  function StoryErrorFallback({ name, error }) {
15
17
  return (
16
18
  <div className={styles.error}>
@@ -25,12 +27,31 @@ export default function StoryPage({ name }) {
25
27
  const searchParams = new URLSearchParams(location.search)
26
28
  const exportFilter = searchParams.get('export')
27
29
  const isEmbed = searchParams.has('_sb_embed')
30
+ const isComponentSet = searchParams.has('_sb_component_set')
31
+
32
+ // When embedded as a canvas iframe, suppress HMR full-reloads.
33
+ // Story content updates via React Fast Refresh; a full-reload
34
+ // causes a visible flash and can create a reload loop when
35
+ // multiple story iframes are on the same canvas.
36
+ useEffect(() => {
37
+ if (!isEmbed || !import.meta.hot) return
38
+ const msg = { active: true }
39
+ import.meta.hot.send('storyboard:canvas-hmr-guard', msg)
40
+ const interval = setInterval(() => {
41
+ import.meta.hot.send('storyboard:canvas-hmr-guard', msg)
42
+ }, 3000)
43
+ return () => {
44
+ clearInterval(interval)
45
+ import.meta.hot.send('storyboard:canvas-hmr-guard', { active: false })
46
+ }
47
+ }, [isEmbed])
28
48
 
29
49
  const story = useMemo(() => getStoryData(name), [name])
30
50
  const [exports, setExports] = useState(null)
31
51
  const [error, setError] = useState(null)
32
52
 
33
53
  useEffect(() => {
54
+ if (isComponentSet) return
34
55
  if (!story?._storyImport) {
35
56
  Promise.resolve().then(() => setError(`Story "${name}" not found or missing import`))
36
57
  return
@@ -55,7 +76,7 @@ export default function StoryPage({ name }) {
55
76
  })
56
77
 
57
78
  return () => { cancelled = true }
58
- }, [name, story])
79
+ }, [name, story, isComponentSet])
59
80
 
60
81
  // Signal snapshot-ready after story renders in embed mode.
61
82
  useEffect(() => {
@@ -67,6 +88,15 @@ export default function StoryPage({ name }) {
67
88
  })
68
89
  }, [isEmbed, exports])
69
90
 
91
+ // Delegate to ComponentSetPage for grid view (after all hooks)
92
+ if (isComponentSet) {
93
+ return (
94
+ <Suspense fallback={isEmbed ? null : <div className={styles.loading}>Loading component set…</div>}>
95
+ <ComponentSetPageLazy name={name} />
96
+ </Suspense>
97
+ )
98
+ }
99
+
70
100
  if (error) {
71
101
  return (
72
102
  <StoryErrorFallback name={name} error={error} />