@toriistudio/v0-playground 0.1.0 → 0.1.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 CHANGED
@@ -17,6 +17,22 @@ Perfect for prototyping components, sharing usage examples, or building your own
17
17
 
18
18
  ---
19
19
 
20
+ ## 📦 Peer Dependencies
21
+
22
+ To use `@toriistudio/v0-playground`, you’ll need to install the following peer dependencies:
23
+
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
26
+ ```
27
+
28
+ Or automate it with:
29
+
30
+ ```json
31
+ "scripts": {
32
+ "install:peers": "npm install $(node -p \"Object.keys(require('./package.json').peerDependencies).join(' ')\")"
33
+ }
34
+ ```
35
+
20
36
  ## 🚀 Installation
21
37
 
22
38
  Install the package and its peer dependencies:
@@ -74,6 +90,26 @@ export default function App() {
74
90
  - Prototype interfaces quickly with real data
75
91
  - Debug and test variants visually
76
92
 
77
- ## 🤙 License
93
+ ## 📄 License
78
94
 
79
95
  MIT
96
+
97
+ ## 🤝 Contributing
98
+
99
+ We welcome contributions!
100
+
101
+ If you'd like to improve the playground, add new features, or fix bugs:
102
+
103
+ 1. **Fork** this repository
104
+ 2. **Clone** your fork: `git clone https://github.com/your-username/v0-playground`
105
+ 3. **Install** dependencies: `yarn` or `npm install`
106
+ 4. Make your changes in a branch: `git checkout -b my-new-feature`
107
+ 5. **Push** your branch and open a pull request
108
+
109
+ Before submitting a PR:
110
+
111
+ - Run `yarn build` to ensure everything compiles
112
+ - Make sure the playground runs without errors (`yalc push` or `npm link` for local testing)
113
+ - Keep the code style clean and consistent
114
+
115
+ We’re excited to see what you’ll build 🛠️✨
package/dist/index.d.mts CHANGED
@@ -41,5 +41,15 @@ declare const useControls: <T extends ControlsSchema>(schema: T, options?: {
41
41
  setValue: (key: string, value: any) => void;
42
42
  jsx: () => string;
43
43
  };
44
+ declare const useUrlSyncedControls: <T extends ControlsSchema>(schema: T, options?: {
45
+ componentName?: string;
46
+ }) => { [K in keyof T]: T[K] extends {
47
+ value: infer V;
48
+ } ? V : never; } & {
49
+ controls: Record<string, any>;
50
+ schema: ControlsSchema;
51
+ setValue: (key: string, value: any) => void;
52
+ jsx: () => string;
53
+ };
44
54
 
45
- export { type ControlType, type ControlsSchema, Playground, useControls };
55
+ export { type ControlType, type ControlsSchema, Playground, useControls, useUrlSyncedControls };
package/dist/index.d.ts CHANGED
@@ -41,5 +41,15 @@ declare const useControls: <T extends ControlsSchema>(schema: T, options?: {
41
41
  setValue: (key: string, value: any) => void;
42
42
  jsx: () => string;
43
43
  };
44
+ declare const useUrlSyncedControls: <T extends ControlsSchema>(schema: T, options?: {
45
+ componentName?: string;
46
+ }) => { [K in keyof T]: T[K] extends {
47
+ value: infer V;
48
+ } ? V : never; } & {
49
+ controls: Record<string, any>;
50
+ schema: ControlsSchema;
51
+ setValue: (key: string, value: any) => void;
52
+ jsx: () => string;
53
+ };
44
54
 
45
- export { type ControlType, type ControlsSchema, Playground, useControls };
55
+ export { type ControlType, type ControlsSchema, Playground, useControls, useUrlSyncedControls };
package/dist/index.js CHANGED
@@ -31,10 +31,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  Playground: () => Playground,
34
- useControls: () => useControls
34
+ useControls: () => useControls,
35
+ useUrlSyncedControls: () => useUrlSyncedControls
35
36
  });
36
37
  module.exports = __toCommonJS(src_exports);
37
38
 
39
+ // src/components/Playground/Playground.tsx
40
+ var import_react5 = require("react");
41
+
38
42
  // src/context/ResizableLayout.tsx
39
43
  var import_react = require("react");
40
44
  var import_lucide_react = require("lucide-react");
@@ -47,7 +51,10 @@ var useResizableLayout = () => {
47
51
  if (!ctx) throw new Error("ResizableLayoutContext not found");
48
52
  return ctx;
49
53
  };
50
- var ResizableLayout = ({ children }) => {
54
+ var ResizableLayout = ({
55
+ children,
56
+ hideControls
57
+ }) => {
51
58
  const [leftPanelWidth, setLeftPanelWidth] = (0, import_react.useState)(25);
52
59
  const [isDesktop, setIsDesktop] = (0, import_react.useState)(false);
53
60
  const [isHydrated, setIsHydrated] = (0, import_react.useState)(false);
@@ -111,7 +118,7 @@ var ResizableLayout = ({ children }) => {
111
118
  className: "flex flex-col md:flex-row min-h-screen w-full overflow-hidden select-none",
112
119
  children: [
113
120
  children,
114
- isHydrated && isDesktop && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
121
+ isHydrated && isDesktop && !hideControls && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
115
122
  "div",
116
123
  {
117
124
  className: "order-3 w-2 bg-stone-800 hover:bg-stone-700 cursor-col-resize items-center justify-center z-10 transition-opacity duration-300",
@@ -135,6 +142,19 @@ var ResizableLayout = ({ children }) => {
135
142
 
136
143
  // src/context/ControlsContext.tsx
137
144
  var import_react2 = require("react");
145
+
146
+ // src/utils/getUrlParams.ts
147
+ var getUrlParams = () => {
148
+ if (typeof window === "undefined") return {};
149
+ const params = new URLSearchParams(window.location.search);
150
+ const entries = {};
151
+ for (const [key, value] of params.entries()) {
152
+ entries[key] = value;
153
+ }
154
+ return entries;
155
+ };
156
+
157
+ // src/context/ControlsContext.tsx
138
158
  var import_jsx_runtime2 = require("react/jsx-runtime");
139
159
  var ControlsContext = (0, import_react2.createContext)(null);
140
160
  var useControlsContext = () => {
@@ -204,11 +224,36 @@ var useControls = (schema, options) => {
204
224
  jsx: jsx11
205
225
  };
206
226
  };
227
+ var useUrlSyncedControls = (schema, options) => {
228
+ const urlParams = getUrlParams();
229
+ const mergedSchema = Object.fromEntries(
230
+ Object.entries(schema).map(([key, control]) => {
231
+ const urlValue = urlParams[key];
232
+ if (!urlValue || !("value" in control)) return [key, control];
233
+ const defaultValue = control.value;
234
+ let parsed = urlValue;
235
+ if (typeof defaultValue === "number") {
236
+ parsed = parseFloat(urlValue);
237
+ if (isNaN(parsed)) parsed = defaultValue;
238
+ } else if (typeof defaultValue === "boolean") {
239
+ parsed = urlValue === "true";
240
+ }
241
+ return [
242
+ key,
243
+ {
244
+ ...control,
245
+ value: parsed
246
+ }
247
+ ];
248
+ })
249
+ );
250
+ return useControls(mergedSchema, options);
251
+ };
207
252
 
208
253
  // src/components/PreviewContainer/PreviewContainer.tsx
209
254
  var import_react3 = require("react");
210
255
  var import_jsx_runtime3 = require("react/jsx-runtime");
211
- var PreviewContainer = ({ children }) => {
256
+ var PreviewContainer = ({ children, hideControls }) => {
212
257
  const { leftPanelWidth, isDesktop, isHydrated, containerRef } = useResizableLayout();
213
258
  const previewRef = (0, import_react3.useRef)(null);
214
259
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -216,7 +261,7 @@ var PreviewContainer = ({ children }) => {
216
261
  {
217
262
  ref: previewRef,
218
263
  className: "order-1 md:order-2 flex-1 bg-black overflow-auto flex items-center justify-center relative",
219
- style: isHydrated && isDesktop ? {
264
+ style: isHydrated && isDesktop && !hideControls ? {
220
265
  width: `${100 - leftPanelWidth}%`,
221
266
  marginLeft: `${leftPanelWidth}%`
222
267
  } : {},
@@ -618,14 +663,20 @@ var ControlPanel_default = ControlPanel;
618
663
 
619
664
  // src/components/Playground/Playground.tsx
620
665
  var import_jsx_runtime10 = require("react/jsx-runtime");
666
+ var NO_CONTROLS_PARAM = "nocontrols";
621
667
  function Playground({ children }) {
622
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizableLayout, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ControlsProvider, { children: [
623
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PreviewContainer_default, { children }),
624
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ControlPanel_default, {})
668
+ const hideControls = (0, import_react5.useMemo)(() => {
669
+ if (typeof window === "undefined") return false;
670
+ return new URLSearchParams(window.location.search).get(NO_CONTROLS_PARAM) === "true";
671
+ }, []);
672
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizableLayout, { hideControls, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ControlsProvider, { children: [
673
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PreviewContainer_default, { hideControls, children }),
674
+ !hideControls && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ControlPanel_default, {})
625
675
  ] }) });
626
676
  }
627
677
  // Annotate the CommonJS export names for ESM import in node:
628
678
  0 && (module.exports = {
629
679
  Playground,
630
- useControls
680
+ useControls,
681
+ useUrlSyncedControls
631
682
  });
package/dist/index.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ // src/components/Playground/Playground.tsx
2
+ import { useMemo as useMemo3 } from "react";
3
+
1
4
  // src/context/ResizableLayout.tsx
2
5
  import {
3
6
  createContext,
@@ -16,7 +19,10 @@ var useResizableLayout = () => {
16
19
  if (!ctx) throw new Error("ResizableLayoutContext not found");
17
20
  return ctx;
18
21
  };
19
- var ResizableLayout = ({ children }) => {
22
+ var ResizableLayout = ({
23
+ children,
24
+ hideControls
25
+ }) => {
20
26
  const [leftPanelWidth, setLeftPanelWidth] = useState(25);
21
27
  const [isDesktop, setIsDesktop] = useState(false);
22
28
  const [isHydrated, setIsHydrated] = useState(false);
@@ -80,7 +86,7 @@ var ResizableLayout = ({ children }) => {
80
86
  className: "flex flex-col md:flex-row min-h-screen w-full overflow-hidden select-none",
81
87
  children: [
82
88
  children,
83
- isHydrated && isDesktop && /* @__PURE__ */ jsx(
89
+ isHydrated && isDesktop && !hideControls && /* @__PURE__ */ jsx(
84
90
  "div",
85
91
  {
86
92
  className: "order-3 w-2 bg-stone-800 hover:bg-stone-700 cursor-col-resize items-center justify-center z-10 transition-opacity duration-300",
@@ -111,6 +117,19 @@ import {
111
117
  useEffect as useEffect2,
112
118
  useCallback
113
119
  } from "react";
120
+
121
+ // src/utils/getUrlParams.ts
122
+ var getUrlParams = () => {
123
+ if (typeof window === "undefined") return {};
124
+ const params = new URLSearchParams(window.location.search);
125
+ const entries = {};
126
+ for (const [key, value] of params.entries()) {
127
+ entries[key] = value;
128
+ }
129
+ return entries;
130
+ };
131
+
132
+ // src/context/ControlsContext.tsx
114
133
  import { jsx as jsx2 } from "react/jsx-runtime";
115
134
  var ControlsContext = createContext2(null);
116
135
  var useControlsContext = () => {
@@ -180,11 +199,36 @@ var useControls = (schema, options) => {
180
199
  jsx: jsx11
181
200
  };
182
201
  };
202
+ var useUrlSyncedControls = (schema, options) => {
203
+ const urlParams = getUrlParams();
204
+ const mergedSchema = Object.fromEntries(
205
+ Object.entries(schema).map(([key, control]) => {
206
+ const urlValue = urlParams[key];
207
+ if (!urlValue || !("value" in control)) return [key, control];
208
+ const defaultValue = control.value;
209
+ let parsed = urlValue;
210
+ if (typeof defaultValue === "number") {
211
+ parsed = parseFloat(urlValue);
212
+ if (isNaN(parsed)) parsed = defaultValue;
213
+ } else if (typeof defaultValue === "boolean") {
214
+ parsed = urlValue === "true";
215
+ }
216
+ return [
217
+ key,
218
+ {
219
+ ...control,
220
+ value: parsed
221
+ }
222
+ ];
223
+ })
224
+ );
225
+ return useControls(mergedSchema, options);
226
+ };
183
227
 
184
228
  // src/components/PreviewContainer/PreviewContainer.tsx
185
229
  import { useRef as useRef2 } from "react";
186
230
  import { jsx as jsx3 } from "react/jsx-runtime";
187
- var PreviewContainer = ({ children }) => {
231
+ var PreviewContainer = ({ children, hideControls }) => {
188
232
  const { leftPanelWidth, isDesktop, isHydrated, containerRef } = useResizableLayout();
189
233
  const previewRef = useRef2(null);
190
234
  return /* @__PURE__ */ jsx3(
@@ -192,7 +236,7 @@ var PreviewContainer = ({ children }) => {
192
236
  {
193
237
  ref: previewRef,
194
238
  className: "order-1 md:order-2 flex-1 bg-black overflow-auto flex items-center justify-center relative",
195
- style: isHydrated && isDesktop ? {
239
+ style: isHydrated && isDesktop && !hideControls ? {
196
240
  width: `${100 - leftPanelWidth}%`,
197
241
  marginLeft: `${leftPanelWidth}%`
198
242
  } : {},
@@ -594,13 +638,19 @@ var ControlPanel_default = ControlPanel;
594
638
 
595
639
  // src/components/Playground/Playground.tsx
596
640
  import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
641
+ var NO_CONTROLS_PARAM = "nocontrols";
597
642
  function Playground({ children }) {
598
- return /* @__PURE__ */ jsx10(ResizableLayout, { children: /* @__PURE__ */ jsxs5(ControlsProvider, { children: [
599
- /* @__PURE__ */ jsx10(PreviewContainer_default, { children }),
600
- /* @__PURE__ */ jsx10(ControlPanel_default, {})
643
+ const hideControls = useMemo3(() => {
644
+ if (typeof window === "undefined") return false;
645
+ return new URLSearchParams(window.location.search).get(NO_CONTROLS_PARAM) === "true";
646
+ }, []);
647
+ return /* @__PURE__ */ jsx10(ResizableLayout, { hideControls, children: /* @__PURE__ */ jsxs5(ControlsProvider, { children: [
648
+ /* @__PURE__ */ jsx10(PreviewContainer_default, { hideControls, children }),
649
+ !hideControls && /* @__PURE__ */ jsx10(ControlPanel_default, {})
601
650
  ] }) });
602
651
  }
603
652
  export {
604
653
  Playground,
605
- useControls
654
+ useControls,
655
+ useUrlSyncedControls
606
656
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toriistudio/v0-playground",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "V0 Playground",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -29,7 +29,7 @@
29
29
  ],
30
30
  "scripts": {
31
31
  "build": "tsup",
32
- "dev:push": "yarn build && yalc push",
32
+ "local:push": "yarn build && yalc push",
33
33
  "prepublishOnly": "npm run build",
34
34
  "test": "echo \"Error: no test specified\" && exit 1"
35
35
  },