@dheme/react 2.1.0 → 2.3.0

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
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -34,6 +35,7 @@ __export(index_exports, {
34
35
  DhemeScript: () => DhemeScript,
35
36
  ThemeActionsContext: () => ThemeActionsContext,
36
37
  ThemeDataContext: () => ThemeDataContext,
38
+ ThemeGenerator: () => ThemeGenerator,
37
39
  applyThemeCSSVariables: () => applyThemeCSSVariables,
38
40
  buildCacheKey: () => buildCacheKey,
39
41
  getBlockingScriptPayload: () => getBlockingScriptPayload,
@@ -406,33 +408,1142 @@ function DhemeScript({
406
408
  });
407
409
  }
408
410
 
409
- // src/hooks/useTheme.ts
411
+ // src/components/ThemeGenerator.tsx
412
+ var import_react7 = require("react");
413
+
414
+ // src/hooks/useThemeActions.ts
410
415
  var import_react5 = require("react");
411
- function useTheme() {
412
- const context = (0, import_react5.useContext)(ThemeDataContext);
416
+ function useThemeActions() {
417
+ const context = (0, import_react5.useContext)(ThemeActionsContext);
413
418
  if (!context) {
414
- throw new Error("useTheme must be used within a <DhemeProvider>");
419
+ throw new Error("useThemeActions must be used within a <DhemeProvider>");
415
420
  }
416
421
  return context;
417
422
  }
418
423
 
419
- // src/hooks/useThemeActions.ts
424
+ // src/hooks/useDebounce.ts
420
425
  var import_react6 = require("react");
421
- function useThemeActions() {
422
- const context = (0, import_react6.useContext)(ThemeActionsContext);
426
+ function useDebounce(value, delay) {
427
+ const [debouncedValue, setDebouncedValue] = (0, import_react6.useState)(value);
428
+ (0, import_react6.useEffect)(() => {
429
+ const timer = setTimeout(() => setDebouncedValue(value), delay);
430
+ return () => clearTimeout(timer);
431
+ }, [value, delay]);
432
+ return debouncedValue;
433
+ }
434
+
435
+ // src/components/ThemeGenerator.tsx
436
+ var import_jsx_runtime = require("react/jsx-runtime");
437
+ function SparklesIcon({ className }) {
438
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
439
+ "svg",
440
+ {
441
+ className,
442
+ xmlns: "http://www.w3.org/2000/svg",
443
+ width: "16",
444
+ height: "16",
445
+ viewBox: "0 0 24 24",
446
+ fill: "none",
447
+ stroke: "currentColor",
448
+ strokeWidth: "2",
449
+ strokeLinecap: "round",
450
+ strokeLinejoin: "round",
451
+ children: [
452
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }),
453
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 3v4" }),
454
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 17v4" }),
455
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 5h4" }),
456
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 19h4" })
457
+ ]
458
+ }
459
+ );
460
+ }
461
+ function XIcon({ className }) {
462
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
463
+ "svg",
464
+ {
465
+ className,
466
+ xmlns: "http://www.w3.org/2000/svg",
467
+ width: "16",
468
+ height: "16",
469
+ viewBox: "0 0 24 24",
470
+ fill: "none",
471
+ stroke: "currentColor",
472
+ strokeWidth: "2",
473
+ strokeLinecap: "round",
474
+ strokeLinejoin: "round",
475
+ children: [
476
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18 6 6 18" }),
477
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m6 6 12 12" })
478
+ ]
479
+ }
480
+ );
481
+ }
482
+ function PlusIcon({ className }) {
483
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
484
+ "svg",
485
+ {
486
+ className,
487
+ xmlns: "http://www.w3.org/2000/svg",
488
+ width: "16",
489
+ height: "16",
490
+ viewBox: "0 0 24 24",
491
+ fill: "none",
492
+ stroke: "currentColor",
493
+ strokeWidth: "2",
494
+ strokeLinecap: "round",
495
+ strokeLinejoin: "round",
496
+ children: [
497
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 12h14" }),
498
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 5v14" })
499
+ ]
500
+ }
501
+ );
502
+ }
503
+ function ChevronUpIcon({ className }) {
504
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
505
+ "svg",
506
+ {
507
+ className,
508
+ xmlns: "http://www.w3.org/2000/svg",
509
+ width: "16",
510
+ height: "16",
511
+ viewBox: "0 0 24 24",
512
+ fill: "none",
513
+ stroke: "currentColor",
514
+ strokeWidth: "2",
515
+ strokeLinecap: "round",
516
+ strokeLinejoin: "round",
517
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m18 15-6-6-6 6" })
518
+ }
519
+ );
520
+ }
521
+ function RotateCcwIcon({ className }) {
522
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
523
+ "svg",
524
+ {
525
+ className,
526
+ xmlns: "http://www.w3.org/2000/svg",
527
+ width: "16",
528
+ height: "16",
529
+ viewBox: "0 0 24 24",
530
+ fill: "none",
531
+ stroke: "currentColor",
532
+ strokeWidth: "2",
533
+ strokeLinecap: "round",
534
+ strokeLinejoin: "round",
535
+ children: [
536
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
537
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 3v5h5" })
538
+ ]
539
+ }
540
+ );
541
+ }
542
+ function HexColorPicker({ color, onChange }) {
543
+ const hexToRgb = (hex) => {
544
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
545
+ return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : { r: 0, g: 0, b: 0 };
546
+ };
547
+ const rgbToHsv = (r, g, b) => {
548
+ r /= 255;
549
+ g /= 255;
550
+ b /= 255;
551
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
552
+ const d = max - min;
553
+ let h = 0;
554
+ const s = max === 0 ? 0 : d / max;
555
+ const v = max;
556
+ if (max !== min) {
557
+ switch (max) {
558
+ case r:
559
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
560
+ break;
561
+ case g:
562
+ h = ((b - r) / d + 2) / 6;
563
+ break;
564
+ case b:
565
+ h = ((r - g) / d + 4) / 6;
566
+ break;
567
+ }
568
+ }
569
+ return { h: h * 360, s: s * 100, v: v * 100 };
570
+ };
571
+ const hsvToHex = (h, s, v) => {
572
+ s /= 100;
573
+ v /= 100;
574
+ const i = Math.floor(h / 60);
575
+ const f = h / 60 - i;
576
+ const p = v * (1 - s);
577
+ const q = v * (1 - f * s);
578
+ const t = v * (1 - (1 - f) * s);
579
+ let r = 0, g = 0, b = 0;
580
+ switch (i % 6) {
581
+ case 0:
582
+ r = v;
583
+ g = t;
584
+ b = p;
585
+ break;
586
+ case 1:
587
+ r = q;
588
+ g = v;
589
+ b = p;
590
+ break;
591
+ case 2:
592
+ r = p;
593
+ g = v;
594
+ b = t;
595
+ break;
596
+ case 3:
597
+ r = p;
598
+ g = q;
599
+ b = v;
600
+ break;
601
+ case 4:
602
+ r = t;
603
+ g = p;
604
+ b = v;
605
+ break;
606
+ case 5:
607
+ r = v;
608
+ g = p;
609
+ b = q;
610
+ break;
611
+ }
612
+ return "#" + [r, g, b].map(
613
+ (x) => Math.round(x * 255).toString(16).padStart(2, "0")
614
+ ).join("");
615
+ };
616
+ const rgb = hexToRgb(color);
617
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
618
+ const gradientRef = (0, import_react7.useRef)(null);
619
+ const hueRef = (0, import_react7.useRef)(null);
620
+ const isDraggingGradient = (0, import_react7.useRef)(false);
621
+ const isDraggingHue = (0, import_react7.useRef)(false);
622
+ const handleGradientPointer = (0, import_react7.useCallback)(
623
+ (e) => {
624
+ const rect = gradientRef.current?.getBoundingClientRect();
625
+ if (!rect) return;
626
+ const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
627
+ const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
628
+ onChange(hsvToHex(hsv.h, x * 100, (1 - y) * 100));
629
+ },
630
+ [hsv.h, onChange]
631
+ );
632
+ const handleHuePointer = (0, import_react7.useCallback)(
633
+ (e) => {
634
+ const rect = hueRef.current?.getBoundingClientRect();
635
+ if (!rect) return;
636
+ const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
637
+ onChange(hsvToHex(x * 360, hsv.s, hsv.v));
638
+ },
639
+ [hsv.s, hsv.v, onChange]
640
+ );
641
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "8px", userSelect: "none" }, children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
643
+ "div",
644
+ {
645
+ ref: gradientRef,
646
+ style: {
647
+ position: "relative",
648
+ height: "120px",
649
+ borderRadius: "6px",
650
+ cursor: "crosshair",
651
+ background: `linear-gradient(to right, #fff, hsl(${hsv.h}, 100%, 50%))`,
652
+ touchAction: "none"
653
+ },
654
+ onPointerDown: (e) => {
655
+ isDraggingGradient.current = true;
656
+ e.currentTarget.setPointerCapture(e.pointerId);
657
+ handleGradientPointer(e);
658
+ },
659
+ onPointerMove: (e) => {
660
+ if (isDraggingGradient.current) handleGradientPointer(e);
661
+ },
662
+ onPointerUp: () => {
663
+ isDraggingGradient.current = false;
664
+ },
665
+ children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
667
+ "div",
668
+ {
669
+ style: {
670
+ position: "absolute",
671
+ inset: 0,
672
+ borderRadius: "6px",
673
+ background: "linear-gradient(to top, #000, transparent)"
674
+ }
675
+ }
676
+ ),
677
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
678
+ "div",
679
+ {
680
+ style: {
681
+ position: "absolute",
682
+ left: `${hsv.s}%`,
683
+ top: `${100 - hsv.v}%`,
684
+ transform: "translate(-50%, -50%)",
685
+ width: "12px",
686
+ height: "12px",
687
+ borderRadius: "50%",
688
+ border: "2px solid white",
689
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.3)",
690
+ pointerEvents: "none"
691
+ }
692
+ }
693
+ )
694
+ ]
695
+ }
696
+ ),
697
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
698
+ "div",
699
+ {
700
+ ref: hueRef,
701
+ style: {
702
+ position: "relative",
703
+ height: "12px",
704
+ borderRadius: "6px",
705
+ cursor: "pointer",
706
+ background: "linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)",
707
+ touchAction: "none"
708
+ },
709
+ onPointerDown: (e) => {
710
+ isDraggingHue.current = true;
711
+ e.currentTarget.setPointerCapture(e.pointerId);
712
+ handleHuePointer(e);
713
+ },
714
+ onPointerMove: (e) => {
715
+ if (isDraggingHue.current) handleHuePointer(e);
716
+ },
717
+ onPointerUp: () => {
718
+ isDraggingHue.current = false;
719
+ },
720
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
721
+ "div",
722
+ {
723
+ style: {
724
+ position: "absolute",
725
+ left: `${hsv.h / 360 * 100}%`,
726
+ top: "50%",
727
+ transform: "translate(-50%, -50%)",
728
+ width: "16px",
729
+ height: "16px",
730
+ borderRadius: "50%",
731
+ border: "2px solid white",
732
+ boxShadow: "0 0 0 1px rgba(0,0,0,0.3)",
733
+ background: `hsl(${hsv.h}, 100%, 50%)`,
734
+ pointerEvents: "none"
735
+ }
736
+ }
737
+ )
738
+ }
739
+ )
740
+ ] });
741
+ }
742
+ function SectionHeading({ children }) {
743
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
744
+ "div",
745
+ {
746
+ style: {
747
+ fontSize: "10px",
748
+ fontWeight: 700,
749
+ letterSpacing: "0.08em",
750
+ textTransform: "uppercase",
751
+ color: "hsl(var(--muted-foreground))",
752
+ marginBottom: "10px"
753
+ },
754
+ children
755
+ }
756
+ );
757
+ }
758
+ function ColorRow({
759
+ label,
760
+ badge,
761
+ color,
762
+ disabled,
763
+ onColorChange,
764
+ onInputChange,
765
+ actionButton
766
+ }) {
767
+ const [pickerOpen, setPickerOpen] = (0, import_react7.useState)(false);
768
+ const hex = color.replace("#", "").toUpperCase();
769
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: "10px" }, children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
771
+ "div",
772
+ {
773
+ style: {
774
+ display: "flex",
775
+ alignItems: "center",
776
+ justifyContent: "space-between",
777
+ marginBottom: "6px"
778
+ },
779
+ children: [
780
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "12px", fontWeight: 500, color: "hsl(var(--foreground))" }, children: label }),
782
+ badge && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
783
+ "span",
784
+ {
785
+ style: {
786
+ fontSize: "9px",
787
+ fontWeight: 600,
788
+ letterSpacing: "0.05em",
789
+ padding: "1px 5px",
790
+ borderRadius: "99px",
791
+ background: "hsl(var(--muted))",
792
+ color: "hsl(var(--muted-foreground))"
793
+ },
794
+ children: badge
795
+ }
796
+ )
797
+ ] }),
798
+ actionButton
799
+ ]
800
+ }
801
+ ),
802
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
804
+ "button",
805
+ {
806
+ onClick: () => !disabled && setPickerOpen((p) => !p),
807
+ style: {
808
+ width: "32px",
809
+ height: "32px",
810
+ borderRadius: "6px",
811
+ border: "1px solid hsl(var(--border))",
812
+ background: disabled ? "hsl(var(--muted))" : color,
813
+ cursor: disabled ? "not-allowed" : "pointer",
814
+ flexShrink: 0,
815
+ opacity: disabled ? 0.4 : 1,
816
+ transition: "opacity 0.15s"
817
+ }
818
+ }
819
+ ),
820
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
821
+ "div",
822
+ {
823
+ style: {
824
+ display: "flex",
825
+ alignItems: "center",
826
+ border: "1px solid hsl(var(--border))",
827
+ borderRadius: "6px",
828
+ overflow: "hidden",
829
+ background: disabled ? "hsl(var(--muted))" : "hsl(var(--background))",
830
+ flex: 1,
831
+ opacity: disabled ? 0.5 : 1
832
+ },
833
+ children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
835
+ "span",
836
+ {
837
+ style: {
838
+ padding: "0 8px",
839
+ fontSize: "12px",
840
+ color: "hsl(var(--muted-foreground))",
841
+ fontFamily: "monospace"
842
+ },
843
+ children: "#"
844
+ }
845
+ ),
846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
847
+ "input",
848
+ {
849
+ type: "text",
850
+ value: hex,
851
+ disabled,
852
+ maxLength: 6,
853
+ onChange: (e) => {
854
+ const v = e.target.value.replace(/[^0-9a-fA-F]/g, "");
855
+ onInputChange(v);
856
+ },
857
+ style: {
858
+ background: "transparent",
859
+ border: "none",
860
+ outline: "none",
861
+ fontSize: "12px",
862
+ fontFamily: "monospace",
863
+ fontWeight: 500,
864
+ color: "hsl(var(--foreground))",
865
+ width: "100%",
866
+ padding: "7px 8px 7px 0",
867
+ cursor: disabled ? "not-allowed" : "text"
868
+ }
869
+ }
870
+ )
871
+ ]
872
+ }
873
+ )
874
+ ] }),
875
+ pickerOpen && !disabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
876
+ "div",
877
+ {
878
+ style: {
879
+ marginTop: "8px",
880
+ padding: "10px",
881
+ background: "hsl(var(--card))",
882
+ border: "1px solid hsl(var(--border))",
883
+ borderRadius: "8px",
884
+ boxShadow: "0 4px 20px rgba(0,0,0,0.15)"
885
+ },
886
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HexColorPicker, { color, onChange: onColorChange })
887
+ }
888
+ )
889
+ ] });
890
+ }
891
+ function SliderRow({ label, value, onChange, min, max, step, display }) {
892
+ const pct = (value[0] - min) / (max - min) * 100;
893
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "10px" }, children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "12px", color: "hsl(var(--foreground))", minWidth: "70px" }, children: label }),
895
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
896
+ "input",
897
+ {
898
+ type: "range",
899
+ min,
900
+ max,
901
+ step,
902
+ value: value[0],
903
+ onChange: (e) => onChange([Number(e.target.value)]),
904
+ style: {
905
+ flex: 1,
906
+ height: "4px",
907
+ appearance: "none",
908
+ WebkitAppearance: "none",
909
+ background: `linear-gradient(to right, hsl(var(--primary)) ${pct}%, hsl(var(--muted)) ${pct}%)`,
910
+ borderRadius: "2px",
911
+ cursor: "pointer",
912
+ outline: "none"
913
+ }
914
+ }
915
+ ),
916
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
917
+ "span",
918
+ {
919
+ style: {
920
+ fontSize: "11px",
921
+ fontFamily: "monospace",
922
+ fontWeight: 500,
923
+ color: "hsl(var(--muted-foreground))",
924
+ minWidth: "48px",
925
+ textAlign: "right"
926
+ },
927
+ children: display(value[0])
928
+ }
929
+ )
930
+ ] });
931
+ }
932
+ function ToggleRow({ label, checked, onChange }) {
933
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
934
+ "div",
935
+ {
936
+ style: {
937
+ display: "flex",
938
+ alignItems: "center",
939
+ justifyContent: "space-between",
940
+ marginBottom: "8px"
941
+ },
942
+ children: [
943
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "12px", color: "hsl(var(--foreground))" }, children: label }),
944
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
945
+ "button",
946
+ {
947
+ role: "switch",
948
+ "aria-checked": checked,
949
+ onClick: () => onChange(!checked),
950
+ style: {
951
+ width: "36px",
952
+ height: "20px",
953
+ borderRadius: "10px",
954
+ background: checked ? "hsl(var(--primary))" : "hsl(var(--muted))",
955
+ border: "none",
956
+ cursor: "pointer",
957
+ position: "relative",
958
+ transition: "background 0.2s",
959
+ flexShrink: 0
960
+ },
961
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
962
+ "span",
963
+ {
964
+ style: {
965
+ position: "absolute",
966
+ top: "2px",
967
+ left: checked ? "18px" : "2px",
968
+ width: "16px",
969
+ height: "16px",
970
+ borderRadius: "50%",
971
+ background: "white",
972
+ transition: "left 0.2s",
973
+ boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
974
+ }
975
+ }
976
+ )
977
+ }
978
+ )
979
+ ]
980
+ }
981
+ );
982
+ }
983
+ function cn(...classes) {
984
+ return classes.filter(Boolean).join(" ");
985
+ }
986
+ function ThemeGenerator({
987
+ defaultTheme = "#4332f6",
988
+ defaultSecondaryColor = "#ab67f1",
989
+ defaultSaturation = 10,
990
+ defaultLightness = 2,
991
+ defaultRadius = 0,
992
+ position = "bottom-right",
993
+ open: controlledOpen,
994
+ onOpenChange,
995
+ labels: labelsProp,
996
+ className
997
+ }) {
998
+ const { generateTheme } = useThemeActions();
999
+ const labels = {
1000
+ title: "Theme Generator",
1001
+ description: "Generate complete themes from a single color. Changes apply in real time.",
1002
+ baseColors: "Base Colors",
1003
+ primary: "Primary",
1004
+ secondary: "Secondary",
1005
+ optional: "Optional",
1006
+ fineTuning: "Fine Tuning",
1007
+ saturation: "Saturation",
1008
+ lightness: "Lightness",
1009
+ borderRadius: "Border Radius",
1010
+ advancedOptions: "Advanced Options",
1011
+ colorfulCard: "Colorful Card",
1012
+ colorfulBackground: "Colorful Background",
1013
+ colorfulBorder: "Colorful Border",
1014
+ reset: "Reset",
1015
+ fabPrimaryLabel: "Primary",
1016
+ ...labelsProp
1017
+ };
1018
+ const [internalOpen, setInternalOpen] = (0, import_react7.useState)(controlledOpen ?? false);
1019
+ const isOpen = controlledOpen !== void 0 ? controlledOpen : internalOpen;
1020
+ const setIsOpen = (0, import_react7.useCallback)(
1021
+ (next) => {
1022
+ if (controlledOpen === void 0) setInternalOpen(next);
1023
+ onOpenChange?.(next);
1024
+ },
1025
+ [controlledOpen, onOpenChange]
1026
+ );
1027
+ const [localPrimary, setLocalPrimary] = (0, import_react7.useState)(defaultTheme);
1028
+ const [localSecondary, setLocalSecondary] = (0, import_react7.useState)(defaultSecondaryColor);
1029
+ const [isSecondaryEnabled, setIsSecondaryEnabled] = (0, import_react7.useState)(false);
1030
+ const [localSaturation, setLocalSaturation] = (0, import_react7.useState)([defaultSaturation]);
1031
+ const [localLightness, setLocalLightness] = (0, import_react7.useState)([defaultLightness]);
1032
+ const [localRadius, setLocalRadius] = (0, import_react7.useState)([defaultRadius]);
1033
+ const [localCardIsColored, setLocalCardIsColored] = (0, import_react7.useState)(false);
1034
+ const [localBackgroundIsColored, setLocalBackgroundIsColored] = (0, import_react7.useState)(false);
1035
+ const [localBorderIsColored, setLocalBorderIsColored] = (0, import_react7.useState)(false);
1036
+ const paramsRef = (0, import_react7.useRef)({
1037
+ theme: localPrimary,
1038
+ secondaryColor: isSecondaryEnabled ? localSecondary : void 0,
1039
+ saturationAdjust: localSaturation[0],
1040
+ lightnessAdjust: localLightness[0],
1041
+ radius: localRadius[0],
1042
+ cardIsColored: localCardIsColored,
1043
+ backgroundIsColored: localBackgroundIsColored,
1044
+ borderIsColored: localBorderIsColored
1045
+ });
1046
+ (0, import_react7.useEffect)(() => {
1047
+ paramsRef.current = {
1048
+ theme: localPrimary,
1049
+ secondaryColor: isSecondaryEnabled ? localSecondary : void 0,
1050
+ saturationAdjust: localSaturation[0],
1051
+ lightnessAdjust: localLightness[0],
1052
+ radius: localRadius[0],
1053
+ cardIsColored: localCardIsColored,
1054
+ backgroundIsColored: localBackgroundIsColored,
1055
+ borderIsColored: localBorderIsColored
1056
+ };
1057
+ });
1058
+ const debouncedPrimary = useDebounce(localPrimary, 150);
1059
+ const debouncedSecondary = useDebounce(localSecondary, 150);
1060
+ const debouncedSaturation = useDebounce(localSaturation[0], 200);
1061
+ const debouncedLightness = useDebounce(localLightness[0], 200);
1062
+ const debouncedRadius = useDebounce(localRadius[0], 200);
1063
+ (0, import_react7.useEffect)(() => {
1064
+ generateTheme(paramsRef.current);
1065
+ }, [debouncedPrimary]);
1066
+ (0, import_react7.useEffect)(() => {
1067
+ if (isSecondaryEnabled) generateTheme(paramsRef.current);
1068
+ }, [debouncedSecondary]);
1069
+ (0, import_react7.useEffect)(() => {
1070
+ generateTheme(paramsRef.current);
1071
+ }, [debouncedSaturation]);
1072
+ (0, import_react7.useEffect)(() => {
1073
+ generateTheme(paramsRef.current);
1074
+ }, [debouncedLightness]);
1075
+ (0, import_react7.useEffect)(() => {
1076
+ generateTheme(paramsRef.current);
1077
+ }, [debouncedRadius]);
1078
+ const handleEnableSecondary = () => {
1079
+ setIsSecondaryEnabled(true);
1080
+ generateTheme({ ...paramsRef.current, secondaryColor: localSecondary });
1081
+ };
1082
+ const handleDisableSecondary = () => {
1083
+ setIsSecondaryEnabled(false);
1084
+ generateTheme({ ...paramsRef.current, secondaryColor: void 0 });
1085
+ };
1086
+ const handleToggle = (key, value) => {
1087
+ if (key === "cardIsColored") setLocalCardIsColored(value);
1088
+ if (key === "backgroundIsColored") setLocalBackgroundIsColored(value);
1089
+ if (key === "borderIsColored") setLocalBorderIsColored(value);
1090
+ generateTheme({ ...paramsRef.current, [key]: value });
1091
+ };
1092
+ const handleReset = () => {
1093
+ setLocalPrimary(defaultTheme);
1094
+ setLocalSecondary(defaultSecondaryColor);
1095
+ setLocalSaturation([defaultSaturation]);
1096
+ setLocalLightness([defaultLightness]);
1097
+ setLocalRadius([defaultRadius]);
1098
+ setIsSecondaryEnabled(false);
1099
+ setLocalCardIsColored(false);
1100
+ setLocalBackgroundIsColored(false);
1101
+ setLocalBorderIsColored(false);
1102
+ generateTheme({
1103
+ theme: defaultTheme,
1104
+ saturationAdjust: defaultSaturation,
1105
+ lightnessAdjust: defaultLightness,
1106
+ radius: defaultRadius,
1107
+ cardIsColored: false,
1108
+ backgroundIsColored: false,
1109
+ borderIsColored: false
1110
+ });
1111
+ };
1112
+ const positionStyle = position === "bottom-left" ? { bottom: "24px", left: "24px", alignItems: "flex-start" } : { bottom: "24px", right: "24px", alignItems: "flex-end" };
1113
+ const panelOrigin = position === "bottom-left" ? "bottom left" : "bottom right";
1114
+ const panelPosition = position === "bottom-left" ? { bottom: 0, left: 0 } : { bottom: 0, right: 0 };
1115
+ const styleId = "dheme-slider-styles";
1116
+ if (typeof document !== "undefined" && !document.getElementById(styleId)) {
1117
+ const style = document.createElement("style");
1118
+ style.id = styleId;
1119
+ style.textContent = `
1120
+ input[type=range].dheme-slider::-webkit-slider-thumb {
1121
+ -webkit-appearance: none;
1122
+ width: 14px; height: 14px;
1123
+ border-radius: 50%;
1124
+ background: hsl(var(--primary, 221.2 83.2% 53.3%));
1125
+ border: 2px solid white;
1126
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.2);
1127
+ cursor: pointer;
1128
+ }
1129
+ input[type=range].dheme-slider::-moz-range-thumb {
1130
+ width: 14px; height: 14px;
1131
+ border-radius: 50%;
1132
+ background: hsl(var(--primary, 221.2 83.2% 53.3%));
1133
+ border: 2px solid white;
1134
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.2);
1135
+ cursor: pointer;
1136
+ }
1137
+ `;
1138
+ document.head.appendChild(style);
1139
+ }
1140
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1141
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1142
+ "div",
1143
+ {
1144
+ onClick: () => setIsOpen(false),
1145
+ style: {
1146
+ position: "fixed",
1147
+ inset: 0,
1148
+ background: "rgba(0,0,0,0.2)",
1149
+ backdropFilter: "blur(1px)",
1150
+ zIndex: 40
1151
+ }
1152
+ }
1153
+ ),
1154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1155
+ "div",
1156
+ {
1157
+ className: cn(className),
1158
+ style: {
1159
+ position: "fixed",
1160
+ zIndex: 50,
1161
+ display: "flex",
1162
+ flexDirection: "column",
1163
+ gap: "16px",
1164
+ ...positionStyle
1165
+ },
1166
+ children: [
1167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1168
+ "div",
1169
+ {
1170
+ style: {
1171
+ position: "absolute",
1172
+ ...panelPosition,
1173
+ width: isOpen ? "340px" : "180px",
1174
+ height: isOpen ? "auto" : "56px",
1175
+ opacity: isOpen ? 1 : 0,
1176
+ transform: isOpen ? "scale(1) translateY(0)" : "scale(0.9) translateY(32px)",
1177
+ pointerEvents: isOpen ? "auto" : "none",
1178
+ transformOrigin: panelOrigin,
1179
+ transition: "all 500ms cubic-bezier(0.32, 0.72, 0, 1)",
1180
+ borderRadius: "12px",
1181
+ border: "1px solid hsl(var(--border))",
1182
+ background: "hsl(var(--card))",
1183
+ boxShadow: "0 25px 50px rgba(0,0,0,0.15)",
1184
+ overflow: "hidden"
1185
+ },
1186
+ children: [
1187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1188
+ "div",
1189
+ {
1190
+ style: {
1191
+ display: "flex",
1192
+ alignItems: "center",
1193
+ justifyContent: "space-between",
1194
+ padding: "14px 16px",
1195
+ borderBottom: "1px solid hsl(var(--border))",
1196
+ background: "hsl(var(--muted) / 0.3)"
1197
+ },
1198
+ children: [
1199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SparklesIcon, { className: "dheme-sparkles" }),
1201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "13px", fontWeight: 600, color: "hsl(var(--foreground))" }, children: labels.title })
1202
+ ] }),
1203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1204
+ "button",
1205
+ {
1206
+ onClick: () => setIsOpen(false),
1207
+ style: {
1208
+ background: "none",
1209
+ border: "none",
1210
+ cursor: "pointer",
1211
+ padding: "2px",
1212
+ color: "hsl(var(--muted-foreground))",
1213
+ display: "flex",
1214
+ alignItems: "center",
1215
+ justifyContent: "center",
1216
+ borderRadius: "4px"
1217
+ },
1218
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XIcon, {})
1219
+ }
1220
+ )
1221
+ ]
1222
+ }
1223
+ ),
1224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1225
+ "div",
1226
+ {
1227
+ style: {
1228
+ maxHeight: "calc(100vh - 200px)",
1229
+ overflowY: "auto",
1230
+ background: "hsl(var(--background))"
1231
+ },
1232
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "16px", display: "flex", flexDirection: "column", gap: "24px" }, children: [
1233
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1234
+ "p",
1235
+ {
1236
+ style: {
1237
+ fontSize: "11px",
1238
+ color: "hsl(var(--muted-foreground))",
1239
+ margin: 0,
1240
+ lineHeight: 1.5
1241
+ },
1242
+ children: labels.description
1243
+ }
1244
+ ),
1245
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
1246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionHeading, { children: labels.baseColors }),
1247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1248
+ ColorRow,
1249
+ {
1250
+ label: labels.primary,
1251
+ color: localPrimary,
1252
+ onColorChange: setLocalPrimary,
1253
+ onInputChange: (v) => {
1254
+ if (v.length === 6) setLocalPrimary(`#${v}`);
1255
+ }
1256
+ }
1257
+ ),
1258
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1259
+ ColorRow,
1260
+ {
1261
+ label: labels.secondary,
1262
+ badge: labels.optional,
1263
+ color: localSecondary,
1264
+ disabled: !isSecondaryEnabled,
1265
+ onColorChange: (c) => {
1266
+ if (isSecondaryEnabled) setLocalSecondary(c);
1267
+ },
1268
+ onInputChange: (v) => {
1269
+ if (isSecondaryEnabled && v.length === 6) setLocalSecondary(`#${v}`);
1270
+ },
1271
+ actionButton: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1272
+ "button",
1273
+ {
1274
+ onClick: isSecondaryEnabled ? handleDisableSecondary : handleEnableSecondary,
1275
+ style: {
1276
+ background: "none",
1277
+ border: "none",
1278
+ cursor: "pointer",
1279
+ padding: "2px",
1280
+ color: "hsl(var(--muted-foreground))",
1281
+ display: "flex",
1282
+ alignItems: "center",
1283
+ justifyContent: "center",
1284
+ borderRadius: "4px"
1285
+ },
1286
+ children: isSecondaryEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {})
1287
+ }
1288
+ )
1289
+ }
1290
+ )
1291
+ ] }),
1292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
1293
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionHeading, { children: labels.fineTuning }),
1294
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1295
+ SliderRow,
1296
+ {
1297
+ label: labels.saturation,
1298
+ value: localSaturation,
1299
+ onChange: setLocalSaturation,
1300
+ min: -100,
1301
+ max: 100,
1302
+ step: 1,
1303
+ display: (v) => `${v > 0 ? "+" : ""}${v}%`
1304
+ }
1305
+ ),
1306
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1307
+ SliderRow,
1308
+ {
1309
+ label: labels.lightness,
1310
+ value: localLightness,
1311
+ onChange: setLocalLightness,
1312
+ min: -100,
1313
+ max: 100,
1314
+ step: 1,
1315
+ display: (v) => `${v > 0 ? "+" : ""}${v}%`
1316
+ }
1317
+ ),
1318
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1319
+ SliderRow,
1320
+ {
1321
+ label: labels.borderRadius,
1322
+ value: localRadius,
1323
+ onChange: setLocalRadius,
1324
+ min: 0,
1325
+ max: 2,
1326
+ step: 0.1,
1327
+ display: (v) => `${v.toFixed(1)}rem`
1328
+ }
1329
+ )
1330
+ ] }),
1331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
1332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionHeading, { children: labels.advancedOptions }),
1333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1334
+ ToggleRow,
1335
+ {
1336
+ label: labels.colorfulCard,
1337
+ checked: localCardIsColored,
1338
+ onChange: (v) => handleToggle("cardIsColored", v)
1339
+ }
1340
+ ),
1341
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1342
+ ToggleRow,
1343
+ {
1344
+ label: labels.colorfulBackground,
1345
+ checked: localBackgroundIsColored,
1346
+ onChange: (v) => handleToggle("backgroundIsColored", v)
1347
+ }
1348
+ ),
1349
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1350
+ ToggleRow,
1351
+ {
1352
+ label: labels.colorfulBorder,
1353
+ checked: localBorderIsColored,
1354
+ onChange: (v) => handleToggle("borderIsColored", v)
1355
+ }
1356
+ )
1357
+ ] })
1358
+ ] })
1359
+ }
1360
+ ),
1361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1362
+ "div",
1363
+ {
1364
+ style: {
1365
+ padding: "10px 12px",
1366
+ borderTop: "1px solid hsl(var(--border))",
1367
+ background: "hsl(var(--muted) / 0.2)"
1368
+ },
1369
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1370
+ "button",
1371
+ {
1372
+ onClick: handleReset,
1373
+ style: {
1374
+ width: "100%",
1375
+ height: "32px",
1376
+ background: "none",
1377
+ border: "1px solid hsl(var(--border))",
1378
+ borderRadius: "6px",
1379
+ cursor: "pointer",
1380
+ display: "flex",
1381
+ alignItems: "center",
1382
+ justifyContent: "center",
1383
+ gap: "6px",
1384
+ fontSize: "12px",
1385
+ color: "hsl(var(--muted-foreground))",
1386
+ transition: "background 0.15s"
1387
+ },
1388
+ onMouseEnter: (e) => {
1389
+ e.currentTarget.style.background = "hsl(var(--muted))";
1390
+ },
1391
+ onMouseLeave: (e) => {
1392
+ e.currentTarget.style.background = "none";
1393
+ },
1394
+ children: [
1395
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RotateCcwIcon, {}),
1396
+ labels.reset
1397
+ ]
1398
+ }
1399
+ )
1400
+ }
1401
+ )
1402
+ ]
1403
+ }
1404
+ ),
1405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1406
+ "div",
1407
+ {
1408
+ onClick: () => setIsOpen(true),
1409
+ style: {
1410
+ width: isOpen ? "56px" : "180px",
1411
+ height: "56px",
1412
+ borderRadius: "99px",
1413
+ border: "1px solid hsl(var(--border))",
1414
+ background: "hsl(var(--card))",
1415
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
1416
+ cursor: isOpen ? "default" : "pointer",
1417
+ position: "relative",
1418
+ overflow: "hidden",
1419
+ opacity: isOpen ? 0 : 1,
1420
+ transform: isOpen ? "scale(0.5) translateY(16px)" : "scale(1) translateY(0)",
1421
+ pointerEvents: isOpen ? "none" : "auto",
1422
+ transition: "all 500ms cubic-bezier(0.32, 0.72, 0, 1)"
1423
+ },
1424
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1425
+ "div",
1426
+ {
1427
+ style: {
1428
+ position: "absolute",
1429
+ inset: 0,
1430
+ display: "flex",
1431
+ alignItems: "center",
1432
+ justifyContent: "space-between",
1433
+ padding: "0 8px 0 12px"
1434
+ },
1435
+ children: [
1436
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1437
+ "div",
1438
+ {
1439
+ style: {
1440
+ width: "32px",
1441
+ height: "32px",
1442
+ borderRadius: "50%",
1443
+ background: localPrimary,
1444
+ border: "1px solid hsl(var(--border))",
1445
+ boxShadow: "0 0 0 2px hsl(var(--background))",
1446
+ flexShrink: 0
1447
+ }
1448
+ }
1449
+ ),
1450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1451
+ "div",
1452
+ {
1453
+ style: {
1454
+ display: "flex",
1455
+ flexDirection: "column",
1456
+ gap: "2px",
1457
+ flex: 1,
1458
+ padding: "0 8px"
1459
+ },
1460
+ children: [
1461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1462
+ "span",
1463
+ {
1464
+ style: {
1465
+ fontSize: "10px",
1466
+ fontWeight: 700,
1467
+ letterSpacing: "0.08em",
1468
+ textTransform: "uppercase",
1469
+ color: "hsl(var(--foreground))"
1470
+ },
1471
+ children: labels.fabPrimaryLabel
1472
+ }
1473
+ ),
1474
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1475
+ "div",
1476
+ {
1477
+ style: {
1478
+ display: "flex",
1479
+ gap: "8px",
1480
+ fontSize: "10px",
1481
+ fontFamily: "monospace",
1482
+ color: "hsl(var(--muted-foreground))"
1483
+ },
1484
+ children: [
1485
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1486
+ "S:",
1487
+ localSaturation[0] > 0 ? "+" : "",
1488
+ localSaturation[0],
1489
+ "%"
1490
+ ] }),
1491
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1492
+ "L:",
1493
+ localLightness[0] > 0 ? "+" : "",
1494
+ localLightness[0],
1495
+ "%"
1496
+ ] })
1497
+ ]
1498
+ }
1499
+ )
1500
+ ]
1501
+ }
1502
+ ),
1503
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1504
+ "div",
1505
+ {
1506
+ style: {
1507
+ width: "32px",
1508
+ height: "32px",
1509
+ display: "flex",
1510
+ alignItems: "center",
1511
+ justifyContent: "center",
1512
+ borderRadius: "50%",
1513
+ background: "hsl(var(--muted) / 0.5)",
1514
+ flexShrink: 0
1515
+ },
1516
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronUpIcon, {})
1517
+ }
1518
+ )
1519
+ ]
1520
+ }
1521
+ )
1522
+ }
1523
+ )
1524
+ ]
1525
+ }
1526
+ )
1527
+ ] });
1528
+ }
1529
+
1530
+ // src/hooks/useTheme.ts
1531
+ var import_react8 = require("react");
1532
+ function useTheme() {
1533
+ const context = (0, import_react8.useContext)(ThemeDataContext);
423
1534
  if (!context) {
424
- throw new Error("useThemeActions must be used within a <DhemeProvider>");
1535
+ throw new Error("useTheme must be used within a <DhemeProvider>");
425
1536
  }
426
1537
  return context;
427
1538
  }
428
1539
 
429
1540
  // src/hooks/useGenerateTheme.ts
430
- var import_react7 = require("react");
1541
+ var import_react9 = require("react");
431
1542
  function useGenerateTheme() {
432
1543
  const { generateTheme: generate } = useThemeActions();
433
- const [isGenerating, setIsGenerating] = (0, import_react7.useState)(false);
434
- const [error, setError] = (0, import_react7.useState)(null);
435
- const generateTheme = (0, import_react7.useCallback)(
1544
+ const [isGenerating, setIsGenerating] = (0, import_react9.useState)(false);
1545
+ const [error, setError] = (0, import_react9.useState)(null);
1546
+ const generateTheme = (0, import_react9.useCallback)(
436
1547
  async (params) => {
437
1548
  setIsGenerating(true);
438
1549
  setError(null);
@@ -462,6 +1573,7 @@ function useDhemeClient() {
462
1573
  DhemeScript,
463
1574
  ThemeActionsContext,
464
1575
  ThemeDataContext,
1576
+ ThemeGenerator,
465
1577
  applyThemeCSSVariables,
466
1578
  buildCacheKey,
467
1579
  getBlockingScriptPayload,