@fakhrirafiki/theme-engine 0.4.2 → 0.4.4

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
@@ -3827,7 +3827,15 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3827
3827
  'destructive', 'destructive-foreground', 'border', 'input', 'ring',
3828
3828
  'chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5',
3829
3829
  'sidebar', 'sidebar-foreground', 'sidebar-primary', 'sidebar-primary-foreground',
3830
- 'sidebar-accent', 'sidebar-accent-foreground', 'sidebar-border', 'sidebar-ring'
3830
+ 'sidebar-accent', 'sidebar-accent-foreground', 'sidebar-border', 'sidebar-ring',
3831
+ // Semantic accent colors for status and feedback
3832
+ 'accent-info', 'accent-info-foreground',
3833
+ 'accent-success', 'accent-success-foreground',
3834
+ 'accent-warning', 'accent-warning-foreground',
3835
+ 'accent-danger', 'accent-danger-foreground',
3836
+ 'accent-brand', 'accent-brand-foreground',
3837
+ 'accent-feature', 'accent-feature-foreground',
3838
+ 'accent-highlight', 'accent-highlight-foreground'
3831
3839
  ],
3832
3840
  typography: ['font-sans', 'font-serif', 'font-mono'],
3833
3841
  layout: ['radius'],
@@ -3835,6 +3843,106 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3835
3843
  spacing: ['letter-spacing', 'spacing']
3836
3844
  };
3837
3845
 
3846
+ // Normalize hex/rgb/hsl() colors into "H S% L%" for hsl(var(--token)) usage.
3847
+ function normalizeColorValueToHslTriplet(value) {
3848
+ if (!value) return value;
3849
+ const trimmed = String(value).trim();
3850
+ if (!trimmed) return value;
3851
+ if (trimmed.startsWith('var(')) return trimmed;
3852
+
3853
+ // Already a triplet: "210 40% 98%"
3854
+ if (/^\\d+(?:\\.\\d+)?\\s+\\d+(?:\\.\\d+)?%\\s+\\d+(?:\\.\\d+)?%$/.test(trimmed)) {
3855
+ return trimmed;
3856
+ }
3857
+
3858
+ function clamp(n, min, max) {
3859
+ return Math.min(max, Math.max(min, n));
3860
+ }
3861
+
3862
+ function rgbToHsl(r, g, b) {
3863
+ r /= 255;
3864
+ g /= 255;
3865
+ b /= 255;
3866
+ const max = Math.max(r, g, b);
3867
+ const min = Math.min(r, g, b);
3868
+ const diff = max - min;
3869
+ let h = 0;
3870
+ let s = 0;
3871
+ const l = (max + min) / 2;
3872
+
3873
+ if (diff !== 0) {
3874
+ s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
3875
+ switch (max) {
3876
+ case r:
3877
+ h = (g - b) / diff + (g < b ? 6 : 0);
3878
+ break;
3879
+ case g:
3880
+ h = (b - r) / diff + 2;
3881
+ break;
3882
+ case b:
3883
+ h = (r - g) / diff + 4;
3884
+ break;
3885
+ }
3886
+ h /= 6;
3887
+ }
3888
+
3889
+ return {
3890
+ h: Math.round(h * 360),
3891
+ s: Math.round(s * 100),
3892
+ l: Math.round(l * 100),
3893
+ };
3894
+ }
3895
+
3896
+ function hexToRgb(hex) {
3897
+ let clean = hex.replace('#', '').trim();
3898
+ if (clean.length === 3) clean = clean.split('').map(function(c) { return c + c; }).join('');
3899
+ if (clean.length === 8) clean = clean.substring(0, 6); // ignore alpha
3900
+ if (clean.length !== 6) return null;
3901
+
3902
+ const r = parseInt(clean.substring(0, 2), 16);
3903
+ const g = parseInt(clean.substring(2, 4), 16);
3904
+ const b = parseInt(clean.substring(4, 6), 16);
3905
+ if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
3906
+ return { r: r, g: g, b: b };
3907
+ }
3908
+
3909
+ function toTriplet(hsl) {
3910
+ return String(clamp(hsl.h, 0, 360)) + ' ' + String(clamp(hsl.s, 0, 100)) + '% ' + String(clamp(hsl.l, 0, 100)) + '%';
3911
+ }
3912
+
3913
+ // hsl(...) or raw "H S% L%"
3914
+ if (/^hsl\\(/i.test(trimmed)) {
3915
+ const cleaned = trimmed.replace(/hsl\\(|\\)/gi, '').replace(/[,%]/g, ' ').trim();
3916
+ const parts = cleaned.split(/\\s+/).filter(Boolean);
3917
+ if (parts.length === 3) {
3918
+ const h = parseFloat(parts[0]) || 0;
3919
+ const s = parseFloat(parts[1]) || 0;
3920
+ const l = parseFloat(parts[2]) || 0;
3921
+ return toTriplet({ h: h, s: s, l: l });
3922
+ }
3923
+ }
3924
+
3925
+ // rgb(...)
3926
+ if (/^rgb\\(/i.test(trimmed)) {
3927
+ const cleaned = trimmed.replace(/rgb\\(|\\)/gi, '').trim();
3928
+ const parts = cleaned.split(',').map(function(p) { return p.trim(); });
3929
+ if (parts.length === 3) {
3930
+ const r = clamp(parseFloat(parts[0]) || 0, 0, 255);
3931
+ const g = clamp(parseFloat(parts[1]) || 0, 0, 255);
3932
+ const b = clamp(parseFloat(parts[2]) || 0, 0, 255);
3933
+ return toTriplet(rgbToHsl(r, g, b));
3934
+ }
3935
+ }
3936
+
3937
+ // hex
3938
+ if (trimmed[0] === '#') {
3939
+ const rgb = hexToRgb(trimmed);
3940
+ if (rgb) return toTriplet(rgbToHsl(rgb.r, rgb.g, rgb.b));
3941
+ }
3942
+
3943
+ return value;
3944
+ }
3945
+
3838
3946
  // Function to apply all preset properties - with proper clearing and defaults
3839
3947
  function applyPresetProperties(colors) {
3840
3948
  if (!colors) return;
@@ -3866,6 +3974,10 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3866
3974
 
3867
3975
  // Apply all properties with defaults for missing ones
3868
3976
  let appliedCount = 0;
3977
+ const colorProps = {};
3978
+ CSS_CATEGORIES.colors.forEach(function(prop) { colorProps[prop] = true; });
3979
+ colorProps['shadow-color'] = true;
3980
+
3869
3981
  allProperties.forEach(function(prop) {
3870
3982
  let value = colors[prop];
3871
3983
 
@@ -3875,6 +3987,10 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3875
3987
  }
3876
3988
 
3877
3989
  if (value) {
3990
+ if (colorProps[prop]) {
3991
+ value = normalizeColorValueToHslTriplet(value);
3992
+ }
3993
+
3878
3994
  const cssVar = '--' + prop;
3879
3995
  // Apply directly like TweakCN does - no conversion, no !important
3880
3996
  root.style.setProperty(cssVar, value);
@@ -3936,6 +4052,138 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3936
4052
  return /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: scriptContent }, suppressHydrationWarning: true });
3937
4053
  }
3938
4054
 
4055
+ // src/utils/colors.ts
4056
+ function parseHSL(hslString) {
4057
+ try {
4058
+ const cleaned = hslString.replace(/hsl\(|\)/g, "").replace(/[,%]/g, " ").trim();
4059
+ const parts = cleaned.split(/\s+/).filter(Boolean);
4060
+ if (parts.length !== 3) return null;
4061
+ const h = parseFloat(parts[0]) || 0;
4062
+ const s = parseFloat(parts[1]) || 0;
4063
+ const l = parseFloat(parts[2]) || 0;
4064
+ return {
4065
+ h: Math.max(0, Math.min(360, h)),
4066
+ s: Math.max(0, Math.min(100, s)),
4067
+ l: Math.max(0, Math.min(100, l))
4068
+ };
4069
+ } catch {
4070
+ return null;
4071
+ }
4072
+ }
4073
+ function parseHex(hexString) {
4074
+ try {
4075
+ let hex = hexString.replace("#", "");
4076
+ if (hex.length === 3) {
4077
+ hex = hex.split("").map((char) => char + char).join("");
4078
+ }
4079
+ if (hex.length !== 6) return null;
4080
+ const r = parseInt(hex.substring(0, 2), 16);
4081
+ const s = parseInt(hex.substring(2, 4), 16);
4082
+ const l = parseInt(hex.substring(4, 6), 16);
4083
+ if (isNaN(r) || isNaN(s) || isNaN(l)) return null;
4084
+ return { r, g: s, b: l };
4085
+ } catch {
4086
+ return null;
4087
+ }
4088
+ }
4089
+ function hslToRgb(hsl) {
4090
+ const h = hsl.h / 360;
4091
+ const s = hsl.s / 100;
4092
+ const l = hsl.l / 100;
4093
+ if (s === 0) {
4094
+ const gray = Math.round(l * 255);
4095
+ return { r: gray, g: gray, b: gray };
4096
+ }
4097
+ const hue2rgb = (p2, q2, t) => {
4098
+ if (t < 0) t += 1;
4099
+ if (t > 1) t -= 1;
4100
+ if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
4101
+ if (t < 1 / 2) return q2;
4102
+ if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
4103
+ return p2;
4104
+ };
4105
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
4106
+ const p = 2 * l - q;
4107
+ const r = Math.round(hue2rgb(p, q, h + 1 / 3) * 255);
4108
+ const g = Math.round(hue2rgb(p, q, h) * 255);
4109
+ const b = Math.round(hue2rgb(p, q, h - 1 / 3) * 255);
4110
+ return { r, g, b };
4111
+ }
4112
+ function rgbToHsl(rgb) {
4113
+ const r = rgb.r / 255;
4114
+ const g = rgb.g / 255;
4115
+ const b = rgb.b / 255;
4116
+ const max = Math.max(r, g, b);
4117
+ const min = Math.min(r, g, b);
4118
+ const diff = max - min;
4119
+ let h = 0;
4120
+ let s = 0;
4121
+ const l = (max + min) / 2;
4122
+ if (diff !== 0) {
4123
+ s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
4124
+ switch (max) {
4125
+ case r:
4126
+ h = (g - b) / diff + (g < b ? 6 : 0);
4127
+ break;
4128
+ case g:
4129
+ h = (b - r) / diff + 2;
4130
+ break;
4131
+ case b:
4132
+ h = (r - g) / diff + 4;
4133
+ break;
4134
+ }
4135
+ h /= 6;
4136
+ }
4137
+ return {
4138
+ h: Math.round(h * 360),
4139
+ s: Math.round(s * 100),
4140
+ l: Math.round(l * 100)
4141
+ };
4142
+ }
4143
+ function formatHSL(hsl, includeHslWrapper = true) {
4144
+ const values = `${hsl.h} ${hsl.s}% ${hsl.l}%`;
4145
+ return includeHslWrapper ? `hsl(${values})` : values;
4146
+ }
4147
+ function formatRGB(rgb) {
4148
+ return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
4149
+ }
4150
+ function formatHex(rgb) {
4151
+ const toHex = (n) => {
4152
+ const hex = Math.round(Math.max(0, Math.min(255, n))).toString(16);
4153
+ return hex.length === 1 ? "0" + hex : hex;
4154
+ };
4155
+ return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;
4156
+ }
4157
+ function formatColor(colorInput, outputFormat = "hsl", includeFunctionWrapper = true) {
4158
+ let hsl = parseHSL(colorInput);
4159
+ if (!hsl) {
4160
+ const rgb = parseHex(colorInput);
4161
+ if (rgb) {
4162
+ hsl = rgbToHsl(rgb);
4163
+ }
4164
+ }
4165
+ if (!hsl) {
4166
+ return colorInput;
4167
+ }
4168
+ switch (outputFormat) {
4169
+ case "hsl":
4170
+ return formatHSL(hsl, includeFunctionWrapper);
4171
+ case "rgb":
4172
+ return formatRGB(hslToRgb(hsl));
4173
+ case "hex":
4174
+ return formatHex(hslToRgb(hsl));
4175
+ default:
4176
+ return colorInput;
4177
+ }
4178
+ }
4179
+ function withAlpha(colorInput, alpha) {
4180
+ const hsl = parseHSL(colorInput);
4181
+ if (!hsl) return colorInput;
4182
+ const rgb = hslToRgb(hsl);
4183
+ const clampedAlpha = Math.max(0, Math.min(1, alpha));
4184
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${clampedAlpha})`;
4185
+ }
4186
+
3939
4187
  // src/providers/UnifiedThemeProvider.tsx
3940
4188
  import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
3941
4189
  var UnifiedThemeContext = createContext(void 0);
@@ -3958,6 +4206,14 @@ function setStoredMode(mode, storageKey) {
3958
4206
  } catch {
3959
4207
  }
3960
4208
  }
4209
+ function normalizeColorValueToHslTriplet(value) {
4210
+ const trimmed = value.trim();
4211
+ const parsedHsl = parseHSL(trimmed);
4212
+ if (parsedHsl) return formatHSL(parsedHsl, false);
4213
+ const parsedRgb = parseHex(trimmed);
4214
+ if (parsedRgb) return formatHSL(rgbToHsl(parsedRgb), false);
4215
+ return value;
4216
+ }
3961
4217
  function ThemeProvider({
3962
4218
  children,
3963
4219
  defaultMode = "system",
@@ -4133,6 +4389,9 @@ function ThemeProvider({
4133
4389
  value = defaultValues[prop];
4134
4390
  }
4135
4391
  if (value) {
4392
+ if (CSS_PROPERTY_CATEGORIES.colors.includes(prop) || prop === "shadow-color") {
4393
+ value = normalizeColorValueToHslTriplet(String(value));
4394
+ }
4136
4395
  const cssVar = `--${prop}`;
4137
4396
  root.style.setProperty(cssVar, value);
4138
4397
  appliedCount++;
@@ -4418,140 +4677,6 @@ ThemeToggle.displayName = "ThemeToggle";
4418
4677
  // src/components/ThemePresetButtons.tsx
4419
4678
  import { useEffect as useEffect2, useState as useState2, useCallback as useCallback2, useMemo as useMemo3 } from "react";
4420
4679
  import { clsx as clsx2 } from "clsx";
4421
-
4422
- // src/utils/colors.ts
4423
- function parseHSL(hslString) {
4424
- try {
4425
- const cleaned = hslString.replace(/hsl\(|\)/g, "").replace(/[,%]/g, " ").trim();
4426
- const parts = cleaned.split(/\s+/).filter(Boolean);
4427
- if (parts.length !== 3) return null;
4428
- const h = parseFloat(parts[0]) || 0;
4429
- const s = parseFloat(parts[1]) || 0;
4430
- const l = parseFloat(parts[2]) || 0;
4431
- return {
4432
- h: Math.max(0, Math.min(360, h)),
4433
- s: Math.max(0, Math.min(100, s)),
4434
- l: Math.max(0, Math.min(100, l))
4435
- };
4436
- } catch {
4437
- return null;
4438
- }
4439
- }
4440
- function parseHex(hexString) {
4441
- try {
4442
- let hex = hexString.replace("#", "");
4443
- if (hex.length === 3) {
4444
- hex = hex.split("").map((char) => char + char).join("");
4445
- }
4446
- if (hex.length !== 6) return null;
4447
- const r = parseInt(hex.substring(0, 2), 16);
4448
- const s = parseInt(hex.substring(2, 4), 16);
4449
- const l = parseInt(hex.substring(4, 6), 16);
4450
- if (isNaN(r) || isNaN(s) || isNaN(l)) return null;
4451
- return { r, g: s, b: l };
4452
- } catch {
4453
- return null;
4454
- }
4455
- }
4456
- function hslToRgb(hsl) {
4457
- const h = hsl.h / 360;
4458
- const s = hsl.s / 100;
4459
- const l = hsl.l / 100;
4460
- if (s === 0) {
4461
- const gray = Math.round(l * 255);
4462
- return { r: gray, g: gray, b: gray };
4463
- }
4464
- const hue2rgb = (p2, q2, t) => {
4465
- if (t < 0) t += 1;
4466
- if (t > 1) t -= 1;
4467
- if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
4468
- if (t < 1 / 2) return q2;
4469
- if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
4470
- return p2;
4471
- };
4472
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
4473
- const p = 2 * l - q;
4474
- const r = Math.round(hue2rgb(p, q, h + 1 / 3) * 255);
4475
- const g = Math.round(hue2rgb(p, q, h) * 255);
4476
- const b = Math.round(hue2rgb(p, q, h - 1 / 3) * 255);
4477
- return { r, g, b };
4478
- }
4479
- function rgbToHsl(rgb) {
4480
- const r = rgb.r / 255;
4481
- const g = rgb.g / 255;
4482
- const b = rgb.b / 255;
4483
- const max = Math.max(r, g, b);
4484
- const min = Math.min(r, g, b);
4485
- const diff = max - min;
4486
- let h = 0;
4487
- let s = 0;
4488
- const l = (max + min) / 2;
4489
- if (diff !== 0) {
4490
- s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
4491
- switch (max) {
4492
- case r:
4493
- h = (g - b) / diff + (g < b ? 6 : 0);
4494
- break;
4495
- case g:
4496
- h = (b - r) / diff + 2;
4497
- break;
4498
- case b:
4499
- h = (r - g) / diff + 4;
4500
- break;
4501
- }
4502
- h /= 6;
4503
- }
4504
- return {
4505
- h: Math.round(h * 360),
4506
- s: Math.round(s * 100),
4507
- l: Math.round(l * 100)
4508
- };
4509
- }
4510
- function formatHSL(hsl, includeHslWrapper = true) {
4511
- const values = `${hsl.h} ${hsl.s}% ${hsl.l}%`;
4512
- return includeHslWrapper ? `hsl(${values})` : values;
4513
- }
4514
- function formatRGB(rgb) {
4515
- return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
4516
- }
4517
- function formatHex(rgb) {
4518
- const toHex = (n) => {
4519
- const hex = Math.round(Math.max(0, Math.min(255, n))).toString(16);
4520
- return hex.length === 1 ? "0" + hex : hex;
4521
- };
4522
- return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;
4523
- }
4524
- function formatColor(colorInput, outputFormat = "hsl", includeFunctionWrapper = true) {
4525
- let hsl = parseHSL(colorInput);
4526
- if (!hsl) {
4527
- const rgb = parseHex(colorInput);
4528
- if (rgb) {
4529
- hsl = rgbToHsl(rgb);
4530
- }
4531
- }
4532
- if (!hsl) {
4533
- return colorInput;
4534
- }
4535
- switch (outputFormat) {
4536
- case "hsl":
4537
- return formatHSL(hsl, includeFunctionWrapper);
4538
- case "rgb":
4539
- return formatRGB(hslToRgb(hsl));
4540
- case "hex":
4541
- return formatHex(hslToRgb(hsl));
4542
- default:
4543
- return colorInput;
4544
- }
4545
- }
4546
- function withAlpha(colorInput, alpha) {
4547
- const hsl = parseHSL(colorInput);
4548
- if (!hsl) return colorInput;
4549
- const rgb = hslToRgb(hsl);
4550
- const clampedAlpha = Math.max(0, Math.min(1, alpha));
4551
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${clampedAlpha})`;
4552
- }
4553
-
4554
- // src/components/ThemePresetButtons.tsx
4555
4680
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
4556
4681
  var DEFAULT_ANIMATION = {
4557
4682
  enabled: true,
@@ -4574,6 +4699,17 @@ var DEFAULT_LAYOUT = {
4574
4699
  colorBoxCount: 3,
4575
4700
  enableMask: true
4576
4701
  };
4702
+ function getPresetButtonWidthPx(label, layout) {
4703
+ const approxCharWidthPx = 7.25;
4704
+ const maxWidthPx = 360;
4705
+ const dotSizePx = 12;
4706
+ const dotGapPx = 4;
4707
+ const dotsWidthPx = layout.showColorBoxes ? layout.colorBoxCount * dotSizePx + Math.max(0, layout.colorBoxCount - 1) * dotGapPx : 0;
4708
+ const contentPaddingPx = 28;
4709
+ const estimatedTextWidthPx = Math.ceil(label.length * approxCharWidthPx);
4710
+ const estimatedWidthPx = contentPaddingPx + dotsWidthPx + estimatedTextWidthPx;
4711
+ return Math.min(maxWidthPx, Math.max(layout.buttonWidth, estimatedWidthPx));
4712
+ }
4577
4713
  var ColorBox = ({ color, className }) => {
4578
4714
  return /* @__PURE__ */ jsx4(
4579
4715
  "div",
@@ -4593,6 +4729,8 @@ var PresetButton = ({
4593
4729
  renderColorBox
4594
4730
  }) => {
4595
4731
  const colors = preset.colors[mode];
4732
+ const label = preset.name.replace(/-/g, " ");
4733
+ const buttonWidth = Math.max(layout.buttonWidth, Number(preset.metadata?.buttonWidth ?? 0) || layout.buttonWidth);
4596
4734
  if (renderPreset) {
4597
4735
  return /* @__PURE__ */ jsx4(
4598
4736
  "div",
@@ -4604,23 +4742,23 @@ var PresetButton = ({
4604
4742
  }
4605
4743
  );
4606
4744
  }
4607
- return /* @__PURE__ */ jsx4("div", { className: "flex-shrink-0", style: { minWidth: layout.buttonWidth }, children: /* @__PURE__ */ jsx4(
4608
- "div",
4745
+ return /* @__PURE__ */ jsx4("div", { className: "flex-shrink-0", style: { width: buttonWidth }, children: /* @__PURE__ */ jsxs3(
4746
+ "button",
4609
4747
  {
4748
+ type: "button",
4610
4749
  className: clsx2(
4611
4750
  "theme-preset-button",
4612
- "cursor-pointer bg-card hover:bg-card/80 border border-border rounded-lg",
4613
- "flex w-full h-full items-center justify-center relative transition-all duration-200",
4614
- "hover:shadow-lg hover:border-primary/20 px-4 py-3",
4615
- isSelected ? "ring-2 ring-primary/50 shadow-md bg-primary/5" : ""
4751
+ isSelected && "theme-preset-button--selected"
4616
4752
  ),
4617
4753
  onClick,
4618
- children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2.5 text-center", children: [
4619
- layout.showColorBoxes && /* @__PURE__ */ jsx4("div", { className: "flex gap-1", children: [colors.primary, colors.secondary, colors.accent].slice(0, layout.colorBoxCount).map(
4754
+ "aria-pressed": isSelected,
4755
+ title: label,
4756
+ children: [
4757
+ layout.showColorBoxes && /* @__PURE__ */ jsx4("div", { className: "theme-preset-button__colors", children: [colors.primary, colors.secondary, colors.accent].slice(0, layout.colorBoxCount).map(
4620
4758
  (color, index) => renderColorBox ? /* @__PURE__ */ jsx4("span", { children: renderColorBox(color, index) }, index) : /* @__PURE__ */ jsx4(ColorBox, { color }, index)
4621
4759
  ) }),
4622
- /* @__PURE__ */ jsx4("span", { className: "capitalize px-1 leading-tight text-sm font-medium text-foreground", children: preset.name.replace(/-/g, " ") })
4623
- ] })
4760
+ /* @__PURE__ */ jsx4("div", { className: "theme-preset-button__text", children: /* @__PURE__ */ jsx4("span", { className: "theme-preset-button__label", children: label }) })
4761
+ ]
4624
4762
  }
4625
4763
  ) });
4626
4764
  };
@@ -4636,7 +4774,7 @@ var AnimatedRow = ({
4636
4774
  }) => {
4637
4775
  if (presets.length === 0) return null;
4638
4776
  const duplicatedPresets = Array(animation.duplicationFactor).fill(presets).flat();
4639
- const totalWidth = presets.length * (layout.buttonWidth + layout.buttonGap);
4777
+ const totalWidth = presets.reduce((sum, preset) => sum + (Number(preset.metadata?.buttonWidth) || layout.buttonWidth), 0) + presets.length * layout.buttonGap;
4640
4778
  const effectiveScrollSpeed = Math.max(0.1, animation.scrollSpeed || 1);
4641
4779
  const animationDuration = presets.length * animation.duration / effectiveScrollSpeed;
4642
4780
  return /* @__PURE__ */ jsx4(
@@ -4704,41 +4842,47 @@ var ThemePresetButtons = ({
4704
4842
  try {
4705
4843
  setLoading(true);
4706
4844
  setError(null);
4707
- let allPresets = [];
4845
+ const builtInPresetList = [];
4846
+ const customPresetList = [];
4708
4847
  if (showBuiltIn) {
4709
- const builtInPresetList = Object.entries(builtInPresets).map(([id, preset]) => ({
4710
- id,
4711
- name: preset.label,
4712
- colors: {
4713
- light: preset.styles.light,
4714
- dark: preset.styles.dark
4715
- },
4716
- metadata: {
4717
- category: preset.label.toLowerCase().includes("minimal") ? "minimal" : preset.label.toLowerCase().includes("violet") || preset.label.toLowerCase().includes("purple") ? "vibrant" : "modern",
4718
- tags: [preset.label.toLowerCase().replace(/\s+/g, "-")],
4719
- createdAt: preset.createdAt,
4720
- provider: "built-in"
4721
- }
4722
- }));
4723
- allPresets.push(...builtInPresetList);
4848
+ builtInPresetList.push(
4849
+ ...Object.entries(builtInPresets).map(([id, preset]) => ({
4850
+ id,
4851
+ name: preset.label,
4852
+ colors: {
4853
+ light: preset.styles.light,
4854
+ dark: preset.styles.dark
4855
+ },
4856
+ metadata: {
4857
+ buttonWidth: getPresetButtonWidthPx(preset.label, layout),
4858
+ category: preset.label.toLowerCase().includes("minimal") ? "minimal" : preset.label.toLowerCase().includes("violet") || preset.label.toLowerCase().includes("purple") ? "vibrant" : "modern",
4859
+ tags: [preset.label.toLowerCase().replace(/\s+/g, "-")],
4860
+ createdAt: preset.createdAt,
4861
+ provider: "built-in"
4862
+ }
4863
+ }))
4864
+ );
4724
4865
  }
4725
4866
  if (showCustom) {
4726
- const customPresetList = Object.entries(customPresets).map(([id, preset]) => ({
4727
- id,
4728
- name: preset.label,
4729
- colors: {
4730
- light: preset.styles.light,
4731
- dark: preset.styles.dark
4732
- },
4733
- metadata: {
4734
- category: preset.label.toLowerCase().includes("minimal") ? "minimal" : preset.label.toLowerCase().includes("violet") || preset.label.toLowerCase().includes("purple") ? "vibrant" : "modern",
4735
- tags: [preset.label.toLowerCase().replace(/\s+/g, "-")],
4736
- createdAt: preset.createdAt,
4737
- provider: "custom"
4738
- }
4739
- }));
4740
- allPresets.push(...customPresetList);
4867
+ customPresetList.push(
4868
+ ...Object.entries(customPresets).map(([id, preset]) => ({
4869
+ id,
4870
+ name: preset.label,
4871
+ colors: {
4872
+ light: preset.styles.light,
4873
+ dark: preset.styles.dark
4874
+ },
4875
+ metadata: {
4876
+ buttonWidth: getPresetButtonWidthPx(preset.label, layout),
4877
+ category: preset.label.toLowerCase().includes("minimal") ? "minimal" : preset.label.toLowerCase().includes("violet") || preset.label.toLowerCase().includes("purple") ? "vibrant" : "modern",
4878
+ tags: [preset.label.toLowerCase().replace(/\s+/g, "-")],
4879
+ createdAt: preset.createdAt,
4880
+ provider: "custom"
4881
+ }
4882
+ }))
4883
+ );
4741
4884
  }
4885
+ let allPresets = [...customPresetList, ...builtInPresetList];
4742
4886
  if (categories && categories.length > 0) {
4743
4887
  allPresets = allPresets.filter(
4744
4888
  (preset) => categories.includes(preset.metadata?.category || "unknown")
@@ -4754,7 +4898,7 @@ var ThemePresetButtons = ({
4754
4898
  } finally {
4755
4899
  setLoading(false);
4756
4900
  }
4757
- }, [availablePresets, builtInPresets, customPresets, categories, maxPresets, showBuiltIn, showCustom]);
4901
+ }, [availablePresets, builtInPresets, customPresets, categories, maxPresets, showBuiltIn, showCustom, layout]);
4758
4902
  useEffect2(() => {
4759
4903
  loadPresets();
4760
4904
  }, [loadPresets]);