@toriistudio/v0-playground 0.1.2 → 0.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.
package/dist/index.js CHANGED
@@ -208,7 +208,7 @@ var useControls = (schema, options) => {
208
208
  }
209
209
  }, [JSON.stringify(schema), JSON.stringify(ctx.values)]);
210
210
  const typedValues = ctx.values;
211
- const jsx11 = (0, import_react2.useCallback)(() => {
211
+ const jsx12 = (0, import_react2.useCallback)(() => {
212
212
  if (!options?.componentName) return "";
213
213
  const props = Object.entries(typedValues).map(([key, val]) => {
214
214
  if (typeof val === "string") return `${key}="${val}"`;
@@ -222,7 +222,7 @@ var useControls = (schema, options) => {
222
222
  controls: ctx.values,
223
223
  schema: ctx.schema,
224
224
  setValue: ctx.setValue,
225
- jsx: jsx11
225
+ jsx: jsx12
226
226
  };
227
227
  };
228
228
  var useUrlSyncedControls = (schema, options) => {
@@ -482,8 +482,53 @@ var SelectSeparator = React8.forwardRef(({ className, ...props }, ref) => /* @__
482
482
  ));
483
483
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
484
484
 
485
- // src/components/ControlPanel/ControlPanel.tsx
485
+ // src/components/ui/button.tsx
486
+ var React9 = __toESM(require("react"));
487
+ var import_react_slot = require("@radix-ui/react-slot");
488
+ var import_class_variance_authority2 = require("class-variance-authority");
486
489
  var import_jsx_runtime9 = require("react/jsx-runtime");
490
+ var buttonVariants = (0, import_class_variance_authority2.cva)(
491
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
492
+ {
493
+ variants: {
494
+ variant: {
495
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
496
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
497
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
498
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
499
+ ghost: "bg-gray-800 hover:bg-gray-900",
500
+ link: "text-primary underline-offset-4 hover:underline"
501
+ },
502
+ size: {
503
+ default: "h-9 px-4 py-2",
504
+ sm: "h-8 rounded-md px-3 text-xs",
505
+ lg: "h-10 rounded-md px-8",
506
+ icon: "h-9 w-9"
507
+ }
508
+ },
509
+ defaultVariants: {
510
+ variant: "default",
511
+ size: "default"
512
+ }
513
+ }
514
+ );
515
+ var Button = React9.forwardRef(
516
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
517
+ const Comp = asChild ? import_react_slot.Slot : "button";
518
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
519
+ Comp,
520
+ {
521
+ className: cn(buttonVariants({ variant, size, className })),
522
+ ref,
523
+ ...props
524
+ }
525
+ );
526
+ }
527
+ );
528
+ Button.displayName = "Button";
529
+
530
+ // src/components/ControlPanel/ControlPanel.tsx
531
+ var import_jsx_runtime10 = require("react/jsx-runtime");
487
532
  var ControlPanel = () => {
488
533
  const [copied, setCopied] = (0, import_react4.useState)(false);
489
534
  const { leftPanelWidth, isDesktop, isHydrated, sidebarNarrow } = useResizableLayout();
@@ -494,7 +539,18 @@ var ControlPanel = () => {
494
539
  const buttonControls = Object.entries(schema).filter(
495
540
  ([, control]) => control.type === "button"
496
541
  );
497
- const jsx11 = (0, import_react4.useMemo)(() => {
542
+ const previewUrl = (0, import_react4.useMemo)(() => {
543
+ if (typeof window === "undefined") return "";
544
+ const params = new URLSearchParams();
545
+ params.set("nocontrols", "true");
546
+ for (const [key, value] of Object.entries(values)) {
547
+ if (value !== void 0 && value !== null) {
548
+ params.set(key, value.toString());
549
+ }
550
+ }
551
+ return `${window.location.pathname}?${params.toString()}`;
552
+ }, [values]);
553
+ const jsx12 = (0, import_react4.useMemo)(() => {
498
554
  if (!componentName) return "";
499
555
  const props = Object.entries(values).map(([key, val]) => {
500
556
  if (typeof val === "string") return `${key}="${val}"`;
@@ -503,7 +559,7 @@ var ControlPanel = () => {
503
559
  }).join(" ");
504
560
  return `<${componentName} ${props} />`;
505
561
  }, [componentName, values]);
506
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
562
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
507
563
  "div",
508
564
  {
509
565
  className: `order-2 md:order-1 w-full md:h-auto p-2 md:p-4 bg-stone-900 font-mono text-stone-300 transition-opacity duration-300 ${!isHydrated ? "opacity-0" : "opacity-100"}`,
@@ -520,19 +576,19 @@ var ControlPanel = () => {
520
576
  overflowY: "auto"
521
577
  } : {}
522
578
  },
523
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-4 p-2 md:p-4 border border-stone-700 rounded-md", children: [
524
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-1", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "text-lg text-stone-100 font-bold", children: "Controls" }) }),
525
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-4 pt-2", children: [
579
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-4 p-2 md:p-4 border border-stone-700 rounded-md", children: [
580
+ /* @__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" }) }),
581
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-4 pt-2", children: [
526
582
  normalControls.map(([key, control]) => {
527
583
  const value = values[key];
528
584
  switch (control.type) {
529
585
  case "boolean":
530
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
586
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
531
587
  "div",
532
588
  {
533
589
  className: "flex items-center space-x-4 border-t border-stone-700 pt-4",
534
590
  children: [
535
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
591
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
536
592
  Switch,
537
593
  {
538
594
  id: key,
@@ -541,19 +597,19 @@ var ControlPanel = () => {
541
597
  className: "data-[state=checked]:bg-stone-700 data-[state=unchecked]:bg-stone-700/40"
542
598
  }
543
599
  ),
544
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Label, { htmlFor: key, className: "cursor-pointer", children: key })
600
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { htmlFor: key, className: "cursor-pointer", children: key })
545
601
  ]
546
602
  },
547
603
  key
548
604
  );
549
605
  case "number":
550
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2 w-full", children: [
551
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Label, { className: "text-stone-300", htmlFor: key, children: [
606
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-2 w-full", children: [
607
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Label, { className: "text-stone-300", htmlFor: key, children: [
552
608
  key,
553
609
  ": ",
554
610
  value
555
611
  ] }) }),
556
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
612
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
557
613
  Slider,
558
614
  {
559
615
  id: key,
@@ -567,7 +623,7 @@ var ControlPanel = () => {
567
623
  )
568
624
  ] }, key);
569
625
  case "string":
570
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
626
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
571
627
  Input,
572
628
  {
573
629
  id: key,
@@ -579,9 +635,9 @@ var ControlPanel = () => {
579
635
  key
580
636
  );
581
637
  case "color":
582
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2 w-full", children: [
583
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: key }) }),
584
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
638
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-2 w-full", children: [
639
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: key }) }),
640
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
585
641
  "input",
586
642
  {
587
643
  type: "color",
@@ -593,20 +649,20 @@ var ControlPanel = () => {
593
649
  )
594
650
  ] }, key);
595
651
  case "select":
596
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
652
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
597
653
  "div",
598
654
  {
599
655
  className: "space-y-2 border-t border-stone-700 pt-4",
600
656
  children: [
601
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: key }),
602
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
657
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Label, { className: "text-stone-300", htmlFor: key, children: key }),
658
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
603
659
  Select,
604
660
  {
605
661
  value,
606
662
  onValueChange: (val) => setValue(key, val),
607
663
  children: [
608
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectValue, { placeholder: "Select option" }) }),
609
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectContent, { children: control.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectItem, { value: opt, children: opt }, opt)) })
664
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectValue, { placeholder: "Select option" }) }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectContent, { children: control.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SelectItem, { value: opt, children: opt }, opt)) })
610
666
  ]
611
667
  }
612
668
  )
@@ -618,31 +674,31 @@ var ControlPanel = () => {
618
674
  return null;
619
675
  }
620
676
  }),
621
- (buttonControls.length > 0 || jsx11) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "border-t border-stone-700", children: [
622
- jsx11 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
677
+ (buttonControls.length > 0 || jsx12) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "border-t border-stone-700", children: [
678
+ jsx12 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
623
679
  "button",
624
680
  {
625
681
  onClick: () => {
626
- navigator.clipboard.writeText(jsx11);
682
+ navigator.clipboard.writeText(jsx12);
627
683
  setCopied(true);
628
684
  setTimeout(() => setCopied(false), 5e3);
629
685
  },
630
686
  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",
631
- children: copied ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
632
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.Check, { className: "w-4 h-4" }),
687
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
688
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Check, { className: "w-4 h-4" }),
633
689
  "Copied"
634
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
635
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.Copy, { className: "w-4 h-4" }),
690
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
691
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.Copy, { className: "w-4 h-4" }),
636
692
  "Copy to Clipboard"
637
693
  ] })
638
694
  }
639
695
  ) }, "control-panel-jsx"),
640
- buttonControls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-wrap gap-2 pt-4", children: buttonControls.map(
641
- ([key, control]) => control.type === "button" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
696
+ buttonControls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap gap-2 pt-4", children: buttonControls.map(
697
+ ([key, control]) => control.type === "button" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
642
698
  "div",
643
699
  {
644
700
  className: "flex-1",
645
- children: control.render ? control.render() : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
701
+ children: control.render ? control.render() : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
646
702
  "button",
647
703
  {
648
704
  onClick: control.onClick,
@@ -655,7 +711,20 @@ var ControlPanel = () => {
655
711
  ) : null
656
712
  ) })
657
713
  ] })
658
- ] })
714
+ ] }),
715
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Button, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
716
+ "a",
717
+ {
718
+ href: previewUrl,
719
+ target: "_blank",
720
+ rel: "noopener noreferrer",
721
+ className: "w-full px-4 py-2 text-sm text-center bg-stone-800 hover:bg-stone-700 text-white rounded",
722
+ children: [
723
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.SquareArrowOutUpRight, {}),
724
+ " Open in a New Tab"
725
+ ]
726
+ }
727
+ ) })
659
728
  ] })
660
729
  }
661
730
  );
@@ -663,16 +732,16 @@ var ControlPanel = () => {
663
732
  var ControlPanel_default = ControlPanel;
664
733
 
665
734
  // src/components/Playground/Playground.tsx
666
- var import_jsx_runtime10 = require("react/jsx-runtime");
735
+ var import_jsx_runtime11 = require("react/jsx-runtime");
667
736
  var NO_CONTROLS_PARAM = "nocontrols";
668
737
  function Playground({ children }) {
669
738
  const hideControls = (0, import_react5.useMemo)(() => {
670
739
  if (typeof window === "undefined") return false;
671
740
  return new URLSearchParams(window.location.search).get(NO_CONTROLS_PARAM) === "true";
672
741
  }, []);
673
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ResizableLayout, { hideControls, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ControlsProvider, { children: [
674
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PreviewContainer_default, { hideControls, children }),
675
- !hideControls && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ControlPanel_default, {})
742
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ResizableLayout, { hideControls, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ControlsProvider, { children: [
743
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PreviewContainer_default, { hideControls, children }),
744
+ !hideControls && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ControlPanel_default, {})
676
745
  ] }) });
677
746
  }
678
747
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.mjs CHANGED
@@ -182,7 +182,7 @@ var useControls = (schema, options) => {
182
182
  }
183
183
  }, [JSON.stringify(schema), JSON.stringify(ctx.values)]);
184
184
  const typedValues = ctx.values;
185
- const jsx11 = useCallback(() => {
185
+ const jsx12 = useCallback(() => {
186
186
  if (!options?.componentName) return "";
187
187
  const props = Object.entries(typedValues).map(([key, val]) => {
188
188
  if (typeof val === "string") return `${key}="${val}"`;
@@ -196,7 +196,7 @@ var useControls = (schema, options) => {
196
196
  controls: ctx.values,
197
197
  schema: ctx.schema,
198
198
  setValue: ctx.setValue,
199
- jsx: jsx11
199
+ jsx: jsx12
200
200
  };
201
201
  };
202
202
  var useUrlSyncedControls = (schema, options) => {
@@ -248,7 +248,7 @@ var PreviewContainer_default = PreviewContainer;
248
248
 
249
249
  // src/components/ControlPanel/ControlPanel.tsx
250
250
  import { useState as useState3, useMemo as useMemo2 } from "react";
251
- import { Check as Check2, Copy } from "lucide-react";
251
+ import { Check as Check2, Copy, SquareArrowOutUpRight } from "lucide-react";
252
252
 
253
253
  // src/components/ui/switch.tsx
254
254
  import * as React4 from "react";
@@ -456,8 +456,53 @@ var SelectSeparator = React8.forwardRef(({ className, ...props }, ref) => /* @__
456
456
  ));
457
457
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
458
458
 
459
+ // src/components/ui/button.tsx
460
+ import * as React9 from "react";
461
+ import { Slot } from "@radix-ui/react-slot";
462
+ import { cva as cva2 } from "class-variance-authority";
463
+ import { jsx as jsx9 } from "react/jsx-runtime";
464
+ var buttonVariants = cva2(
465
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
466
+ {
467
+ variants: {
468
+ variant: {
469
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
470
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
471
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
472
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
473
+ ghost: "bg-gray-800 hover:bg-gray-900",
474
+ link: "text-primary underline-offset-4 hover:underline"
475
+ },
476
+ size: {
477
+ default: "h-9 px-4 py-2",
478
+ sm: "h-8 rounded-md px-3 text-xs",
479
+ lg: "h-10 rounded-md px-8",
480
+ icon: "h-9 w-9"
481
+ }
482
+ },
483
+ defaultVariants: {
484
+ variant: "default",
485
+ size: "default"
486
+ }
487
+ }
488
+ );
489
+ var Button = React9.forwardRef(
490
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
491
+ const Comp = asChild ? Slot : "button";
492
+ return /* @__PURE__ */ jsx9(
493
+ Comp,
494
+ {
495
+ className: cn(buttonVariants({ variant, size, className })),
496
+ ref,
497
+ ...props
498
+ }
499
+ );
500
+ }
501
+ );
502
+ Button.displayName = "Button";
503
+
459
504
  // src/components/ControlPanel/ControlPanel.tsx
460
- import { Fragment, jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
505
+ import { Fragment, jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
461
506
  var ControlPanel = () => {
462
507
  const [copied, setCopied] = useState3(false);
463
508
  const { leftPanelWidth, isDesktop, isHydrated, sidebarNarrow } = useResizableLayout();
@@ -468,7 +513,18 @@ var ControlPanel = () => {
468
513
  const buttonControls = Object.entries(schema).filter(
469
514
  ([, control]) => control.type === "button"
470
515
  );
471
- const jsx11 = useMemo2(() => {
516
+ const previewUrl = useMemo2(() => {
517
+ if (typeof window === "undefined") return "";
518
+ const params = new URLSearchParams();
519
+ params.set("nocontrols", "true");
520
+ for (const [key, value] of Object.entries(values)) {
521
+ if (value !== void 0 && value !== null) {
522
+ params.set(key, value.toString());
523
+ }
524
+ }
525
+ return `${window.location.pathname}?${params.toString()}`;
526
+ }, [values]);
527
+ const jsx12 = useMemo2(() => {
472
528
  if (!componentName) return "";
473
529
  const props = Object.entries(values).map(([key, val]) => {
474
530
  if (typeof val === "string") return `${key}="${val}"`;
@@ -477,7 +533,7 @@ var ControlPanel = () => {
477
533
  }).join(" ");
478
534
  return `<${componentName} ${props} />`;
479
535
  }, [componentName, values]);
480
- return /* @__PURE__ */ jsx9(
536
+ return /* @__PURE__ */ jsx10(
481
537
  "div",
482
538
  {
483
539
  className: `order-2 md:order-1 w-full md:h-auto p-2 md:p-4 bg-stone-900 font-mono text-stone-300 transition-opacity duration-300 ${!isHydrated ? "opacity-0" : "opacity-100"}`,
@@ -495,7 +551,7 @@ var ControlPanel = () => {
495
551
  } : {}
496
552
  },
497
553
  children: /* @__PURE__ */ jsxs4("div", { className: "space-y-4 p-2 md:p-4 border border-stone-700 rounded-md", children: [
498
- /* @__PURE__ */ jsx9("div", { className: "space-y-1", children: /* @__PURE__ */ jsx9("h1", { className: "text-lg text-stone-100 font-bold", children: "Controls" }) }),
554
+ /* @__PURE__ */ jsx10("div", { className: "space-y-1", children: /* @__PURE__ */ jsx10("h1", { className: "text-lg text-stone-100 font-bold", children: "Controls" }) }),
499
555
  /* @__PURE__ */ jsxs4("div", { className: "space-y-4 pt-2", children: [
500
556
  normalControls.map(([key, control]) => {
501
557
  const value = values[key];
@@ -506,7 +562,7 @@ var ControlPanel = () => {
506
562
  {
507
563
  className: "flex items-center space-x-4 border-t border-stone-700 pt-4",
508
564
  children: [
509
- /* @__PURE__ */ jsx9(
565
+ /* @__PURE__ */ jsx10(
510
566
  Switch,
511
567
  {
512
568
  id: key,
@@ -515,19 +571,19 @@ var ControlPanel = () => {
515
571
  className: "data-[state=checked]:bg-stone-700 data-[state=unchecked]:bg-stone-700/40"
516
572
  }
517
573
  ),
518
- /* @__PURE__ */ jsx9(Label, { htmlFor: key, className: "cursor-pointer", children: key })
574
+ /* @__PURE__ */ jsx10(Label, { htmlFor: key, className: "cursor-pointer", children: key })
519
575
  ]
520
576
  },
521
577
  key
522
578
  );
523
579
  case "number":
524
580
  return /* @__PURE__ */ jsxs4("div", { className: "space-y-2 w-full", children: [
525
- /* @__PURE__ */ jsx9("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ jsxs4(Label, { className: "text-stone-300", htmlFor: key, children: [
581
+ /* @__PURE__ */ jsx10("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ jsxs4(Label, { className: "text-stone-300", htmlFor: key, children: [
526
582
  key,
527
583
  ": ",
528
584
  value
529
585
  ] }) }),
530
- /* @__PURE__ */ jsx9(
586
+ /* @__PURE__ */ jsx10(
531
587
  Slider,
532
588
  {
533
589
  id: key,
@@ -541,7 +597,7 @@ var ControlPanel = () => {
541
597
  )
542
598
  ] }, key);
543
599
  case "string":
544
- return /* @__PURE__ */ jsx9(
600
+ return /* @__PURE__ */ jsx10(
545
601
  Input,
546
602
  {
547
603
  id: key,
@@ -554,8 +610,8 @@ var ControlPanel = () => {
554
610
  );
555
611
  case "color":
556
612
  return /* @__PURE__ */ jsxs4("div", { className: "space-y-2 w-full", children: [
557
- /* @__PURE__ */ jsx9("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ jsx9(Label, { className: "text-stone-300", htmlFor: key, children: key }) }),
558
- /* @__PURE__ */ jsx9(
613
+ /* @__PURE__ */ jsx10("div", { className: "flex items-center justify-between pb-1", children: /* @__PURE__ */ jsx10(Label, { className: "text-stone-300", htmlFor: key, children: key }) }),
614
+ /* @__PURE__ */ jsx10(
559
615
  "input",
560
616
  {
561
617
  type: "color",
@@ -572,15 +628,15 @@ var ControlPanel = () => {
572
628
  {
573
629
  className: "space-y-2 border-t border-stone-700 pt-4",
574
630
  children: [
575
- /* @__PURE__ */ jsx9(Label, { className: "text-stone-300", htmlFor: key, children: key }),
631
+ /* @__PURE__ */ jsx10(Label, { className: "text-stone-300", htmlFor: key, children: key }),
576
632
  /* @__PURE__ */ jsxs4(
577
633
  Select,
578
634
  {
579
635
  value,
580
636
  onValueChange: (val) => setValue(key, val),
581
637
  children: [
582
- /* @__PURE__ */ jsx9(SelectTrigger, { children: /* @__PURE__ */ jsx9(SelectValue, { placeholder: "Select option" }) }),
583
- /* @__PURE__ */ jsx9(SelectContent, { children: control.options.map((opt) => /* @__PURE__ */ jsx9(SelectItem, { value: opt, children: opt }, opt)) })
638
+ /* @__PURE__ */ jsx10(SelectTrigger, { children: /* @__PURE__ */ jsx10(SelectValue, { placeholder: "Select option" }) }),
639
+ /* @__PURE__ */ jsx10(SelectContent, { children: control.options.map((opt) => /* @__PURE__ */ jsx10(SelectItem, { value: opt, children: opt }, opt)) })
584
640
  ]
585
641
  }
586
642
  )
@@ -592,31 +648,31 @@ var ControlPanel = () => {
592
648
  return null;
593
649
  }
594
650
  }),
595
- (buttonControls.length > 0 || jsx11) && /* @__PURE__ */ jsxs4("div", { className: "border-t border-stone-700", children: [
596
- jsx11 && /* @__PURE__ */ jsx9("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ jsx9(
651
+ (buttonControls.length > 0 || jsx12) && /* @__PURE__ */ jsxs4("div", { className: "border-t border-stone-700", children: [
652
+ jsx12 && /* @__PURE__ */ jsx10("div", { className: "flex-1 pt-4", children: /* @__PURE__ */ jsx10(
597
653
  "button",
598
654
  {
599
655
  onClick: () => {
600
- navigator.clipboard.writeText(jsx11);
656
+ navigator.clipboard.writeText(jsx12);
601
657
  setCopied(true);
602
658
  setTimeout(() => setCopied(false), 5e3);
603
659
  },
604
660
  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",
605
661
  children: copied ? /* @__PURE__ */ jsxs4(Fragment, { children: [
606
- /* @__PURE__ */ jsx9(Check2, { className: "w-4 h-4" }),
662
+ /* @__PURE__ */ jsx10(Check2, { className: "w-4 h-4" }),
607
663
  "Copied"
608
664
  ] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
609
- /* @__PURE__ */ jsx9(Copy, { className: "w-4 h-4" }),
665
+ /* @__PURE__ */ jsx10(Copy, { className: "w-4 h-4" }),
610
666
  "Copy to Clipboard"
611
667
  ] })
612
668
  }
613
669
  ) }, "control-panel-jsx"),
614
- buttonControls.length > 0 && /* @__PURE__ */ jsx9("div", { className: "flex flex-wrap gap-2 pt-4", children: buttonControls.map(
615
- ([key, control]) => control.type === "button" ? /* @__PURE__ */ jsx9(
670
+ buttonControls.length > 0 && /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap gap-2 pt-4", children: buttonControls.map(
671
+ ([key, control]) => control.type === "button" ? /* @__PURE__ */ jsx10(
616
672
  "div",
617
673
  {
618
674
  className: "flex-1",
619
- children: control.render ? control.render() : /* @__PURE__ */ jsx9(
675
+ children: control.render ? control.render() : /* @__PURE__ */ jsx10(
620
676
  "button",
621
677
  {
622
678
  onClick: control.onClick,
@@ -629,7 +685,20 @@ var ControlPanel = () => {
629
685
  ) : null
630
686
  ) })
631
687
  ] })
632
- ] })
688
+ ] }),
689
+ /* @__PURE__ */ jsx10(Button, { asChild: true, children: /* @__PURE__ */ jsxs4(
690
+ "a",
691
+ {
692
+ href: previewUrl,
693
+ target: "_blank",
694
+ rel: "noopener noreferrer",
695
+ className: "w-full px-4 py-2 text-sm text-center bg-stone-800 hover:bg-stone-700 text-white rounded",
696
+ children: [
697
+ /* @__PURE__ */ jsx10(SquareArrowOutUpRight, {}),
698
+ " Open in a New Tab"
699
+ ]
700
+ }
701
+ ) })
633
702
  ] })
634
703
  }
635
704
  );
@@ -637,16 +706,16 @@ var ControlPanel = () => {
637
706
  var ControlPanel_default = ControlPanel;
638
707
 
639
708
  // src/components/Playground/Playground.tsx
640
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
709
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
641
710
  var NO_CONTROLS_PARAM = "nocontrols";
642
711
  function Playground({ children }) {
643
712
  const hideControls = useMemo3(() => {
644
713
  if (typeof window === "undefined") return false;
645
714
  return new URLSearchParams(window.location.search).get(NO_CONTROLS_PARAM) === "true";
646
715
  }, []);
647
- return /* @__PURE__ */ jsx10(ResizableLayout, { hideControls, children: /* @__PURE__ */ jsxs5(ControlsProvider, { children: [
648
- /* @__PURE__ */ jsx10(PreviewContainer_default, { hideControls, children }),
649
- !hideControls && /* @__PURE__ */ jsx10(ControlPanel_default, {})
716
+ return /* @__PURE__ */ jsx11(ResizableLayout, { hideControls, children: /* @__PURE__ */ jsxs5(ControlsProvider, { children: [
717
+ /* @__PURE__ */ jsx11(PreviewContainer_default, { hideControls, children }),
718
+ !hideControls && /* @__PURE__ */ jsx11(ControlPanel_default, {})
650
719
  ] }) });
651
720
  }
652
721
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toriistudio/v0-playground",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "V0 Playground",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",