@astherix/ui 0.40.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/README.md +933 -0
- package/dist/chunk-IZEGCTZW.js +588 -0
- package/dist/chunk-IZEGCTZW.js.map +1 -0
- package/dist/cli.js +345 -0
- package/dist/controlStyles-Bco2sOHE.d.ts +4 -0
- package/dist/editor.d.ts +43 -0
- package/dist/editor.js +373 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.d.ts +2470 -0
- package/dist/index.js +10582 -0
- package/dist/index.js.map +1 -0
- package/dist/theme.d.ts +78 -0
- package/dist/theme.js +287 -0
- package/dist/theme.js.map +1 -0
- package/package.json +69 -0
- package/theme.css +909 -0
- package/ui.theme.schema.json +462 -0
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
type ColorInput = string | {
|
|
2
|
+
/** The color in light mode. */
|
|
3
|
+
light: string;
|
|
4
|
+
/** The color in dark mode. Worked out from light if left out. */
|
|
5
|
+
dark?: string;
|
|
6
|
+
/** Text/icons on the color in light mode. Worked out for contrast if left out. */
|
|
7
|
+
foreground?: string;
|
|
8
|
+
darkForeground?: string;
|
|
9
|
+
};
|
|
10
|
+
type SurfaceInput = string | {
|
|
11
|
+
light: string;
|
|
12
|
+
dark?: string;
|
|
13
|
+
};
|
|
14
|
+
interface ThemeConfig {
|
|
15
|
+
$schema?: string;
|
|
16
|
+
colors?: {
|
|
17
|
+
/** Buttons, links, focus rings, selected states. Hover, pressed edge and dark mode are derived. */
|
|
18
|
+
primary?: ColorInput;
|
|
19
|
+
/** Extra accents (e.g. Switch color="secondary" / "tertiary"). */
|
|
20
|
+
secondary?: ColorInput;
|
|
21
|
+
tertiary?: ColorInput;
|
|
22
|
+
success?: ColorInput;
|
|
23
|
+
warning?: ColorInput;
|
|
24
|
+
danger?: ColorInput;
|
|
25
|
+
info?: ColorInput;
|
|
26
|
+
/** Page background. */
|
|
27
|
+
background?: SurfaceInput;
|
|
28
|
+
/** Cards, fields, menus. */
|
|
29
|
+
surface?: SurfaceInput;
|
|
30
|
+
/** Main text. */
|
|
31
|
+
foreground?: SurfaceInput;
|
|
32
|
+
/** Secondary text. */
|
|
33
|
+
muted?: SurfaceInput;
|
|
34
|
+
/** Hairlines and field borders. */
|
|
35
|
+
border?: SurfaceInput;
|
|
36
|
+
};
|
|
37
|
+
fonts?: {
|
|
38
|
+
/** Body font stack, e.g. "Inter, system-ui, sans-serif". */
|
|
39
|
+
sans?: string;
|
|
40
|
+
/** Headings (defaults to sans). */
|
|
41
|
+
heading?: string;
|
|
42
|
+
mono?: string;
|
|
43
|
+
/** Google Fonts to load, e.g. ["Inter:wght@400..700", "Fraunces:wght@600"]. */
|
|
44
|
+
googleFonts?: string[];
|
|
45
|
+
};
|
|
46
|
+
/** Corner roundness: a preset, or exact values. */
|
|
47
|
+
radius?: "none" | "sm" | "md" | "lg" | {
|
|
48
|
+
control?: string;
|
|
49
|
+
controlSm?: string;
|
|
50
|
+
controlLg?: string;
|
|
51
|
+
card?: string;
|
|
52
|
+
modal?: string;
|
|
53
|
+
};
|
|
54
|
+
/** Spacing and control sizes: a preset, or a multiplier (1 = default, 0.9 = tighter, 1.1 = roomier). */
|
|
55
|
+
density?: "compact" | "comfortable" | "spacious" | number;
|
|
56
|
+
/** Shadow strength. */
|
|
57
|
+
shadows?: "none" | "subtle" | "default" | "strong";
|
|
58
|
+
/** Root font size, e.g. "16px" (default) or "15px". Scales everything sized in rem. */
|
|
59
|
+
baseFontSize?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Identity helper for typed theme configs in .ts files. */
|
|
62
|
+
declare const defineTheme: (config: ThemeConfig) => ThemeConfig;
|
|
63
|
+
/** Google Fonts stylesheet URL for the config (or null). */
|
|
64
|
+
declare function getFontsHref(config: ThemeConfig): string | null;
|
|
65
|
+
interface ThemeCssOptions {
|
|
66
|
+
/** Where light values go. Default ":root". Use e.g. "[data-tenant=acme]" to scope a theme. */
|
|
67
|
+
selector?: string;
|
|
68
|
+
/** Where dark values go. Default ".dark" (the class the framework's dark mode uses). */
|
|
69
|
+
darkSelector?: string;
|
|
70
|
+
/** Add an @import for Google Fonts at the top (for CSS files). Default false. */
|
|
71
|
+
includeFontImport?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** Turn a theme config into CSS custom properties that override the defaults in theme.css. */
|
|
74
|
+
declare function createThemeCss(config: ThemeConfig, options?: ThemeCssOptions): string;
|
|
75
|
+
/** Check a config and return friendly problems (empty when it's fine). */
|
|
76
|
+
declare function validateTheme(config: unknown): string[];
|
|
77
|
+
|
|
78
|
+
export { type ColorInput, type SurfaceInput, type ThemeConfig, type ThemeCssOptions, createThemeCss, defineTheme, getFontsHref, validateTheme };
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// src/theme/color.ts
|
|
2
|
+
function parseHex(hex) {
|
|
3
|
+
const m = hex.trim().match(/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i);
|
|
4
|
+
if (!m) return null;
|
|
5
|
+
let h = m[1];
|
|
6
|
+
if (h.length === 3) h = h.split("").map((c) => c + c).join("");
|
|
7
|
+
return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);
|
|
8
|
+
}
|
|
9
|
+
function toHex([r, g, b]) {
|
|
10
|
+
return "#" + [r, g, b].map((v) => Math.round(Math.min(1, Math.max(0, v)) * 255).toString(16).padStart(2, "0")).join("");
|
|
11
|
+
}
|
|
12
|
+
var toLinear = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
13
|
+
var fromLinear = (c) => c <= 31308e-7 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055;
|
|
14
|
+
function rgbToOklch(rgb) {
|
|
15
|
+
const [r, g, b] = rgb.map(toLinear);
|
|
16
|
+
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
17
|
+
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
18
|
+
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
19
|
+
const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
|
|
20
|
+
const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
|
|
21
|
+
const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
|
|
22
|
+
return [L, Math.hypot(A, B), (Math.atan2(B, A) * 180 / Math.PI + 360) % 360];
|
|
23
|
+
}
|
|
24
|
+
function oklchToRgbUnclamped([L, C, H]) {
|
|
25
|
+
const a = C * Math.cos(H * Math.PI / 180);
|
|
26
|
+
const b = C * Math.sin(H * Math.PI / 180);
|
|
27
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
28
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
29
|
+
const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
30
|
+
return [
|
|
31
|
+
fromLinear(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
|
|
32
|
+
fromLinear(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
|
|
33
|
+
fromLinear(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s)
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
function oklchToRgb([L, C, H]) {
|
|
37
|
+
let c = C;
|
|
38
|
+
for (let i = 0; i < 24; i++) {
|
|
39
|
+
const rgb = oklchToRgbUnclamped([L, c, H]);
|
|
40
|
+
if (rgb.every((v) => v >= -5e-4 && v <= 1.0005)) return rgb;
|
|
41
|
+
c *= 0.9;
|
|
42
|
+
}
|
|
43
|
+
return oklchToRgbUnclamped([L, 0, H]);
|
|
44
|
+
}
|
|
45
|
+
function luminance(rgb) {
|
|
46
|
+
const [r, g, b] = rgb.map(toLinear);
|
|
47
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
48
|
+
}
|
|
49
|
+
function contrast(a, b) {
|
|
50
|
+
const [x, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);
|
|
51
|
+
return (x + 0.05) / (y + 0.05);
|
|
52
|
+
}
|
|
53
|
+
function adjust(hex, dL, chroma = 1) {
|
|
54
|
+
const rgb = parseHex(hex);
|
|
55
|
+
if (!rgb) return hex;
|
|
56
|
+
const [L, C, H] = rgbToOklch(rgb);
|
|
57
|
+
return toHex(oklchToRgb([Math.min(0.98, Math.max(0.05, L + dL)), C * chroma, H]));
|
|
58
|
+
}
|
|
59
|
+
function lightness(hex) {
|
|
60
|
+
const rgb = parseHex(hex);
|
|
61
|
+
return rgb ? rgbToOklch(rgb)[0] : 0.5;
|
|
62
|
+
}
|
|
63
|
+
function readableOn(hex) {
|
|
64
|
+
const rgb = parseHex(hex);
|
|
65
|
+
if (!rgb) return "#ffffff";
|
|
66
|
+
const white = [1, 1, 1];
|
|
67
|
+
const [, C, H] = rgbToOklch(rgb);
|
|
68
|
+
const darkHex = toHex(oklchToRgb([0.2, Math.min(C, 0.06), H]));
|
|
69
|
+
return contrast(rgb, white) >= 4.5 || contrast(rgb, white) >= contrast(rgb, parseHex(darkHex)) ? "#ffffff" : darkHex;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/theme/index.ts
|
|
73
|
+
var defineTheme = (config) => config;
|
|
74
|
+
function resolveColor(input) {
|
|
75
|
+
if (!input) return null;
|
|
76
|
+
const c = typeof input === "string" ? { light: input } : input;
|
|
77
|
+
if (!parseHex(c.light)) return null;
|
|
78
|
+
const L = lightness(c.light);
|
|
79
|
+
const dark = c.dark ?? adjust(c.light, Math.max(0, 0.72 - L), 0.95);
|
|
80
|
+
return {
|
|
81
|
+
light: c.light,
|
|
82
|
+
dark,
|
|
83
|
+
fg: c.foreground ?? readableOn(c.light),
|
|
84
|
+
darkFg: c.darkForeground ?? readableOn(dark)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function surface(input) {
|
|
88
|
+
if (!input) return null;
|
|
89
|
+
return typeof input === "string" ? { light: input, dark: void 0 } : input;
|
|
90
|
+
}
|
|
91
|
+
var radiusPresets = {
|
|
92
|
+
none: { controlSm: "0", control: "0", controlLg: "0", card: "0", modal: "0" },
|
|
93
|
+
sm: { controlSm: "0.125rem", control: "0.25rem", controlLg: "0.375rem", card: "0.375rem", modal: "0.5rem" },
|
|
94
|
+
md: { controlSm: "0.1875rem", control: "0.375rem", controlLg: "0.75rem", card: "0.75rem", modal: "0.875rem" },
|
|
95
|
+
lg: { controlSm: "0.375rem", control: "0.625rem", controlLg: "1rem", card: "1rem", modal: "1.25rem" }
|
|
96
|
+
};
|
|
97
|
+
var densityUnits = { compact: 0.225, comfortable: 0.25, spacious: 0.28 };
|
|
98
|
+
function shadows(strength, dark) {
|
|
99
|
+
if (strength === 0) return { sm: "none", md: "none", lg: "none", xl: "none", modal: "none" };
|
|
100
|
+
const c = dark ? "0 0 0" : "var(--ui-shadow-color)";
|
|
101
|
+
const k = (a) => Math.min(1, +(a * strength * (dark ? 3 : 1)).toFixed(3));
|
|
102
|
+
return {
|
|
103
|
+
sm: `0 1px 2px rgb(${c} / ${k(0.14)})`,
|
|
104
|
+
md: `0 1px 2px rgb(${c} / ${k(0.12)}), 0 3px 8px -1px rgb(${c} / ${k(0.14)})`,
|
|
105
|
+
lg: `0 2px 4px rgb(${c} / ${k(0.1)}), 0 8px 18px -4px rgb(${c} / ${k(0.22)})`,
|
|
106
|
+
xl: `0 3px 6px rgb(${c} / ${k(0.1)}), 0 16px 32px -8px rgb(${c} / ${k(0.32)})`,
|
|
107
|
+
modal: `0 2px 6px rgb(${c} / ${k(0.08)}), 0 24px 56px -12px rgb(${c} / ${k(0.35)})`
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
var COLOR_ALIASES = [
|
|
111
|
+
"bg",
|
|
112
|
+
"surface",
|
|
113
|
+
"fg",
|
|
114
|
+
"fg-muted",
|
|
115
|
+
"border",
|
|
116
|
+
"border-strong",
|
|
117
|
+
"ring",
|
|
118
|
+
"primary",
|
|
119
|
+
"primary-hover",
|
|
120
|
+
"primary-edge",
|
|
121
|
+
"primary-fg",
|
|
122
|
+
"secondary",
|
|
123
|
+
"secondary-hover",
|
|
124
|
+
"danger",
|
|
125
|
+
"danger-hover",
|
|
126
|
+
"danger-edge",
|
|
127
|
+
"danger-fg",
|
|
128
|
+
"success",
|
|
129
|
+
"success-fg",
|
|
130
|
+
"warning",
|
|
131
|
+
"warning-fg",
|
|
132
|
+
"info",
|
|
133
|
+
"info-fg",
|
|
134
|
+
"tone-secondary",
|
|
135
|
+
"tone-secondary-fg",
|
|
136
|
+
"tone-tertiary",
|
|
137
|
+
"tone-tertiary-fg"
|
|
138
|
+
];
|
|
139
|
+
function getFontsHref(config) {
|
|
140
|
+
const fams = config.fonts?.googleFonts?.filter(Boolean);
|
|
141
|
+
if (!fams?.length) return null;
|
|
142
|
+
return `https://fonts.googleapis.com/css2?${fams.map((f) => `family=${f.trim().replace(/ /g, "+")}`).join("&")}&display=swap`;
|
|
143
|
+
}
|
|
144
|
+
function createThemeCss(config, options = {}) {
|
|
145
|
+
const { selector = ":root", darkSelector = ".dark", includeFontImport = false } = options;
|
|
146
|
+
const light = [];
|
|
147
|
+
const dark = [];
|
|
148
|
+
const set = (arr, name, value) => value && arr.push(` ${name}: ${value};`);
|
|
149
|
+
const c = config.colors ?? {};
|
|
150
|
+
const primary = resolveColor(c.primary);
|
|
151
|
+
if (primary) {
|
|
152
|
+
set(light, "--ui-primary", primary.light);
|
|
153
|
+
set(light, "--ui-primary-hover", adjust(primary.light, -0.07));
|
|
154
|
+
set(light, "--ui-primary-edge", adjust(primary.light, -0.26));
|
|
155
|
+
set(light, "--ui-primary-fg", primary.fg);
|
|
156
|
+
set(light, "--ui-ring", primary.light);
|
|
157
|
+
set(dark, "--ui-primary", primary.dark);
|
|
158
|
+
set(dark, "--ui-primary-hover", adjust(primary.dark, 0.07));
|
|
159
|
+
set(dark, "--ui-primary-edge", adjust(primary.dark, -0.16));
|
|
160
|
+
set(dark, "--ui-primary-fg", primary.darkFg);
|
|
161
|
+
set(dark, "--ui-ring", adjust(primary.dark, 0.06));
|
|
162
|
+
}
|
|
163
|
+
const tones = [
|
|
164
|
+
["secondary", "--ui-tone-secondary", false],
|
|
165
|
+
["tertiary", "--ui-tone-tertiary", false],
|
|
166
|
+
["success", "--ui-success", false],
|
|
167
|
+
["warning", "--ui-warning", false],
|
|
168
|
+
["danger", "--ui-danger", true],
|
|
169
|
+
["info", "--ui-info", false]
|
|
170
|
+
];
|
|
171
|
+
for (const [key, name, hasStates] of tones) {
|
|
172
|
+
const p = resolveColor(c[key]);
|
|
173
|
+
if (!p) continue;
|
|
174
|
+
set(light, name, p.light);
|
|
175
|
+
set(light, `${name}-fg`, p.fg);
|
|
176
|
+
set(dark, name, p.dark);
|
|
177
|
+
set(dark, `${name}-fg`, p.darkFg);
|
|
178
|
+
if (hasStates) {
|
|
179
|
+
set(light, `${name}-hover`, adjust(p.light, -0.07));
|
|
180
|
+
set(light, `${name}-edge`, adjust(p.light, -0.26));
|
|
181
|
+
set(dark, `${name}-hover`, adjust(p.dark, 0.07));
|
|
182
|
+
set(dark, `${name}-edge`, adjust(p.dark, -0.16));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const surfaces = [
|
|
186
|
+
["background", ["--ui-bg"]],
|
|
187
|
+
["surface", ["--ui-surface", "--ui-secondary"]],
|
|
188
|
+
["foreground", ["--ui-fg"]],
|
|
189
|
+
["muted", ["--ui-fg-muted"]],
|
|
190
|
+
["border", ["--ui-border"]]
|
|
191
|
+
];
|
|
192
|
+
for (const [key, names] of surfaces) {
|
|
193
|
+
const s = surface(c[key]);
|
|
194
|
+
if (!s) continue;
|
|
195
|
+
names.forEach((n) => {
|
|
196
|
+
set(light, n, s.light);
|
|
197
|
+
set(dark, n, s.dark);
|
|
198
|
+
});
|
|
199
|
+
if (key === "border") {
|
|
200
|
+
set(light, "--ui-border-strong", parseHex(s.light) ? adjust(s.light, -0.08) : void 0);
|
|
201
|
+
set(dark, "--ui-border-strong", s.dark && parseHex(s.dark) ? adjust(s.dark, 0.08) : void 0);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const f = config.fonts ?? {};
|
|
205
|
+
set(light, "--font-sans", f.sans);
|
|
206
|
+
set(light, "--font-heading", f.heading ?? (f.sans ? "var(--font-sans)" : void 0));
|
|
207
|
+
set(light, "--font-mono", f.mono);
|
|
208
|
+
if (config.radius) {
|
|
209
|
+
const r = typeof config.radius === "string" ? radiusPresets[config.radius] : config.radius;
|
|
210
|
+
if (r) {
|
|
211
|
+
set(light, "--radius-control-sm", r.controlSm);
|
|
212
|
+
set(light, "--radius-control", r.control);
|
|
213
|
+
set(light, "--radius-control-lg", r.controlLg);
|
|
214
|
+
set(light, "--radius-card", r.card);
|
|
215
|
+
set(light, "--radius-modal", r.modal);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (config.density !== void 0) {
|
|
219
|
+
const unit = typeof config.density === "number" ? 0.25 * config.density : densityUnits[config.density];
|
|
220
|
+
if (unit) set(light, "--spacing", `${+unit.toFixed(4)}rem`);
|
|
221
|
+
}
|
|
222
|
+
if (config.shadows) {
|
|
223
|
+
const k = { none: 0, subtle: 0.6, default: 1, strong: 1.6 }[config.shadows];
|
|
224
|
+
const l = shadows(k, false);
|
|
225
|
+
const d = shadows(k, true);
|
|
226
|
+
["sm", "md", "lg", "xl", "modal"].forEach((s) => {
|
|
227
|
+
set(light, `--ui-shadow-${s}`, l[s]);
|
|
228
|
+
set(dark, `--ui-shadow-${s}`, d[s]);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (selector !== ":root" && (light.length || dark.length)) {
|
|
232
|
+
for (const name of COLOR_ALIASES) light.push(` --color-${name}: var(--ui-${name});`);
|
|
233
|
+
if (f.sans || f.heading) light.push(" font-family: var(--font-sans);");
|
|
234
|
+
}
|
|
235
|
+
const blocks = [];
|
|
236
|
+
const href = getFontsHref(config);
|
|
237
|
+
if (includeFontImport && href) blocks.push(`@import url("${href}");`);
|
|
238
|
+
blocks.push("/* Generated by @astherix/ui from your theme config. Import it after @astherix/ui/theme.css. */");
|
|
239
|
+
if (config.baseFontSize) blocks.push(`html {
|
|
240
|
+
font-size: ${config.baseFontSize};
|
|
241
|
+
}`);
|
|
242
|
+
if (light.length) blocks.push(`${selector} {
|
|
243
|
+
${light.join("\n")}
|
|
244
|
+
}`);
|
|
245
|
+
if (dark.length) {
|
|
246
|
+
const darkSel = selector === ":root" ? darkSelector : `${darkSelector} ${selector}, ${selector}${darkSelector}`;
|
|
247
|
+
blocks.push(`${darkSel} {
|
|
248
|
+
${dark.join("\n")}
|
|
249
|
+
}`);
|
|
250
|
+
}
|
|
251
|
+
return blocks.join("\n\n") + "\n";
|
|
252
|
+
}
|
|
253
|
+
function validateTheme(config) {
|
|
254
|
+
const problems = [];
|
|
255
|
+
if (!config || typeof config !== "object") return ["The theme must be a JSON object."];
|
|
256
|
+
const t = config;
|
|
257
|
+
const known = ["$schema", "colors", "fonts", "radius", "density", "shadows", "baseFontSize"];
|
|
258
|
+
Object.keys(t).forEach((k) => !known.includes(k) && problems.push(`Unknown setting "${k}". Known settings: ${known.slice(1).join(", ")}.`));
|
|
259
|
+
const checkColor = (name, v) => {
|
|
260
|
+
if (v === void 0) return;
|
|
261
|
+
const vals = typeof v === "string" ? [v] : v && typeof v === "object" ? Object.values(v) : [v];
|
|
262
|
+
vals.forEach((x) => {
|
|
263
|
+
if (typeof x !== "string" || !parseHex(x) && !/^(rgb|hsl|oklch|oklab|color|var)\(/.test(x))
|
|
264
|
+
problems.push(`colors.${name}: "${String(x)}" isn't a color. Use a hex value like "#16a34a".`);
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
Object.entries(t.colors ?? {}).forEach(([k, v]) => checkColor(k, v));
|
|
268
|
+
const derived = ["primary", "secondary", "tertiary", "success", "warning", "danger", "info"];
|
|
269
|
+
derived.forEach((k) => {
|
|
270
|
+
const v = t.colors?.[k];
|
|
271
|
+
const light = typeof v === "string" ? v : v?.light;
|
|
272
|
+
const alreadyReported = problems.some((p) => p.startsWith(`colors.${k}:`));
|
|
273
|
+
if (light && !parseHex(light) && !alreadyReported) problems.push(`colors.${k}: use a hex color (like "#16a34a") so hover and dark shades can be worked out.`);
|
|
274
|
+
});
|
|
275
|
+
if (typeof t.radius === "string" && !(t.radius in radiusPresets)) problems.push(`radius: use "none", "sm", "md" or "lg", or an object with exact values.`);
|
|
276
|
+
if (typeof t.density === "string" && !(t.density in densityUnits)) problems.push(`density: use "compact", "comfortable" or "spacious", or a number like 0.9.`);
|
|
277
|
+
if (typeof t.density === "number" && (t.density < 0.6 || t.density > 1.6)) problems.push(`density: ${t.density} is extreme. Use a number between 0.6 and 1.6.`);
|
|
278
|
+
if (t.shadows && !["none", "subtle", "default", "strong"].includes(t.shadows)) problems.push(`shadows: use "none", "subtle", "default" or "strong".`);
|
|
279
|
+
return problems;
|
|
280
|
+
}
|
|
281
|
+
export {
|
|
282
|
+
createThemeCss,
|
|
283
|
+
defineTheme,
|
|
284
|
+
getFontsHref,
|
|
285
|
+
validateTheme
|
|
286
|
+
};
|
|
287
|
+
//# sourceMappingURL=theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/theme/color.ts","../src/theme/index.ts"],"sourcesContent":["/*\n * Small color helpers (no dependencies): parse hex, convert to OKLCH to lighten/darken\n * evenly across hues, back to hex (staying inside sRGB), and WCAG contrast.\n */\n\nexport type RGB = [number, number, number]; // 0–1\nexport type OKLCH = [number, number, number]; // L 0–1, C, H degrees\n\nexport function parseHex(hex: string): RGB | null {\n const m = hex.trim().match(/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i);\n if (!m) return null;\n let h = m[1];\n if (h.length === 3) h = h.split(\"\").map((c) => c + c).join(\"\");\n return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16) / 255) as RGB;\n}\n\nexport function toHex([r, g, b]: RGB) {\n return \"#\" + [r, g, b].map((v) => Math.round(Math.min(1, Math.max(0, v)) * 255).toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nconst toLinear = (c: number) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);\nconst fromLinear = (c: number) => (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055);\n\nexport function rgbToOklch(rgb: RGB): OKLCH {\n const [r, g, b] = rgb.map(toLinear);\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;\n const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;\n const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;\n return [L, Math.hypot(A, B), ((Math.atan2(B, A) * 180) / Math.PI + 360) % 360];\n}\n\nfunction oklchToRgbUnclamped([L, C, H]: OKLCH): RGB {\n const a = C * Math.cos((H * Math.PI) / 180);\n const b = C * Math.sin((H * Math.PI) / 180);\n const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return [\n fromLinear(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),\n fromLinear(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),\n fromLinear(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s),\n ];\n}\n\n/** OKLCH → sRGB, reducing chroma until it fits (keeps lightness and hue). */\nexport function oklchToRgb([L, C, H]: OKLCH): RGB {\n let c = C;\n for (let i = 0; i < 24; i++) {\n const rgb = oklchToRgbUnclamped([L, c, H]);\n if (rgb.every((v) => v >= -0.0005 && v <= 1.0005)) return rgb;\n c *= 0.9;\n }\n return oklchToRgbUnclamped([L, 0, H]);\n}\n\nexport function luminance(rgb: RGB) {\n const [r, g, b] = rgb.map(toLinear);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\nexport function contrast(a: RGB, b: RGB) {\n const [x, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);\n return (x + 0.05) / (y + 0.05);\n}\n\n/** Shift lightness (in OKLCH, so every hue changes evenly) and optionally scale chroma. */\nexport function adjust(hex: string, dL: number, chroma = 1) {\n const rgb = parseHex(hex);\n if (!rgb) return hex;\n const [L, C, H] = rgbToOklch(rgb);\n return toHex(oklchToRgb([Math.min(0.98, Math.max(0.05, L + dL)), C * chroma, H]));\n}\n\nexport function lightness(hex: string) {\n const rgb = parseHex(hex);\n return rgb ? rgbToOklch(rgb)[0] : 0.5;\n}\n\n/** Pick readable text for a background: white, or a near-black tinted with the same hue. */\nexport function readableOn(hex: string) {\n const rgb = parseHex(hex);\n if (!rgb) return \"#ffffff\";\n const white: RGB = [1, 1, 1];\n const [, C, H] = rgbToOklch(rgb);\n const darkHex = toHex(oklchToRgb([0.2, Math.min(C, 0.06), H]));\n return contrast(rgb, white) >= 4.5 || contrast(rgb, white) >= contrast(rgb, parseHex(darkHex)!) ? \"#ffffff\" : darkHex;\n}\n","/*\n * @astherix/ui/theme — turn a theme config (ui.theme.json) into CSS variables.\n * Pure functions, no React: safe on the server, in build scripts and in the CLI.\n */\nimport { adjust, lightness, parseHex, readableOn } from \"./color\";\n\nexport type ColorInput =\n | string\n | {\n /** The color in light mode. */\n light: string;\n /** The color in dark mode. Worked out from light if left out. */\n dark?: string;\n /** Text/icons on the color in light mode. Worked out for contrast if left out. */\n foreground?: string;\n darkForeground?: string;\n };\n\nexport type SurfaceInput = string | { light: string; dark?: string };\n\nexport interface ThemeConfig {\n $schema?: string;\n colors?: {\n /** Buttons, links, focus rings, selected states. Hover, pressed edge and dark mode are derived. */\n primary?: ColorInput;\n /** Extra accents (e.g. Switch color=\"secondary\" / \"tertiary\"). */\n secondary?: ColorInput;\n tertiary?: ColorInput;\n success?: ColorInput;\n warning?: ColorInput;\n danger?: ColorInput;\n info?: ColorInput;\n /** Page background. */\n background?: SurfaceInput;\n /** Cards, fields, menus. */\n surface?: SurfaceInput;\n /** Main text. */\n foreground?: SurfaceInput;\n /** Secondary text. */\n muted?: SurfaceInput;\n /** Hairlines and field borders. */\n border?: SurfaceInput;\n };\n fonts?: {\n /** Body font stack, e.g. \"Inter, system-ui, sans-serif\". */\n sans?: string;\n /** Headings (defaults to sans). */\n heading?: string;\n mono?: string;\n /** Google Fonts to load, e.g. [\"Inter:wght@400..700\", \"Fraunces:wght@600\"]. */\n googleFonts?: string[];\n };\n /** Corner roundness: a preset, or exact values. */\n radius?:\n | \"none\"\n | \"sm\"\n | \"md\"\n | \"lg\"\n | { control?: string; controlSm?: string; controlLg?: string; card?: string; modal?: string };\n /** Spacing and control sizes: a preset, or a multiplier (1 = default, 0.9 = tighter, 1.1 = roomier). */\n density?: \"compact\" | \"comfortable\" | \"spacious\" | number;\n /** Shadow strength. */\n shadows?: \"none\" | \"subtle\" | \"default\" | \"strong\";\n /** Root font size, e.g. \"16px\" (default) or \"15px\". Scales everything sized in rem. */\n baseFontSize?: string;\n}\n\n/** Identity helper for typed theme configs in .ts files. */\nexport const defineTheme = (config: ThemeConfig) => config;\n\ntype Pair = { light: string; dark: string; fg: string; darkFg: string };\n\nfunction resolveColor(input: ColorInput | undefined): Pair | null {\n if (!input) return null;\n const c = typeof input === \"string\" ? { light: input } : input;\n if (!parseHex(c.light)) return null;\n // Dark mode needs a lighter, slightly calmer version to glow on dark backgrounds.\n const L = lightness(c.light);\n const dark = c.dark ?? adjust(c.light, Math.max(0, 0.72 - L), 0.95);\n return {\n light: c.light,\n dark,\n fg: c.foreground ?? readableOn(c.light),\n darkFg: c.darkForeground ?? readableOn(dark),\n };\n}\n\nfunction surface(input: SurfaceInput | undefined) {\n if (!input) return null;\n return typeof input === \"string\" ? { light: input, dark: undefined } : input;\n}\n\nconst radiusPresets = {\n none: { controlSm: \"0\", control: \"0\", controlLg: \"0\", card: \"0\", modal: \"0\" },\n sm: { controlSm: \"0.125rem\", control: \"0.25rem\", controlLg: \"0.375rem\", card: \"0.375rem\", modal: \"0.5rem\" },\n md: { controlSm: \"0.1875rem\", control: \"0.375rem\", controlLg: \"0.75rem\", card: \"0.75rem\", modal: \"0.875rem\" },\n lg: { controlSm: \"0.375rem\", control: \"0.625rem\", controlLg: \"1rem\", card: \"1rem\", modal: \"1.25rem\" },\n};\n\nconst densityUnits = { compact: 0.225, comfortable: 0.25, spacious: 0.28 };\n\nfunction shadows(strength: number, dark: boolean) {\n if (strength === 0) return { sm: \"none\", md: \"none\", lg: \"none\", xl: \"none\", modal: \"none\" };\n const c = dark ? \"0 0 0\" : \"var(--ui-shadow-color)\";\n const k = (a: number) => Math.min(1, +(a * strength * (dark ? 3 : 1)).toFixed(3));\n return {\n sm: `0 1px 2px rgb(${c} / ${k(0.14)})`,\n md: `0 1px 2px rgb(${c} / ${k(0.12)}), 0 3px 8px -1px rgb(${c} / ${k(0.14)})`,\n lg: `0 2px 4px rgb(${c} / ${k(0.1)}), 0 8px 18px -4px rgb(${c} / ${k(0.22)})`,\n xl: `0 3px 6px rgb(${c} / ${k(0.1)}), 0 16px 32px -8px rgb(${c} / ${k(0.32)})`,\n modal: `0 2px 6px rgb(${c} / ${k(0.08)}), 0 24px 56px -12px rgb(${c} / ${k(0.35)})`,\n };\n}\n\nconst COLOR_ALIASES = [\n \"bg\", \"surface\", \"fg\", \"fg-muted\", \"border\", \"border-strong\", \"ring\",\n \"primary\", \"primary-hover\", \"primary-edge\", \"primary-fg\",\n \"secondary\", \"secondary-hover\",\n \"danger\", \"danger-hover\", \"danger-edge\", \"danger-fg\",\n \"success\", \"success-fg\", \"warning\", \"warning-fg\", \"info\", \"info-fg\",\n \"tone-secondary\", \"tone-secondary-fg\", \"tone-tertiary\", \"tone-tertiary-fg\",\n];\n\n/** Google Fonts stylesheet URL for the config (or null). */\nexport function getFontsHref(config: ThemeConfig) {\n const fams = config.fonts?.googleFonts?.filter(Boolean);\n if (!fams?.length) return null;\n return `https://fonts.googleapis.com/css2?${fams.map((f) => `family=${f.trim().replace(/ /g, \"+\")}`).join(\"&\")}&display=swap`;\n}\n\nexport interface ThemeCssOptions {\n /** Where light values go. Default \":root\". Use e.g. \"[data-tenant=acme]\" to scope a theme. */\n selector?: string;\n /** Where dark values go. Default \".dark\" (the class the framework's dark mode uses). */\n darkSelector?: string;\n /** Add an @import for Google Fonts at the top (for CSS files). Default false. */\n includeFontImport?: boolean;\n}\n\n/** Turn a theme config into CSS custom properties that override the defaults in theme.css. */\nexport function createThemeCss(config: ThemeConfig, options: ThemeCssOptions = {}) {\n const { selector = \":root\", darkSelector = \".dark\", includeFontImport = false } = options;\n const light: string[] = [];\n const dark: string[] = [];\n const set = (arr: string[], name: string, value: string | undefined) => value && arr.push(` ${name}: ${value};`);\n\n const c = config.colors ?? {};\n const primary = resolveColor(c.primary);\n if (primary) {\n // Hover a little darker, the raised bottom edge much darker; in dark mode hover goes lighter.\n set(light, \"--ui-primary\", primary.light);\n set(light, \"--ui-primary-hover\", adjust(primary.light, -0.07));\n set(light, \"--ui-primary-edge\", adjust(primary.light, -0.26));\n set(light, \"--ui-primary-fg\", primary.fg);\n set(light, \"--ui-ring\", primary.light);\n set(dark, \"--ui-primary\", primary.dark);\n set(dark, \"--ui-primary-hover\", adjust(primary.dark, 0.07));\n set(dark, \"--ui-primary-edge\", adjust(primary.dark, -0.16));\n set(dark, \"--ui-primary-fg\", primary.darkFg);\n set(dark, \"--ui-ring\", adjust(primary.dark, 0.06));\n }\n const tones: Array<[keyof NonNullable<ThemeConfig[\"colors\"]>, string, boolean]> = [\n [\"secondary\", \"--ui-tone-secondary\", false],\n [\"tertiary\", \"--ui-tone-tertiary\", false],\n [\"success\", \"--ui-success\", false],\n [\"warning\", \"--ui-warning\", false],\n [\"danger\", \"--ui-danger\", true],\n [\"info\", \"--ui-info\", false],\n ];\n for (const [key, name, hasStates] of tones) {\n const p = resolveColor(c[key] as ColorInput | undefined);\n if (!p) continue;\n set(light, name, p.light);\n set(light, `${name}-fg`, p.fg);\n set(dark, name, p.dark);\n set(dark, `${name}-fg`, p.darkFg);\n if (hasStates) {\n set(light, `${name}-hover`, adjust(p.light, -0.07));\n set(light, `${name}-edge`, adjust(p.light, -0.26));\n set(dark, `${name}-hover`, adjust(p.dark, 0.07));\n set(dark, `${name}-edge`, adjust(p.dark, -0.16));\n }\n }\n const surfaces: Array<[keyof NonNullable<ThemeConfig[\"colors\"]>, string[]]> = [\n [\"background\", [\"--ui-bg\"]],\n [\"surface\", [\"--ui-surface\", \"--ui-secondary\"]],\n [\"foreground\", [\"--ui-fg\"]],\n [\"muted\", [\"--ui-fg-muted\"]],\n [\"border\", [\"--ui-border\"]],\n ];\n for (const [key, names] of surfaces) {\n const s = surface(c[key] as SurfaceInput | undefined);\n if (!s) continue;\n names.forEach((n) => {\n set(light, n, s.light);\n set(dark, n, s.dark);\n });\n if (key === \"border\") {\n set(light, \"--ui-border-strong\", parseHex(s.light) ? adjust(s.light, -0.08) : undefined);\n set(dark, \"--ui-border-strong\", s.dark && parseHex(s.dark) ? adjust(s.dark, 0.08) : undefined);\n }\n }\n\n const f = config.fonts ?? {};\n set(light, \"--font-sans\", f.sans);\n set(light, \"--font-heading\", f.heading ?? (f.sans ? \"var(--font-sans)\" : undefined));\n set(light, \"--font-mono\", f.mono);\n\n if (config.radius) {\n const r = typeof config.radius === \"string\" ? radiusPresets[config.radius] : config.radius;\n if (r) {\n set(light, \"--radius-control-sm\", r.controlSm);\n set(light, \"--radius-control\", r.control);\n set(light, \"--radius-control-lg\", r.controlLg);\n set(light, \"--radius-card\", r.card);\n set(light, \"--radius-modal\", r.modal);\n }\n }\n\n if (config.density !== undefined) {\n // Tailwind v4 sizes every spacing utility (padding, gaps, control heights) from --spacing.\n const unit = typeof config.density === \"number\" ? 0.25 * config.density : densityUnits[config.density];\n if (unit) set(light, \"--spacing\", `${+unit.toFixed(4)}rem`);\n }\n\n if (config.shadows) {\n const k = { none: 0, subtle: 0.6, default: 1, strong: 1.6 }[config.shadows];\n const l = shadows(k, false);\n const d = shadows(k, true);\n ([\"sm\", \"md\", \"lg\", \"xl\", \"modal\"] as const).forEach((s) => {\n set(light, `--ui-shadow-${s}`, l[s]);\n set(dark, `--ui-shadow-${s}`, d[s]);\n });\n }\n\n // When a theme is scoped to part of the page, the framework's --color-* aliases (declared on\n // :root) would still point at the page-level values. Re-declare them on the scope so they\n // resolve against this theme.\n if (selector !== \":root\" && (light.length || dark.length)) {\n for (const name of COLOR_ALIASES) light.push(` --color-${name}: var(--ui-${name});`);\n if (f.sans || f.heading) light.push(\" font-family: var(--font-sans);\");\n }\n\n const blocks: string[] = [];\n const href = getFontsHref(config);\n if (includeFontImport && href) blocks.push(`@import url(\"${href}\");`);\n blocks.push(\"/* Generated by @astherix/ui from your theme config. Import it after @astherix/ui/theme.css. */\");\n if (config.baseFontSize) blocks.push(`html {\\n font-size: ${config.baseFontSize};\\n}`);\n if (light.length) blocks.push(`${selector} {\\n${light.join(\"\\n\")}\\n}`);\n if (dark.length) {\n // Scope dark values to the same element when the theme is scoped (e.g. per tenant).\n const darkSel = selector === \":root\" ? darkSelector : `${darkSelector} ${selector}, ${selector}${darkSelector}`;\n blocks.push(`${darkSel} {\\n${dark.join(\"\\n\")}\\n}`);\n }\n return blocks.join(\"\\n\\n\") + \"\\n\";\n}\n\n/** Check a config and return friendly problems (empty when it's fine). */\nexport function validateTheme(config: unknown): string[] {\n const problems: string[] = [];\n if (!config || typeof config !== \"object\") return [\"The theme must be a JSON object.\"];\n const t = config as ThemeConfig;\n const known = [\"$schema\", \"colors\", \"fonts\", \"radius\", \"density\", \"shadows\", \"baseFontSize\"];\n Object.keys(t).forEach((k) => !known.includes(k) && problems.push(`Unknown setting \"${k}\". Known settings: ${known.slice(1).join(\", \")}.`));\n const checkColor = (name: string, v: unknown) => {\n if (v === undefined) return;\n const vals = typeof v === \"string\" ? [v] : v && typeof v === \"object\" ? Object.values(v as object) : [v];\n vals.forEach((x) => {\n if (typeof x !== \"string\" || (!parseHex(x) && !/^(rgb|hsl|oklch|oklab|color|var)\\(/.test(x)))\n problems.push(`colors.${name}: \"${String(x)}\" isn't a color. Use a hex value like \"#16a34a\".`);\n });\n };\n Object.entries(t.colors ?? {}).forEach(([k, v]) => checkColor(k, v));\n const derived = [\"primary\", \"secondary\", \"tertiary\", \"success\", \"warning\", \"danger\", \"info\"];\n derived.forEach((k) => {\n const v = (t.colors as Record<string, ColorInput> | undefined)?.[k];\n const light = typeof v === \"string\" ? v : v?.light;\n const alreadyReported = problems.some((p) => p.startsWith(`colors.${k}:`));\n if (light && !parseHex(light) && !alreadyReported) problems.push(`colors.${k}: use a hex color (like \"#16a34a\") so hover and dark shades can be worked out.`);\n });\n if (typeof t.radius === \"string\" && !(t.radius in radiusPresets)) problems.push(`radius: use \"none\", \"sm\", \"md\" or \"lg\", or an object with exact values.`);\n if (typeof t.density === \"string\" && !(t.density in densityUnits)) problems.push(`density: use \"compact\", \"comfortable\" or \"spacious\", or a number like 0.9.`);\n if (typeof t.density === \"number\" && (t.density < 0.6 || t.density > 1.6)) problems.push(`density: ${t.density} is extreme. Use a number between 0.6 and 1.6.`);\n if (t.shadows && ![\"none\", \"subtle\", \"default\", \"strong\"].includes(t.shadows)) problems.push(`shadows: use \"none\", \"subtle\", \"default\" or \"strong\".`);\n return problems;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@astherix/ui",
|
|
3
|
+
"version": "0.40.0",
|
|
4
|
+
"description": "Tailwind-based React UI components for Next.js and Laravel + Inertia apps",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jmdollosa/astherix-ui.git",
|
|
9
|
+
"directory": "packages/ui"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./editor": {
|
|
18
|
+
"types": "./dist/editor.d.ts",
|
|
19
|
+
"import": "./dist/editor.js"
|
|
20
|
+
},
|
|
21
|
+
"./theme.css": "./theme.css",
|
|
22
|
+
"./theme": {
|
|
23
|
+
"types": "./dist/theme.d.ts",
|
|
24
|
+
"import": "./dist/theme.js"
|
|
25
|
+
},
|
|
26
|
+
"./ui.theme.schema.json": "./ui.theme.schema.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"theme.css",
|
|
31
|
+
"ui.theme.schema.json"
|
|
32
|
+
],
|
|
33
|
+
"sideEffects": [
|
|
34
|
+
"*.css"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "rm -rf dist && tsup",
|
|
38
|
+
"dev": "tsup --watch",
|
|
39
|
+
"typecheck": "tsc --noEmit"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@radix-ui/react-slot": "^1.1.0",
|
|
43
|
+
"@tiptap/extensions": "^3.31.3",
|
|
44
|
+
"@tiptap/pm": "^3.31.3",
|
|
45
|
+
"@tiptap/react": "^3.31.3",
|
|
46
|
+
"@tiptap/starter-kit": "^3.31.3",
|
|
47
|
+
"class-variance-authority": "^0.7.1",
|
|
48
|
+
"clsx": "^2.1.1",
|
|
49
|
+
"tailwind-merge": "^3.0.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react": ">=18",
|
|
53
|
+
"react-dom": ">=18",
|
|
54
|
+
"tailwindcss": ">=4"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22.20.4",
|
|
58
|
+
"@types/react": "^19.0.0",
|
|
59
|
+
"@types/react-dom": "^19.0.0",
|
|
60
|
+
"react": "^19.0.0",
|
|
61
|
+
"react-dom": "^19.0.0",
|
|
62
|
+
"tailwindcss": "^4.0.0",
|
|
63
|
+
"tsup": "^8.3.0",
|
|
64
|
+
"typescript": "^5.6.0"
|
|
65
|
+
},
|
|
66
|
+
"bin": {
|
|
67
|
+
"jm-ui": "./dist/cli.js"
|
|
68
|
+
}
|
|
69
|
+
}
|