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