@toriistudio/v0-playground 0.2.10 → 0.3.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.
- package/README.md +20 -1
- package/dist/index.d.mts +36 -3
- package/dist/index.d.ts +36 -3
- package/dist/index.js +154 -35
- package/dist/index.mjs +150 -35
- package/package.json +13 -2
- package/dist/preset.d.mts +0 -5
- package/dist/preset.d.ts +0 -5
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Perfect for prototyping components, sharing usage examples, or building your own
|
|
|
22
22
|
To use `@toriistudio/v0-playground`, you’ll need to install the following peer dependencies:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
yarn add @radix-ui/react-label @radix-ui/react-select @radix-ui/react-slider @radix-ui/react-slot @radix-ui/react-switch class-variance-authority clsx lucide-react tailwind-merge tailwindcss-animate
|
|
25
|
+
yarn add @radix-ui/react-label @radix-ui/react-select @radix-ui/react-slider @radix-ui/react-slot @radix-ui/react-switch class-variance-authority clsx lucide-react tailwind-merge tailwindcss-animate @react-three/drei @react-three/fiber three lodash
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
Or automate it with:
|
|
@@ -83,6 +83,25 @@ export default function App() {
|
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
### R3F Canvas
|
|
87
|
+
|
|
88
|
+
`PlaygroundCanvas` wraps the playground with a react-three-fiber canvas. Pass any
|
|
89
|
+
`Canvas` props through `mediaProps`:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { PlaygroundCanvas } from "@toriistudio/v0-playground";
|
|
93
|
+
|
|
94
|
+
export default function Scene() {
|
|
95
|
+
return (
|
|
96
|
+
<PlaygroundCanvas mediaProps={{ size: { width: 300, height: 300 } }}>
|
|
97
|
+
<MyR3FComponent />
|
|
98
|
+
</PlaygroundCanvas>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
See [`examples/r3f-canvas`](./examples/r3f-canvas) for a full working example.
|
|
104
|
+
|
|
86
105
|
## 💡 Example Use Cases
|
|
87
106
|
|
|
88
107
|
- Build custom component sandboxes
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,43 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import React__default, { ReactNode } from 'react';
|
|
4
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
5
|
+
import { VariantProps } from 'class-variance-authority';
|
|
3
6
|
|
|
4
7
|
declare function Playground({ children }: {
|
|
5
8
|
children: ReactNode;
|
|
6
9
|
}): react_jsx_runtime.JSX.Element | null;
|
|
7
10
|
|
|
11
|
+
type CanvasMediaProps = {
|
|
12
|
+
debugOrbit?: boolean;
|
|
13
|
+
size: {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
} | null;
|
|
17
|
+
};
|
|
18
|
+
type CanvasProps = {
|
|
19
|
+
mediaProps?: CanvasMediaProps;
|
|
20
|
+
children: React__default.ReactNode;
|
|
21
|
+
};
|
|
22
|
+
declare const Canvas: React__default.FC<CanvasProps>;
|
|
23
|
+
|
|
24
|
+
type PlaygroundCanvasProps = {
|
|
25
|
+
children: React__default.ReactNode;
|
|
26
|
+
mediaProps?: CanvasMediaProps;
|
|
27
|
+
};
|
|
28
|
+
declare const PlaygroundCanvas: React__default.FC<PlaygroundCanvasProps>;
|
|
29
|
+
|
|
30
|
+
declare function CameraLogger(): react_jsx_runtime.JSX.Element;
|
|
31
|
+
|
|
32
|
+
declare const buttonVariants: (props?: ({
|
|
33
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
34
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
35
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
36
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
37
|
+
asChild?: boolean;
|
|
38
|
+
}
|
|
39
|
+
declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
40
|
+
|
|
8
41
|
type BaseControl = {
|
|
9
42
|
hidden?: boolean;
|
|
10
43
|
};
|
|
@@ -31,7 +64,7 @@ type ControlType = ({
|
|
|
31
64
|
type: "button";
|
|
32
65
|
onClick?: () => void;
|
|
33
66
|
label?: string;
|
|
34
|
-
render?: () =>
|
|
67
|
+
render?: () => React__default.ReactNode;
|
|
35
68
|
} & BaseControl);
|
|
36
69
|
type ControlsSchema = Record<string, ControlType>;
|
|
37
70
|
type ControlsConfig = {
|
|
@@ -64,4 +97,4 @@ declare const useUrlSyncedControls: <T extends ControlsSchema>(schema: T, option
|
|
|
64
97
|
jsx: () => string;
|
|
65
98
|
};
|
|
66
99
|
|
|
67
|
-
export { type ControlType, ControlsProvider, type ControlsSchema, Playground, useControls, useUrlSyncedControls };
|
|
100
|
+
export { Button, CameraLogger, Canvas, type ControlType, ControlsProvider, type ControlsSchema, Playground, PlaygroundCanvas, useControls, useUrlSyncedControls };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,43 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import React__default, { ReactNode } from 'react';
|
|
4
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
5
|
+
import { VariantProps } from 'class-variance-authority';
|
|
3
6
|
|
|
4
7
|
declare function Playground({ children }: {
|
|
5
8
|
children: ReactNode;
|
|
6
9
|
}): react_jsx_runtime.JSX.Element | null;
|
|
7
10
|
|
|
11
|
+
type CanvasMediaProps = {
|
|
12
|
+
debugOrbit?: boolean;
|
|
13
|
+
size: {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
} | null;
|
|
17
|
+
};
|
|
18
|
+
type CanvasProps = {
|
|
19
|
+
mediaProps?: CanvasMediaProps;
|
|
20
|
+
children: React__default.ReactNode;
|
|
21
|
+
};
|
|
22
|
+
declare const Canvas: React__default.FC<CanvasProps>;
|
|
23
|
+
|
|
24
|
+
type PlaygroundCanvasProps = {
|
|
25
|
+
children: React__default.ReactNode;
|
|
26
|
+
mediaProps?: CanvasMediaProps;
|
|
27
|
+
};
|
|
28
|
+
declare const PlaygroundCanvas: React__default.FC<PlaygroundCanvasProps>;
|
|
29
|
+
|
|
30
|
+
declare function CameraLogger(): react_jsx_runtime.JSX.Element;
|
|
31
|
+
|
|
32
|
+
declare const buttonVariants: (props?: ({
|
|
33
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
34
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
35
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
36
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
37
|
+
asChild?: boolean;
|
|
38
|
+
}
|
|
39
|
+
declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
40
|
+
|
|
8
41
|
type BaseControl = {
|
|
9
42
|
hidden?: boolean;
|
|
10
43
|
};
|
|
@@ -31,7 +64,7 @@ type ControlType = ({
|
|
|
31
64
|
type: "button";
|
|
32
65
|
onClick?: () => void;
|
|
33
66
|
label?: string;
|
|
34
|
-
render?: () =>
|
|
67
|
+
render?: () => React__default.ReactNode;
|
|
35
68
|
} & BaseControl);
|
|
36
69
|
type ControlsSchema = Record<string, ControlType>;
|
|
37
70
|
type ControlsConfig = {
|
|
@@ -64,4 +97,4 @@ declare const useUrlSyncedControls: <T extends ControlsSchema>(schema: T, option
|
|
|
64
97
|
jsx: () => string;
|
|
65
98
|
};
|
|
66
99
|
|
|
67
|
-
export { type ControlType, ControlsProvider, type ControlsSchema, Playground, useControls, useUrlSyncedControls };
|
|
100
|
+
export { Button, CameraLogger, Canvas, type ControlType, ControlsProvider, type ControlsSchema, Playground, PlaygroundCanvas, useControls, useUrlSyncedControls };
|
package/dist/index.js
CHANGED
|
@@ -30,8 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
Button: () => Button,
|
|
34
|
+
CameraLogger: () => CameraLogger,
|
|
35
|
+
Canvas: () => Canvas_default,
|
|
33
36
|
ControlsProvider: () => ControlsProvider,
|
|
34
37
|
Playground: () => Playground,
|
|
38
|
+
PlaygroundCanvas: () => PlaygroundCanvas_default,
|
|
35
39
|
useControls: () => useControls,
|
|
36
40
|
useUrlSyncedControls: () => useUrlSyncedControls
|
|
37
41
|
});
|
|
@@ -214,35 +218,6 @@ var ControlsProvider = ({ children }) => {
|
|
|
214
218
|
var useControls = (schema, options) => {
|
|
215
219
|
const ctx = (0, import_react2.useContext)(ControlsContext);
|
|
216
220
|
if (!ctx) throw new Error("useControls must be used within ControlsProvider");
|
|
217
|
-
(0, import_react2.useEffect)(() => {
|
|
218
|
-
ctx.registerSchema(schema, options);
|
|
219
|
-
}, [JSON.stringify(schema), JSON.stringify(options)]);
|
|
220
|
-
(0, import_react2.useEffect)(() => {
|
|
221
|
-
for (const key in schema) {
|
|
222
|
-
if (!(key in ctx.values) && "value" in schema[key]) {
|
|
223
|
-
ctx.setValue(key, schema[key].value);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}, [JSON.stringify(schema), JSON.stringify(ctx.values)]);
|
|
227
|
-
const typedValues = ctx.values;
|
|
228
|
-
const jsx12 = (0, import_react2.useCallback)(() => {
|
|
229
|
-
if (!options?.componentName) return "";
|
|
230
|
-
const props = Object.entries(typedValues).map(([key, val]) => {
|
|
231
|
-
if (typeof val === "string") return `${key}="${val}"`;
|
|
232
|
-
if (typeof val === "boolean") return `${key}={${val}}`;
|
|
233
|
-
return `${key}={${JSON.stringify(val)}}`;
|
|
234
|
-
}).join(" ");
|
|
235
|
-
return `<${options.componentName} ${props} />`;
|
|
236
|
-
}, [options?.componentName, JSON.stringify(typedValues)]);
|
|
237
|
-
return {
|
|
238
|
-
...typedValues,
|
|
239
|
-
controls: ctx.values,
|
|
240
|
-
schema: ctx.schema,
|
|
241
|
-
setValue: ctx.setValue,
|
|
242
|
-
jsx: jsx12
|
|
243
|
-
};
|
|
244
|
-
};
|
|
245
|
-
var useUrlSyncedControls = (schema, options) => {
|
|
246
221
|
const urlParams = getUrlParams();
|
|
247
222
|
const mergedSchema = Object.fromEntries(
|
|
248
223
|
Object.entries(schema).map(([key, control]) => {
|
|
@@ -265,8 +240,35 @@ var useUrlSyncedControls = (schema, options) => {
|
|
|
265
240
|
];
|
|
266
241
|
})
|
|
267
242
|
);
|
|
268
|
-
|
|
243
|
+
(0, import_react2.useEffect)(() => {
|
|
244
|
+
ctx.registerSchema(mergedSchema, options);
|
|
245
|
+
}, [JSON.stringify(mergedSchema), JSON.stringify(options)]);
|
|
246
|
+
(0, import_react2.useEffect)(() => {
|
|
247
|
+
for (const key in mergedSchema) {
|
|
248
|
+
if (!(key in ctx.values) && "value" in mergedSchema[key]) {
|
|
249
|
+
ctx.setValue(key, mergedSchema[key].value);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}, [JSON.stringify(mergedSchema), JSON.stringify(ctx.values)]);
|
|
253
|
+
const typedValues = ctx.values;
|
|
254
|
+
const jsx15 = (0, import_react2.useCallback)(() => {
|
|
255
|
+
if (!options?.componentName) return "";
|
|
256
|
+
const props = Object.entries(typedValues).map(([key, val]) => {
|
|
257
|
+
if (typeof val === "string") return `${key}="${val}"`;
|
|
258
|
+
if (typeof val === "boolean") return `${key}={${val}}`;
|
|
259
|
+
return `${key}={${JSON.stringify(val)}}`;
|
|
260
|
+
}).join(" ");
|
|
261
|
+
return `<${options.componentName} ${props} />`;
|
|
262
|
+
}, [options?.componentName, JSON.stringify(typedValues)]);
|
|
263
|
+
return {
|
|
264
|
+
...typedValues,
|
|
265
|
+
controls: ctx.values,
|
|
266
|
+
schema: ctx.schema,
|
|
267
|
+
setValue: ctx.setValue,
|
|
268
|
+
jsx: jsx15
|
|
269
|
+
};
|
|
269
270
|
};
|
|
271
|
+
var useUrlSyncedControls = useControls;
|
|
270
272
|
|
|
271
273
|
// src/components/PreviewContainer/PreviewContainer.tsx
|
|
272
274
|
var import_react3 = require("react");
|
|
@@ -283,7 +285,7 @@ var PreviewContainer = ({ children, hideControls }) => {
|
|
|
283
285
|
width: `${100 - leftPanelWidth}%`,
|
|
284
286
|
marginLeft: `${leftPanelWidth}%`
|
|
285
287
|
} : {},
|
|
286
|
-
children
|
|
288
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-screen h-screen flex items-center justify-center", children })
|
|
287
289
|
}
|
|
288
290
|
);
|
|
289
291
|
};
|
|
@@ -576,7 +578,7 @@ var ControlPanel = () => {
|
|
|
576
578
|
const buttonControls = Object.entries(schema).filter(
|
|
577
579
|
([, control]) => control.type === "button" && !control.hidden
|
|
578
580
|
);
|
|
579
|
-
const
|
|
581
|
+
const jsx15 = (0, import_react5.useMemo)(() => {
|
|
580
582
|
if (!componentName) return "";
|
|
581
583
|
const props = Object.entries(values).map(([key, val]) => {
|
|
582
584
|
if (typeof val === "string") return `${key}="${val}"`;
|
|
@@ -702,16 +704,16 @@ var ControlPanel = () => {
|
|
|
702
704
|
return null;
|
|
703
705
|
}
|
|
704
706
|
}),
|
|
705
|
-
(buttonControls.length > 0 ||
|
|
707
|
+
(buttonControls.length > 0 || jsx15) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
706
708
|
"div",
|
|
707
709
|
{
|
|
708
710
|
className: `${normalControls.length > 0 ? "border-t" : ""} border-stone-700`,
|
|
709
711
|
children: [
|
|
710
|
-
|
|
712
|
+
jsx15 && config?.showCopyButton !== false && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
711
713
|
"button",
|
|
712
714
|
{
|
|
713
715
|
onClick: () => {
|
|
714
|
-
navigator.clipboard.writeText(
|
|
716
|
+
navigator.clipboard.writeText(jsx15);
|
|
715
717
|
setCopied(true);
|
|
716
718
|
setTimeout(() => setCopied(false), 5e3);
|
|
717
719
|
},
|
|
@@ -800,10 +802,127 @@ function Playground({ children }) {
|
|
|
800
802
|
!hideControls && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ControlPanel_default, {})
|
|
801
803
|
] }) });
|
|
802
804
|
}
|
|
805
|
+
|
|
806
|
+
// src/components/Canvas/Canvas.tsx
|
|
807
|
+
var import_react8 = __toESM(require("react"));
|
|
808
|
+
var import_fiber2 = require("@react-three/fiber");
|
|
809
|
+
var import_fiber3 = require("@react-three/fiber");
|
|
810
|
+
|
|
811
|
+
// src/components/CameraLogger/CameraLogger.tsx
|
|
812
|
+
var import_react7 = require("react");
|
|
813
|
+
var import_drei = require("@react-three/drei");
|
|
814
|
+
var import_fiber = require("@react-three/fiber");
|
|
815
|
+
var import_lodash = require("lodash");
|
|
816
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
817
|
+
function CameraLogger() {
|
|
818
|
+
const { camera } = (0, import_fiber.useThree)();
|
|
819
|
+
const controlsRef = (0, import_react7.useRef)(null);
|
|
820
|
+
const logRef = (0, import_react7.useRef)(null);
|
|
821
|
+
(0, import_react7.useEffect)(() => {
|
|
822
|
+
logRef.current = (0, import_lodash.debounce)(() => {
|
|
823
|
+
console.info("Camera position:", camera.position.toArray());
|
|
824
|
+
}, 200);
|
|
825
|
+
}, [camera]);
|
|
826
|
+
(0, import_react7.useEffect)(() => {
|
|
827
|
+
const controls = controlsRef.current;
|
|
828
|
+
const handler = logRef.current;
|
|
829
|
+
if (!controls || !handler) return;
|
|
830
|
+
controls.addEventListener("change", handler);
|
|
831
|
+
return () => controls.removeEventListener("change", handler);
|
|
832
|
+
}, []);
|
|
833
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_drei.OrbitControls, { ref: controlsRef });
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// src/components/Canvas/Canvas.tsx
|
|
837
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
838
|
+
var ResponsiveCamera = ({
|
|
839
|
+
height,
|
|
840
|
+
width
|
|
841
|
+
}) => {
|
|
842
|
+
const { camera } = (0, import_fiber2.useThree)();
|
|
843
|
+
(0, import_react8.useEffect)(() => {
|
|
844
|
+
const isMobile = width < 768;
|
|
845
|
+
const zoomFactor = isMobile ? 70 : 100;
|
|
846
|
+
camera.position.z = height / zoomFactor;
|
|
847
|
+
camera.updateProjectionMatrix();
|
|
848
|
+
}, [height, camera, width]);
|
|
849
|
+
return null;
|
|
850
|
+
};
|
|
851
|
+
var Canvas = ({ mediaProps, children }) => {
|
|
852
|
+
const canvasRef = (0, import_react8.useRef)(null);
|
|
853
|
+
const [parentSize, setParentSize] = (0, import_react8.useState)(null);
|
|
854
|
+
(0, import_react8.useEffect)(() => {
|
|
855
|
+
let observer = null;
|
|
856
|
+
const tryObserve = () => {
|
|
857
|
+
const node = canvasRef.current;
|
|
858
|
+
if (!node || !node.parentElement) {
|
|
859
|
+
setTimeout(tryObserve, 50);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const parent = node.parentElement;
|
|
863
|
+
observer = new ResizeObserver(([entry]) => {
|
|
864
|
+
const { width, height } = entry.contentRect;
|
|
865
|
+
setParentSize({ width, height });
|
|
866
|
+
});
|
|
867
|
+
observer.observe(parent);
|
|
868
|
+
};
|
|
869
|
+
tryObserve();
|
|
870
|
+
return () => {
|
|
871
|
+
if (observer) observer.disconnect();
|
|
872
|
+
};
|
|
873
|
+
}, []);
|
|
874
|
+
const mergedMediaProps = {
|
|
875
|
+
...mediaProps || {},
|
|
876
|
+
size: mediaProps?.size || { width: 400, height: 400 }
|
|
877
|
+
};
|
|
878
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
879
|
+
"div",
|
|
880
|
+
{
|
|
881
|
+
ref: canvasRef,
|
|
882
|
+
className: "w-full h-full pointer-events-none relative touch-none",
|
|
883
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
884
|
+
import_fiber2.Canvas,
|
|
885
|
+
{
|
|
886
|
+
resize: { polyfill: ResizeObserver },
|
|
887
|
+
style: { width: parentSize?.width, height: parentSize?.height },
|
|
888
|
+
gl: { preserveDrawingBuffer: true },
|
|
889
|
+
children: [
|
|
890
|
+
parentSize?.height && parentSize?.width && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
891
|
+
ResponsiveCamera,
|
|
892
|
+
{
|
|
893
|
+
height: parentSize.height,
|
|
894
|
+
width: parentSize.width
|
|
895
|
+
}
|
|
896
|
+
),
|
|
897
|
+
mediaProps?.debugOrbit && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CameraLogger, {}),
|
|
898
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("ambientLight", { intensity: 1 }),
|
|
899
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("pointLight", { position: [10, 10, 10] }),
|
|
900
|
+
import_react8.default.cloneElement(children, mergedMediaProps)
|
|
901
|
+
]
|
|
902
|
+
}
|
|
903
|
+
)
|
|
904
|
+
}
|
|
905
|
+
);
|
|
906
|
+
};
|
|
907
|
+
var Canvas_default = Canvas;
|
|
908
|
+
|
|
909
|
+
// src/components/PlaygroundCanvas/PlaygroundCanvas.tsx
|
|
910
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
911
|
+
var PlaygroundCanvas = ({
|
|
912
|
+
children,
|
|
913
|
+
mediaProps
|
|
914
|
+
}) => {
|
|
915
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Playground, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Canvas_default, { mediaProps, children }) });
|
|
916
|
+
};
|
|
917
|
+
var PlaygroundCanvas_default = PlaygroundCanvas;
|
|
803
918
|
// Annotate the CommonJS export names for ESM import in node:
|
|
804
919
|
0 && (module.exports = {
|
|
920
|
+
Button,
|
|
921
|
+
CameraLogger,
|
|
922
|
+
Canvas,
|
|
805
923
|
ControlsProvider,
|
|
806
924
|
Playground,
|
|
925
|
+
PlaygroundCanvas,
|
|
807
926
|
useControls,
|
|
808
927
|
useUrlSyncedControls
|
|
809
928
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -188,35 +188,6 @@ var ControlsProvider = ({ children }) => {
|
|
|
188
188
|
var useControls = (schema, options) => {
|
|
189
189
|
const ctx = useContext2(ControlsContext);
|
|
190
190
|
if (!ctx) throw new Error("useControls must be used within ControlsProvider");
|
|
191
|
-
useEffect2(() => {
|
|
192
|
-
ctx.registerSchema(schema, options);
|
|
193
|
-
}, [JSON.stringify(schema), JSON.stringify(options)]);
|
|
194
|
-
useEffect2(() => {
|
|
195
|
-
for (const key in schema) {
|
|
196
|
-
if (!(key in ctx.values) && "value" in schema[key]) {
|
|
197
|
-
ctx.setValue(key, schema[key].value);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}, [JSON.stringify(schema), JSON.stringify(ctx.values)]);
|
|
201
|
-
const typedValues = ctx.values;
|
|
202
|
-
const jsx12 = useCallback(() => {
|
|
203
|
-
if (!options?.componentName) return "";
|
|
204
|
-
const props = Object.entries(typedValues).map(([key, val]) => {
|
|
205
|
-
if (typeof val === "string") return `${key}="${val}"`;
|
|
206
|
-
if (typeof val === "boolean") return `${key}={${val}}`;
|
|
207
|
-
return `${key}={${JSON.stringify(val)}}`;
|
|
208
|
-
}).join(" ");
|
|
209
|
-
return `<${options.componentName} ${props} />`;
|
|
210
|
-
}, [options?.componentName, JSON.stringify(typedValues)]);
|
|
211
|
-
return {
|
|
212
|
-
...typedValues,
|
|
213
|
-
controls: ctx.values,
|
|
214
|
-
schema: ctx.schema,
|
|
215
|
-
setValue: ctx.setValue,
|
|
216
|
-
jsx: jsx12
|
|
217
|
-
};
|
|
218
|
-
};
|
|
219
|
-
var useUrlSyncedControls = (schema, options) => {
|
|
220
191
|
const urlParams = getUrlParams();
|
|
221
192
|
const mergedSchema = Object.fromEntries(
|
|
222
193
|
Object.entries(schema).map(([key, control]) => {
|
|
@@ -239,8 +210,35 @@ var useUrlSyncedControls = (schema, options) => {
|
|
|
239
210
|
];
|
|
240
211
|
})
|
|
241
212
|
);
|
|
242
|
-
|
|
213
|
+
useEffect2(() => {
|
|
214
|
+
ctx.registerSchema(mergedSchema, options);
|
|
215
|
+
}, [JSON.stringify(mergedSchema), JSON.stringify(options)]);
|
|
216
|
+
useEffect2(() => {
|
|
217
|
+
for (const key in mergedSchema) {
|
|
218
|
+
if (!(key in ctx.values) && "value" in mergedSchema[key]) {
|
|
219
|
+
ctx.setValue(key, mergedSchema[key].value);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}, [JSON.stringify(mergedSchema), JSON.stringify(ctx.values)]);
|
|
223
|
+
const typedValues = ctx.values;
|
|
224
|
+
const jsx15 = useCallback(() => {
|
|
225
|
+
if (!options?.componentName) return "";
|
|
226
|
+
const props = Object.entries(typedValues).map(([key, val]) => {
|
|
227
|
+
if (typeof val === "string") return `${key}="${val}"`;
|
|
228
|
+
if (typeof val === "boolean") return `${key}={${val}}`;
|
|
229
|
+
return `${key}={${JSON.stringify(val)}}`;
|
|
230
|
+
}).join(" ");
|
|
231
|
+
return `<${options.componentName} ${props} />`;
|
|
232
|
+
}, [options?.componentName, JSON.stringify(typedValues)]);
|
|
233
|
+
return {
|
|
234
|
+
...typedValues,
|
|
235
|
+
controls: ctx.values,
|
|
236
|
+
schema: ctx.schema,
|
|
237
|
+
setValue: ctx.setValue,
|
|
238
|
+
jsx: jsx15
|
|
239
|
+
};
|
|
243
240
|
};
|
|
241
|
+
var useUrlSyncedControls = useControls;
|
|
244
242
|
|
|
245
243
|
// src/components/PreviewContainer/PreviewContainer.tsx
|
|
246
244
|
import { useRef as useRef2 } from "react";
|
|
@@ -257,7 +255,7 @@ var PreviewContainer = ({ children, hideControls }) => {
|
|
|
257
255
|
width: `${100 - leftPanelWidth}%`,
|
|
258
256
|
marginLeft: `${leftPanelWidth}%`
|
|
259
257
|
} : {},
|
|
260
|
-
children
|
|
258
|
+
children: /* @__PURE__ */ jsx3("div", { className: "w-screen h-screen flex items-center justify-center", children })
|
|
261
259
|
}
|
|
262
260
|
);
|
|
263
261
|
};
|
|
@@ -550,7 +548,7 @@ var ControlPanel = () => {
|
|
|
550
548
|
const buttonControls = Object.entries(schema).filter(
|
|
551
549
|
([, control]) => control.type === "button" && !control.hidden
|
|
552
550
|
);
|
|
553
|
-
const
|
|
551
|
+
const jsx15 = useMemo2(() => {
|
|
554
552
|
if (!componentName) return "";
|
|
555
553
|
const props = Object.entries(values).map(([key, val]) => {
|
|
556
554
|
if (typeof val === "string") return `${key}="${val}"`;
|
|
@@ -676,16 +674,16 @@ var ControlPanel = () => {
|
|
|
676
674
|
return null;
|
|
677
675
|
}
|
|
678
676
|
}),
|
|
679
|
-
(buttonControls.length > 0 ||
|
|
677
|
+
(buttonControls.length > 0 || jsx15) && /* @__PURE__ */ jsxs4(
|
|
680
678
|
"div",
|
|
681
679
|
{
|
|
682
680
|
className: `${normalControls.length > 0 ? "border-t" : ""} border-stone-700`,
|
|
683
681
|
children: [
|
|
684
|
-
|
|
682
|
+
jsx15 && config?.showCopyButton !== false && /* @__PURE__ */ jsx10("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ jsx10(
|
|
685
683
|
"button",
|
|
686
684
|
{
|
|
687
685
|
onClick: () => {
|
|
688
|
-
navigator.clipboard.writeText(
|
|
686
|
+
navigator.clipboard.writeText(jsx15);
|
|
689
687
|
setCopied(true);
|
|
690
688
|
setTimeout(() => setCopied(false), 5e3);
|
|
691
689
|
},
|
|
@@ -774,9 +772,126 @@ function Playground({ children }) {
|
|
|
774
772
|
!hideControls && /* @__PURE__ */ jsx11(ControlPanel_default, {})
|
|
775
773
|
] }) });
|
|
776
774
|
}
|
|
775
|
+
|
|
776
|
+
// src/components/Canvas/Canvas.tsx
|
|
777
|
+
import React11, { useEffect as useEffect6, useRef as useRef4, useState as useState6 } from "react";
|
|
778
|
+
import { Canvas as ThreeCanvas, useThree as useThree2 } from "@react-three/fiber";
|
|
779
|
+
import "@react-three/fiber";
|
|
780
|
+
|
|
781
|
+
// src/components/CameraLogger/CameraLogger.tsx
|
|
782
|
+
import { useRef as useRef3, useEffect as useEffect5 } from "react";
|
|
783
|
+
import { OrbitControls } from "@react-three/drei";
|
|
784
|
+
import { useThree } from "@react-three/fiber";
|
|
785
|
+
import { debounce } from "lodash";
|
|
786
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
787
|
+
function CameraLogger() {
|
|
788
|
+
const { camera } = useThree();
|
|
789
|
+
const controlsRef = useRef3(null);
|
|
790
|
+
const logRef = useRef3(null);
|
|
791
|
+
useEffect5(() => {
|
|
792
|
+
logRef.current = debounce(() => {
|
|
793
|
+
console.info("Camera position:", camera.position.toArray());
|
|
794
|
+
}, 200);
|
|
795
|
+
}, [camera]);
|
|
796
|
+
useEffect5(() => {
|
|
797
|
+
const controls = controlsRef.current;
|
|
798
|
+
const handler = logRef.current;
|
|
799
|
+
if (!controls || !handler) return;
|
|
800
|
+
controls.addEventListener("change", handler);
|
|
801
|
+
return () => controls.removeEventListener("change", handler);
|
|
802
|
+
}, []);
|
|
803
|
+
return /* @__PURE__ */ jsx12(OrbitControls, { ref: controlsRef });
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// src/components/Canvas/Canvas.tsx
|
|
807
|
+
import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
808
|
+
var ResponsiveCamera = ({
|
|
809
|
+
height,
|
|
810
|
+
width
|
|
811
|
+
}) => {
|
|
812
|
+
const { camera } = useThree2();
|
|
813
|
+
useEffect6(() => {
|
|
814
|
+
const isMobile = width < 768;
|
|
815
|
+
const zoomFactor = isMobile ? 70 : 100;
|
|
816
|
+
camera.position.z = height / zoomFactor;
|
|
817
|
+
camera.updateProjectionMatrix();
|
|
818
|
+
}, [height, camera, width]);
|
|
819
|
+
return null;
|
|
820
|
+
};
|
|
821
|
+
var Canvas = ({ mediaProps, children }) => {
|
|
822
|
+
const canvasRef = useRef4(null);
|
|
823
|
+
const [parentSize, setParentSize] = useState6(null);
|
|
824
|
+
useEffect6(() => {
|
|
825
|
+
let observer = null;
|
|
826
|
+
const tryObserve = () => {
|
|
827
|
+
const node = canvasRef.current;
|
|
828
|
+
if (!node || !node.parentElement) {
|
|
829
|
+
setTimeout(tryObserve, 50);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const parent = node.parentElement;
|
|
833
|
+
observer = new ResizeObserver(([entry]) => {
|
|
834
|
+
const { width, height } = entry.contentRect;
|
|
835
|
+
setParentSize({ width, height });
|
|
836
|
+
});
|
|
837
|
+
observer.observe(parent);
|
|
838
|
+
};
|
|
839
|
+
tryObserve();
|
|
840
|
+
return () => {
|
|
841
|
+
if (observer) observer.disconnect();
|
|
842
|
+
};
|
|
843
|
+
}, []);
|
|
844
|
+
const mergedMediaProps = {
|
|
845
|
+
...mediaProps || {},
|
|
846
|
+
size: mediaProps?.size || { width: 400, height: 400 }
|
|
847
|
+
};
|
|
848
|
+
return /* @__PURE__ */ jsx13(
|
|
849
|
+
"div",
|
|
850
|
+
{
|
|
851
|
+
ref: canvasRef,
|
|
852
|
+
className: "w-full h-full pointer-events-none relative touch-none",
|
|
853
|
+
children: /* @__PURE__ */ jsxs6(
|
|
854
|
+
ThreeCanvas,
|
|
855
|
+
{
|
|
856
|
+
resize: { polyfill: ResizeObserver },
|
|
857
|
+
style: { width: parentSize?.width, height: parentSize?.height },
|
|
858
|
+
gl: { preserveDrawingBuffer: true },
|
|
859
|
+
children: [
|
|
860
|
+
parentSize?.height && parentSize?.width && /* @__PURE__ */ jsx13(
|
|
861
|
+
ResponsiveCamera,
|
|
862
|
+
{
|
|
863
|
+
height: parentSize.height,
|
|
864
|
+
width: parentSize.width
|
|
865
|
+
}
|
|
866
|
+
),
|
|
867
|
+
mediaProps?.debugOrbit && /* @__PURE__ */ jsx13(CameraLogger, {}),
|
|
868
|
+
/* @__PURE__ */ jsx13("ambientLight", { intensity: 1 }),
|
|
869
|
+
/* @__PURE__ */ jsx13("pointLight", { position: [10, 10, 10] }),
|
|
870
|
+
React11.cloneElement(children, mergedMediaProps)
|
|
871
|
+
]
|
|
872
|
+
}
|
|
873
|
+
)
|
|
874
|
+
}
|
|
875
|
+
);
|
|
876
|
+
};
|
|
877
|
+
var Canvas_default = Canvas;
|
|
878
|
+
|
|
879
|
+
// src/components/PlaygroundCanvas/PlaygroundCanvas.tsx
|
|
880
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
881
|
+
var PlaygroundCanvas = ({
|
|
882
|
+
children,
|
|
883
|
+
mediaProps
|
|
884
|
+
}) => {
|
|
885
|
+
return /* @__PURE__ */ jsx14(Playground, { children: /* @__PURE__ */ jsx14(Canvas_default, { mediaProps, children }) });
|
|
886
|
+
};
|
|
887
|
+
var PlaygroundCanvas_default = PlaygroundCanvas;
|
|
777
888
|
export {
|
|
889
|
+
Button,
|
|
890
|
+
CameraLogger,
|
|
891
|
+
Canvas_default as Canvas,
|
|
778
892
|
ControlsProvider,
|
|
779
893
|
Playground,
|
|
894
|
+
PlaygroundCanvas_default as PlaygroundCanvas,
|
|
780
895
|
useControls,
|
|
781
896
|
useUrlSyncedControls
|
|
782
897
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toriistudio/v0-playground",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "V0 Playground",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -59,13 +59,18 @@
|
|
|
59
59
|
"@radix-ui/react-slider": "^1.3.4",
|
|
60
60
|
"@radix-ui/react-slot": "^1.2.3",
|
|
61
61
|
"@radix-ui/react-switch": "^1.2.4",
|
|
62
|
+
"@react-three/drei": "^10.3.0",
|
|
63
|
+
"@react-three/fiber": "^9.1.2",
|
|
62
64
|
"class-variance-authority": "^0.7.1",
|
|
63
65
|
"clsx": "^2.1.1",
|
|
66
|
+
"lodash": "^4.17.21",
|
|
64
67
|
"lucide-react": "^0.522.0",
|
|
65
68
|
"react": "^19.1.0",
|
|
66
69
|
"react-dom": "^19.1.0",
|
|
67
70
|
"tailwind-merge": "^3.3.1",
|
|
68
|
-
"tailwindcss-animate": "^1.0.7"
|
|
71
|
+
"tailwindcss-animate": "^1.0.7",
|
|
72
|
+
"three": "^0.177.0",
|
|
73
|
+
"three-stdlib": "^2.36.0"
|
|
69
74
|
},
|
|
70
75
|
"devDependencies": {
|
|
71
76
|
"@radix-ui/react-label": "^2.1.6",
|
|
@@ -73,15 +78,21 @@
|
|
|
73
78
|
"@radix-ui/react-slider": "^1.3.4",
|
|
74
79
|
"@radix-ui/react-slot": "^1.2.3",
|
|
75
80
|
"@radix-ui/react-switch": "^1.2.4",
|
|
81
|
+
"@react-three/drei": "^10.3.0",
|
|
82
|
+
"@react-three/fiber": "^9.1.2",
|
|
83
|
+
"@types/lodash": "^4.17.19",
|
|
76
84
|
"@types/node": "^24.0.3",
|
|
77
85
|
"@types/react": "^19.1.2",
|
|
78
86
|
"@types/react-dom": "^19.1.3",
|
|
79
87
|
"class-variance-authority": "^0.7.1",
|
|
80
88
|
"clsx": "^2.1.1",
|
|
89
|
+
"lodash": "^4.17.21",
|
|
81
90
|
"lucide-react": "^0.522.0",
|
|
82
91
|
"tailwind-merge": "^3.3.1",
|
|
83
92
|
"tailwindcss": "^4.1.10",
|
|
84
93
|
"tailwindcss-animate": "^1.0.7",
|
|
94
|
+
"three": "^0.177.0",
|
|
95
|
+
"three-stdlib": "^2.36.0",
|
|
85
96
|
"tsup": "^8.4.0",
|
|
86
97
|
"typescript": "^5.8.3"
|
|
87
98
|
},
|
package/dist/preset.d.mts
DELETED