@dfosco/storyboard 0.10.2 → 0.10.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/dist/storyboard-ui.js +2 -2
- package/dist/storyboard-ui.js.map +1 -1
- package/package.json +3 -3
- package/src/core/stores/configSchema.js +1 -1
- package/src/internals/canvas/CanvasPage.jsx +40 -11
- package/src/internals/canvas/CanvasPage.module.css +0 -17
- package/src/internals/canvas/ConnectorLayer.module.css +16 -0
- package/src/internals/canvas/widgets/PrototypeEmbed.jsx +1 -1
- package/src/internals/hooks/useThemeState.test.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dfosco/storyboard",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Storyboard prototyping framework — core engine, React integration, and canvas",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"remark-gfm": "^4.0.1",
|
|
118
118
|
"remark-html": "^16.0.1",
|
|
119
119
|
"tailwind-merge": "^3.5.0",
|
|
120
|
-
"ws": "^8.
|
|
120
|
+
"ws": "^8.21.0",
|
|
121
121
|
"zod": "^3.23.8"
|
|
122
122
|
},
|
|
123
123
|
"peerDependencies": {
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
"tailwindcss": "^4.2.2",
|
|
195
195
|
"tw-animate-css": "^1.4.0",
|
|
196
196
|
"typescript": "^5.6.0",
|
|
197
|
-
"vite": "^7.3.
|
|
197
|
+
"vite": "^7.3.5"
|
|
198
198
|
},
|
|
199
199
|
"optionalDependencies": {
|
|
200
200
|
"node-pty": "^1.0.0"
|
|
@@ -329,7 +329,7 @@ export const configDefaults = {
|
|
|
329
329
|
surfaces: {
|
|
330
330
|
prototype: { label: 'Prototype', sync: true },
|
|
331
331
|
canvas: { label: 'Canvas', sync: true },
|
|
332
|
-
toolbar: { label: 'Tools', sync:
|
|
332
|
+
toolbar: { label: 'Tools', sync: true },
|
|
333
333
|
codeBoxes: { label: 'Code boxes', sync: true },
|
|
334
334
|
},
|
|
335
335
|
// Default global theme. "system" follows OS prefers-color-scheme.
|
|
@@ -64,6 +64,35 @@ function zoomLimits() {
|
|
|
64
64
|
return { ZOOM_MIN: z.min, ZOOM_MAX: z.max, ZOOM_STEP: z.step }
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Compute the .canvasZoom wrapper's layout width/height for a given scale,
|
|
69
|
+
* honoring `canvas.surface.{width,height}`.
|
|
70
|
+
*
|
|
71
|
+
* Default ("auto"): `max(10000, 100 / scale) <axis-unit>` — keeps the
|
|
72
|
+
* surface huge so users can pan into an infinite-feeling canvas; layout
|
|
73
|
+
* shrinks with zoom-in but the 10000-unit floor keeps scroll available.
|
|
74
|
+
*
|
|
75
|
+
* Viewport: the surface clamps to 100<axis-unit> at scale = 1, then grows
|
|
76
|
+
* proportionally with scale on zoom-in (`100 * scale <axis-unit>`) so the
|
|
77
|
+
* scroller still produces horizontal/vertical scroll above 100% zoom.
|
|
78
|
+
* At scale < 1 the layout stays at 100<axis-unit> (zoom-out doesn't add
|
|
79
|
+
* extra empty space).
|
|
80
|
+
*/
|
|
81
|
+
function getZoomLayoutDim(scale, surfaceAxis, axisUnit) {
|
|
82
|
+
if (surfaceAxis === 'viewport') {
|
|
83
|
+
return `${Math.max(100, 100 * scale)}${axisUnit}`
|
|
84
|
+
}
|
|
85
|
+
return `${Math.max(10000, 100 / scale)}${axisUnit}`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getZoomLayoutWidth(scale, surface) {
|
|
89
|
+
return getZoomLayoutDim(scale, surface?.width, 'vw')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getZoomLayoutHeight(scale, surface) {
|
|
93
|
+
return getZoomLayoutDim(scale, surface?.height, 'vh')
|
|
94
|
+
}
|
|
95
|
+
|
|
67
96
|
/** Saved viewport state older than this is considered stale — zoom-to-fit instead. */
|
|
68
97
|
const VIEWPORT_TTL_MS = 15 * 60 * 1000
|
|
69
98
|
|
|
@@ -135,7 +164,7 @@ function getConnectedComponent(widgetId, connectors) {
|
|
|
135
164
|
|
|
136
165
|
function resolveCanvasThemeFromStorage() {
|
|
137
166
|
if (typeof localStorage === 'undefined') return 'light'
|
|
138
|
-
let sync = { prototype: true, toolbar:
|
|
167
|
+
let sync = { prototype: true, toolbar: true, codeBoxes: true, canvas: true }
|
|
139
168
|
try {
|
|
140
169
|
const rawSync = localStorage.getItem('sb-theme-sync')
|
|
141
170
|
if (rawSync) sync = { ...sync, ...JSON.parse(rawSync) }
|
|
@@ -2058,8 +2087,8 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2058
2087
|
const zoomEl = zoomElRef.current
|
|
2059
2088
|
if (zoomEl) {
|
|
2060
2089
|
zoomEl.style.transform = `scale(${newScale})`
|
|
2061
|
-
zoomEl.style.width =
|
|
2062
|
-
zoomEl.style.height =
|
|
2090
|
+
zoomEl.style.width = getZoomLayoutWidth(newScale, interactionRef.current?.surface)
|
|
2091
|
+
zoomEl.style.height = getZoomLayoutHeight(newScale, interactionRef.current?.surface)
|
|
2063
2092
|
}
|
|
2064
2093
|
setZoom(fitZoom)
|
|
2065
2094
|
el.scrollLeft = (bounds.minX - FIT_PADDING) * newScale
|
|
@@ -2281,8 +2310,8 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2281
2310
|
// Imperative DOM update — no React re-render
|
|
2282
2311
|
zoomRef.current = clampedZoom
|
|
2283
2312
|
zoomEl.style.transform = `scale(${newScale})`
|
|
2284
|
-
zoomEl.style.width =
|
|
2285
|
-
zoomEl.style.height =
|
|
2313
|
+
zoomEl.style.width = getZoomLayoutWidth(newScale, interactionRef.current?.surface)
|
|
2314
|
+
zoomEl.style.height = getZoomLayoutHeight(newScale, interactionRef.current?.surface)
|
|
2286
2315
|
|
|
2287
2316
|
// Hint GPU compositing during active zoom
|
|
2288
2317
|
zoomEl.dataset.zooming = ''
|
|
@@ -2618,8 +2647,8 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2618
2647
|
const zoomEl = zoomElRef.current
|
|
2619
2648
|
if (zoomEl) {
|
|
2620
2649
|
zoomEl.style.transform = `scale(${newScale})`
|
|
2621
|
-
zoomEl.style.width =
|
|
2622
|
-
zoomEl.style.height =
|
|
2650
|
+
zoomEl.style.width = getZoomLayoutWidth(newScale, interactionRef.current?.surface)
|
|
2651
|
+
zoomEl.style.height = getZoomLayoutHeight(newScale, interactionRef.current?.surface)
|
|
2623
2652
|
}
|
|
2624
2653
|
setZoom(fitZoom)
|
|
2625
2654
|
|
|
@@ -2852,8 +2881,8 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
2852
2881
|
const zoomEl = zoomElRef.current
|
|
2853
2882
|
if (zoomEl) {
|
|
2854
2883
|
zoomEl.style.transform = `scale(${newScale})`
|
|
2855
|
-
zoomEl.style.width =
|
|
2856
|
-
zoomEl.style.height =
|
|
2884
|
+
zoomEl.style.width = getZoomLayoutWidth(newScale, interactionRef.current?.surface)
|
|
2885
|
+
zoomEl.style.height = getZoomLayoutHeight(newScale, interactionRef.current?.surface)
|
|
2857
2886
|
}
|
|
2858
2887
|
setZoom(targetZoom)
|
|
2859
2888
|
}
|
|
@@ -4098,8 +4127,8 @@ export default function CanvasPage({ canvasId: canvasIdProp, name, siblingPages
|
|
|
4098
4127
|
style={{
|
|
4099
4128
|
transform: `scale(${scale})`,
|
|
4100
4129
|
transformOrigin: '0 0',
|
|
4101
|
-
width:
|
|
4102
|
-
height:
|
|
4130
|
+
width: getZoomLayoutWidth(scale, interactionRef.current?.surface),
|
|
4131
|
+
height: getZoomLayoutHeight(scale, interactionRef.current?.surface),
|
|
4103
4132
|
...(spaceHeld ? { pointerEvents: 'none' } : {}),
|
|
4104
4133
|
}}
|
|
4105
4134
|
>
|
|
@@ -41,23 +41,6 @@
|
|
|
41
41
|
will-change: transform;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/* When the consumer sets `canvas.surface.width/height` to "viewport"
|
|
45
|
-
(see canvasInteractionStore.js), the canvas surface clamps to 100vw/100vh
|
|
46
|
-
— but the .canvasZoom wrapper still has its inline `max(10000, 100/scale)vw`
|
|
47
|
-
sizing, which would leave a huge stretch of empty wrapper to the right /
|
|
48
|
-
below the canvas and let the user scroll into it. Clamp the wrapper to
|
|
49
|
-
the viewport too. !important is required to beat the inline style set
|
|
50
|
-
by applyZoom(). Each axis is independent — only the configured axis is
|
|
51
|
-
constrained. */
|
|
52
|
-
:global(html[data-canvas-surface-width="viewport"]) .canvasZoom {
|
|
53
|
-
width: 100vw !important;
|
|
54
|
-
min-width: 100vw !important;
|
|
55
|
-
}
|
|
56
|
-
:global(html[data-canvas-surface-height="viewport"]) .canvasZoom {
|
|
57
|
-
height: 100vh !important;
|
|
58
|
-
min-height: 100vh !important;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
44
|
/* Selection outline is now handled by WidgetChrome.module.css (.widgetSlotSelected) */
|
|
62
45
|
|
|
63
46
|
.canvasLogo {
|
|
@@ -28,6 +28,22 @@
|
|
|
28
28
|
transition: opacity 0.2s ease;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/* When the consumer sets `canvas.surface.{width,height}` to "viewport"
|
|
32
|
+
(see canvasInteractionStore.js), the canvas surface clamps to 100vw/100vh.
|
|
33
|
+
The SVG layers below are siblings of .tc-canvas inside .canvasZoom and
|
|
34
|
+
have a hardcoded `width: 100000px; height: 100000px` inline style — so
|
|
35
|
+
without clamping them too, they grow the scroll container even though
|
|
36
|
+
.tc-canvas is viewport-sized. Override with !important to beat the
|
|
37
|
+
inline width/height. Each axis is independent. */
|
|
38
|
+
:global(html[data-canvas-surface-width="viewport"]) .connectorLayer,
|
|
39
|
+
:global(html[data-canvas-surface-width="viewport"]) .endpointLayer {
|
|
40
|
+
width: 100vw !important;
|
|
41
|
+
}
|
|
42
|
+
:global(html[data-canvas-surface-height="viewport"]) .connectorLayer,
|
|
43
|
+
:global(html[data-canvas-surface-height="viewport"]) .endpointLayer {
|
|
44
|
+
height: 100vh !important;
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
.connectorPath {
|
|
32
48
|
fill: none;
|
|
33
49
|
stroke: var(--connector-stroke, var(--color-foreground-accent, #0969da));
|
|
@@ -31,7 +31,7 @@ function formatName(name) {
|
|
|
31
31
|
|
|
32
32
|
function resolveCanvasThemeFromStorage() {
|
|
33
33
|
if (typeof localStorage === 'undefined') return 'light'
|
|
34
|
-
let sync = { prototype: true, toolbar:
|
|
34
|
+
let sync = { prototype: true, toolbar: true, codeBoxes: true, canvas: false }
|
|
35
35
|
try {
|
|
36
36
|
const rawSync = localStorage.getItem('sb-theme-sync')
|
|
37
37
|
if (rawSync) sync = { ...sync, ...JSON.parse(rawSync) }
|
|
@@ -49,7 +49,7 @@ describe('useThemeSyncTargets', () => {
|
|
|
49
49
|
it('returns default sync targets', () => {
|
|
50
50
|
const { result } = renderHook(() => useThemeSyncTargets())
|
|
51
51
|
expect(result.current.prototype).toBe(true)
|
|
52
|
-
expect(result.current.toolbar).toBe(
|
|
52
|
+
expect(result.current.toolbar).toBe(true)
|
|
53
53
|
expect(result.current.codeBoxes).toBe(true)
|
|
54
54
|
expect(result.current.canvas).toBe(true)
|
|
55
55
|
})
|