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