@toriistudio/v0-playground 0.2.7 → 0.2.10
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/index.d.mts +16 -7
- package/dist/index.d.ts +16 -7
- package/dist/index.js +83 -43
- package/dist/index.mjs +83 -43
- package/package.json +4 -2
package/dist/index.d.mts
CHANGED
|
@@ -5,37 +5,45 @@ declare function Playground({ children }: {
|
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
}): react_jsx_runtime.JSX.Element | null;
|
|
7
7
|
|
|
8
|
-
type
|
|
8
|
+
type BaseControl = {
|
|
9
|
+
hidden?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type ControlType = ({
|
|
9
12
|
type: "boolean";
|
|
10
13
|
value: boolean;
|
|
11
|
-
} | {
|
|
14
|
+
} & BaseControl) | ({
|
|
12
15
|
type: "number";
|
|
13
16
|
value: number;
|
|
14
17
|
min?: number;
|
|
15
18
|
max?: number;
|
|
16
19
|
step?: number;
|
|
17
|
-
} | {
|
|
20
|
+
} & BaseControl) | ({
|
|
18
21
|
type: "string";
|
|
19
22
|
value: string;
|
|
20
|
-
} | {
|
|
23
|
+
} & BaseControl) | ({
|
|
21
24
|
type: "color";
|
|
22
25
|
value: string;
|
|
23
|
-
} | {
|
|
26
|
+
} & BaseControl) | ({
|
|
24
27
|
type: "select";
|
|
25
28
|
value: string;
|
|
26
29
|
options: string[];
|
|
27
|
-
} | {
|
|
30
|
+
} & BaseControl) | ({
|
|
28
31
|
type: "button";
|
|
29
32
|
onClick?: () => void;
|
|
30
33
|
label?: string;
|
|
31
34
|
render?: () => React.ReactNode;
|
|
32
|
-
};
|
|
35
|
+
} & BaseControl);
|
|
33
36
|
type ControlsSchema = Record<string, ControlType>;
|
|
37
|
+
type ControlsConfig = {
|
|
38
|
+
showCopyButton?: boolean;
|
|
39
|
+
mainLabel?: string;
|
|
40
|
+
};
|
|
34
41
|
declare const ControlsProvider: ({ children }: {
|
|
35
42
|
children: ReactNode;
|
|
36
43
|
}) => react_jsx_runtime.JSX.Element;
|
|
37
44
|
declare const useControls: <T extends ControlsSchema>(schema: T, options?: {
|
|
38
45
|
componentName?: string;
|
|
46
|
+
config?: ControlsConfig;
|
|
39
47
|
}) => { [K in keyof T]: T[K] extends {
|
|
40
48
|
value: infer V;
|
|
41
49
|
} ? V : never; } & {
|
|
@@ -46,6 +54,7 @@ declare const useControls: <T extends ControlsSchema>(schema: T, options?: {
|
|
|
46
54
|
};
|
|
47
55
|
declare const useUrlSyncedControls: <T extends ControlsSchema>(schema: T, options?: {
|
|
48
56
|
componentName?: string;
|
|
57
|
+
config?: ControlsConfig;
|
|
49
58
|
}) => { [K in keyof T]: T[K] extends {
|
|
50
59
|
value: infer V;
|
|
51
60
|
} ? V : never; } & {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,37 +5,45 @@ declare function Playground({ children }: {
|
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
}): react_jsx_runtime.JSX.Element | null;
|
|
7
7
|
|
|
8
|
-
type
|
|
8
|
+
type BaseControl = {
|
|
9
|
+
hidden?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type ControlType = ({
|
|
9
12
|
type: "boolean";
|
|
10
13
|
value: boolean;
|
|
11
|
-
} | {
|
|
14
|
+
} & BaseControl) | ({
|
|
12
15
|
type: "number";
|
|
13
16
|
value: number;
|
|
14
17
|
min?: number;
|
|
15
18
|
max?: number;
|
|
16
19
|
step?: number;
|
|
17
|
-
} | {
|
|
20
|
+
} & BaseControl) | ({
|
|
18
21
|
type: "string";
|
|
19
22
|
value: string;
|
|
20
|
-
} | {
|
|
23
|
+
} & BaseControl) | ({
|
|
21
24
|
type: "color";
|
|
22
25
|
value: string;
|
|
23
|
-
} | {
|
|
26
|
+
} & BaseControl) | ({
|
|
24
27
|
type: "select";
|
|
25
28
|
value: string;
|
|
26
29
|
options: string[];
|
|
27
|
-
} | {
|
|
30
|
+
} & BaseControl) | ({
|
|
28
31
|
type: "button";
|
|
29
32
|
onClick?: () => void;
|
|
30
33
|
label?: string;
|
|
31
34
|
render?: () => React.ReactNode;
|
|
32
|
-
};
|
|
35
|
+
} & BaseControl);
|
|
33
36
|
type ControlsSchema = Record<string, ControlType>;
|
|
37
|
+
type ControlsConfig = {
|
|
38
|
+
showCopyButton?: boolean;
|
|
39
|
+
mainLabel?: string;
|
|
40
|
+
};
|
|
34
41
|
declare const ControlsProvider: ({ children }: {
|
|
35
42
|
children: ReactNode;
|
|
36
43
|
}) => react_jsx_runtime.JSX.Element;
|
|
37
44
|
declare const useControls: <T extends ControlsSchema>(schema: T, options?: {
|
|
38
45
|
componentName?: string;
|
|
46
|
+
config?: ControlsConfig;
|
|
39
47
|
}) => { [K in keyof T]: T[K] extends {
|
|
40
48
|
value: infer V;
|
|
41
49
|
} ? V : never; } & {
|
|
@@ -46,6 +54,7 @@ declare const useControls: <T extends ControlsSchema>(schema: T, options?: {
|
|
|
46
54
|
};
|
|
47
55
|
declare const useUrlSyncedControls: <T extends ControlsSchema>(schema: T, options?: {
|
|
48
56
|
componentName?: string;
|
|
57
|
+
config?: ControlsConfig;
|
|
49
58
|
}) => { [K in keyof T]: T[K] extends {
|
|
50
59
|
value: infer V;
|
|
51
60
|
} ? V : never; } & {
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
39
39
|
|
|
40
40
|
// src/components/Playground/Playground.tsx
|
|
41
41
|
var import_react6 = require("react");
|
|
42
|
+
var import_lucide_react4 = require("lucide-react");
|
|
42
43
|
|
|
43
44
|
// src/context/ResizableLayout.tsx
|
|
44
45
|
var import_react = require("react");
|
|
@@ -166,6 +167,9 @@ var useControlsContext = () => {
|
|
|
166
167
|
var ControlsProvider = ({ children }) => {
|
|
167
168
|
const [schema, setSchema] = (0, import_react2.useState)({});
|
|
168
169
|
const [values, setValues] = (0, import_react2.useState)({});
|
|
170
|
+
const [config, setConfig] = (0, import_react2.useState)({
|
|
171
|
+
showCopyButton: true
|
|
172
|
+
});
|
|
169
173
|
const [componentName, setComponentName] = (0, import_react2.useState)();
|
|
170
174
|
const setValue = (key, value) => {
|
|
171
175
|
setValues((prev) => ({ ...prev, [key]: value }));
|
|
@@ -174,12 +178,18 @@ var ControlsProvider = ({ children }) => {
|
|
|
174
178
|
if (opts?.componentName) {
|
|
175
179
|
setComponentName(opts.componentName);
|
|
176
180
|
}
|
|
181
|
+
if (opts?.config) {
|
|
182
|
+
setConfig((prev) => ({
|
|
183
|
+
...prev,
|
|
184
|
+
...opts.config
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
177
187
|
setSchema((prevSchema) => ({ ...prevSchema, ...newSchema }));
|
|
178
188
|
setValues((prevValues) => {
|
|
179
189
|
const updated = { ...prevValues };
|
|
180
190
|
for (const key in newSchema) {
|
|
191
|
+
const control = newSchema[key];
|
|
181
192
|
if (!(key in updated)) {
|
|
182
|
-
const control = newSchema[key];
|
|
183
193
|
if ("value" in control) {
|
|
184
194
|
updated[key] = control.value;
|
|
185
195
|
}
|
|
@@ -189,8 +199,15 @@ var ControlsProvider = ({ children }) => {
|
|
|
189
199
|
});
|
|
190
200
|
};
|
|
191
201
|
const contextValue = (0, import_react2.useMemo)(
|
|
192
|
-
() => ({
|
|
193
|
-
|
|
202
|
+
() => ({
|
|
203
|
+
schema,
|
|
204
|
+
values,
|
|
205
|
+
setValue,
|
|
206
|
+
registerSchema,
|
|
207
|
+
componentName,
|
|
208
|
+
config
|
|
209
|
+
}),
|
|
210
|
+
[schema, values, componentName, config]
|
|
194
211
|
);
|
|
195
212
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ControlsContext.Provider, { value: contextValue, children });
|
|
196
213
|
};
|
|
@@ -551,13 +568,13 @@ var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
|
551
568
|
var ControlPanel = () => {
|
|
552
569
|
const [copied, setCopied] = (0, import_react5.useState)(false);
|
|
553
570
|
const { leftPanelWidth, isDesktop, isHydrated, sidebarNarrow } = useResizableLayout();
|
|
554
|
-
const { schema, setValue, values, componentName } = useControlsContext();
|
|
571
|
+
const { schema, setValue, values, componentName, config } = useControlsContext();
|
|
555
572
|
const previewUrl = usePreviewUrl(values);
|
|
556
573
|
const normalControls = Object.entries(schema).filter(
|
|
557
|
-
([, control]) => control.type !== "button"
|
|
574
|
+
([, control]) => control.type !== "button" && !control.hidden
|
|
558
575
|
);
|
|
559
576
|
const buttonControls = Object.entries(schema).filter(
|
|
560
|
-
([, control]) => control.type === "button"
|
|
577
|
+
([, control]) => control.type === "button" && !control.hidden
|
|
561
578
|
);
|
|
562
579
|
const jsx12 = (0, import_react5.useMemo)(() => {
|
|
563
580
|
if (!componentName) return "";
|
|
@@ -588,7 +605,7 @@ var ControlPanel = () => {
|
|
|
588
605
|
} : {}
|
|
589
606
|
},
|
|
590
607
|
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mb-10 space-y-4 p-2 md:p-4 border border-stone-700 rounded-md", children: [
|
|
591
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "space-y-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-lg text-stone-100 font-bold", children: "Controls" }) }),
|
|
608
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "space-y-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-lg text-stone-100 font-bold", children: config?.mainLabel ?? "Controls" }) }),
|
|
592
609
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-4 pt-2", children: [
|
|
593
610
|
normalControls.map(([key, control]) => {
|
|
594
611
|
const value = values[key];
|
|
@@ -685,43 +702,49 @@ var ControlPanel = () => {
|
|
|
685
702
|
return null;
|
|
686
703
|
}
|
|
687
704
|
}),
|
|
688
|
-
(buttonControls.length > 0 || jsx12) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
{
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
"
|
|
705
|
+
(buttonControls.length > 0 || jsx12) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
706
|
+
"div",
|
|
707
|
+
{
|
|
708
|
+
className: `${normalControls.length > 0 ? "border-t" : ""} border-stone-700`,
|
|
709
|
+
children: [
|
|
710
|
+
jsx12 && config?.showCopyButton !== false && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
711
|
+
"button",
|
|
712
|
+
{
|
|
713
|
+
onClick: () => {
|
|
714
|
+
navigator.clipboard.writeText(jsx12);
|
|
715
|
+
setCopied(true);
|
|
716
|
+
setTimeout(() => setCopied(false), 5e3);
|
|
717
|
+
},
|
|
718
|
+
className: "w-full px-4 py-2 text-sm bg-stone-700 hover:bg-stone-600 text-white rounded flex items-center justify-center gap-2",
|
|
719
|
+
children: copied ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
720
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Check, { className: "w-4 h-4" }),
|
|
721
|
+
"Copied"
|
|
722
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
723
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Copy, { className: "w-4 h-4" }),
|
|
724
|
+
"Copy to Clipboard"
|
|
725
|
+
] })
|
|
726
|
+
}
|
|
727
|
+
) }, "control-panel-jsx"),
|
|
728
|
+
buttonControls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap gap-2 pt-4", children: buttonControls.map(
|
|
729
|
+
([key, control]) => control.type === "button" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
730
|
+
"div",
|
|
714
731
|
{
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
732
|
+
className: "flex-1",
|
|
733
|
+
children: control.render ? control.render() : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
734
|
+
"button",
|
|
735
|
+
{
|
|
736
|
+
onClick: control.onClick,
|
|
737
|
+
className: "w-full px-4 py-2 text-sm bg-stone-700 hover:bg-stone-600 text-white rounded",
|
|
738
|
+
children: control.label ?? key
|
|
739
|
+
}
|
|
740
|
+
)
|
|
741
|
+
},
|
|
742
|
+
`control-panel-custom-${key}`
|
|
743
|
+
) : null
|
|
744
|
+
) })
|
|
745
|
+
]
|
|
746
|
+
}
|
|
747
|
+
)
|
|
725
748
|
] }),
|
|
726
749
|
previewUrl && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
727
750
|
"a",
|
|
@@ -747,6 +770,7 @@ var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
|
747
770
|
var NO_CONTROLS_PARAM = "nocontrols";
|
|
748
771
|
function Playground({ children }) {
|
|
749
772
|
const [isHydrated, setIsHydrated] = (0, import_react6.useState)(false);
|
|
773
|
+
const [copied, setCopied] = (0, import_react6.useState)(false);
|
|
750
774
|
(0, import_react6.useEffect)(() => {
|
|
751
775
|
setIsHydrated(true);
|
|
752
776
|
}, []);
|
|
@@ -754,8 +778,24 @@ function Playground({ children }) {
|
|
|
754
778
|
if (typeof window === "undefined") return false;
|
|
755
779
|
return new URLSearchParams(window.location.search).get(NO_CONTROLS_PARAM) === "true";
|
|
756
780
|
}, []);
|
|
781
|
+
const handleCopy = () => {
|
|
782
|
+
navigator.clipboard.writeText(window.location.href);
|
|
783
|
+
setCopied(true);
|
|
784
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
785
|
+
};
|
|
757
786
|
if (!isHydrated) return null;
|
|
758
787
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ResizableLayout, { hideControls, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ControlsProvider, { children: [
|
|
788
|
+
hideControls && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
789
|
+
"button",
|
|
790
|
+
{
|
|
791
|
+
onClick: handleCopy,
|
|
792
|
+
className: "absolute top-4 right-4 z-50 flex items-center gap-1 rounded bg-black/70 px-3 py-1 text-white hover:bg-black",
|
|
793
|
+
children: [
|
|
794
|
+
copied ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react4.Check, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react4.Copy, { size: 16 }),
|
|
795
|
+
copied ? "Copied!" : "Share"
|
|
796
|
+
]
|
|
797
|
+
}
|
|
798
|
+
),
|
|
759
799
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PreviewContainer_default, { hideControls, children }),
|
|
760
800
|
!hideControls && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ControlPanel_default, {})
|
|
761
801
|
] }) });
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/components/Playground/Playground.tsx
|
|
2
2
|
import { useEffect as useEffect4, useMemo as useMemo3, useState as useState5 } from "react";
|
|
3
|
+
import { Check as Check3, Copy as Copy2 } from "lucide-react";
|
|
3
4
|
|
|
4
5
|
// src/context/ResizableLayout.tsx
|
|
5
6
|
import {
|
|
@@ -140,6 +141,9 @@ var useControlsContext = () => {
|
|
|
140
141
|
var ControlsProvider = ({ children }) => {
|
|
141
142
|
const [schema, setSchema] = useState2({});
|
|
142
143
|
const [values, setValues] = useState2({});
|
|
144
|
+
const [config, setConfig] = useState2({
|
|
145
|
+
showCopyButton: true
|
|
146
|
+
});
|
|
143
147
|
const [componentName, setComponentName] = useState2();
|
|
144
148
|
const setValue = (key, value) => {
|
|
145
149
|
setValues((prev) => ({ ...prev, [key]: value }));
|
|
@@ -148,12 +152,18 @@ var ControlsProvider = ({ children }) => {
|
|
|
148
152
|
if (opts?.componentName) {
|
|
149
153
|
setComponentName(opts.componentName);
|
|
150
154
|
}
|
|
155
|
+
if (opts?.config) {
|
|
156
|
+
setConfig((prev) => ({
|
|
157
|
+
...prev,
|
|
158
|
+
...opts.config
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
151
161
|
setSchema((prevSchema) => ({ ...prevSchema, ...newSchema }));
|
|
152
162
|
setValues((prevValues) => {
|
|
153
163
|
const updated = { ...prevValues };
|
|
154
164
|
for (const key in newSchema) {
|
|
165
|
+
const control = newSchema[key];
|
|
155
166
|
if (!(key in updated)) {
|
|
156
|
-
const control = newSchema[key];
|
|
157
167
|
if ("value" in control) {
|
|
158
168
|
updated[key] = control.value;
|
|
159
169
|
}
|
|
@@ -163,8 +173,15 @@ var ControlsProvider = ({ children }) => {
|
|
|
163
173
|
});
|
|
164
174
|
};
|
|
165
175
|
const contextValue = useMemo(
|
|
166
|
-
() => ({
|
|
167
|
-
|
|
176
|
+
() => ({
|
|
177
|
+
schema,
|
|
178
|
+
values,
|
|
179
|
+
setValue,
|
|
180
|
+
registerSchema,
|
|
181
|
+
componentName,
|
|
182
|
+
config
|
|
183
|
+
}),
|
|
184
|
+
[schema, values, componentName, config]
|
|
168
185
|
);
|
|
169
186
|
return /* @__PURE__ */ jsx2(ControlsContext.Provider, { value: contextValue, children });
|
|
170
187
|
};
|
|
@@ -525,13 +542,13 @@ import { Fragment, jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
|
525
542
|
var ControlPanel = () => {
|
|
526
543
|
const [copied, setCopied] = useState4(false);
|
|
527
544
|
const { leftPanelWidth, isDesktop, isHydrated, sidebarNarrow } = useResizableLayout();
|
|
528
|
-
const { schema, setValue, values, componentName } = useControlsContext();
|
|
545
|
+
const { schema, setValue, values, componentName, config } = useControlsContext();
|
|
529
546
|
const previewUrl = usePreviewUrl(values);
|
|
530
547
|
const normalControls = Object.entries(schema).filter(
|
|
531
|
-
([, control]) => control.type !== "button"
|
|
548
|
+
([, control]) => control.type !== "button" && !control.hidden
|
|
532
549
|
);
|
|
533
550
|
const buttonControls = Object.entries(schema).filter(
|
|
534
|
-
([, control]) => control.type === "button"
|
|
551
|
+
([, control]) => control.type === "button" && !control.hidden
|
|
535
552
|
);
|
|
536
553
|
const jsx12 = useMemo2(() => {
|
|
537
554
|
if (!componentName) return "";
|
|
@@ -562,7 +579,7 @@ var ControlPanel = () => {
|
|
|
562
579
|
} : {}
|
|
563
580
|
},
|
|
564
581
|
children: /* @__PURE__ */ jsxs4("div", { className: "mb-10 space-y-4 p-2 md:p-4 border border-stone-700 rounded-md", children: [
|
|
565
|
-
/* @__PURE__ */ jsx10("div", { className: "space-y-1", children: /* @__PURE__ */ jsx10("h1", { className: "text-lg text-stone-100 font-bold", children: "Controls" }) }),
|
|
582
|
+
/* @__PURE__ */ jsx10("div", { className: "space-y-1", children: /* @__PURE__ */ jsx10("h1", { className: "text-lg text-stone-100 font-bold", children: config?.mainLabel ?? "Controls" }) }),
|
|
566
583
|
/* @__PURE__ */ jsxs4("div", { className: "space-y-4 pt-2", children: [
|
|
567
584
|
normalControls.map(([key, control]) => {
|
|
568
585
|
const value = values[key];
|
|
@@ -659,43 +676,49 @@ var ControlPanel = () => {
|
|
|
659
676
|
return null;
|
|
660
677
|
}
|
|
661
678
|
}),
|
|
662
|
-
(buttonControls.length > 0 || jsx12) && /* @__PURE__ */ jsxs4(
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
{
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
"
|
|
679
|
+
(buttonControls.length > 0 || jsx12) && /* @__PURE__ */ jsxs4(
|
|
680
|
+
"div",
|
|
681
|
+
{
|
|
682
|
+
className: `${normalControls.length > 0 ? "border-t" : ""} border-stone-700`,
|
|
683
|
+
children: [
|
|
684
|
+
jsx12 && config?.showCopyButton !== false && /* @__PURE__ */ jsx10("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ jsx10(
|
|
685
|
+
"button",
|
|
686
|
+
{
|
|
687
|
+
onClick: () => {
|
|
688
|
+
navigator.clipboard.writeText(jsx12);
|
|
689
|
+
setCopied(true);
|
|
690
|
+
setTimeout(() => setCopied(false), 5e3);
|
|
691
|
+
},
|
|
692
|
+
className: "w-full px-4 py-2 text-sm bg-stone-700 hover:bg-stone-600 text-white rounded flex items-center justify-center gap-2",
|
|
693
|
+
children: copied ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
694
|
+
/* @__PURE__ */ jsx10(Check2, { className: "w-4 h-4" }),
|
|
695
|
+
"Copied"
|
|
696
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
697
|
+
/* @__PURE__ */ jsx10(Copy, { className: "w-4 h-4" }),
|
|
698
|
+
"Copy to Clipboard"
|
|
699
|
+
] })
|
|
700
|
+
}
|
|
701
|
+
) }, "control-panel-jsx"),
|
|
702
|
+
buttonControls.length > 0 && /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap gap-2 pt-4", children: buttonControls.map(
|
|
703
|
+
([key, control]) => control.type === "button" ? /* @__PURE__ */ jsx10(
|
|
704
|
+
"div",
|
|
688
705
|
{
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
706
|
+
className: "flex-1",
|
|
707
|
+
children: control.render ? control.render() : /* @__PURE__ */ jsx10(
|
|
708
|
+
"button",
|
|
709
|
+
{
|
|
710
|
+
onClick: control.onClick,
|
|
711
|
+
className: "w-full px-4 py-2 text-sm bg-stone-700 hover:bg-stone-600 text-white rounded",
|
|
712
|
+
children: control.label ?? key
|
|
713
|
+
}
|
|
714
|
+
)
|
|
715
|
+
},
|
|
716
|
+
`control-panel-custom-${key}`
|
|
717
|
+
) : null
|
|
718
|
+
) })
|
|
719
|
+
]
|
|
720
|
+
}
|
|
721
|
+
)
|
|
699
722
|
] }),
|
|
700
723
|
previewUrl && /* @__PURE__ */ jsx10(Button, { asChild: true, children: /* @__PURE__ */ jsxs4(
|
|
701
724
|
"a",
|
|
@@ -721,6 +744,7 @@ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
|
721
744
|
var NO_CONTROLS_PARAM = "nocontrols";
|
|
722
745
|
function Playground({ children }) {
|
|
723
746
|
const [isHydrated, setIsHydrated] = useState5(false);
|
|
747
|
+
const [copied, setCopied] = useState5(false);
|
|
724
748
|
useEffect4(() => {
|
|
725
749
|
setIsHydrated(true);
|
|
726
750
|
}, []);
|
|
@@ -728,8 +752,24 @@ function Playground({ children }) {
|
|
|
728
752
|
if (typeof window === "undefined") return false;
|
|
729
753
|
return new URLSearchParams(window.location.search).get(NO_CONTROLS_PARAM) === "true";
|
|
730
754
|
}, []);
|
|
755
|
+
const handleCopy = () => {
|
|
756
|
+
navigator.clipboard.writeText(window.location.href);
|
|
757
|
+
setCopied(true);
|
|
758
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
759
|
+
};
|
|
731
760
|
if (!isHydrated) return null;
|
|
732
761
|
return /* @__PURE__ */ jsx11(ResizableLayout, { hideControls, children: /* @__PURE__ */ jsxs5(ControlsProvider, { children: [
|
|
762
|
+
hideControls && /* @__PURE__ */ jsxs5(
|
|
763
|
+
"button",
|
|
764
|
+
{
|
|
765
|
+
onClick: handleCopy,
|
|
766
|
+
className: "absolute top-4 right-4 z-50 flex items-center gap-1 rounded bg-black/70 px-3 py-1 text-white hover:bg-black",
|
|
767
|
+
children: [
|
|
768
|
+
copied ? /* @__PURE__ */ jsx11(Check3, { size: 16 }) : /* @__PURE__ */ jsx11(Copy2, { size: 16 }),
|
|
769
|
+
copied ? "Copied!" : "Share"
|
|
770
|
+
]
|
|
771
|
+
}
|
|
772
|
+
),
|
|
733
773
|
/* @__PURE__ */ jsx11(PreviewContainer_default, { hideControls, children }),
|
|
734
774
|
!hideControls && /* @__PURE__ */ jsx11(ControlPanel_default, {})
|
|
735
775
|
] }) });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toriistudio/v0-playground",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "V0 Playground",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "tsup",
|
|
32
32
|
"local:push": "yarn build && yalc push",
|
|
33
|
-
"
|
|
33
|
+
"release:patch": "npm version patch && git push --follow-tags && npm publish",
|
|
34
|
+
"release:minor": "npm version minor && git push --follow-tags && npm publish",
|
|
35
|
+
"release:major": "npm version major && git push --follow-tags && npm publish",
|
|
34
36
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
35
37
|
},
|
|
36
38
|
"repository": {
|