windrunner 1.0.3 → 1.1.1

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.d.ts CHANGED
@@ -1,33 +1,62 @@
1
- export interface WindrunnerOptions {
2
- id?: string;
3
- autoStart?: boolean;
4
- preflight?: boolean;
5
- compatMode?: "none" | "full";
6
- compatStyleId?: string;
7
- compatGenerateCss?: (options: Record<string, any>) => string;
8
- theme?: Record<string, any>;
9
- onReady?: () => void;
10
- [key: string]: any;
11
- }
12
-
13
- export interface Runtime {
14
- processClassName(className: string): string | undefined;
15
- processClassList(classList: string | string[] | ArrayLike<string>): string[];
16
- processElement(el: Element | null): void;
17
- scan(root?: Document | Element): void;
18
- observe(root?: Element): void;
19
- flush(): void;
20
- start(): void;
21
- disconnect(): void;
22
- isCompatLoaded(): boolean;
23
- getCacheSize(): number;
24
- getInsertedRuleCount(): number;
25
- }
26
-
27
- export function createWindrunner(options?: WindrunnerOptions): Runtime;
28
- export function compileRuntimeClassNameWithContext(className: string, context: any): string;
29
- export function compileClass(className: string, options?: any): string;
30
- export function parseClass(className: string, screens?: Record<string,string>, containers?: Record<string,string>): { original: string; baseToken: string; variants: string[]; breakpoint: string | null; containerBreakpoint: string | null; important: boolean; starting: boolean } | null;
31
-
32
- declare function windrunner(options?: WindrunnerOptions): Runtime;
33
- export default windrunner;
1
+ // ─── Plugin Types ─────────────────────────────────────────────────────────────
2
+
3
+ export interface PluginAPI {
4
+ addUtility(pattern: string | RegExp, handler: string | ((match: RegExpMatchArray, theme: any) => string)): void;
5
+ addUtilities(utilities: Record<string, string | ((match: RegExpMatchArray, theme: any) => string)>): void;
6
+ addVariant(name: string, handler: (selector: string) => string): void;
7
+ addVariants(variants: Record<string, (selector: string) => string>): void;
8
+ theme(key?: string): any;
9
+ config(): any;
10
+ }
11
+
12
+ export interface Plugin {
13
+ __isWindrunnerPlugin: true;
14
+ handler: (api: PluginAPI) => void;
15
+ }
16
+
17
+ export function plugin(handler: (api: PluginAPI) => void): Plugin;
18
+
19
+ export function defineUtilities(definitions: Record<string, string | Record<string, string>>): Record<string, string>;
20
+
21
+ export function defineResponsiveUtilities(
22
+ base: string,
23
+ values: Record<string, string>,
24
+ toDeclaration: (key: string, value: string) => string
25
+ ): Record<string, string>;
26
+
27
+ // ─── Runtime Options with Plugins ─────────────────────────────────────────────
28
+
29
+ export interface WindrunnerOptions {
30
+ id?: string;
31
+ autoStart?: boolean;
32
+ preflight?: boolean;
33
+ compatMode?: "none" | "full";
34
+ compatStyleId?: string;
35
+ compatGenerateCss?: (options: Record<string, any>) => string;
36
+ theme?: Record<string, any>;
37
+ plugins?: Plugin[];
38
+ onReady?: () => void;
39
+ [key: string]: any;
40
+ }
41
+
42
+ export interface Runtime {
43
+ processClassName(className: string): string | undefined;
44
+ processClassList(classList: string | string[] | ArrayLike<string>): string[];
45
+ processElement(el: Element | null): void;
46
+ scan(root?: Document | Element): void;
47
+ observe(root?: Element): void;
48
+ flush(): void;
49
+ start(): void;
50
+ disconnect(): void;
51
+ isCompatLoaded(): boolean;
52
+ getCacheSize(): number;
53
+ getInsertedRuleCount(): number;
54
+ }
55
+
56
+ export function createWindrunner(options?: WindrunnerOptions): Runtime;
57
+ export function compileRuntimeClassNameWithContext(className: string, context: any): string;
58
+ export function compileClass(className: string, options?: any): string;
59
+ export function parseClass(className: string, screens?: Record<string,string>, containers?: Record<string,string>): { original: string; baseToken: string; variants: string[]; breakpoint: string | null; containerBreakpoint: string | null; important: boolean; starting: boolean } | null;
60
+
61
+ declare function windrunner(options?: WindrunnerOptions): Runtime;
62
+ export default windrunner;
package/dist/index.esm.js CHANGED
@@ -1436,6 +1436,13 @@ function getConfigOptions(options = {}, pluginKeys = []) {
1436
1436
  };
1437
1437
  }
1438
1438
 
1439
+ // src/constants.js
1440
+ var TIME_VALUE_WITH_UNIT_REGEX = /^\d+(?:\.\d+)?(?:ms|s)$/;
1441
+ var TIME_VALUE_NUMERIC_REGEX = /^\d+(?:\.\d+)?$/;
1442
+ var CSS_ESCAPE_BACKSLASH_REGEX = /\\/g;
1443
+ var CSS_ESCAPE_SPECIAL_CHARS_REGEX = /([ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g;
1444
+ var CSS_ESCAPE_LEADING_DIGIT_REGEX = /^(\d)/;
1445
+
1439
1446
  // src/resolvers.js
1440
1447
  function resolveArbitraryValue(valueKey) {
1441
1448
  if (valueKey.startsWith("[") && valueKey.endsWith("]")) {
@@ -1455,8 +1462,8 @@ function resolveThemeValue(scale, valueKey) {
1455
1462
  function resolveTimeValue(valueKey) {
1456
1463
  const arbitrary = resolveArbitraryValue(valueKey);
1457
1464
  if (arbitrary !== void 0) return arbitrary;
1458
- if (/^\d+(?:\.\d+)?(?:ms|s)$/.test(valueKey)) return valueKey;
1459
- if (/^\d+(?:\.\d+)?$/.test(valueKey)) return `${valueKey}ms`;
1465
+ if (TIME_VALUE_WITH_UNIT_REGEX.test(valueKey)) return valueKey;
1466
+ if (TIME_VALUE_NUMERIC_REGEX.test(valueKey)) return `${valueKey}ms`;
1460
1467
  return void 0;
1461
1468
  }
1462
1469
  function resolveColorValue(colors, colorKey) {
@@ -1492,15 +1499,21 @@ function resolveColorWithOpacity(colors, rawKey) {
1492
1499
  const color = resolveColorValue(colors, colorKey);
1493
1500
  if (color === void 0) return void 0;
1494
1501
  const arbOpacity = resolveArbitraryValue(opacityStr);
1495
- const opacityVal = arbOpacity !== void 0 ? arbOpacity : String(parseFloat(opacityStr) / 100);
1496
- if (!opacityVal || opacityVal === "NaN") return void 0;
1497
- return `color-mix(in oklch, ${color} ${parseFloat(opacityStr)}%, transparent)`;
1502
+ if (arbOpacity !== void 0) {
1503
+ const opacityVal = parseFloat(arbOpacity);
1504
+ if (isNaN(opacityVal)) return void 0;
1505
+ const finalOpacity = opacityVal <= 1 ? opacityVal * 100 : opacityVal;
1506
+ return `color-mix(in oklch, ${color} ${finalOpacity}%, transparent)`;
1507
+ }
1508
+ const opacityNum = parseFloat(opacityStr);
1509
+ if (isNaN(opacityNum)) return void 0;
1510
+ return `color-mix(in oklch, ${color} ${opacityNum}%, transparent)`;
1498
1511
  }
1499
1512
  function escapeCssIdentifier(value) {
1500
1513
  if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
1501
1514
  return CSS.escape(value);
1502
1515
  }
1503
- return String(value).replace(/\\/g, "\\\\").replace(/([ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, "\\$1").replace(/^(\d)/, "\\3$1 ");
1516
+ return String(value).replace(CSS_ESCAPE_BACKSLASH_REGEX, "\\\\").replace(CSS_ESCAPE_SPECIAL_CHARS_REGEX, "\\$1").replace(CSS_ESCAPE_LEADING_DIGIT_REGEX, "\\3$1 ");
1504
1517
  }
1505
1518
  function appendImportant(declaration, isImportant) {
1506
1519
  if (!isImportant) return declaration;
@@ -3260,13 +3273,340 @@ function buildDivideDeclaration(baseToken, theme2) {
3260
3273
  return void 0;
3261
3274
  }
3262
3275
 
3276
+ // src/plugins.js
3277
+ var PluginRegistry = class {
3278
+ constructor() {
3279
+ this.utilities = /* @__PURE__ */ new Map();
3280
+ this.variants = /* @__PURE__ */ new Map();
3281
+ }
3282
+ /**
3283
+ * Register a custom utility
3284
+ * @param {string|RegExp} pattern - Class name pattern (e.g., "glass" or /^glass-(.+)$/)
3285
+ * @param {Function|string} handler - Function that returns CSS or CSS string
3286
+ */
3287
+ addUtility(pattern, handler) {
3288
+ this.utilities.set(pattern, handler);
3289
+ }
3290
+ /**
3291
+ * Register multiple utilities at once
3292
+ * @param {Object} utilities - Object mapping patterns to handlers
3293
+ */
3294
+ addUtilities(utilities) {
3295
+ Object.entries(utilities).forEach(([pattern, handler]) => {
3296
+ this.addUtility(pattern, handler);
3297
+ });
3298
+ }
3299
+ /**
3300
+ * Register a custom variant
3301
+ * @param {string} name - Variant name (e.g., "parent-hover")
3302
+ * @param {Function} handler - Function that transforms selector
3303
+ */
3304
+ addVariant(name, handler) {
3305
+ this.variants.set(name, handler);
3306
+ }
3307
+ /**
3308
+ * Register multiple variants at once
3309
+ * @param {Object} variants - Object mapping names to handlers
3310
+ */
3311
+ addVariants(variants2) {
3312
+ Object.entries(variants2).forEach(([name, handler]) => {
3313
+ this.addVariant(name, handler);
3314
+ });
3315
+ }
3316
+ /**
3317
+ * Check if a token matches any custom utility pattern
3318
+ * @param {string} token - The base token to match
3319
+ * @returns {Object|null} - { handler, match } or null
3320
+ */
3321
+ matchUtility(token) {
3322
+ for (const [pattern, handler] of this.utilities) {
3323
+ if (pattern instanceof RegExp) {
3324
+ const match = pattern.exec(token);
3325
+ if (match) {
3326
+ return { handler, match };
3327
+ }
3328
+ } else if (pattern === token) {
3329
+ return { handler, match: [token] };
3330
+ }
3331
+ }
3332
+ return null;
3333
+ }
3334
+ /**
3335
+ * Check if a variant name matches any custom variant
3336
+ * @param {string} variantName - The variant to check
3337
+ * @returns {Function|null} - Handler function or null
3338
+ */
3339
+ matchVariant(variantName) {
3340
+ return this.variants.get(variantName) || null;
3341
+ }
3342
+ /**
3343
+ * Get all registered utility patterns (for debugging)
3344
+ */
3345
+ getUtilities() {
3346
+ return Array.from(this.utilities.keys());
3347
+ }
3348
+ /**
3349
+ * Get all registered variant names (for debugging)
3350
+ */
3351
+ getVariants() {
3352
+ return Array.from(this.variants.keys());
3353
+ }
3354
+ /**
3355
+ * Clear all registered plugins
3356
+ */
3357
+ clear() {
3358
+ this.utilities.clear();
3359
+ this.variants.clear();
3360
+ }
3361
+ };
3362
+ function plugin(handler) {
3363
+ if (typeof handler !== "function") {
3364
+ throw new Error("Plugin handler must be a function");
3365
+ }
3366
+ return {
3367
+ __isWindrunnerPlugin: true,
3368
+ handler
3369
+ };
3370
+ }
3371
+ function isPlugin(obj) {
3372
+ return obj && obj.__isWindrunnerPlugin === true && typeof obj.handler === "function";
3373
+ }
3374
+ function defineUtilities(definitions) {
3375
+ const utilities = {};
3376
+ Object.entries(definitions).forEach(([className, css]) => {
3377
+ utilities[className] = typeof css === "string" ? css : objectToCss(css);
3378
+ });
3379
+ return utilities;
3380
+ }
3381
+ function defineResponsiveUtilities(base, values, toDeclaration) {
3382
+ const utilities = {};
3383
+ Object.entries(values).forEach(([key, value]) => {
3384
+ const className = key === "DEFAULT" ? base : `${base}-${key}`;
3385
+ utilities[className] = toDeclaration(key, value);
3386
+ });
3387
+ return utilities;
3388
+ }
3389
+ function objectToCss(obj) {
3390
+ return Object.entries(obj).map(([prop, value]) => `${prop}: ${value};`).join(" ");
3391
+ }
3392
+
3263
3393
  // src/compiler.js
3264
- function compileBaseToken(baseToken, theme2) {
3394
+ var PREFIX_ROUTER = {
3395
+ // Layout & Display
3396
+ "block": [buildLayoutDeclaration],
3397
+ "inline": [buildLayoutDeclaration],
3398
+ "flex": [buildFlexGridDeclaration, buildLayoutDeclaration],
3399
+ "grid": [buildFlexGridDeclaration, buildLayoutDeclaration],
3400
+ "hidden": [buildLayoutDeclaration],
3401
+ "table": [buildLayoutDeclaration],
3402
+ "flow": [buildLayoutDeclaration],
3403
+ // Position
3404
+ "static": [buildLayoutDeclaration],
3405
+ "fixed": [buildLayoutDeclaration],
3406
+ "absolute": [buildLayoutDeclaration],
3407
+ "relative": [buildLayoutDeclaration],
3408
+ "sticky": [buildLayoutDeclaration],
3409
+ "inset": [buildPositionInsetDeclaration, buildInsetShadowDeclaration, buildInsetRingDeclaration],
3410
+ "top": [buildPositionInsetDeclaration],
3411
+ "right": [buildPositionInsetDeclaration],
3412
+ "bottom": [buildPositionInsetDeclaration],
3413
+ "left": [buildPositionInsetDeclaration],
3414
+ "start": [buildPositionInsetDeclaration],
3415
+ "end": [buildPositionInsetDeclaration],
3416
+ "z": [buildLayoutDeclaration],
3417
+ // Spacing
3418
+ "m": [buildSpacingDeclaration],
3419
+ "mx": [buildSpacingDeclaration],
3420
+ "my": [buildSpacingDeclaration],
3421
+ "mt": [buildSpacingDeclaration],
3422
+ "mr": [buildSpacingDeclaration],
3423
+ "mb": [buildSpacingDeclaration],
3424
+ "ml": [buildSpacingDeclaration],
3425
+ "ms": [buildSpacingDeclaration],
3426
+ "me": [buildSpacingDeclaration],
3427
+ "p": [buildSpacingDeclaration],
3428
+ "px": [buildSpacingDeclaration],
3429
+ "py": [buildSpacingDeclaration],
3430
+ "pt": [buildSpacingDeclaration],
3431
+ "pr": [buildSpacingDeclaration],
3432
+ "pb": [buildSpacingDeclaration],
3433
+ "pl": [buildSpacingDeclaration],
3434
+ "ps": [buildSpacingDeclaration],
3435
+ "pe": [buildSpacingDeclaration],
3436
+ "space": [buildSpaceBetweenDeclaration],
3437
+ // Sizing
3438
+ "w": [buildDimensionDeclaration],
3439
+ "h": [buildDimensionDeclaration],
3440
+ "min": [buildDimensionDeclaration],
3441
+ "max": [buildDimensionDeclaration],
3442
+ "size": [buildDimensionDeclaration],
3443
+ // Typography
3444
+ "text": [buildTypographyDeclaration, buildColorDeclaration],
3445
+ "font": [buildTypographyDeclaration],
3446
+ "leading": [buildTypographyDeclaration],
3447
+ "tracking": [buildTypographyDeclaration],
3448
+ "line": [buildTypographyDeclaration],
3449
+ "whitespace": [buildTypographyDeclaration],
3450
+ "break": [buildTypographyDeclaration],
3451
+ "hyphens": [buildTypographyDeclaration],
3452
+ "list": [buildTypographyDeclaration],
3453
+ "italic": [buildTypographyDeclaration],
3454
+ "underline": [buildTypographyDeclaration],
3455
+ "overline": [buildTypographyDeclaration],
3456
+ "uppercase": [buildTypographyDeclaration],
3457
+ "lowercase": [buildTypographyDeclaration],
3458
+ "capitalize": [buildTypographyDeclaration],
3459
+ "normal": [buildTypographyDeclaration, buildLayoutDeclaration],
3460
+ "truncate": [buildTypographyDeclaration],
3461
+ // Colors & Backgrounds
3462
+ "bg": [buildBackgroundDeclaration, buildColorDeclaration, buildGradientDeclaration],
3463
+ "from": [buildGradientDeclaration],
3464
+ "via": [buildGradientDeclaration],
3465
+ "to": [buildGradientDeclaration],
3466
+ "fill": [buildColorDeclaration],
3467
+ "stroke": [buildColorDeclaration],
3468
+ // Borders
3469
+ "border": [buildBorderDeclaration, buildColorDeclaration],
3470
+ "rounded": [buildBorderRadiusDeclaration],
3471
+ "divide": [buildDivideDeclaration],
3472
+ // Effects
3473
+ "shadow": [buildShadowDeclaration, buildTextShadowDeclaration],
3474
+ "opacity": [buildOpacityDeclaration],
3475
+ "ring": [buildRingDeclaration, buildRingOffsetDeclaration],
3476
+ // Transforms
3477
+ "scale": [buildTransformDeclaration],
3478
+ "rotate": [buildTransformDeclaration],
3479
+ "translate": [buildTransformDeclaration],
3480
+ "skew": [buildTransformDeclaration],
3481
+ "origin": [buildTransformDeclaration],
3482
+ "transform": [buildTransformDeclaration],
3483
+ // Filters
3484
+ "blur": [buildFilterDeclaration],
3485
+ "brightness": [buildFilterDeclaration],
3486
+ "contrast": [buildFilterDeclaration],
3487
+ "grayscale": [buildFilterDeclaration],
3488
+ "hue": [buildFilterDeclaration],
3489
+ "invert": [buildFilterDeclaration],
3490
+ "saturate": [buildFilterDeclaration],
3491
+ "sepia": [buildFilterDeclaration],
3492
+ "drop": [buildFilterDeclaration],
3493
+ "backdrop": [buildFilterDeclaration],
3494
+ // Transitions & Animations
3495
+ "transition": [buildTransitionDeclaration],
3496
+ "duration": [buildTransitionDeclaration],
3497
+ "ease": [buildTransitionDeclaration],
3498
+ "delay": [buildTransitionDeclaration],
3499
+ "animate": [buildAnimationDeclaration],
3500
+ // Interactivity
3501
+ "cursor": [buildInteractivityDeclaration],
3502
+ "pointer": [buildInteractivityDeclaration],
3503
+ "resize": [buildInteractivityDeclaration],
3504
+ "select": [buildInteractivityDeclaration],
3505
+ "appearance": [buildInteractivityDeclaration],
3506
+ "outline": [buildColorDeclaration, buildInteractivityDeclaration],
3507
+ "caret": [buildColorDeclaration],
3508
+ "accent": [buildColorDeclaration],
3509
+ // Flexbox & Grid specific
3510
+ "items": [buildFlexGridDeclaration],
3511
+ "justify": [buildFlexGridDeclaration],
3512
+ "place": [buildFlexGridDeclaration],
3513
+ "content": [buildFlexGridDeclaration, buildTypographyDeclaration],
3514
+ "self": [buildFlexGridDeclaration],
3515
+ "order": [buildFlexGridDeclaration],
3516
+ "gap": [buildGapDeclaration],
3517
+ "grow": [buildFlexGridDeclaration],
3518
+ "shrink": [buildFlexGridDeclaration],
3519
+ "basis": [buildFlexGridDeclaration],
3520
+ "cols": [buildFlexGridDeclaration],
3521
+ "rows": [buildFlexGridDeclaration],
3522
+ "col": [buildFlexGridDeclaration],
3523
+ "row": [buildFlexGridDeclaration],
3524
+ "auto": [buildFlexGridDeclaration, buildLayoutDeclaration],
3525
+ // Misc
3526
+ "overflow": [buildLayoutDeclaration],
3527
+ "overscroll": [buildInteractivityDeclaration],
3528
+ "scroll": [buildScrollSnapDeclaration, buildInteractivityDeclaration],
3529
+ "snap": [buildScrollSnapDeclaration],
3530
+ "touch": [buildInteractivityDeclaration],
3531
+ "will": [buildInteractivityDeclaration],
3532
+ "mix": [buildBlendingDeclaration],
3533
+ "blend": [buildBlendingDeclaration],
3534
+ "isolation": [buildLayoutDeclaration],
3535
+ "object": [buildLayoutDeclaration],
3536
+ "container": [buildContainerQueryDeclaration],
3537
+ "columns": [buildLayoutDeclaration],
3538
+ "aspect": [buildLayoutDeclaration],
3539
+ "clear": [buildLayoutDeclaration],
3540
+ "float": [buildLayoutDeclaration],
3541
+ "box": [buildLayoutDeclaration],
3542
+ "visible": [buildLayoutDeclaration],
3543
+ "invisible": [buildLayoutDeclaration],
3544
+ "collapse": [buildLayoutDeclaration],
3545
+ "mask": [buildMaskDeclaration],
3546
+ "forced": [buildForcedColorDeclaration],
3547
+ "field": [buildInteractivityDeclaration],
3548
+ "placeholder": [buildColorDeclaration],
3549
+ "sr": [buildAccessibilityDeclaration],
3550
+ "not": [buildAccessibilityDeclaration]
3551
+ };
3552
+ function extractPrefix(token) {
3553
+ const dashIndex = token.indexOf("-");
3554
+ if (dashIndex === -1) return token;
3555
+ const prefix = token.slice(0, dashIndex);
3556
+ const secondDashIndex = token.indexOf("-", dashIndex + 1);
3557
+ if (secondDashIndex !== -1) {
3558
+ const twoPartPrefix = token.slice(0, secondDashIndex);
3559
+ if (PREFIX_ROUTER[twoPartPrefix]) return twoPartPrefix;
3560
+ }
3561
+ return prefix;
3562
+ }
3563
+ function checkAllBuilders(baseToken, theme2) {
3265
3564
  return buildLayoutDeclaration(baseToken, theme2) || buildPositionInsetDeclaration(baseToken, theme2) || buildSpacingDeclaration(baseToken, theme2) || buildSpaceBetweenDeclaration(baseToken, theme2) || buildGapDeclaration(baseToken, theme2) || buildDimensionDeclaration(baseToken, theme2) || buildFlexGridDeclaration(baseToken, theme2) || buildBorderDeclaration(baseToken, theme2) || buildBorderRadiusDeclaration(baseToken, theme2) || buildBorderSpacingDeclaration(baseToken, theme2) || buildDivideDeclaration(baseToken, theme2) || buildOpacityDeclaration(baseToken, theme2) || buildShadowDeclaration(baseToken, theme2) || buildInsetShadowDeclaration(baseToken, theme2) || buildInsetRingDeclaration(baseToken, theme2) || buildRingDeclaration(baseToken, theme2) || buildRingOffsetDeclaration(baseToken, theme2) || buildTextShadowDeclaration(baseToken, theme2) || buildTransitionDeclaration(baseToken) || buildTransformDeclaration(baseToken, theme2) || buildFilterDeclaration(baseToken, theme2) || buildBackgroundDeclaration(baseToken, theme2) || buildGradientDeclaration(baseToken, theme2) || buildColorDeclaration(baseToken, theme2) || buildTypographyDeclaration(baseToken, theme2) || buildBlendingDeclaration(baseToken) || buildInteractivityDeclaration(baseToken, theme2) || buildAnimationDeclaration(baseToken) || buildMaskDeclaration(baseToken) || buildContainerQueryDeclaration(baseToken) || buildScrollSnapDeclaration(baseToken) || buildAccessibilityDeclaration(baseToken) || buildZoomDeclaration(baseToken, theme2) || buildForcedColorDeclaration(baseToken);
3266
3565
  }
3267
- function applyVariants(selector, variants2) {
3566
+ function compileBaseToken(baseToken, theme2, pluginRegistry) {
3567
+ if (pluginRegistry) {
3568
+ const pluginMatch = pluginRegistry.matchUtility(baseToken);
3569
+ if (pluginMatch) {
3570
+ const { handler, match } = pluginMatch;
3571
+ try {
3572
+ if (typeof handler === "function") {
3573
+ const result = handler(match, theme2);
3574
+ if (result) return result;
3575
+ } else if (typeof handler === "string") {
3576
+ return handler;
3577
+ }
3578
+ } catch (error) {
3579
+ console.warn(`[Windrunner] Plugin utility handler error for "${baseToken}":`, error);
3580
+ }
3581
+ }
3582
+ }
3583
+ const prefix = extractPrefix(baseToken);
3584
+ const builders = PREFIX_ROUTER[prefix];
3585
+ if (builders) {
3586
+ for (let i = 0; i < builders.length; i += 1) {
3587
+ const result = builders[i](baseToken, theme2);
3588
+ if (result) return result;
3589
+ }
3590
+ }
3591
+ return checkAllBuilders(baseToken, theme2);
3592
+ }
3593
+ function applyVariants(selector, variants2, pluginRegistry) {
3268
3594
  let currentSelector = selector;
3269
3595
  for (const variant of variants2) {
3596
+ if (pluginRegistry) {
3597
+ const customHandler = pluginRegistry.matchVariant(variant);
3598
+ if (customHandler) {
3599
+ try {
3600
+ const result = customHandler(currentSelector);
3601
+ if (result) {
3602
+ currentSelector = result;
3603
+ continue;
3604
+ }
3605
+ } catch (error) {
3606
+ console.warn(`[Windrunner] Plugin variant handler error for "${variant}":`, error);
3607
+ }
3608
+ }
3609
+ }
3270
3610
  switch (variant) {
3271
3611
  case "dark":
3272
3612
  currentSelector = `.dark ${currentSelector}`;
@@ -3307,15 +3647,66 @@ function applyVariants(selector, variants2) {
3307
3647
  case "invalid":
3308
3648
  currentSelector = `${currentSelector}:invalid`;
3309
3649
  break;
3650
+ case "target":
3651
+ currentSelector = `${currentSelector}:target`;
3652
+ break;
3653
+ case "enabled":
3654
+ currentSelector = `${currentSelector}:enabled`;
3655
+ break;
3656
+ case "default":
3657
+ currentSelector = `${currentSelector}:default`;
3658
+ break;
3659
+ case "optional":
3660
+ currentSelector = `${currentSelector}:optional`;
3661
+ break;
3662
+ case "user-valid":
3663
+ currentSelector = `${currentSelector}:user-valid`;
3664
+ break;
3665
+ case "user-invalid":
3666
+ currentSelector = `${currentSelector}:user-invalid`;
3667
+ break;
3668
+ case "in-range":
3669
+ currentSelector = `${currentSelector}:in-range`;
3670
+ break;
3671
+ case "out-of-range":
3672
+ currentSelector = `${currentSelector}:out-of-range`;
3673
+ break;
3674
+ case "placeholder-shown":
3675
+ currentSelector = `${currentSelector}:placeholder-shown`;
3676
+ break;
3677
+ case "autofill":
3678
+ currentSelector = `${currentSelector}:autofill`;
3679
+ break;
3680
+ case "details-content":
3681
+ currentSelector = `${currentSelector}:details-content`;
3682
+ break;
3310
3683
  case "placeholder":
3311
3684
  currentSelector = `${currentSelector}::placeholder`;
3312
3685
  break;
3686
+ case "backdrop":
3687
+ currentSelector = `${currentSelector}::backdrop`;
3688
+ break;
3313
3689
  case "before":
3314
3690
  currentSelector = `${currentSelector}::before`;
3315
3691
  break;
3316
3692
  case "after":
3317
3693
  currentSelector = `${currentSelector}::after`;
3318
3694
  break;
3695
+ case "first-letter":
3696
+ currentSelector = `${currentSelector}::first-letter`;
3697
+ break;
3698
+ case "first-line":
3699
+ currentSelector = `${currentSelector}::first-line`;
3700
+ break;
3701
+ case "marker":
3702
+ currentSelector = `${currentSelector}::marker`;
3703
+ break;
3704
+ case "selection":
3705
+ currentSelector = `${currentSelector}::selection`;
3706
+ break;
3707
+ case "file":
3708
+ currentSelector = `${currentSelector}::file-selector-button`;
3709
+ break;
3319
3710
  case "first":
3320
3711
  currentSelector = `${currentSelector}:first-child`;
3321
3712
  break;
@@ -3396,21 +3787,63 @@ function applyVariants(selector, variants2) {
3396
3787
  }
3397
3788
  function resolveRuntimeContext(options = {}) {
3398
3789
  const config = getConfigOptions(options, []);
3790
+ const pluginRegistry = new PluginRegistry();
3791
+ if (options.plugins && Array.isArray(options.plugins)) {
3792
+ options.plugins.forEach((pluginDef) => {
3793
+ if (isPlugin(pluginDef)) {
3794
+ try {
3795
+ pluginDef.handler({
3796
+ addUtility: (pattern, handler) => pluginRegistry.addUtility(pattern, handler),
3797
+ addUtilities: (utilities) => pluginRegistry.addUtilities(utilities),
3798
+ addVariant: (name, handler) => pluginRegistry.addVariant(name, handler),
3799
+ addVariants: (variants2) => pluginRegistry.addVariants(variants2),
3800
+ theme: (key) => {
3801
+ if (!key) return config.theme || {};
3802
+ const keys = key.split(".");
3803
+ let value = config.theme || {};
3804
+ for (const k of keys) {
3805
+ value = value[k];
3806
+ if (value === void 0) break;
3807
+ }
3808
+ return value;
3809
+ },
3810
+ config: () => config
3811
+ });
3812
+ } catch (error) {
3813
+ console.error("[Windrunner] Plugin initialization error:", error);
3814
+ }
3815
+ }
3816
+ });
3817
+ }
3399
3818
  return {
3400
3819
  config,
3401
3820
  theme: config.theme || {},
3402
3821
  screens: config.theme && config.theme.screens || config.screens || {},
3403
- containers: config.theme && config.theme.containers || config.containers || {}
3822
+ containers: config.theme && config.theme.containers || config.containers || {},
3823
+ plugins: pluginRegistry
3404
3824
  };
3405
3825
  }
3406
3826
  function getBaseTailwindOptions(options = {}) {
3407
3827
  const { id, autoStart, compatMode, compatStyleId, compatGenerateCss, ...tailwindOptions } = options;
3408
3828
  return tailwindOptions;
3409
3829
  }
3830
+ var parseCache = /* @__PURE__ */ new Map();
3831
+ var PARSE_CACHE_MAX_SIZE = 2e3;
3832
+ function getConfigHash(screens, containers) {
3833
+ const screensEmpty = !screens || Object.keys(screens).length === 0;
3834
+ const containersEmpty = !containers || Object.keys(containers).length === 0;
3835
+ if (screensEmpty && containersEmpty) return "default";
3836
+ return `${Object.keys(screens || {}).join(",")}|${Object.keys(containers || {}).join(",")}`;
3837
+ }
3410
3838
  function parseClass(className, screens = {}, containers = {}) {
3411
3839
  if (typeof className !== "string") return null;
3412
3840
  const token = className.trim();
3413
3841
  if (!token) return null;
3842
+ const configHash = getConfigHash(screens, containers);
3843
+ const cacheKey = `${token}:${configHash}`;
3844
+ if (parseCache.has(cacheKey)) {
3845
+ return parseCache.get(cacheKey);
3846
+ }
3414
3847
  const important = token.startsWith("!");
3415
3848
  const normalized = important ? token.slice(1) : token;
3416
3849
  const parts = splitByVariantDelimiter(normalized);
@@ -3439,15 +3872,21 @@ function parseClass(className, screens = {}, containers = {}) {
3439
3872
  }
3440
3873
  variants2.push(part);
3441
3874
  }
3442
- return { original: token, baseToken, variants: variants2, breakpoint, containerBreakpoint, important, starting };
3875
+ const result = { original: token, baseToken, variants: variants2, breakpoint, containerBreakpoint, important, starting };
3876
+ if (parseCache.size >= PARSE_CACHE_MAX_SIZE) {
3877
+ const firstKey = parseCache.keys().next().value;
3878
+ parseCache.delete(firstKey);
3879
+ }
3880
+ parseCache.set(cacheKey, result);
3881
+ return result;
3443
3882
  }
3444
3883
  function compileRuntimeClassNameWithContext(className, context) {
3445
3884
  const parsed = parseClass(className, context.screens, context.containers);
3446
3885
  if (!parsed) return "";
3447
- const declaration = compileBaseToken(parsed.baseToken, context.theme);
3886
+ const declaration = compileBaseToken(parsed.baseToken, context.theme, context.plugins);
3448
3887
  if (!declaration) return "";
3449
3888
  const selector = `.${escapeCssIdentifier(parsed.original)}`;
3450
- const variantSelector = applyVariants(selector, parsed.variants);
3889
+ const variantSelector = applyVariants(selector, parsed.variants, context.plugins);
3451
3890
  if (!variantSelector) return "";
3452
3891
  const finalDeclaration = appendImportant(
3453
3892
  isChildScoped(declaration) ? declaration.declaration : declaration,
@@ -3737,6 +4176,9 @@ function windrunner(options = {}) {
3737
4176
  export {
3738
4177
  compileClass,
3739
4178
  createWindrunner,
4179
+ defineResponsiveUtilities,
4180
+ defineUtilities,
3740
4181
  parseClass,
4182
+ plugin,
3741
4183
  windrunner
3742
4184
  };