@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.js CHANGED
@@ -3870,7 +3870,15 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3870
3870
  'destructive', 'destructive-foreground', 'border', 'input', 'ring',
3871
3871
  'chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5',
3872
3872
  'sidebar', 'sidebar-foreground', 'sidebar-primary', 'sidebar-primary-foreground',
3873
- 'sidebar-accent', 'sidebar-accent-foreground', 'sidebar-border', 'sidebar-ring'
3873
+ 'sidebar-accent', 'sidebar-accent-foreground', 'sidebar-border', 'sidebar-ring',
3874
+ // Semantic accent colors for status and feedback
3875
+ 'accent-info', 'accent-info-foreground',
3876
+ 'accent-success', 'accent-success-foreground',
3877
+ 'accent-warning', 'accent-warning-foreground',
3878
+ 'accent-danger', 'accent-danger-foreground',
3879
+ 'accent-brand', 'accent-brand-foreground',
3880
+ 'accent-feature', 'accent-feature-foreground',
3881
+ 'accent-highlight', 'accent-highlight-foreground'
3874
3882
  ],
3875
3883
  typography: ['font-sans', 'font-serif', 'font-mono'],
3876
3884
  layout: ['radius'],
@@ -3878,6 +3886,106 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3878
3886
  spacing: ['letter-spacing', 'spacing']
3879
3887
  };
3880
3888
 
3889
+ // Normalize hex/rgb/hsl() colors into "H S% L%" for hsl(var(--token)) usage.
3890
+ function normalizeColorValueToHslTriplet(value) {
3891
+ if (!value) return value;
3892
+ const trimmed = String(value).trim();
3893
+ if (!trimmed) return value;
3894
+ if (trimmed.startsWith('var(')) return trimmed;
3895
+
3896
+ // Already a triplet: "210 40% 98%"
3897
+ if (/^\\d+(?:\\.\\d+)?\\s+\\d+(?:\\.\\d+)?%\\s+\\d+(?:\\.\\d+)?%$/.test(trimmed)) {
3898
+ return trimmed;
3899
+ }
3900
+
3901
+ function clamp(n, min, max) {
3902
+ return Math.min(max, Math.max(min, n));
3903
+ }
3904
+
3905
+ function rgbToHsl(r, g, b) {
3906
+ r /= 255;
3907
+ g /= 255;
3908
+ b /= 255;
3909
+ const max = Math.max(r, g, b);
3910
+ const min = Math.min(r, g, b);
3911
+ const diff = max - min;
3912
+ let h = 0;
3913
+ let s = 0;
3914
+ const l = (max + min) / 2;
3915
+
3916
+ if (diff !== 0) {
3917
+ s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
3918
+ switch (max) {
3919
+ case r:
3920
+ h = (g - b) / diff + (g < b ? 6 : 0);
3921
+ break;
3922
+ case g:
3923
+ h = (b - r) / diff + 2;
3924
+ break;
3925
+ case b:
3926
+ h = (r - g) / diff + 4;
3927
+ break;
3928
+ }
3929
+ h /= 6;
3930
+ }
3931
+
3932
+ return {
3933
+ h: Math.round(h * 360),
3934
+ s: Math.round(s * 100),
3935
+ l: Math.round(l * 100),
3936
+ };
3937
+ }
3938
+
3939
+ function hexToRgb(hex) {
3940
+ let clean = hex.replace('#', '').trim();
3941
+ if (clean.length === 3) clean = clean.split('').map(function(c) { return c + c; }).join('');
3942
+ if (clean.length === 8) clean = clean.substring(0, 6); // ignore alpha
3943
+ if (clean.length !== 6) return null;
3944
+
3945
+ const r = parseInt(clean.substring(0, 2), 16);
3946
+ const g = parseInt(clean.substring(2, 4), 16);
3947
+ const b = parseInt(clean.substring(4, 6), 16);
3948
+ if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
3949
+ return { r: r, g: g, b: b };
3950
+ }
3951
+
3952
+ function toTriplet(hsl) {
3953
+ return String(clamp(hsl.h, 0, 360)) + ' ' + String(clamp(hsl.s, 0, 100)) + '% ' + String(clamp(hsl.l, 0, 100)) + '%';
3954
+ }
3955
+
3956
+ // hsl(...) or raw "H S% L%"
3957
+ if (/^hsl\\(/i.test(trimmed)) {
3958
+ const cleaned = trimmed.replace(/hsl\\(|\\)/gi, '').replace(/[,%]/g, ' ').trim();
3959
+ const parts = cleaned.split(/\\s+/).filter(Boolean);
3960
+ if (parts.length === 3) {
3961
+ const h = parseFloat(parts[0]) || 0;
3962
+ const s = parseFloat(parts[1]) || 0;
3963
+ const l = parseFloat(parts[2]) || 0;
3964
+ return toTriplet({ h: h, s: s, l: l });
3965
+ }
3966
+ }
3967
+
3968
+ // rgb(...)
3969
+ if (/^rgb\\(/i.test(trimmed)) {
3970
+ const cleaned = trimmed.replace(/rgb\\(|\\)/gi, '').trim();
3971
+ const parts = cleaned.split(',').map(function(p) { return p.trim(); });
3972
+ if (parts.length === 3) {
3973
+ const r = clamp(parseFloat(parts[0]) || 0, 0, 255);
3974
+ const g = clamp(parseFloat(parts[1]) || 0, 0, 255);
3975
+ const b = clamp(parseFloat(parts[2]) || 0, 0, 255);
3976
+ return toTriplet(rgbToHsl(r, g, b));
3977
+ }
3978
+ }
3979
+
3980
+ // hex
3981
+ if (trimmed[0] === '#') {
3982
+ const rgb = hexToRgb(trimmed);
3983
+ if (rgb) return toTriplet(rgbToHsl(rgb.r, rgb.g, rgb.b));
3984
+ }
3985
+
3986
+ return value;
3987
+ }
3988
+
3881
3989
  // Function to apply all preset properties - with proper clearing and defaults
3882
3990
  function applyPresetProperties(colors) {
3883
3991
  if (!colors) return;
@@ -3909,6 +4017,10 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3909
4017
 
3910
4018
  // Apply all properties with defaults for missing ones
3911
4019
  let appliedCount = 0;
4020
+ const colorProps = {};
4021
+ CSS_CATEGORIES.colors.forEach(function(prop) { colorProps[prop] = true; });
4022
+ colorProps['shadow-color'] = true;
4023
+
3912
4024
  allProperties.forEach(function(prop) {
3913
4025
  let value = colors[prop];
3914
4026
 
@@ -3918,6 +4030,10 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3918
4030
  }
3919
4031
 
3920
4032
  if (value) {
4033
+ if (colorProps[prop]) {
4034
+ value = normalizeColorValueToHslTriplet(value);
4035
+ }
4036
+
3921
4037
  const cssVar = '--' + prop;
3922
4038
  // Apply directly like TweakCN does - no conversion, no !important
3923
4039
  root.style.setProperty(cssVar, value);
@@ -3979,6 +4095,138 @@ function ThemeScript({ presetStorageKey = "theme-preset", defaultPreset }) {
3979
4095
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("script", { dangerouslySetInnerHTML: { __html: scriptContent }, suppressHydrationWarning: true });
3980
4096
  }
3981
4097
 
4098
+ // src/utils/colors.ts
4099
+ function parseHSL(hslString) {
4100
+ try {
4101
+ const cleaned = hslString.replace(/hsl\(|\)/g, "").replace(/[,%]/g, " ").trim();
4102
+ const parts = cleaned.split(/\s+/).filter(Boolean);
4103
+ if (parts.length !== 3) return null;
4104
+ const h = parseFloat(parts[0]) || 0;
4105
+ const s = parseFloat(parts[1]) || 0;
4106
+ const l = parseFloat(parts[2]) || 0;
4107
+ return {
4108
+ h: Math.max(0, Math.min(360, h)),
4109
+ s: Math.max(0, Math.min(100, s)),
4110
+ l: Math.max(0, Math.min(100, l))
4111
+ };
4112
+ } catch {
4113
+ return null;
4114
+ }
4115
+ }
4116
+ function parseHex(hexString) {
4117
+ try {
4118
+ let hex = hexString.replace("#", "");
4119
+ if (hex.length === 3) {
4120
+ hex = hex.split("").map((char) => char + char).join("");
4121
+ }
4122
+ if (hex.length !== 6) return null;
4123
+ const r = parseInt(hex.substring(0, 2), 16);
4124
+ const s = parseInt(hex.substring(2, 4), 16);
4125
+ const l = parseInt(hex.substring(4, 6), 16);
4126
+ if (isNaN(r) || isNaN(s) || isNaN(l)) return null;
4127
+ return { r, g: s, b: l };
4128
+ } catch {
4129
+ return null;
4130
+ }
4131
+ }
4132
+ function hslToRgb(hsl) {
4133
+ const h = hsl.h / 360;
4134
+ const s = hsl.s / 100;
4135
+ const l = hsl.l / 100;
4136
+ if (s === 0) {
4137
+ const gray = Math.round(l * 255);
4138
+ return { r: gray, g: gray, b: gray };
4139
+ }
4140
+ const hue2rgb = (p2, q2, t) => {
4141
+ if (t < 0) t += 1;
4142
+ if (t > 1) t -= 1;
4143
+ if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
4144
+ if (t < 1 / 2) return q2;
4145
+ if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
4146
+ return p2;
4147
+ };
4148
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
4149
+ const p = 2 * l - q;
4150
+ const r = Math.round(hue2rgb(p, q, h + 1 / 3) * 255);
4151
+ const g = Math.round(hue2rgb(p, q, h) * 255);
4152
+ const b = Math.round(hue2rgb(p, q, h - 1 / 3) * 255);
4153
+ return { r, g, b };
4154
+ }
4155
+ function rgbToHsl(rgb) {
4156
+ const r = rgb.r / 255;
4157
+ const g = rgb.g / 255;
4158
+ const b = rgb.b / 255;
4159
+ const max = Math.max(r, g, b);
4160
+ const min = Math.min(r, g, b);
4161
+ const diff = max - min;
4162
+ let h = 0;
4163
+ let s = 0;
4164
+ const l = (max + min) / 2;
4165
+ if (diff !== 0) {
4166
+ s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
4167
+ switch (max) {
4168
+ case r:
4169
+ h = (g - b) / diff + (g < b ? 6 : 0);
4170
+ break;
4171
+ case g:
4172
+ h = (b - r) / diff + 2;
4173
+ break;
4174
+ case b:
4175
+ h = (r - g) / diff + 4;
4176
+ break;
4177
+ }
4178
+ h /= 6;
4179
+ }
4180
+ return {
4181
+ h: Math.round(h * 360),
4182
+ s: Math.round(s * 100),
4183
+ l: Math.round(l * 100)
4184
+ };
4185
+ }
4186
+ function formatHSL(hsl, includeHslWrapper = true) {
4187
+ const values = `${hsl.h} ${hsl.s}% ${hsl.l}%`;
4188
+ return includeHslWrapper ? `hsl(${values})` : values;
4189
+ }
4190
+ function formatRGB(rgb) {
4191
+ return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
4192
+ }
4193
+ function formatHex(rgb) {
4194
+ const toHex = (n) => {
4195
+ const hex = Math.round(Math.max(0, Math.min(255, n))).toString(16);
4196
+ return hex.length === 1 ? "0" + hex : hex;
4197
+ };
4198
+ return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;
4199
+ }
4200
+ function formatColor(colorInput, outputFormat = "hsl", includeFunctionWrapper = true) {
4201
+ let hsl = parseHSL(colorInput);
4202
+ if (!hsl) {
4203
+ const rgb = parseHex(colorInput);
4204
+ if (rgb) {
4205
+ hsl = rgbToHsl(rgb);
4206
+ }
4207
+ }
4208
+ if (!hsl) {
4209
+ return colorInput;
4210
+ }
4211
+ switch (outputFormat) {
4212
+ case "hsl":
4213
+ return formatHSL(hsl, includeFunctionWrapper);
4214
+ case "rgb":
4215
+ return formatRGB(hslToRgb(hsl));
4216
+ case "hex":
4217
+ return formatHex(hslToRgb(hsl));
4218
+ default:
4219
+ return colorInput;
4220
+ }
4221
+ }
4222
+ function withAlpha(colorInput, alpha) {
4223
+ const hsl = parseHSL(colorInput);
4224
+ if (!hsl) return colorInput;
4225
+ const rgb = hslToRgb(hsl);
4226
+ const clampedAlpha = Math.max(0, Math.min(1, alpha));
4227
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${clampedAlpha})`;
4228
+ }
4229
+
3982
4230
  // src/providers/UnifiedThemeProvider.tsx
3983
4231
  var import_jsx_runtime2 = require("react/jsx-runtime");
3984
4232
  var UnifiedThemeContext = (0, import_react2.createContext)(void 0);
@@ -4001,6 +4249,14 @@ function setStoredMode(mode, storageKey) {
4001
4249
  } catch {
4002
4250
  }
4003
4251
  }
4252
+ function normalizeColorValueToHslTriplet(value) {
4253
+ const trimmed = value.trim();
4254
+ const parsedHsl = parseHSL(trimmed);
4255
+ if (parsedHsl) return formatHSL(parsedHsl, false);
4256
+ const parsedRgb = parseHex(trimmed);
4257
+ if (parsedRgb) return formatHSL(rgbToHsl(parsedRgb), false);
4258
+ return value;
4259
+ }
4004
4260
  function ThemeProvider({
4005
4261
  children,
4006
4262
  defaultMode = "system",
@@ -4176,6 +4432,9 @@ function ThemeProvider({
4176
4432
  value = defaultValues[prop];
4177
4433
  }
4178
4434
  if (value) {
4435
+ if (CSS_PROPERTY_CATEGORIES.colors.includes(prop) || prop === "shadow-color") {
4436
+ value = normalizeColorValueToHslTriplet(String(value));
4437
+ }
4179
4438
  const cssVar = `--${prop}`;
4180
4439
  root.style.setProperty(cssVar, value);
4181
4440
  appliedCount++;
@@ -4461,140 +4720,6 @@ ThemeToggle.displayName = "ThemeToggle";
4461
4720
  // src/components/ThemePresetButtons.tsx
4462
4721
  var import_react4 = require("react");
4463
4722
  var import_clsx2 = require("clsx");
4464
-
4465
- // src/utils/colors.ts
4466
- function parseHSL(hslString) {
4467
- try {
4468
- const cleaned = hslString.replace(/hsl\(|\)/g, "").replace(/[,%]/g, " ").trim();
4469
- const parts = cleaned.split(/\s+/).filter(Boolean);
4470
- if (parts.length !== 3) return null;
4471
- const h = parseFloat(parts[0]) || 0;
4472
- const s = parseFloat(parts[1]) || 0;
4473
- const l = parseFloat(parts[2]) || 0;
4474
- return {
4475
- h: Math.max(0, Math.min(360, h)),
4476
- s: Math.max(0, Math.min(100, s)),
4477
- l: Math.max(0, Math.min(100, l))
4478
- };
4479
- } catch {
4480
- return null;
4481
- }
4482
- }
4483
- function parseHex(hexString) {
4484
- try {
4485
- let hex = hexString.replace("#", "");
4486
- if (hex.length === 3) {
4487
- hex = hex.split("").map((char) => char + char).join("");
4488
- }
4489
- if (hex.length !== 6) return null;
4490
- const r = parseInt(hex.substring(0, 2), 16);
4491
- const s = parseInt(hex.substring(2, 4), 16);
4492
- const l = parseInt(hex.substring(4, 6), 16);
4493
- if (isNaN(r) || isNaN(s) || isNaN(l)) return null;
4494
- return { r, g: s, b: l };
4495
- } catch {
4496
- return null;
4497
- }
4498
- }
4499
- function hslToRgb(hsl) {
4500
- const h = hsl.h / 360;
4501
- const s = hsl.s / 100;
4502
- const l = hsl.l / 100;
4503
- if (s === 0) {
4504
- const gray = Math.round(l * 255);
4505
- return { r: gray, g: gray, b: gray };
4506
- }
4507
- const hue2rgb = (p2, q2, t) => {
4508
- if (t < 0) t += 1;
4509
- if (t > 1) t -= 1;
4510
- if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
4511
- if (t < 1 / 2) return q2;
4512
- if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
4513
- return p2;
4514
- };
4515
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
4516
- const p = 2 * l - q;
4517
- const r = Math.round(hue2rgb(p, q, h + 1 / 3) * 255);
4518
- const g = Math.round(hue2rgb(p, q, h) * 255);
4519
- const b = Math.round(hue2rgb(p, q, h - 1 / 3) * 255);
4520
- return { r, g, b };
4521
- }
4522
- function rgbToHsl(rgb) {
4523
- const r = rgb.r / 255;
4524
- const g = rgb.g / 255;
4525
- const b = rgb.b / 255;
4526
- const max = Math.max(r, g, b);
4527
- const min = Math.min(r, g, b);
4528
- const diff = max - min;
4529
- let h = 0;
4530
- let s = 0;
4531
- const l = (max + min) / 2;
4532
- if (diff !== 0) {
4533
- s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
4534
- switch (max) {
4535
- case r:
4536
- h = (g - b) / diff + (g < b ? 6 : 0);
4537
- break;
4538
- case g:
4539
- h = (b - r) / diff + 2;
4540
- break;
4541
- case b:
4542
- h = (r - g) / diff + 4;
4543
- break;
4544
- }
4545
- h /= 6;
4546
- }
4547
- return {
4548
- h: Math.round(h * 360),
4549
- s: Math.round(s * 100),
4550
- l: Math.round(l * 100)
4551
- };
4552
- }
4553
- function formatHSL(hsl, includeHslWrapper = true) {
4554
- const values = `${hsl.h} ${hsl.s}% ${hsl.l}%`;
4555
- return includeHslWrapper ? `hsl(${values})` : values;
4556
- }
4557
- function formatRGB(rgb) {
4558
- return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
4559
- }
4560
- function formatHex(rgb) {
4561
- const toHex = (n) => {
4562
- const hex = Math.round(Math.max(0, Math.min(255, n))).toString(16);
4563
- return hex.length === 1 ? "0" + hex : hex;
4564
- };
4565
- return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;
4566
- }
4567
- function formatColor(colorInput, outputFormat = "hsl", includeFunctionWrapper = true) {
4568
- let hsl = parseHSL(colorInput);
4569
- if (!hsl) {
4570
- const rgb = parseHex(colorInput);
4571
- if (rgb) {
4572
- hsl = rgbToHsl(rgb);
4573
- }
4574
- }
4575
- if (!hsl) {
4576
- return colorInput;
4577
- }
4578
- switch (outputFormat) {
4579
- case "hsl":
4580
- return formatHSL(hsl, includeFunctionWrapper);
4581
- case "rgb":
4582
- return formatRGB(hslToRgb(hsl));
4583
- case "hex":
4584
- return formatHex(hslToRgb(hsl));
4585
- default:
4586
- return colorInput;
4587
- }
4588
- }
4589
- function withAlpha(colorInput, alpha) {
4590
- const hsl = parseHSL(colorInput);
4591
- if (!hsl) return colorInput;
4592
- const rgb = hslToRgb(hsl);
4593
- const clampedAlpha = Math.max(0, Math.min(1, alpha));
4594
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${clampedAlpha})`;
4595
- }
4596
-
4597
- // src/components/ThemePresetButtons.tsx
4598
4723
  var import_jsx_runtime4 = require("react/jsx-runtime");
4599
4724
  var DEFAULT_ANIMATION = {
4600
4725
  enabled: true,
@@ -4617,6 +4742,17 @@ var DEFAULT_LAYOUT = {
4617
4742
  colorBoxCount: 3,
4618
4743
  enableMask: true
4619
4744
  };
4745
+ function getPresetButtonWidthPx(label, layout) {
4746
+ const approxCharWidthPx = 7.25;
4747
+ const maxWidthPx = 360;
4748
+ const dotSizePx = 12;
4749
+ const dotGapPx = 4;
4750
+ const dotsWidthPx = layout.showColorBoxes ? layout.colorBoxCount * dotSizePx + Math.max(0, layout.colorBoxCount - 1) * dotGapPx : 0;
4751
+ const contentPaddingPx = 28;
4752
+ const estimatedTextWidthPx = Math.ceil(label.length * approxCharWidthPx);
4753
+ const estimatedWidthPx = contentPaddingPx + dotsWidthPx + estimatedTextWidthPx;
4754
+ return Math.min(maxWidthPx, Math.max(layout.buttonWidth, estimatedWidthPx));
4755
+ }
4620
4756
  var ColorBox = ({ color, className }) => {
4621
4757
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4622
4758
  "div",
@@ -4636,6 +4772,8 @@ var PresetButton = ({
4636
4772
  renderColorBox
4637
4773
  }) => {
4638
4774
  const colors = preset.colors[mode];
4775
+ const label = preset.name.replace(/-/g, " ");
4776
+ const buttonWidth = Math.max(layout.buttonWidth, Number(preset.metadata?.buttonWidth ?? 0) || layout.buttonWidth);
4639
4777
  if (renderPreset) {
4640
4778
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4641
4779
  "div",
@@ -4647,23 +4785,23 @@ var PresetButton = ({
4647
4785
  }
4648
4786
  );
4649
4787
  }
4650
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-shrink-0", style: { minWidth: layout.buttonWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4651
- "div",
4788
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-shrink-0", style: { width: buttonWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4789
+ "button",
4652
4790
  {
4791
+ type: "button",
4653
4792
  className: (0, import_clsx2.clsx)(
4654
4793
  "theme-preset-button",
4655
- "cursor-pointer bg-card hover:bg-card/80 border border-border rounded-lg",
4656
- "flex w-full h-full items-center justify-center relative transition-all duration-200",
4657
- "hover:shadow-lg hover:border-primary/20 px-4 py-3",
4658
- isSelected ? "ring-2 ring-primary/50 shadow-md bg-primary/5" : ""
4794
+ isSelected && "theme-preset-button--selected"
4659
4795
  ),
4660
4796
  onClick,
4661
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2.5 text-center", children: [
4662
- layout.showColorBoxes && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-1", children: [colors.primary, colors.secondary, colors.accent].slice(0, layout.colorBoxCount).map(
4797
+ "aria-pressed": isSelected,
4798
+ title: label,
4799
+ children: [
4800
+ layout.showColorBoxes && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "theme-preset-button__colors", children: [colors.primary, colors.secondary, colors.accent].slice(0, layout.colorBoxCount).map(
4663
4801
  (color, index) => renderColorBox ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: renderColorBox(color, index) }, index) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ColorBox, { color }, index)
4664
4802
  ) }),
4665
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "capitalize px-1 leading-tight text-sm font-medium text-foreground", children: preset.name.replace(/-/g, " ") })
4666
- ] })
4803
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "theme-preset-button__text", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "theme-preset-button__label", children: label }) })
4804
+ ]
4667
4805
  }
4668
4806
  ) });
4669
4807
  };
@@ -4679,7 +4817,7 @@ var AnimatedRow = ({
4679
4817
  }) => {
4680
4818
  if (presets.length === 0) return null;
4681
4819
  const duplicatedPresets = Array(animation.duplicationFactor).fill(presets).flat();
4682
- const totalWidth = presets.length * (layout.buttonWidth + layout.buttonGap);
4820
+ const totalWidth = presets.reduce((sum, preset) => sum + (Number(preset.metadata?.buttonWidth) || layout.buttonWidth), 0) + presets.length * layout.buttonGap;
4683
4821
  const effectiveScrollSpeed = Math.max(0.1, animation.scrollSpeed || 1);
4684
4822
  const animationDuration = presets.length * animation.duration / effectiveScrollSpeed;
4685
4823
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -4747,41 +4885,47 @@ var ThemePresetButtons = ({
4747
4885
  try {
4748
4886
  setLoading(true);
4749
4887
  setError(null);
4750
- let allPresets = [];
4888
+ const builtInPresetList = [];
4889
+ const customPresetList = [];
4751
4890
  if (showBuiltIn) {
4752
- const builtInPresetList = Object.entries(builtInPresets).map(([id, preset]) => ({
4753
- id,
4754
- name: preset.label,
4755
- colors: {
4756
- light: preset.styles.light,
4757
- dark: preset.styles.dark
4758
- },
4759
- metadata: {
4760
- category: preset.label.toLowerCase().includes("minimal") ? "minimal" : preset.label.toLowerCase().includes("violet") || preset.label.toLowerCase().includes("purple") ? "vibrant" : "modern",
4761
- tags: [preset.label.toLowerCase().replace(/\s+/g, "-")],
4762
- createdAt: preset.createdAt,
4763
- provider: "built-in"
4764
- }
4765
- }));
4766
- allPresets.push(...builtInPresetList);
4891
+ builtInPresetList.push(
4892
+ ...Object.entries(builtInPresets).map(([id, preset]) => ({
4893
+ id,
4894
+ name: preset.label,
4895
+ colors: {
4896
+ light: preset.styles.light,
4897
+ dark: preset.styles.dark
4898
+ },
4899
+ metadata: {
4900
+ buttonWidth: getPresetButtonWidthPx(preset.label, layout),
4901
+ category: preset.label.toLowerCase().includes("minimal") ? "minimal" : preset.label.toLowerCase().includes("violet") || preset.label.toLowerCase().includes("purple") ? "vibrant" : "modern",
4902
+ tags: [preset.label.toLowerCase().replace(/\s+/g, "-")],
4903
+ createdAt: preset.createdAt,
4904
+ provider: "built-in"
4905
+ }
4906
+ }))
4907
+ );
4767
4908
  }
4768
4909
  if (showCustom) {
4769
- const customPresetList = Object.entries(customPresets).map(([id, preset]) => ({
4770
- id,
4771
- name: preset.label,
4772
- colors: {
4773
- light: preset.styles.light,
4774
- dark: preset.styles.dark
4775
- },
4776
- metadata: {
4777
- category: preset.label.toLowerCase().includes("minimal") ? "minimal" : preset.label.toLowerCase().includes("violet") || preset.label.toLowerCase().includes("purple") ? "vibrant" : "modern",
4778
- tags: [preset.label.toLowerCase().replace(/\s+/g, "-")],
4779
- createdAt: preset.createdAt,
4780
- provider: "custom"
4781
- }
4782
- }));
4783
- allPresets.push(...customPresetList);
4910
+ customPresetList.push(
4911
+ ...Object.entries(customPresets).map(([id, preset]) => ({
4912
+ id,
4913
+ name: preset.label,
4914
+ colors: {
4915
+ light: preset.styles.light,
4916
+ dark: preset.styles.dark
4917
+ },
4918
+ metadata: {
4919
+ buttonWidth: getPresetButtonWidthPx(preset.label, layout),
4920
+ category: preset.label.toLowerCase().includes("minimal") ? "minimal" : preset.label.toLowerCase().includes("violet") || preset.label.toLowerCase().includes("purple") ? "vibrant" : "modern",
4921
+ tags: [preset.label.toLowerCase().replace(/\s+/g, "-")],
4922
+ createdAt: preset.createdAt,
4923
+ provider: "custom"
4924
+ }
4925
+ }))
4926
+ );
4784
4927
  }
4928
+ let allPresets = [...customPresetList, ...builtInPresetList];
4785
4929
  if (categories && categories.length > 0) {
4786
4930
  allPresets = allPresets.filter(
4787
4931
  (preset) => categories.includes(preset.metadata?.category || "unknown")
@@ -4797,7 +4941,7 @@ var ThemePresetButtons = ({
4797
4941
  } finally {
4798
4942
  setLoading(false);
4799
4943
  }
4800
- }, [availablePresets, builtInPresets, customPresets, categories, maxPresets, showBuiltIn, showCustom]);
4944
+ }, [availablePresets, builtInPresets, customPresets, categories, maxPresets, showBuiltIn, showCustom, layout]);
4801
4945
  (0, import_react4.useEffect)(() => {
4802
4946
  loadPresets();
4803
4947
  }, [loadPresets]);