@kbach/react 1.0.0-beta.0 → 1.0.0-beta.2
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 +45 -68
- package/dist/{chunk-AFY36PRX.mjs → chunk-N5HCQIBB.mjs} +57 -10
- package/dist/chunk-NKR3PSWO.mjs +115 -0
- package/dist/{chunk-VBPQAIZL.mjs → chunk-PAJ6KHDB.mjs} +1 -1
- package/dist/{chunk-G3RJJNSY.mjs → chunk-ZQJKL2MV.mjs} +1 -1
- package/dist/config-BQ_b-qxF.d.mts +105 -0
- package/dist/config-BQ_b-qxF.d.ts +105 -0
- package/dist/index.d.mts +65 -34
- package/dist/index.d.ts +65 -34
- package/dist/index.js +247 -43
- package/dist/index.mjs +89 -32
- package/dist/jsx-dev-runtime.js +57 -10
- package/dist/jsx-dev-runtime.mjs +3 -3
- package/dist/jsx-runtime.js +57 -10
- package/dist/jsx-runtime.mjs +3 -3
- package/dist/vite-plugin.d.mts +15 -1
- package/dist/vite-plugin.d.ts +15 -1
- package/dist/vite-plugin.js +132 -12
- package/dist/vite-plugin.mjs +21 -4
- package/package.json +2 -2
- package/dist/theme-BKAb78KP.d.mts +0 -20
- package/dist/theme-BKAb78KP.d.ts +0 -20
package/dist/index.mjs
CHANGED
|
@@ -4,14 +4,20 @@ import {
|
|
|
4
4
|
isKbachReady,
|
|
5
5
|
isRuntimeCSSDisabled,
|
|
6
6
|
kb
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ZQJKL2MV.mjs";
|
|
8
|
+
import {
|
|
9
|
+
applyKbachConfig,
|
|
10
|
+
ensureColorVariablesInjected,
|
|
11
|
+
parseHexRgb,
|
|
12
|
+
resolveKbachConfig
|
|
13
|
+
} from "./chunk-NKR3PSWO.mjs";
|
|
8
14
|
import {
|
|
9
15
|
BASE_RESET,
|
|
10
16
|
RESET_STYLE_ID,
|
|
11
17
|
defaultTheme,
|
|
12
18
|
getTheme,
|
|
13
19
|
setTheme
|
|
14
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-N5HCQIBB.mjs";
|
|
15
21
|
|
|
16
22
|
// src/KbachReset.tsx
|
|
17
23
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -20,6 +26,9 @@ function KbachReset() {
|
|
|
20
26
|
return /* @__PURE__ */ jsx("style", { id: RESET_STYLE_ID, children: BASE_RESET });
|
|
21
27
|
}
|
|
22
28
|
|
|
29
|
+
// src/useTheme.ts
|
|
30
|
+
import { useMemo, useSyncExternalStore } from "react";
|
|
31
|
+
|
|
23
32
|
// src/darkModeStore.ts
|
|
24
33
|
var STORAGE_KEY = "kbach-theme";
|
|
25
34
|
var listeners = /* @__PURE__ */ new Set();
|
|
@@ -107,31 +116,19 @@ function seedDefaultMode(fallback) {
|
|
|
107
116
|
notify();
|
|
108
117
|
}
|
|
109
118
|
|
|
110
|
-
// src/
|
|
111
|
-
import { useSyncExternalStore } from "react";
|
|
112
|
-
var getServerSnapshot = () => false;
|
|
113
|
-
function useGlobalDarkMode() {
|
|
114
|
-
return useSyncExternalStore(subscribeGlobalDarkMode, getGlobalDarkMode, getServerSnapshot);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// src/ThemeProvider.tsx
|
|
118
|
-
import { useEffect, useMemo, useRef, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
119
|
-
|
|
120
|
-
// src/ThemeContext.tsx
|
|
121
|
-
import { createContext, useContext } from "react";
|
|
122
|
-
var ThemeContext = createContext(null);
|
|
119
|
+
// src/useTheme.ts
|
|
123
120
|
function useTheme() {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return ctx;
|
|
121
|
+
const isDark2 = useSyncExternalStore(subscribeGlobalDarkMode, getGlobalDarkMode, () => false);
|
|
122
|
+
const mode2 = useSyncExternalStore(subscribeGlobalDarkMode, getGlobalThemeMode, () => "system");
|
|
123
|
+
return useMemo(
|
|
124
|
+
() => ({ mode: mode2, isDark: isDark2, setMode: setGlobalThemeMode, toggle: toggleGlobalDarkMode }),
|
|
125
|
+
[mode2, isDark2]
|
|
126
|
+
);
|
|
131
127
|
}
|
|
132
128
|
|
|
133
129
|
// src/ThemeProvider.tsx
|
|
134
|
-
import {
|
|
130
|
+
import { useEffect, useRef } from "react";
|
|
131
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
135
132
|
var mountedProviderCount = 0;
|
|
136
133
|
var warnedMultipleProviders = false;
|
|
137
134
|
function ThemeProvider({ children, defaultMode = "system" }) {
|
|
@@ -140,7 +137,7 @@ function ThemeProvider({ children, defaultMode = "system" }) {
|
|
|
140
137
|
if (mountedProviderCount > 1 && !warnedMultipleProviders) {
|
|
141
138
|
warnedMultipleProviders = true;
|
|
142
139
|
console.warn(
|
|
143
|
-
"[kbach] Multiple <ThemeProvider> instances are mounted at once. Dark-mode state is one global store (
|
|
140
|
+
"[kbach] Multiple <ThemeProvider> instances are mounted at once. Dark-mode state is one global store useTheme() reads everywhere, not scoped per-provider, so their defaultMode seeds will fight over the same state."
|
|
144
141
|
);
|
|
145
142
|
}
|
|
146
143
|
return () => {
|
|
@@ -153,17 +150,76 @@ function ThemeProvider({ children, defaultMode = "system" }) {
|
|
|
153
150
|
seeded.current = true;
|
|
154
151
|
seedDefaultMode(defaultMode);
|
|
155
152
|
}, []);
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
);
|
|
162
|
-
|
|
153
|
+
return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/useColors.ts
|
|
157
|
+
function applyOpacity(color, opacity) {
|
|
158
|
+
const a = Math.max(0, Math.min(1, opacity / 100));
|
|
159
|
+
const varMatch = /^rgb\(var\((--[\w-]+)\)\)$/.exec(color);
|
|
160
|
+
if (varMatch) return `rgba(var(${varMatch[1]}), ${a})`;
|
|
161
|
+
if (color.startsWith("#")) {
|
|
162
|
+
const rgb = parseHexRgb(color);
|
|
163
|
+
return rgb ? `rgba(${rgb.join(",")},${a})` : color;
|
|
164
|
+
}
|
|
165
|
+
if (color.startsWith("rgb(")) return color.replace("rgb(", "rgba(").replace(")", `,${a})`);
|
|
166
|
+
if (color.startsWith("rgba(")) return color.replace(/,\s*[\d.]+\)$/, `,${a})`);
|
|
167
|
+
return color;
|
|
168
|
+
}
|
|
169
|
+
function resolveEntry(name, entry, isDark2) {
|
|
170
|
+
if (typeof entry === "string") return entry;
|
|
171
|
+
const light = parseHexRgb(entry.light);
|
|
172
|
+
const dark = parseHexRgb(entry.dark);
|
|
173
|
+
if (light && dark) return `rgb(var(--kb-color-${name}))`;
|
|
174
|
+
return isDark2 ? entry.dark : entry.light;
|
|
175
|
+
}
|
|
176
|
+
function makeColorsProxy(theme, isDark2) {
|
|
177
|
+
const alpha = (color, opacity) => opacity === void 0 ? color : applyOpacity(color, opacity);
|
|
178
|
+
const cache = /* @__PURE__ */ new Map();
|
|
179
|
+
function resolveNamed(key) {
|
|
180
|
+
let name = key;
|
|
181
|
+
let opacity;
|
|
182
|
+
if (key.includes("/")) {
|
|
183
|
+
const slash = key.indexOf("/");
|
|
184
|
+
name = key.slice(0, slash);
|
|
185
|
+
opacity = Number(key.slice(slash + 1));
|
|
186
|
+
}
|
|
187
|
+
const entry = theme.colors[name];
|
|
188
|
+
if (entry === void 0) return void 0;
|
|
189
|
+
const value = resolveEntry(name, entry, isDark2);
|
|
190
|
+
return opacity === void 0 ? value : applyOpacity(value, opacity);
|
|
191
|
+
}
|
|
192
|
+
function makeFamilyProxy(family) {
|
|
193
|
+
return new Proxy({}, {
|
|
194
|
+
get(_, prop) {
|
|
195
|
+
const key = String(prop);
|
|
196
|
+
if (key === "then") return void 0;
|
|
197
|
+
return resolveNamed(`${family}-${key}`);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return new Proxy({ alpha }, {
|
|
202
|
+
get(_, prop) {
|
|
203
|
+
const key = String(prop);
|
|
204
|
+
if (key === "then") return void 0;
|
|
205
|
+
if (key === "alpha") return alpha;
|
|
206
|
+
const direct = resolveNamed(key);
|
|
207
|
+
if (direct !== void 0) return direct;
|
|
208
|
+
if (!cache.has(key)) cache.set(key, makeFamilyProxy(key));
|
|
209
|
+
return cache.get(key);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
function useColors() {
|
|
214
|
+
const { isDark: isDark2 } = useTheme();
|
|
215
|
+
const theme = getTheme();
|
|
216
|
+
ensureColorVariablesInjected(theme);
|
|
217
|
+
return makeColorsProxy(theme, isDark2);
|
|
163
218
|
}
|
|
164
219
|
export {
|
|
165
220
|
KbachReset,
|
|
166
221
|
ThemeProvider,
|
|
222
|
+
applyKbachConfig,
|
|
167
223
|
defaultTheme,
|
|
168
224
|
disableRuntimeCSS,
|
|
169
225
|
getGlobalDarkMode,
|
|
@@ -173,10 +229,11 @@ export {
|
|
|
173
229
|
isKbachReady,
|
|
174
230
|
isRuntimeCSSDisabled,
|
|
175
231
|
kb,
|
|
232
|
+
resolveKbachConfig,
|
|
176
233
|
setGlobalThemeMode,
|
|
177
234
|
setTheme,
|
|
178
235
|
subscribeGlobalDarkMode,
|
|
179
236
|
toggleGlobalDarkMode,
|
|
180
|
-
|
|
237
|
+
useColors,
|
|
181
238
|
useTheme
|
|
182
239
|
};
|
package/dist/jsx-dev-runtime.js
CHANGED
|
@@ -334,21 +334,54 @@ var defaultTheme = {
|
|
|
334
334
|
...PALETTE,
|
|
335
335
|
surface: { light: "#f9fafb", dark: "#111827" }
|
|
336
336
|
},
|
|
337
|
+
// Real Tailwind's default spacing scale in full — every step is exactly
|
|
338
|
+
// `n * 4px` (Tailwind's base spacing unit), including the fractional
|
|
339
|
+
// `.5` steps (`1.5` -> 6px, `2.5` -> 10px, `3.5` -> 14px) and the "px"
|
|
340
|
+
// literal (a bare 1px, for `p-px`-style hairline spacing). This table is
|
|
341
|
+
// only the "usual stops", though — the Rust/JS engines' shared
|
|
342
|
+
// `spacing_px`/`spacingPx` helper falls back to Tailwind v4's own live
|
|
343
|
+
// `n * 4px` formula for any bare numeric step NOT listed here (`p-0.25`,
|
|
344
|
+
// `gap-1.75`, `w-13`, ...), so this table exists to name the common
|
|
345
|
+
// values and give a theme customization something to override, not to
|
|
346
|
+
// enumerate every resolvable step. "px" is the one exception that must
|
|
347
|
+
// stay listed explicitly — it isn't a number, so the formula can't
|
|
348
|
+
// derive it.
|
|
337
349
|
spacing: {
|
|
338
|
-
// "0" is always a hardcoded zero in real Tailwind's own scale too,
|
|
339
|
-
// regardless of the base spacing unit — needed for `p-0`/`m-0`/
|
|
340
|
-
// `top-0`/`inset-0`/etc., and specifically for `sticky top-0` (a
|
|
341
|
-
// sticky element needs an explicit offset to have any stick point at
|
|
342
|
-
// all; without one it behaves just like `relative`, no different from
|
|
343
|
-
// omitting `top-0` outright). Confirmed missing by hand: this
|
|
344
|
-
// sandbox's own header used `sticky top-0`, which silently never
|
|
345
|
-
// resolved (no "0" spacing key existed) until this was added.
|
|
346
350
|
"0": 0,
|
|
351
|
+
"px": 1,
|
|
352
|
+
"0.5": 2,
|
|
347
353
|
"1": 4,
|
|
354
|
+
"1.5": 6,
|
|
348
355
|
"2": 8,
|
|
356
|
+
"2.5": 10,
|
|
357
|
+
"3": 12,
|
|
358
|
+
"3.5": 14,
|
|
349
359
|
"4": 16,
|
|
360
|
+
"5": 20,
|
|
350
361
|
"6": 24,
|
|
351
|
-
"
|
|
362
|
+
"7": 28,
|
|
363
|
+
"8": 32,
|
|
364
|
+
"9": 36,
|
|
365
|
+
"10": 40,
|
|
366
|
+
"11": 44,
|
|
367
|
+
"12": 48,
|
|
368
|
+
"14": 56,
|
|
369
|
+
"16": 64,
|
|
370
|
+
"20": 80,
|
|
371
|
+
"24": 96,
|
|
372
|
+
"28": 112,
|
|
373
|
+
"32": 128,
|
|
374
|
+
"36": 144,
|
|
375
|
+
"40": 160,
|
|
376
|
+
"44": 176,
|
|
377
|
+
"48": 192,
|
|
378
|
+
"52": 208,
|
|
379
|
+
"56": 224,
|
|
380
|
+
"60": 240,
|
|
381
|
+
"64": 256,
|
|
382
|
+
"72": 288,
|
|
383
|
+
"80": 320,
|
|
384
|
+
"96": 384
|
|
352
385
|
},
|
|
353
386
|
screens: {
|
|
354
387
|
sm: 640,
|
|
@@ -357,7 +390,21 @@ var defaultTheme = {
|
|
|
357
390
|
xl: 1280,
|
|
358
391
|
"2xl": 1536
|
|
359
392
|
},
|
|
360
|
-
|
|
393
|
+
// Byte-for-byte the same three stacks
|
|
394
|
+
// packages/core-engine/src/resolvers/typography.rs's `font_family_value`
|
|
395
|
+
// falls back to — kept here too (rather than leaving this empty and
|
|
396
|
+
// relying purely on the Rust-side fallback) so `defaultTheme.fontFamily.sans`
|
|
397
|
+
// is a real, readable value for anything that wants to extend rather
|
|
398
|
+
// than replace it (see `KbachConfig.extend.fontFamily` in config.ts).
|
|
399
|
+
fontFamily: {
|
|
400
|
+
sans: 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
|
401
|
+
serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',
|
|
402
|
+
mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
|
403
|
+
},
|
|
404
|
+
darkMode: "attribute",
|
|
405
|
+
// Empty by default, matching real Tailwind's own defaults (no forced
|
|
406
|
+
// centering/padding) — see `ContainerConfig`'s own doc comment.
|
|
407
|
+
container: {}
|
|
361
408
|
};
|
|
362
409
|
var activeThemeJson = JSON.stringify(defaultTheme);
|
|
363
410
|
function getThemeJson() {
|
package/dist/jsx-dev-runtime.mjs
CHANGED
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
Fragment,
|
|
3
3
|
jsx,
|
|
4
4
|
jsxs
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-PAJ6KHDB.mjs";
|
|
6
|
+
import "./chunk-ZQJKL2MV.mjs";
|
|
7
|
+
import "./chunk-N5HCQIBB.mjs";
|
|
8
8
|
|
|
9
9
|
// src/jsx-dev-runtime.tsx
|
|
10
10
|
function jsxDEV(type, props, key, isStaticChildren, source, self) {
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -333,21 +333,54 @@ var defaultTheme = {
|
|
|
333
333
|
...PALETTE,
|
|
334
334
|
surface: { light: "#f9fafb", dark: "#111827" }
|
|
335
335
|
},
|
|
336
|
+
// Real Tailwind's default spacing scale in full — every step is exactly
|
|
337
|
+
// `n * 4px` (Tailwind's base spacing unit), including the fractional
|
|
338
|
+
// `.5` steps (`1.5` -> 6px, `2.5` -> 10px, `3.5` -> 14px) and the "px"
|
|
339
|
+
// literal (a bare 1px, for `p-px`-style hairline spacing). This table is
|
|
340
|
+
// only the "usual stops", though — the Rust/JS engines' shared
|
|
341
|
+
// `spacing_px`/`spacingPx` helper falls back to Tailwind v4's own live
|
|
342
|
+
// `n * 4px` formula for any bare numeric step NOT listed here (`p-0.25`,
|
|
343
|
+
// `gap-1.75`, `w-13`, ...), so this table exists to name the common
|
|
344
|
+
// values and give a theme customization something to override, not to
|
|
345
|
+
// enumerate every resolvable step. "px" is the one exception that must
|
|
346
|
+
// stay listed explicitly — it isn't a number, so the formula can't
|
|
347
|
+
// derive it.
|
|
336
348
|
spacing: {
|
|
337
|
-
// "0" is always a hardcoded zero in real Tailwind's own scale too,
|
|
338
|
-
// regardless of the base spacing unit — needed for `p-0`/`m-0`/
|
|
339
|
-
// `top-0`/`inset-0`/etc., and specifically for `sticky top-0` (a
|
|
340
|
-
// sticky element needs an explicit offset to have any stick point at
|
|
341
|
-
// all; without one it behaves just like `relative`, no different from
|
|
342
|
-
// omitting `top-0` outright). Confirmed missing by hand: this
|
|
343
|
-
// sandbox's own header used `sticky top-0`, which silently never
|
|
344
|
-
// resolved (no "0" spacing key existed) until this was added.
|
|
345
349
|
"0": 0,
|
|
350
|
+
"px": 1,
|
|
351
|
+
"0.5": 2,
|
|
346
352
|
"1": 4,
|
|
353
|
+
"1.5": 6,
|
|
347
354
|
"2": 8,
|
|
355
|
+
"2.5": 10,
|
|
356
|
+
"3": 12,
|
|
357
|
+
"3.5": 14,
|
|
348
358
|
"4": 16,
|
|
359
|
+
"5": 20,
|
|
349
360
|
"6": 24,
|
|
350
|
-
"
|
|
361
|
+
"7": 28,
|
|
362
|
+
"8": 32,
|
|
363
|
+
"9": 36,
|
|
364
|
+
"10": 40,
|
|
365
|
+
"11": 44,
|
|
366
|
+
"12": 48,
|
|
367
|
+
"14": 56,
|
|
368
|
+
"16": 64,
|
|
369
|
+
"20": 80,
|
|
370
|
+
"24": 96,
|
|
371
|
+
"28": 112,
|
|
372
|
+
"32": 128,
|
|
373
|
+
"36": 144,
|
|
374
|
+
"40": 160,
|
|
375
|
+
"44": 176,
|
|
376
|
+
"48": 192,
|
|
377
|
+
"52": 208,
|
|
378
|
+
"56": 224,
|
|
379
|
+
"60": 240,
|
|
380
|
+
"64": 256,
|
|
381
|
+
"72": 288,
|
|
382
|
+
"80": 320,
|
|
383
|
+
"96": 384
|
|
351
384
|
},
|
|
352
385
|
screens: {
|
|
353
386
|
sm: 640,
|
|
@@ -356,7 +389,21 @@ var defaultTheme = {
|
|
|
356
389
|
xl: 1280,
|
|
357
390
|
"2xl": 1536
|
|
358
391
|
},
|
|
359
|
-
|
|
392
|
+
// Byte-for-byte the same three stacks
|
|
393
|
+
// packages/core-engine/src/resolvers/typography.rs's `font_family_value`
|
|
394
|
+
// falls back to — kept here too (rather than leaving this empty and
|
|
395
|
+
// relying purely on the Rust-side fallback) so `defaultTheme.fontFamily.sans`
|
|
396
|
+
// is a real, readable value for anything that wants to extend rather
|
|
397
|
+
// than replace it (see `KbachConfig.extend.fontFamily` in config.ts).
|
|
398
|
+
fontFamily: {
|
|
399
|
+
sans: 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
|
400
|
+
serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',
|
|
401
|
+
mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
|
402
|
+
},
|
|
403
|
+
darkMode: "attribute",
|
|
404
|
+
// Empty by default, matching real Tailwind's own defaults (no forced
|
|
405
|
+
// centering/padding) — see `ContainerConfig`'s own doc comment.
|
|
406
|
+
container: {}
|
|
360
407
|
};
|
|
361
408
|
var activeThemeJson = JSON.stringify(defaultTheme);
|
|
362
409
|
function getThemeJson() {
|
package/dist/jsx-runtime.mjs
CHANGED
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
Fragment,
|
|
3
3
|
jsx,
|
|
4
4
|
jsxs
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-PAJ6KHDB.mjs";
|
|
6
|
+
import "./chunk-ZQJKL2MV.mjs";
|
|
7
|
+
import "./chunk-N5HCQIBB.mjs";
|
|
8
8
|
export {
|
|
9
9
|
Fragment,
|
|
10
10
|
jsx,
|
package/dist/vite-plugin.d.mts
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { T as ThemeConfig } from './
|
|
2
|
+
import { T as ThemeConfig, K as KbachConfig } from './config-BQ_b-qxF.mjs';
|
|
3
3
|
|
|
4
4
|
interface KbachPluginOptions {
|
|
5
5
|
/** Defaults to the same defaultTheme the runtime uses — pass your own to keep both in sync. */
|
|
6
6
|
theme?: ThemeConfig;
|
|
7
|
+
/**
|
|
8
|
+
* A `kbach.config.js`-style config object, resolved the same way
|
|
9
|
+
* `applyKbachConfig()` resolves it for the runtime — import the SAME
|
|
10
|
+
* config file here and in your app's own `applyKbachConfig()` call, and
|
|
11
|
+
* build-time CSS generation and runtime resolution always agree, with no
|
|
12
|
+
* synchronization step needed (`resolveKbachConfig` is a pure function:
|
|
13
|
+
* same input, same output, in Node.js here and in the browser there).
|
|
14
|
+
* Ignored if `theme` is also supplied. Only needed for an explicit
|
|
15
|
+
* override, though — if BOTH this and `theme` are omitted, `kbach()`
|
|
16
|
+
* auto-discovers a `kbach.config.js` at the project root on its own (see
|
|
17
|
+
* `loadConfigFile`'s own doc comment), the same zero-config convenience
|
|
18
|
+
* `tailwind.config.js` gets from Tailwind's own PostCSS plugin.
|
|
19
|
+
*/
|
|
20
|
+
config?: KbachConfig;
|
|
7
21
|
/** Directories to scan for class strings (relative to Vite root). */
|
|
8
22
|
include?: string[];
|
|
9
23
|
/**
|
package/dist/vite-plugin.d.ts
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { T as ThemeConfig } from './
|
|
2
|
+
import { T as ThemeConfig, K as KbachConfig } from './config-BQ_b-qxF.js';
|
|
3
3
|
|
|
4
4
|
interface KbachPluginOptions {
|
|
5
5
|
/** Defaults to the same defaultTheme the runtime uses — pass your own to keep both in sync. */
|
|
6
6
|
theme?: ThemeConfig;
|
|
7
|
+
/**
|
|
8
|
+
* A `kbach.config.js`-style config object, resolved the same way
|
|
9
|
+
* `applyKbachConfig()` resolves it for the runtime — import the SAME
|
|
10
|
+
* config file here and in your app's own `applyKbachConfig()` call, and
|
|
11
|
+
* build-time CSS generation and runtime resolution always agree, with no
|
|
12
|
+
* synchronization step needed (`resolveKbachConfig` is a pure function:
|
|
13
|
+
* same input, same output, in Node.js here and in the browser there).
|
|
14
|
+
* Ignored if `theme` is also supplied. Only needed for an explicit
|
|
15
|
+
* override, though — if BOTH this and `theme` are omitted, `kbach()`
|
|
16
|
+
* auto-discovers a `kbach.config.js` at the project root on its own (see
|
|
17
|
+
* `loadConfigFile`'s own doc comment), the same zero-config convenience
|
|
18
|
+
* `tailwind.config.js` gets from Tailwind's own PostCSS plugin.
|
|
19
|
+
*/
|
|
20
|
+
config?: KbachConfig;
|
|
7
21
|
/** Directories to scan for class strings (relative to Vite root). */
|
|
8
22
|
include?: string[];
|
|
9
23
|
/**
|
package/dist/vite-plugin.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(vite_plugin_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(vite_plugin_exports);
|
|
26
26
|
var import_fs2 = require("fs");
|
|
27
27
|
var import_path3 = require("path");
|
|
28
|
+
var import_module = require("module");
|
|
28
29
|
|
|
29
30
|
// src/vite-plugin/wasmNode.ts
|
|
30
31
|
var import_node = require("@kbach/core-engine/node");
|
|
@@ -1421,21 +1422,54 @@ var defaultTheme = {
|
|
|
1421
1422
|
...PALETTE,
|
|
1422
1423
|
surface: { light: "#f9fafb", dark: "#111827" }
|
|
1423
1424
|
},
|
|
1425
|
+
// Real Tailwind's default spacing scale in full — every step is exactly
|
|
1426
|
+
// `n * 4px` (Tailwind's base spacing unit), including the fractional
|
|
1427
|
+
// `.5` steps (`1.5` -> 6px, `2.5` -> 10px, `3.5` -> 14px) and the "px"
|
|
1428
|
+
// literal (a bare 1px, for `p-px`-style hairline spacing). This table is
|
|
1429
|
+
// only the "usual stops", though — the Rust/JS engines' shared
|
|
1430
|
+
// `spacing_px`/`spacingPx` helper falls back to Tailwind v4's own live
|
|
1431
|
+
// `n * 4px` formula for any bare numeric step NOT listed here (`p-0.25`,
|
|
1432
|
+
// `gap-1.75`, `w-13`, ...), so this table exists to name the common
|
|
1433
|
+
// values and give a theme customization something to override, not to
|
|
1434
|
+
// enumerate every resolvable step. "px" is the one exception that must
|
|
1435
|
+
// stay listed explicitly — it isn't a number, so the formula can't
|
|
1436
|
+
// derive it.
|
|
1424
1437
|
spacing: {
|
|
1425
|
-
// "0" is always a hardcoded zero in real Tailwind's own scale too,
|
|
1426
|
-
// regardless of the base spacing unit — needed for `p-0`/`m-0`/
|
|
1427
|
-
// `top-0`/`inset-0`/etc., and specifically for `sticky top-0` (a
|
|
1428
|
-
// sticky element needs an explicit offset to have any stick point at
|
|
1429
|
-
// all; without one it behaves just like `relative`, no different from
|
|
1430
|
-
// omitting `top-0` outright). Confirmed missing by hand: this
|
|
1431
|
-
// sandbox's own header used `sticky top-0`, which silently never
|
|
1432
|
-
// resolved (no "0" spacing key existed) until this was added.
|
|
1433
1438
|
"0": 0,
|
|
1439
|
+
"px": 1,
|
|
1440
|
+
"0.5": 2,
|
|
1434
1441
|
"1": 4,
|
|
1442
|
+
"1.5": 6,
|
|
1435
1443
|
"2": 8,
|
|
1444
|
+
"2.5": 10,
|
|
1445
|
+
"3": 12,
|
|
1446
|
+
"3.5": 14,
|
|
1436
1447
|
"4": 16,
|
|
1448
|
+
"5": 20,
|
|
1437
1449
|
"6": 24,
|
|
1438
|
-
"
|
|
1450
|
+
"7": 28,
|
|
1451
|
+
"8": 32,
|
|
1452
|
+
"9": 36,
|
|
1453
|
+
"10": 40,
|
|
1454
|
+
"11": 44,
|
|
1455
|
+
"12": 48,
|
|
1456
|
+
"14": 56,
|
|
1457
|
+
"16": 64,
|
|
1458
|
+
"20": 80,
|
|
1459
|
+
"24": 96,
|
|
1460
|
+
"28": 112,
|
|
1461
|
+
"32": 128,
|
|
1462
|
+
"36": 144,
|
|
1463
|
+
"40": 160,
|
|
1464
|
+
"44": 176,
|
|
1465
|
+
"48": 192,
|
|
1466
|
+
"52": 208,
|
|
1467
|
+
"56": 224,
|
|
1468
|
+
"60": 240,
|
|
1469
|
+
"64": 256,
|
|
1470
|
+
"72": 288,
|
|
1471
|
+
"80": 320,
|
|
1472
|
+
"96": 384
|
|
1439
1473
|
},
|
|
1440
1474
|
screens: {
|
|
1441
1475
|
sm: 640,
|
|
@@ -1444,10 +1478,83 @@ var defaultTheme = {
|
|
|
1444
1478
|
xl: 1280,
|
|
1445
1479
|
"2xl": 1536
|
|
1446
1480
|
},
|
|
1447
|
-
|
|
1481
|
+
// Byte-for-byte the same three stacks
|
|
1482
|
+
// packages/core-engine/src/resolvers/typography.rs's `font_family_value`
|
|
1483
|
+
// falls back to — kept here too (rather than leaving this empty and
|
|
1484
|
+
// relying purely on the Rust-side fallback) so `defaultTheme.fontFamily.sans`
|
|
1485
|
+
// is a real, readable value for anything that wants to extend rather
|
|
1486
|
+
// than replace it (see `KbachConfig.extend.fontFamily` in config.ts).
|
|
1487
|
+
fontFamily: {
|
|
1488
|
+
sans: 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
|
1489
|
+
serif: 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif',
|
|
1490
|
+
mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
|
|
1491
|
+
},
|
|
1492
|
+
darkMode: "attribute",
|
|
1493
|
+
// Empty by default, matching real Tailwind's own defaults (no forced
|
|
1494
|
+
// centering/padding) — see `ContainerConfig`'s own doc comment.
|
|
1495
|
+
container: {}
|
|
1448
1496
|
};
|
|
1449
1497
|
var activeThemeJson = JSON.stringify(defaultTheme);
|
|
1450
1498
|
|
|
1499
|
+
// src/colorVariables.ts
|
|
1500
|
+
function parseHexRgb(hex) {
|
|
1501
|
+
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
|
|
1502
|
+
if (!m) return null;
|
|
1503
|
+
let h = m[1];
|
|
1504
|
+
if (h.length === 3) h = h.split("").map((c) => c + c).join("");
|
|
1505
|
+
const n = parseInt(h, 16);
|
|
1506
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// src/config.ts
|
|
1510
|
+
function applyOpacityToHex(hex, opacityText) {
|
|
1511
|
+
const rgb = parseHexRgb(hex);
|
|
1512
|
+
if (!rgb) return hex;
|
|
1513
|
+
const a = Number(opacityText) / 100;
|
|
1514
|
+
const clamped = Number.isFinite(a) ? Math.min(1, Math.max(0, a)) : 1;
|
|
1515
|
+
return `rgba(${rgb.join(",")},${clamped})`;
|
|
1516
|
+
}
|
|
1517
|
+
function resolveColorEntry(value, lookup) {
|
|
1518
|
+
if (typeof value !== "string") return value;
|
|
1519
|
+
const slash = value.indexOf("/");
|
|
1520
|
+
const name = slash === -1 ? value : value.slice(0, slash);
|
|
1521
|
+
const opacity = slash === -1 ? void 0 : value.slice(slash + 1);
|
|
1522
|
+
const base = lookup(name);
|
|
1523
|
+
if (base === void 0) return value;
|
|
1524
|
+
if (opacity === void 0) return base;
|
|
1525
|
+
return typeof base === "string" ? applyOpacityToHex(base, opacity) : { light: applyOpacityToHex(base.light, opacity), dark: applyOpacityToHex(base.dark, opacity) };
|
|
1526
|
+
}
|
|
1527
|
+
function resolveColors(extend, existing) {
|
|
1528
|
+
const resolved = {};
|
|
1529
|
+
const lookup = (name) => resolved[name] ?? existing[name];
|
|
1530
|
+
for (const [name, value] of Object.entries(extend)) {
|
|
1531
|
+
resolved[name] = resolveColorEntry(value, lookup);
|
|
1532
|
+
}
|
|
1533
|
+
return resolved;
|
|
1534
|
+
}
|
|
1535
|
+
function resolveKbachConfig(config) {
|
|
1536
|
+
let theme = { ...defaultTheme, ...config.theme };
|
|
1537
|
+
if (config.extend?.colors) {
|
|
1538
|
+
theme = { ...theme, colors: { ...theme.colors, ...resolveColors(config.extend.colors, theme.colors) } };
|
|
1539
|
+
}
|
|
1540
|
+
if (config.extend?.spacing) {
|
|
1541
|
+
theme = { ...theme, spacing: { ...theme.spacing, ...config.extend.spacing } };
|
|
1542
|
+
}
|
|
1543
|
+
if (config.extend?.screens) {
|
|
1544
|
+
theme = { ...theme, screens: { ...theme.screens, ...config.extend.screens } };
|
|
1545
|
+
}
|
|
1546
|
+
if (config.extend?.fontFamily) {
|
|
1547
|
+
theme = { ...theme, fontFamily: { ...theme.fontFamily, ...config.extend.fontFamily } };
|
|
1548
|
+
}
|
|
1549
|
+
if (config.extend?.container) {
|
|
1550
|
+
theme = { ...theme, container: { ...theme.container, ...config.extend.container } };
|
|
1551
|
+
}
|
|
1552
|
+
if (config.darkMode) {
|
|
1553
|
+
theme = { ...theme, darkMode: config.darkMode };
|
|
1554
|
+
}
|
|
1555
|
+
return theme;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1451
1558
|
// src/vite-plugin/index.ts
|
|
1452
1559
|
var DEFAULT_SCAN_DIRS = ["src", "app", "pages", "components"];
|
|
1453
1560
|
function normPath(p) {
|
|
@@ -1465,9 +1572,15 @@ function writeFileEnsuringDir(path, content) {
|
|
|
1465
1572
|
(0, import_fs2.mkdirSync)((0, import_path3.dirname)(path), { recursive: true });
|
|
1466
1573
|
(0, import_fs2.writeFileSync)(path, content, "utf-8");
|
|
1467
1574
|
}
|
|
1575
|
+
function loadConfigFile(root) {
|
|
1576
|
+
const path = (0, import_path3.join)(root, "kbach.config.js");
|
|
1577
|
+
if (!(0, import_fs2.existsSync)(path)) return void 0;
|
|
1578
|
+
const mod = (0, import_module.createRequire)(root)(path);
|
|
1579
|
+
return mod.default ?? mod;
|
|
1580
|
+
}
|
|
1468
1581
|
function kbach(options = {}) {
|
|
1469
|
-
|
|
1470
|
-
|
|
1582
|
+
let theme = options.theme ?? (options.config ? resolveKbachConfig(options.config) : defaultTheme);
|
|
1583
|
+
let themeJson = JSON.stringify(theme);
|
|
1471
1584
|
const includeDirs = options.include ?? DEFAULT_SCAN_DIRS;
|
|
1472
1585
|
const safelist = options.safelist ?? [];
|
|
1473
1586
|
let root = process.cwd();
|
|
@@ -1547,6 +1660,13 @@ function kbach(options = {}) {
|
|
|
1547
1660
|
enforce: "pre",
|
|
1548
1661
|
configResolved(resolved) {
|
|
1549
1662
|
root = resolved.root;
|
|
1663
|
+
if (!options.theme && !options.config) {
|
|
1664
|
+
const discovered = loadConfigFile(root);
|
|
1665
|
+
if (discovered) {
|
|
1666
|
+
theme = resolveKbachConfig(discovered);
|
|
1667
|
+
themeJson = JSON.stringify(theme);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1550
1670
|
},
|
|
1551
1671
|
buildStart() {
|
|
1552
1672
|
projectCssClasses = scanProjectCssSelectors(root, includeDirs);
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveKbachConfig
|
|
3
|
+
} from "./chunk-NKR3PSWO.mjs";
|
|
1
4
|
import {
|
|
2
5
|
buildResetCSS,
|
|
3
6
|
defaultTheme
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-N5HCQIBB.mjs";
|
|
5
8
|
|
|
6
9
|
// src/vite-plugin/index.ts
|
|
7
|
-
import { readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
|
|
10
|
+
import { readFileSync as readFileSync2, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
8
11
|
import { join as join3, dirname } from "path";
|
|
12
|
+
import { createRequire } from "module";
|
|
9
13
|
|
|
10
14
|
// src/vite-plugin/wasmNode.ts
|
|
11
15
|
import { generate_css } from "@kbach/core-engine/node";
|
|
@@ -1053,9 +1057,15 @@ function writeFileEnsuringDir(path, content) {
|
|
|
1053
1057
|
mkdirSync(dirname(path), { recursive: true });
|
|
1054
1058
|
writeFileSync(path, content, "utf-8");
|
|
1055
1059
|
}
|
|
1060
|
+
function loadConfigFile(root) {
|
|
1061
|
+
const path = join3(root, "kbach.config.js");
|
|
1062
|
+
if (!existsSync(path)) return void 0;
|
|
1063
|
+
const mod = createRequire(root)(path);
|
|
1064
|
+
return mod.default ?? mod;
|
|
1065
|
+
}
|
|
1056
1066
|
function kbach(options = {}) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1067
|
+
let theme = options.theme ?? (options.config ? resolveKbachConfig(options.config) : defaultTheme);
|
|
1068
|
+
let themeJson = JSON.stringify(theme);
|
|
1059
1069
|
const includeDirs = options.include ?? DEFAULT_SCAN_DIRS;
|
|
1060
1070
|
const safelist = options.safelist ?? [];
|
|
1061
1071
|
let root = process.cwd();
|
|
@@ -1135,6 +1145,13 @@ function kbach(options = {}) {
|
|
|
1135
1145
|
enforce: "pre",
|
|
1136
1146
|
configResolved(resolved) {
|
|
1137
1147
|
root = resolved.root;
|
|
1148
|
+
if (!options.theme && !options.config) {
|
|
1149
|
+
const discovered = loadConfigFile(root);
|
|
1150
|
+
if (discovered) {
|
|
1151
|
+
theme = resolveKbachConfig(discovered);
|
|
1152
|
+
themeJson = JSON.stringify(theme);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1138
1155
|
},
|
|
1139
1156
|
buildStart() {
|
|
1140
1157
|
projectCssClasses = scanProjectCssSelectors(root, includeDirs);
|