@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 +322 -178
- package/dist/index.mjs +322 -178
- package/dist/styles/components.css +72 -5
- package/dist/styles/tailwind.css +48 -48
- package/package.json +1 -1
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: {
|
|
4608
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
4619
|
-
|
|
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("
|
|
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.
|
|
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
|
-
|
|
4845
|
+
const builtInPresetList = [];
|
|
4846
|
+
const customPresetList = [];
|
|
4708
4847
|
if (showBuiltIn) {
|
|
4709
|
-
|
|
4710
|
-
id,
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
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
|
-
|
|
4727
|
-
id,
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
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]);
|