@schemyx/mcp 0.1.1 → 0.1.3

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.
@@ -25,6 +25,7 @@ exports.extractModelFields = extractModelFields;
25
25
  exports.extractCssVariables = extractCssVariables;
26
26
  exports.extractCssVariableValues = extractCssVariableValues;
27
27
  exports.extractCssRules = extractCssRules;
28
+ exports.extractCssAtRuleBlocks = extractCssAtRuleBlocks;
28
29
  exports.extractCssClasses = extractCssClasses;
29
30
  exports.extractCssIds = extractCssIds;
30
31
  exports.extractCssVariableReferences = extractCssVariableReferences;
@@ -43,6 +44,9 @@ exports.readBalancedRange = readBalancedRange;
43
44
  exports.skipQuoted = skipQuoted;
44
45
  exports.isInsideQuotedString = isInsideQuotedString;
45
46
  exports.extractClassListsFromStrings = extractClassListsFromStrings;
47
+ exports.readPrimaryClassHelperUsage = readPrimaryClassHelperUsage;
48
+ exports.readClassHelperUsages = readClassHelperUsages;
49
+ exports.parseHelperVariantProps = parseHelperVariantProps;
46
50
  exports.analyzeClassSource = analyzeClassSource;
47
51
  exports.toClassSourceAnalysis = toClassSourceAnalysis;
48
52
  exports.isConditionalClassLiteral = isConditionalClassLiteral;
@@ -86,9 +90,20 @@ exports.isCompositionElement = isCompositionElement;
86
90
  exports.filterClasses = filterClasses;
87
91
  exports.isResponsiveClass = isResponsiveClass;
88
92
  exports.isSpacingClass = isSpacingClass;
93
+ exports.isLayoutClass = isLayoutClass;
89
94
  exports.isSurfaceClass = isSurfaceClass;
90
95
  exports.isDecorativeAccentClass = isDecorativeAccentClass;
91
96
  exports.isTypographyClass = isTypographyClass;
97
+ exports.isTypographyScaleBase = isTypographyScaleBase;
98
+ exports.isLayoutSafetyBase = isLayoutSafetyBase;
99
+ exports.isSizingClass = isSizingClass;
100
+ exports.isVisibilityClass = isVisibilityClass;
101
+ exports.isLineHeightClass = isLineHeightClass;
102
+ exports.isMaxWidthClass = isMaxWidthClass;
103
+ exports.createUiLayoutSafety = createUiLayoutSafety;
104
+ exports.createUiResponsiveProfile = createUiResponsiveProfile;
105
+ exports.createUiScaleProfile = createUiScaleProfile;
106
+ exports.responsivePrefix = responsivePrefix;
92
107
  exports.groupClassExpressionsByTarget = groupClassExpressionsByTarget;
93
108
  exports.buildComponentSlots = buildComponentSlots;
94
109
  exports.createUiCompositionGuidance = createUiCompositionGuidance;
@@ -97,6 +112,31 @@ exports.extractTailwindUtilities = extractTailwindUtilities;
97
112
  exports.extractPackageDependencies = extractPackageDependencies;
98
113
  exports.extractUiElements = extractUiElements;
99
114
  exports.summarizeDirectUiChildren = summarizeDirectUiChildren;
115
+ exports.createUiSemanticProfile = createUiSemanticProfile;
116
+ exports.createUiRoleSignature = createUiRoleSignature;
117
+ exports.exactRoleClassFacts = exactRoleClassFacts;
118
+ exports.createRoleScaleKey = createRoleScaleKey;
119
+ exports.createRoleDensityKey = createRoleDensityKey;
120
+ exports.createRoleSurfaceKey = createRoleSurfaceKey;
121
+ exports.createRoleLayoutKey = createRoleLayoutKey;
122
+ exports.isCompactHeroEyebrow = isCompactHeroEyebrow;
123
+ exports.isPlainTextEyebrow = isPlainTextEyebrow;
124
+ exports.isOverlayPill = isOverlayPill;
125
+ exports.isHeroHeadingScale = isHeroHeadingScale;
126
+ exports.isOversizedHeadingScale = isOversizedHeadingScale;
127
+ exports.isMetricValueScale = isMetricValueScale;
128
+ exports.isCompactStatValue = isCompactStatValue;
129
+ exports.isLargeMetricValue = isLargeMetricValue;
130
+ exports.isCompactStatCard = isCompactStatCard;
131
+ exports.isCompactMetricGrid = isCompactMetricGrid;
132
+ exports.isCtaPanelShell = isCtaPanelShell;
133
+ exports.isInteractiveServiceCard = isInteractiveServiceCard;
134
+ exports.isStaticArticleCard = isStaticArticleCard;
135
+ exports.isMediaPanelCard = isMediaPanelCard;
136
+ exports.isQuietAction = isQuietAction;
137
+ exports.isCompactAction = isCompactAction;
138
+ exports.roleClassSort = roleClassSort;
139
+ exports.deriveUiChildSemanticRole = deriveUiChildSemanticRole;
100
140
  exports.deriveLayoutRole = deriveLayoutRole;
101
141
  exports.isStateClass = isStateClass;
102
142
  exports.extractApiCalls = extractApiCalls;
@@ -150,6 +190,8 @@ exports.styleConfidenceForFile = styleConfidenceForFile;
150
190
  exports.entryMatchesConcept = entryMatchesConcept;
151
191
  exports.splitClassList = splitClassList;
152
192
  exports.classifyUiElement = classifyUiElement;
193
+ exports.looksLikeSurfaceContainer = looksLikeSurfaceContainer;
194
+ exports.looksLikePillBadge = looksLikePillBadge;
153
195
  exports.stripTags = stripTags;
154
196
  const path = require("node:path");
155
197
  const constants_1 = require("./constants");
@@ -562,7 +604,13 @@ function extractCssRules(content, relPath) {
562
604
  return [];
563
605
  }
564
606
  const rules = [];
607
+ const mediaBlocks = extractCssAtRuleBlocks(content, 'media');
608
+ const mediaRanges = mediaBlocks.map((block) => ({ start: block.start, end: block.end }));
565
609
  for (const match of content.matchAll(/([^{}@][^{}]{0,240})\{([^{}]{1,2400})\}/g)) {
610
+ const matchIndex = match.index ?? 0;
611
+ if (mediaRanges.some((range) => matchIndex >= range.start && matchIndex <= range.end)) {
612
+ continue;
613
+ }
566
614
  const selector = match[1].trim().replace(/\s+/g, ' ');
567
615
  if (!selector || selector.includes(';') || selector.startsWith('import')) {
568
616
  continue;
@@ -577,7 +625,59 @@ function extractCssRules(content, relPath) {
577
625
  line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
578
626
  });
579
627
  }
580
- return (0, utils_1.uniqueBy)(rules, (rule) => `${rule.selector}:${rule.line}`).slice(0, 200);
628
+ for (const block of mediaBlocks) {
629
+ for (const match of block.body.matchAll(/([^{}@][^{}]{0,240})\{([^{}]{1,2400})\}/g)) {
630
+ const selector = match[1].trim().replace(/\s+/g, ' ');
631
+ if (!selector || selector.includes(';') || selector.startsWith('import')) {
632
+ continue;
633
+ }
634
+ const declarations = parseCssDeclarations(match[2]).slice(0, 40);
635
+ if (!declarations.length) {
636
+ continue;
637
+ }
638
+ rules.push({
639
+ selector,
640
+ declarations,
641
+ line: (0, utils_1.lineNumberAt)(content, block.bodyStart + (match.index ?? 0)),
642
+ media: block.prelude,
643
+ });
644
+ }
645
+ }
646
+ return (0, utils_1.uniqueBy)(rules, (rule) => `${rule.selector}:${rule.line}:${rule.media ?? ''}`).slice(0, 320);
647
+ }
648
+ function extractCssAtRuleBlocks(content, atRule) {
649
+ const blocks = [];
650
+ const pattern = new RegExp(`@${atRule}\\s+([^{}]+)\\{`, 'g');
651
+ for (const match of content.matchAll(pattern)) {
652
+ const start = match.index ?? 0;
653
+ const openIndex = start + match[0].length - 1;
654
+ let depth = 0;
655
+ let end = -1;
656
+ for (let cursor = openIndex; cursor < content.length; cursor += 1) {
657
+ const char = content[cursor];
658
+ if (char === '{') {
659
+ depth += 1;
660
+ }
661
+ else if (char === '}') {
662
+ depth -= 1;
663
+ if (depth === 0) {
664
+ end = cursor;
665
+ break;
666
+ }
667
+ }
668
+ }
669
+ if (end <= openIndex) {
670
+ continue;
671
+ }
672
+ blocks.push({
673
+ prelude: match[1].trim().replace(/\s+/g, ' '),
674
+ body: content.slice(openIndex + 1, end),
675
+ start,
676
+ end,
677
+ bodyStart: openIndex + 1,
678
+ });
679
+ }
680
+ return blocks;
581
681
  }
582
682
  function extractCssClasses(content) {
583
683
  return (0, utils_1.unique)(Array.from(content.matchAll(/\.([a-zA-Z][a-zA-Z0-9_-]+)\b/g)).map((match) => match[1])).slice(0, 80);
@@ -617,6 +717,7 @@ function extractClassExpressions(content) {
617
717
  const classValue = match[2] ?? match[1] ?? '';
618
718
  const classes = splitClassList(classValue);
619
719
  if (classes.length) {
720
+ const helperUsage = readPrimaryClassHelperUsage(classValue);
620
721
  const analysis = analyzeClassSource(classValue);
621
722
  expressions.push({
622
723
  kind: 'attribute',
@@ -626,6 +727,10 @@ function extractClassExpressions(content) {
626
727
  conditionalClasses: analysis.conditionalClasses,
627
728
  line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
628
729
  target: readTagBefore(content, match.index ?? 0),
730
+ ...(helperUsage ? { styleHelper: helperUsage.name } : {}),
731
+ ...(helperUsage && Object.keys(helperUsage.variantProps).length
732
+ ? { styleHelperVariants: helperUsage.variantProps }
733
+ : {}),
629
734
  });
630
735
  }
631
736
  }
@@ -642,7 +747,8 @@ function extractClassExpressions(content) {
642
747
  }
643
748
  const analysis = analyzeClassSource(call);
644
749
  const classes = analysis.classes;
645
- if (classes.length) {
750
+ const helperUsage = readPrimaryClassHelperUsage(call);
751
+ if (classes.length || helperUsage) {
646
752
  expressions.push({
647
753
  kind: helperName === 'cva' ? 'variant-definition' : 'helper',
648
754
  raw: (0, utils_1.truncate)(call, 900),
@@ -651,10 +757,44 @@ function extractClassExpressions(content) {
651
757
  conditionalClasses: analysis.conditionalClasses,
652
758
  line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
653
759
  target: helperName,
760
+ ...(helperUsage ? { styleHelper: helperUsage.name } : {}),
761
+ ...(helperUsage && Object.keys(helperUsage.variantProps).length
762
+ ? { styleHelperVariants: helperUsage.variantProps }
763
+ : {}),
654
764
  });
655
765
  }
656
766
  }
657
767
  }
768
+ for (const match of content.matchAll(/\b([A-Za-z_$][\w$]*(?:Variants?|ClassNames?|Styles?))\s*\(/g)) {
769
+ const helperName = match[1];
770
+ if (/^(?:cn|clsx|classNames|twMerge|cva)$/i.test(helperName)) {
771
+ continue;
772
+ }
773
+ if (isInsideQuotedString(content, match.index ?? 0)) {
774
+ continue;
775
+ }
776
+ const call = readBalancedCall(content, match.index ?? 0);
777
+ if (!call) {
778
+ continue;
779
+ }
780
+ const helperUsage = readPrimaryClassHelperUsage(call);
781
+ if (!helperUsage) {
782
+ continue;
783
+ }
784
+ expressions.push({
785
+ kind: 'helper',
786
+ raw: (0, utils_1.truncate)(call, 900),
787
+ classes: [],
788
+ defaultClasses: [],
789
+ conditionalClasses: [],
790
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
791
+ target: helperName,
792
+ styleHelper: helperUsage.name,
793
+ ...(Object.keys(helperUsage.variantProps).length
794
+ ? { styleHelperVariants: helperUsage.variantProps }
795
+ : {}),
796
+ });
797
+ }
658
798
  for (const match of content.matchAll(/(["'`])([^"'`]{5,900})\1/g)) {
659
799
  const classes = splitClassList(match[2]);
660
800
  if (classes.length >= 2 && looksLikeClassList(classes)) {
@@ -920,6 +1060,56 @@ function isInsideQuotedString(content, targetIndex) {
920
1060
  function extractClassListsFromStrings(source) {
921
1061
  return analyzeClassSource(source).classes;
922
1062
  }
1063
+ function readPrimaryClassHelperUsage(source) {
1064
+ const usages = readClassHelperUsages(source);
1065
+ return (usages.find((usage) => /button|cta|action/i.test(usage.name)) ??
1066
+ usages.find((usage) => Object.keys(usage.variantProps).length > 0) ??
1067
+ usages[0]);
1068
+ }
1069
+ function readClassHelperUsages(source) {
1070
+ const usages = [];
1071
+ const helperPattern = /\b([A-Za-z_$][\w$]*(?:Variants?|ClassNames?|Styles?))\s*\(/g;
1072
+ for (const match of source.matchAll(helperPattern)) {
1073
+ const name = match[1];
1074
+ if (/^(?:cn|clsx|classNames|twMerge|cva)$/i.test(name)) {
1075
+ continue;
1076
+ }
1077
+ const call = readBalancedCall(source, match.index ?? 0);
1078
+ if (!call) {
1079
+ continue;
1080
+ }
1081
+ usages.push({
1082
+ name,
1083
+ variantProps: parseHelperVariantProps(call),
1084
+ call: (0, utils_1.truncate)(call, 700),
1085
+ });
1086
+ }
1087
+ return (0, utils_1.uniqueBy)(usages, (usage) => `${usage.name}:${JSON.stringify(usage.variantProps)}`).slice(0, 12);
1088
+ }
1089
+ function parseHelperVariantProps(call) {
1090
+ const props = {};
1091
+ const openIndex = call.indexOf('{');
1092
+ if (openIndex < 0) {
1093
+ return props;
1094
+ }
1095
+ const block = readBalancedBlock(call, openIndex);
1096
+ if (!block) {
1097
+ return props;
1098
+ }
1099
+ for (const entry of parseTopLevelObjectEntries(block)) {
1100
+ const literalValues = readStringLiterals(entry.rawValue)
1101
+ .map((value) => value.trim())
1102
+ .filter(Boolean)
1103
+ .slice(0, 4);
1104
+ const value = literalValues.length > 1
1105
+ ? literalValues.join('|')
1106
+ : (literalValues[0] ?? cleanupBareValue(entry.rawValue));
1107
+ if (value) {
1108
+ props[entry.key] = value;
1109
+ }
1110
+ }
1111
+ return props;
1112
+ }
923
1113
  function analyzeClassSource(source) {
924
1114
  const classes = new Set();
925
1115
  const defaultClasses = new Set();
@@ -992,7 +1182,8 @@ function directClassCandidate(source) {
992
1182
  return undefined;
993
1183
  }
994
1184
  const withoutTemplateExpressions = trimmed.replace(/\$\{[\s\S]*?\}/g, ' ');
995
- if (/[{}"'`,;]/.test(withoutTemplateExpressions)) {
1185
+ const withoutArbitraryValues = withoutTemplateExpressions.replace(/\[[^\]]+\]/g, '[arbitrary]');
1186
+ if (/[{}"'`,;]/.test(withoutArbitraryValues)) {
996
1187
  return undefined;
997
1188
  }
998
1189
  return withoutTemplateExpressions;
@@ -1010,6 +1201,7 @@ function looksLikeClassList(classes) {
1010
1201
  function isLikelyStyleClass(className) {
1011
1202
  const base = className.split(':').pop() ?? className;
1012
1203
  return (/^(?:-?m[trblxy]?|-?p[trblxy]?|flex|grid|block|inline|hidden|items-|justify-|content-|self-|gap-|space-|w-|h-|min-|max-|size-|rounded|border|bg-|from-|via-|to-|text-|font-|leading-|tracking-|shadow|ring|outline|opacity|transition|duration|ease|animate-|hover|focus|active|disabled|dark|sm|md|lg|xl|2xl|container|sr-only|absolute|relative|fixed|sticky|inset-|top-|right-|bottom-|left-|z-|overflow-|object-|aspect-|backdrop-|blur|scale-|translate-|rotate-)/.test(className) ||
1204
+ /^(?:btn(?:-.+)?|card(?:-.+)?|badge(?:-.+)?|form(?:-.+)?|input-group(?:-.+)?|navbar(?:-.+)?|nav(?:-.+)?|row|col(?:-.+)?|container(?:-.+)?|d-(?:sm-|md-|lg-|xl-|xxl-)?|justify-content-|align-items-|align-self-|fw-|fs-|lh-|display-|lead|small|list-group(?:-.+)?|modal(?:-.+)?|alert(?:-.+)?|rounded-(?:pill|circle|\d)|shadow(?:-.+)?|w-\d+|h-\d+|mw-\d+|mh-\d+|min-vh-|min-vw-|vw-|vh-)/.test(className) ||
1013
1205
  /^(?:button|btn|card|panel|surface|badge|pill|chip|nav|field|input|dialog|modal|drawer|sheet|container|wrapper|section|hero|header|footer|sidebar|menu)$/.test(base) ||
1014
1206
  className.includes('[') ||
1015
1207
  className.includes(']'));
@@ -1468,22 +1660,28 @@ function groupTailwindClasses(classes) {
1468
1660
  addClassGroup(groups, 'state', className);
1469
1661
  }
1470
1662
  }
1471
- if (/^(flex|grid|block|inline|hidden|items-|justify-|content-|self-|place-|order-|col-|row-)/.test(base)) {
1663
+ if (/^(flex|grid|block|inline|hidden|items-|justify-|content-|self-|place-|order-|col-|row-|container|d-|justify-content-|align-items-|align-self-)/.test(base)) {
1472
1664
  addClassGroup(groups, 'layout', className);
1473
1665
  }
1474
1666
  else if (/^(?:-?m[trblxy]?|-?p[trblxy]?|gap-|space-|inset-|top-|right-|bottom-|left-)/.test(base)) {
1475
1667
  addClassGroup(groups, 'spacing', className);
1476
1668
  }
1477
- else if (/^(w-|h-|min-|max-|size-|aspect-|basis-|grow|shrink)/.test(base)) {
1669
+ else if (/^(w-|h-|mw-|mh-|vw-|vh-|min-|max-|size-|aspect-|basis-|grow|shrink)/.test(base)) {
1478
1670
  addClassGroup(groups, 'sizing', className);
1479
1671
  }
1672
+ else if (isTypographyScaleBase(base)) {
1673
+ addClassGroup(groups, 'typography', className);
1674
+ }
1675
+ else if (isLayoutSafetyBase(base)) {
1676
+ addClassGroup(groups, 'layout-safety', className);
1677
+ }
1480
1678
  else if (/^(bg-|from-|via-|to-|text-|decoration-|accent-|caret-|fill-|stroke-)/.test(base)) {
1481
1679
  addClassGroup(groups, 'color', className);
1482
1680
  }
1483
1681
  else if (/^(rounded|border|divide-|outline|ring)/.test(base)) {
1484
1682
  addClassGroup(groups, 'border', className);
1485
1683
  }
1486
- else if (/^(font-|text-|leading-|tracking-|line-clamp|list-|whitespace-|break-|truncate)/.test(base)) {
1684
+ else if (/^(list-)/.test(base)) {
1487
1685
  addClassGroup(groups, 'typography', className);
1488
1686
  }
1489
1687
  else if (/^(shadow|opacity|blur|backdrop|mix-|bg-blend|drop-shadow|filter)/.test(base)) {
@@ -1542,12 +1740,17 @@ function filterClasses(classes, predicate) {
1542
1740
  return (0, utils_1.unique)(classes.filter(predicate));
1543
1741
  }
1544
1742
  function isResponsiveClass(className) {
1545
- return /^(?:sm|md|lg|xl|2xl|max-|min-):/.test(className);
1743
+ return (/^(?:sm|md|lg|xl|2xl|min-[^:]+|max-[^:]+):/.test(className) ||
1744
+ /^(?:d|col|offset|order|m[stebxy]?|p[stebxy]?|gap|text|float|flex|justify-content|align-items|align-self)-(?:sm|md|lg|xl|xxl)-/.test(className));
1546
1745
  }
1547
1746
  function isSpacingClass(className) {
1548
1747
  const base = (0, utils_1.classBase)(className);
1549
1748
  return /^(?:-?m[trblxy]?|-?p[trblxy]?|gap-|space-|inset-|top-|right-|bottom-|left-)/.test(base);
1550
1749
  }
1750
+ function isLayoutClass(className) {
1751
+ const base = (0, utils_1.classBase)(className);
1752
+ return /^(?:flex|grid|block|inline|hidden|items-|justify-|content-|self-|place-|order-|col-|row-|container|d-|justify-content-|align-items-|align-self-)/.test(base);
1753
+ }
1551
1754
  function isSurfaceClass(className) {
1552
1755
  const base = (0, utils_1.classBase)(className);
1553
1756
  return (/^(?:bg-|border|rounded|shadow|ring|outline|divide-|opacity|backdrop|blur|drop-shadow)/.test(base) ||
@@ -1562,7 +1765,110 @@ function isDecorativeAccentClass(className) {
1562
1765
  }
1563
1766
  function isTypographyClass(className) {
1564
1767
  const base = (0, utils_1.classBase)(className);
1565
- return /^(?:text-|font-|leading-|tracking-|line-clamp|whitespace-|break-|truncate|max-w-|text-balance)/.test(base);
1768
+ return isTypographyScaleBase(base);
1769
+ }
1770
+ function isTypographyScaleBase(base) {
1771
+ return (/^(?:font-|leading-|tracking-|line-clamp|whitespace-|break-|truncate|text-balance|text-pretty|text-nowrap|text-wrap)/.test(base) ||
1772
+ /^(?:uppercase|lowercase|capitalize|normal-case)$/.test(base) ||
1773
+ /^(?:fs-|fw-|lh-|display-|lead|small|text-uppercase|text-lowercase|text-capitalize)/.test(base) ||
1774
+ /^text-(?:xs|sm|base|lg|xl|[2-9]xl|\[[^\]]+\])/.test(base) ||
1775
+ /^max-w-\[(?:\d+(?:\.\d+)?)?ch\]/.test(base));
1776
+ }
1777
+ function isLayoutSafetyBase(base) {
1778
+ return /^(?:overflow-|min-w-|min-h-|max-w-|max-h-|w-fit|h-fit|w-max|h-max|w-min|h-min|basis-|grow|shrink|flex-1|flex-none|self-|whitespace-|break-|truncate|line-clamp|text-ellipsis|text-nowrap|text-balance|text-pretty)/.test(base);
1779
+ }
1780
+ function isSizingClass(className) {
1781
+ const base = (0, utils_1.classBase)(className);
1782
+ return /^(?:w-|h-|mw-|mh-|vw-|vh-|min-|max-|size-|aspect-|basis-|grow|shrink|flex-1|flex-none)/.test(base);
1783
+ }
1784
+ function isVisibilityClass(className) {
1785
+ const base = (0, utils_1.classBase)(className);
1786
+ return /^(?:hidden|block|inline|inline-block|flex|inline-flex|grid|inline-grid|table|contents)$/.test(base);
1787
+ }
1788
+ function isLineHeightClass(className) {
1789
+ return (0, utils_1.classBase)(className).startsWith('leading-');
1790
+ }
1791
+ function isMaxWidthClass(className) {
1792
+ return (0, utils_1.classBase)(className).startsWith('max-w-');
1793
+ }
1794
+ function createUiLayoutSafety(classes) {
1795
+ const wrapClasses = filterClasses(classes, (className) => /^(?:whitespace-|break-|truncate|line-clamp|text-ellipsis|text-nowrap|text-balance|text-pretty|text-wrap)/.test((0, utils_1.classBase)(className))).slice(0, 40);
1796
+ const overflowClasses = filterClasses(classes, (className) => /^(?:overflow-|line-clamp|truncate|text-ellipsis)/.test((0, utils_1.classBase)(className))).slice(0, 40);
1797
+ const minSizeClasses = filterClasses(classes, (className) => /^(?:min-w-|min-h-)/.test((0, utils_1.classBase)(className))).slice(0, 40);
1798
+ const maxSizeClasses = filterClasses(classes, (className) => /^(?:max-w-|max-h-)/.test((0, utils_1.classBase)(className))).slice(0, 40);
1799
+ const fitClasses = filterClasses(classes, (className) => /^(?:w-fit|h-fit|w-max|h-max|w-min|h-min|basis-|self-)/.test((0, utils_1.classBase)(className))).slice(0, 40);
1800
+ const flexBehaviorClasses = filterClasses(classes, (className) => /^(?:grow|shrink|flex-1|flex-none|basis-)/.test((0, utils_1.classBase)(className))).slice(0, 40);
1801
+ const layoutSafetyClasses = (0, utils_1.unique)([
1802
+ ...wrapClasses,
1803
+ ...overflowClasses,
1804
+ ...minSizeClasses,
1805
+ ...maxSizeClasses,
1806
+ ...fitClasses,
1807
+ ...flexBehaviorClasses,
1808
+ ]).slice(0, 80);
1809
+ if (!layoutSafetyClasses.length) {
1810
+ return undefined;
1811
+ }
1812
+ return {
1813
+ wrapClasses,
1814
+ overflowClasses,
1815
+ minSizeClasses,
1816
+ maxSizeClasses,
1817
+ fitClasses,
1818
+ flexBehaviorClasses,
1819
+ layoutSafetyClasses,
1820
+ };
1821
+ }
1822
+ function createUiResponsiveProfile(classes) {
1823
+ const responsiveClasses = filterClasses(classes, isResponsiveClass).slice(0, 120);
1824
+ if (!responsiveClasses.length) {
1825
+ return undefined;
1826
+ }
1827
+ const breakpoints = {};
1828
+ for (const className of responsiveClasses) {
1829
+ const prefix = responsivePrefix(className);
1830
+ if (!prefix) {
1831
+ continue;
1832
+ }
1833
+ breakpoints[prefix] = (0, utils_1.unique)([...(breakpoints[prefix] ?? []), className]).slice(0, 60);
1834
+ }
1835
+ return {
1836
+ breakpoints,
1837
+ layoutClasses: filterClasses(responsiveClasses, isLayoutClass).slice(0, 60),
1838
+ typographyClasses: filterClasses(responsiveClasses, isTypographyClass).slice(0, 60),
1839
+ spacingClasses: filterClasses(responsiveClasses, isSpacingClass).slice(0, 60),
1840
+ sizingClasses: filterClasses(responsiveClasses, isSizingClass).slice(0, 60),
1841
+ visibilityClasses: filterClasses(responsiveClasses, isVisibilityClass).slice(0, 60),
1842
+ stateClasses: filterClasses(responsiveClasses, isStateClass).slice(0, 60),
1843
+ };
1844
+ }
1845
+ function createUiScaleProfile(classes) {
1846
+ const typographyClasses = filterClasses(classes, isTypographyClass).slice(0, 80);
1847
+ const spacingClasses = filterClasses(classes, isSpacingClass).slice(0, 80);
1848
+ const sizingClasses = filterClasses(classes, isSizingClass).slice(0, 80);
1849
+ const gapClasses = filterClasses(classes, (className) => /^(?:gap-|space-)/.test((0, utils_1.classBase)(className))).slice(0, 60);
1850
+ const maxWidthClasses = filterClasses(classes, isMaxWidthClass).slice(0, 60);
1851
+ const lineHeightClasses = filterClasses(classes, isLineHeightClass).slice(0, 60);
1852
+ if (!typographyClasses.length &&
1853
+ !spacingClasses.length &&
1854
+ !sizingClasses.length &&
1855
+ !gapClasses.length &&
1856
+ !maxWidthClasses.length &&
1857
+ !lineHeightClasses.length) {
1858
+ return undefined;
1859
+ }
1860
+ return {
1861
+ typographyClasses,
1862
+ spacingClasses,
1863
+ sizingClasses,
1864
+ gapClasses,
1865
+ maxWidthClasses,
1866
+ lineHeightClasses,
1867
+ };
1868
+ }
1869
+ function responsivePrefix(className) {
1870
+ return (className.split(':').find((part) => /^(?:sm|md|lg|xl|2xl|min-|max-)/.test(part)) ??
1871
+ className.match(/^(?:d|col|offset|order|m[stebxy]?|p[stebxy]?|gap|text|float|flex|justify-content|align-items|align-self)-(sm|md|lg|xl|xxl)-/)?.[1]);
1566
1872
  }
1567
1873
  function groupClassExpressionsByTarget(expressions) {
1568
1874
  const grouped = {};
@@ -1630,6 +1936,15 @@ function createUiCompositionGuidance(file) {
1630
1936
  if (classes.has('section-frame') || classes.has('context-surface')) {
1631
1937
  guidance.push('Use section-frame/context-surface only for framed technical panels, not every repeated card.');
1632
1938
  }
1939
+ if (classes.has('section-frame')) {
1940
+ guidance.push('section-frame owns the clipped section-frame::before grid overlay for that surface; do not treat it as a simple border/background token.');
1941
+ }
1942
+ if (classes.has('section-frame') && classes.has('context-surface')) {
1943
+ guidance.push('Keep section-frame and context-surface paired for technical surfaces when child content must sit above the parent grid overlay.');
1944
+ }
1945
+ if (classes.has('sm:section-frame') || classes.has('bg-transparent')) {
1946
+ guidance.push('Preserve transparent mobile surfaces and only add framed behavior at the same responsive breakpoint as the source.');
1947
+ }
1633
1948
  if (file.classReferences.some(isDecorativeAccentClass)) {
1634
1949
  guidance.push('Treat brand-spectrum-card and purple glow shadow classes as accent/highlight-only; do not use them for default repeated cards unless the source element is explicitly current, highlighted, or featured.');
1635
1950
  }
@@ -1647,7 +1962,7 @@ function createUiPatternGuidance(kind, decorativeAccentClasses) {
1647
1962
  }
1648
1963
  function extractTailwindUtilities(classReferences) {
1649
1964
  return classReferences
1650
- .filter((className) => /^(?:-?m[trblxy]?|-?p[trblxy]?|flex|grid|block|inline|hidden|items-|justify-|gap-|space-|w-|h-|min-|max-|rounded|border|bg-|text-|font-|leading-|tracking-|shadow|ring|opacity|transition|duration|ease|hover:|focus:|active:|disabled:|dark:|sm:|md:|lg:|xl:|2xl:|container|sr-only)/.test(className))
1965
+ .filter((className) => /^(?:-?m[trblxy]?|-?p[trblxy]?|flex|grid|block|inline|hidden|items-|justify-|gap-|space-|w-|h-|min-|max-|size-|basis-|grow|shrink|aspect-|overflow-|object-|whitespace-|break-|truncate|line-clamp|text-balance|text-pretty|text-nowrap|rounded|border|bg-|text-|font-|leading-|tracking-|shadow|ring|opacity|transition|duration|ease|hover:|focus:|active:|disabled:|dark:|sm:|md:|lg:|xl:|2xl:|min-[^:]+:|max-[^:]+:|container|sr-only)/.test(className))
1651
1966
  .slice(0, 160);
1652
1967
  }
1653
1968
  function extractPackageDependencies(relPath, content) {
@@ -1701,10 +2016,17 @@ function extractUiElements(content, relPath) {
1701
2016
  const label = extractElementLabel(content, tagInfo);
1702
2017
  const classSource = readClassSource(attrs);
1703
2018
  const classAnalysis = classSource ? analyzeClassSource(classSource) : undefined;
2019
+ const styleHelper = classSource ? readPrimaryClassHelperUsage(classSource) : undefined;
1704
2020
  const classes = classAnalysis?.classes ?? [];
1705
2021
  const role = (0, utils_1.readAttr)(attrs, 'role');
1706
- const props = readUiProps(attrs);
1707
- const variants = readVariantProps(props);
2022
+ const props = {
2023
+ ...readUiProps(attrs),
2024
+ ...(styleHelper ? { styleHelper: styleHelper.name } : {}),
2025
+ };
2026
+ const variants = {
2027
+ ...readVariantProps(props),
2028
+ ...(styleHelper?.variantProps ?? {}),
2029
+ };
1708
2030
  const kind = classifyUiElement(tagInfo.originalTag, attrs, classes, role);
1709
2031
  const parentTagInfo = tagInfo.parentIndex === undefined ? undefined : tags[tagInfo.parentIndex];
1710
2032
  const parentClassSource = parentTagInfo ? readClassSource(parentTagInfo.attrs) : undefined;
@@ -1716,7 +2038,21 @@ function extractUiElements(content, relPath) {
1716
2038
  const layoutRole = deriveLayoutRole(tagInfo.originalTag, kind, classes, label);
1717
2039
  const responsiveClasses = classes.filter(isResponsiveClass);
1718
2040
  const stateClasses = classes.filter(isStateClass);
2041
+ const layoutSafety = createUiLayoutSafety(classes);
2042
+ const responsiveProfile = createUiResponsiveProfile(classes);
2043
+ const scaleProfile = createUiScaleProfile(classes);
1719
2044
  const childSummary = summarizeDirectUiChildren(content, tags, index);
2045
+ const semanticProfile = createUiSemanticProfile(kind, tagInfo.originalTag, classes, childSummary);
2046
+ const roleSignature = createUiRoleSignature({
2047
+ kind,
2048
+ tag: tagInfo.originalTag,
2049
+ classes,
2050
+ children: childSummary,
2051
+ label,
2052
+ layoutRole,
2053
+ parentKind,
2054
+ parentClasses,
2055
+ });
1720
2056
  if (kind === 'unknown') {
1721
2057
  continue;
1722
2058
  }
@@ -1748,6 +2084,11 @@ function extractUiElements(content, relPath) {
1748
2084
  classGroups: groupTailwindClasses(classes),
1749
2085
  ...(responsiveClasses.length ? { responsiveClasses } : {}),
1750
2086
  ...(stateClasses.length ? { stateClasses } : {}),
2087
+ ...(layoutSafety ? { layoutSafety } : {}),
2088
+ ...(responsiveProfile ? { responsiveProfile } : {}),
2089
+ ...(scaleProfile ? { scaleProfile } : {}),
2090
+ ...(semanticProfile ? { semanticProfile } : {}),
2091
+ ...(roleSignature ? { roleSignature } : {}),
1751
2092
  ...(childSummary.length ? { childSummary } : {}),
1752
2093
  ...((0, utils_1.readAttr)(attrs, 'id') ? { id: (0, utils_1.readAttr)(attrs, 'id') } : {}),
1753
2094
  ...(role ? { role } : {}),
@@ -1757,6 +2098,10 @@ function extractUiElements(content, relPath) {
1757
2098
  ...((0, utils_1.readAttr)(attrs, 'method') ? { method: (0, utils_1.readAttr)(attrs, 'method')?.toUpperCase() } : {}),
1758
2099
  ...(Object.keys(props).length ? { props } : {}),
1759
2100
  ...(Object.keys(variants).length ? { variants } : {}),
2101
+ ...(styleHelper ? { styleHelper: styleHelper.name } : {}),
2102
+ ...(styleHelper && Object.keys(styleHelper.variantProps).length
2103
+ ? { styleHelperVariants: styleHelper.variantProps }
2104
+ : {}),
1760
2105
  evidence: `<${tagInfo.originalTag}${classes.length ? ` class="${classes.slice(0, 6).join(' ')}"` : ''}>`,
1761
2106
  });
1762
2107
  }
@@ -1769,11 +2114,31 @@ function summarizeDirectUiChildren(content, tags, parentIndex) {
1769
2114
  .map(({ tagInfo, index }) => {
1770
2115
  const classSource = readClassSource(tagInfo.attrs);
1771
2116
  const classAnalysis = classSource ? analyzeClassSource(classSource) : undefined;
2117
+ const styleHelper = classSource ? readPrimaryClassHelperUsage(classSource) : undefined;
1772
2118
  const classes = classAnalysis?.classes ?? [];
1773
2119
  const role = (0, utils_1.readAttr)(tagInfo.attrs, 'role');
1774
2120
  const kind = classifyUiElement(tagInfo.originalTag, tagInfo.attrs, classes, role);
1775
2121
  const label = extractElementLabel(content, tagInfo);
1776
2122
  const layoutRole = deriveLayoutRole(tagInfo.originalTag, kind, classes, label);
2123
+ const layoutSafety = createUiLayoutSafety(classes);
2124
+ const responsiveProfile = createUiResponsiveProfile(classes);
2125
+ const scaleProfile = createUiScaleProfile(classes);
2126
+ const semanticRole = deriveUiChildSemanticRole(tagInfo.originalTag, kind, classes, label);
2127
+ const roleSignature = createUiRoleSignature({
2128
+ kind,
2129
+ tag: tagInfo.originalTag,
2130
+ classes,
2131
+ label,
2132
+ layoutRole,
2133
+ });
2134
+ const props = {
2135
+ ...readUiProps(tagInfo.attrs),
2136
+ ...(styleHelper ? { styleHelper: styleHelper.name } : {}),
2137
+ };
2138
+ const variants = {
2139
+ ...readVariantProps(props),
2140
+ ...(styleHelper?.variantProps ?? {}),
2141
+ };
1777
2142
  return {
1778
2143
  index,
1779
2144
  kind,
@@ -1787,12 +2152,552 @@ function summarizeDirectUiChildren(content, tags, parentIndex) {
1787
2152
  ...(classAnalysis?.conditionalClasses.length
1788
2153
  ? { conditionalClasses: classAnalysis.conditionalClasses.slice(0, 32) }
1789
2154
  : {}),
2155
+ ...(layoutSafety ? { layoutSafety } : {}),
2156
+ ...(responsiveProfile ? { responsiveProfile } : {}),
2157
+ ...(scaleProfile ? { scaleProfile } : {}),
2158
+ ...(semanticRole ? { semanticRole } : {}),
2159
+ ...(roleSignature ? { roleSignature } : {}),
2160
+ ...(Object.keys(props).length ? { props } : {}),
2161
+ ...(Object.keys(variants).length ? { variants } : {}),
2162
+ ...(styleHelper ? { styleHelper: styleHelper.name } : {}),
2163
+ ...(styleHelper && Object.keys(styleHelper.variantProps).length
2164
+ ? { styleHelperVariants: styleHelper.variantProps }
2165
+ : {}),
1790
2166
  ...(label ? { label } : {}),
1791
2167
  };
1792
2168
  })
1793
- .filter((child) => child.kind !== 'unknown' || child.classes.length || child.label)
2169
+ .filter((child) => child.kind !== 'unknown' ||
2170
+ child.classes.length ||
2171
+ child.label ||
2172
+ Boolean(child.semanticRole))
1794
2173
  .slice(0, 16);
1795
2174
  }
2175
+ function createUiSemanticProfile(kind, tag, classes, children) {
2176
+ const roles = (0, utils_1.unique)(children.flatMap((child) => child.semanticRole ?? [])).slice(0, 24);
2177
+ const textStyleSignatures = (0, utils_1.unique)(children
2178
+ .filter((child) => ['text', 'heading', 'badge'].includes(child.kind) || child.semanticRole)
2179
+ .map((child) => JSON.stringify(groupTailwindClasses(child.defaultClasses?.length ? child.defaultClasses : child.classes)))).filter(Boolean);
2180
+ const hasIcon = roles.includes('icon') || roles.includes('component-icon') || roles.includes('image');
2181
+ const hasDivider = roles.includes('divider');
2182
+ const hasImage = roles.includes('image');
2183
+ const hasMultipleTextStyles = textStyleSignatures.length > 1;
2184
+ const isCompound = children.length > 1 &&
2185
+ (hasIcon ||
2186
+ hasDivider ||
2187
+ hasMultipleTextStyles ||
2188
+ roles.includes('primary-text') ||
2189
+ roles.includes('secondary-text'));
2190
+ if (!isCompound && !roles.length) {
2191
+ return undefined;
2192
+ }
2193
+ return {
2194
+ roles,
2195
+ isCompound,
2196
+ hasIcon,
2197
+ hasDivider,
2198
+ hasImage,
2199
+ hasMultipleTextStyles,
2200
+ textStyleCount: textStyleSignatures.length,
2201
+ childCount: children.length,
2202
+ };
2203
+ }
2204
+ function createUiRoleSignature(input) {
2205
+ const { kind, tag, classes } = input;
2206
+ const children = input.children ?? [];
2207
+ const tagName = tag.toLowerCase();
2208
+ const bases = classes.map(utils_1.classBase);
2209
+ const baseSet = new Set(bases);
2210
+ const childRoles = (0, utils_1.unique)(children.flatMap((child) => [child.semanticRole, child.roleSignature?.role].filter((value) => typeof value === 'string' && Boolean(value)))).slice(0, 24);
2211
+ const childKinds = (0, utils_1.unique)(children.map((child) => child.kind).filter(Boolean)).slice(0, 24);
2212
+ const hasIcon = childRoles.some((role) => /(?:icon|image|logo|mark)/.test(role));
2213
+ const hasDivider = childRoles.includes('divider');
2214
+ const hasMultipleTextSegments = childRoles.filter((role) => /(?:primary|secondary|text|segment)/.test(role)).length > 1;
2215
+ const exactClassFacts = exactRoleClassFacts(classes);
2216
+ const flags = new Set();
2217
+ const layout = createRoleLayoutKey(kind, tagName, classes, children, input.parentClasses ?? []);
2218
+ const scale = createRoleScaleKey(classes, children);
2219
+ const density = createRoleDensityKey(classes, children);
2220
+ const surface = createRoleSurfaceKey(classes);
2221
+ let roleGroup = kind;
2222
+ let role = `${kind}-${input.layoutRole ?? 'element'}`;
2223
+ if (input.layoutRole) {
2224
+ flags.add(`layout-role:${input.layoutRole}`);
2225
+ }
2226
+ if (hasIcon)
2227
+ flags.add('has-icon');
2228
+ if (hasDivider)
2229
+ flags.add('has-divider');
2230
+ if (hasMultipleTextSegments)
2231
+ flags.add('multiple-text-segments');
2232
+ if (baseSet.has('uppercase'))
2233
+ flags.add('uppercase');
2234
+ if (classes.some((className) => className.includes('hover:')))
2235
+ flags.add('has-hover-state');
2236
+ if (baseSet.has('group'))
2237
+ flags.add('group-context');
2238
+ if (classes.some((className) => (0, utils_1.classBase)(className).startsWith('backdrop-blur'))) {
2239
+ flags.add('backdrop-blur');
2240
+ }
2241
+ if (kind === 'section') {
2242
+ roleGroup = 'section';
2243
+ if (baseSet.has('min-h-screen')) {
2244
+ role = 'hero-section-fullscreen';
2245
+ flags.add('full-screen-hero');
2246
+ }
2247
+ else if (layout === 'split-media-layout') {
2248
+ role = 'split-media-section';
2249
+ flags.add('split-media');
2250
+ }
2251
+ else if (layout === 'card-grid-layout') {
2252
+ role = 'card-grid-section';
2253
+ flags.add('card-grid');
2254
+ }
2255
+ else {
2256
+ role = 'section-shell';
2257
+ }
2258
+ }
2259
+ if (kind === 'layout') {
2260
+ roleGroup = 'layout';
2261
+ if (layout === 'compact-metric-grid-layout') {
2262
+ role = 'compact-metric-grid-layout';
2263
+ flags.add('compact-metric-grid');
2264
+ }
2265
+ else if (layout === 'split-media-layout') {
2266
+ role = 'split-media-layout';
2267
+ flags.add('split-media');
2268
+ }
2269
+ else if (layout === 'card-grid-layout') {
2270
+ role = 'card-grid-layout';
2271
+ flags.add('card-grid');
2272
+ }
2273
+ else if (layout === 'hero-content-layout') {
2274
+ role = 'hero-content-layout';
2275
+ flags.add('hero-content');
2276
+ }
2277
+ else {
2278
+ role = `layout-${layout}`;
2279
+ }
2280
+ }
2281
+ if (kind === 'heading') {
2282
+ roleGroup = tagName === 'h1' ? 'hero-heading' : 'heading';
2283
+ if (tagName === 'h1') {
2284
+ if (isOversizedHeadingScale(classes)) {
2285
+ role = 'hero-heading-oversized';
2286
+ flags.add('oversized-heading');
2287
+ }
2288
+ else if (isHeroHeadingScale(classes)) {
2289
+ role = 'hero-heading-standard';
2290
+ flags.add('hero-heading');
2291
+ }
2292
+ else {
2293
+ role = 'page-heading';
2294
+ }
2295
+ }
2296
+ else if (isMetricValueScale(classes)) {
2297
+ role = 'metric-heading';
2298
+ flags.add('metric-value');
2299
+ }
2300
+ else {
2301
+ role = 'section-heading';
2302
+ }
2303
+ }
2304
+ if (kind === 'badge') {
2305
+ roleGroup = 'badge';
2306
+ if (isOverlayPill(classes)) {
2307
+ role = 'overlay-disclaimer-pill';
2308
+ flags.add('overlay-pill');
2309
+ }
2310
+ else if (hasIcon || hasDivider || hasMultipleTextSegments) {
2311
+ role = 'brand-lockup-badge';
2312
+ flags.add('compound-badge');
2313
+ }
2314
+ else if (isCompactHeroEyebrow(classes)) {
2315
+ role = 'hero-eyebrow-compact';
2316
+ flags.add('compact-eyebrow');
2317
+ }
2318
+ else {
2319
+ role = 'badge-pill';
2320
+ }
2321
+ }
2322
+ if (kind === 'card') {
2323
+ roleGroup = 'surface';
2324
+ if (isCompactStatCard(classes, childRoles)) {
2325
+ role = 'hero-stat-card-compact';
2326
+ flags.add('compact-stat-card');
2327
+ }
2328
+ else if (isCtaPanelShell(classes, childRoles)) {
2329
+ role = 'cta-panel-shell';
2330
+ flags.add('cta-panel');
2331
+ }
2332
+ else if (isInteractiveServiceCard(classes)) {
2333
+ role = 'interactive-service-card';
2334
+ flags.add('interactive-card');
2335
+ }
2336
+ else if (isStaticArticleCard(classes)) {
2337
+ role = 'static-article-card';
2338
+ flags.add('static-article-surface');
2339
+ }
2340
+ else if (isMediaPanelCard(classes, childKinds, childRoles)) {
2341
+ role = 'media-panel-card';
2342
+ flags.add('media-panel');
2343
+ }
2344
+ else {
2345
+ role = 'surface-card';
2346
+ }
2347
+ }
2348
+ if (kind === 'text') {
2349
+ roleGroup = 'text';
2350
+ if (isPlainTextEyebrow(classes, input.layoutRole)) {
2351
+ role = 'section-eyebrow-text';
2352
+ flags.add('plain-eyebrow');
2353
+ }
2354
+ else if (isCompactStatValue(classes)) {
2355
+ role = 'compact-stat-value';
2356
+ flags.add('compact-stat-value');
2357
+ }
2358
+ else if (isLargeMetricValue(classes)) {
2359
+ role = 'large-metric-value';
2360
+ flags.add('large-metric-value');
2361
+ }
2362
+ else if (isOverlayPill(classes)) {
2363
+ role = 'overlay-disclaimer-text';
2364
+ flags.add('overlay-pill');
2365
+ }
2366
+ else {
2367
+ role = 'body-copy';
2368
+ }
2369
+ }
2370
+ if (kind === 'button') {
2371
+ roleGroup = 'action';
2372
+ if (isQuietAction(classes)) {
2373
+ role = 'quiet-action';
2374
+ flags.add('low-emphasis-action');
2375
+ }
2376
+ else if (isCompactAction(classes)) {
2377
+ role = 'compact-action-button';
2378
+ flags.add('compact-action');
2379
+ }
2380
+ else {
2381
+ role = 'action-button';
2382
+ }
2383
+ }
2384
+ if (!classes.length && !childRoles.length && role === `${kind}-${input.layoutRole ?? 'element'}`) {
2385
+ return undefined;
2386
+ }
2387
+ return {
2388
+ role,
2389
+ roleGroup,
2390
+ scale,
2391
+ density,
2392
+ surface,
2393
+ layout,
2394
+ flags: Array.from(flags).sort(),
2395
+ exactClassFacts,
2396
+ ...(childRoles.length ? { childRoles } : {}),
2397
+ ...(childKinds.length ? { childKinds } : {}),
2398
+ };
2399
+ }
2400
+ function exactRoleClassFacts(classes) {
2401
+ return (0, utils_1.unique)(classes.filter((className) => {
2402
+ const base = (0, utils_1.classBase)(className);
2403
+ return (isTypographyClass(className) ||
2404
+ isSpacingClass(className) ||
2405
+ isSizingClass(className) ||
2406
+ isLayoutClass(className) ||
2407
+ isStateClass(className) ||
2408
+ /^(?:bg-|text-|border|rounded|shadow|backdrop|opacity|ring|outline|z-|absolute|relative|fixed|sticky|top-|right-|bottom-|left-|overflow-|object-|aspect-|auto-rows-|grid-cols-|grid-rows-|group|transition|duration|ease)/.test(base));
2409
+ })).slice(0, 120);
2410
+ }
2411
+ function createRoleScaleKey(classes, children = []) {
2412
+ const scaleClasses = (0, utils_1.unique)([
2413
+ ...classes,
2414
+ ...children.flatMap((child) => child.classes.slice(0, 24)),
2415
+ ].filter((className) => {
2416
+ const base = (0, utils_1.classBase)(className);
2417
+ return (isTypographyClass(className) ||
2418
+ isSizingClass(className) ||
2419
+ /^(?:max-w-|min-w-|leading-|tracking-|text-|font-|h-|w-|min-h-|max-h-)/.test(base));
2420
+ }))
2421
+ .sort(roleClassSort)
2422
+ .slice(0, 40);
2423
+ return scaleClasses.length ? scaleClasses.join(' ') : 'scale-unspecified';
2424
+ }
2425
+ function createRoleDensityKey(classes, children = []) {
2426
+ const densityClasses = (0, utils_1.unique)([
2427
+ ...classes,
2428
+ ...children.flatMap((child) => child.classes.slice(0, 16)),
2429
+ ].filter((className) => {
2430
+ const base = (0, utils_1.classBase)(className);
2431
+ return /^(?:p[trblxy]?|m[trblxy]?|gap-|space-|h-|min-h-|max-h-|w-|min-w-|max-w-)/.test(base);
2432
+ }))
2433
+ .sort(roleClassSort)
2434
+ .slice(0, 40);
2435
+ if (!densityClasses.length) {
2436
+ return 'density-unspecified';
2437
+ }
2438
+ if (densityClasses.some((className) => /(?:^|:)py-(?:1|1\.5|2)$/.test(className)) ||
2439
+ densityClasses.some((className) => /(?:^|:)h-(?:8|9|10|11|12)$/.test(className))) {
2440
+ return `compact:${densityClasses.join(' ')}`;
2441
+ }
2442
+ if (densityClasses.some((className) => /(?:^|:)p-(?:8|10|12|16|20|24)$/.test(className)) ||
2443
+ densityClasses.some((className) => /(?:^|:)py-(?:8|10|12|16|20|24)$/.test(className))) {
2444
+ return `spacious:${densityClasses.join(' ')}`;
2445
+ }
2446
+ return densityClasses.join(' ');
2447
+ }
2448
+ function createRoleSurfaceKey(classes) {
2449
+ const surfaceClasses = (0, utils_1.unique)(classes.filter((className) => {
2450
+ const base = (0, utils_1.classBase)(className);
2451
+ return /^(?:bg-|border|rounded|shadow|ring|outline|backdrop|opacity|group|transition|hover:border|hover:bg)/.test(base);
2452
+ }))
2453
+ .sort(roleClassSort)
2454
+ .slice(0, 40);
2455
+ return surfaceClasses.length ? surfaceClasses.join(' ') : 'surface-unspecified';
2456
+ }
2457
+ function createRoleLayoutKey(kind, tagName, classes, children = [], parentClasses = []) {
2458
+ const bases = classes.map(utils_1.classBase);
2459
+ const baseSet = new Set(bases);
2460
+ const childKinds = new Set(children.map((child) => child.kind));
2461
+ const childRoles = new Set(children.flatMap((child) => [child.semanticRole, child.roleSignature?.role].filter(Boolean)));
2462
+ const childClassBases = children.flatMap((child) => child.classes.map(utils_1.classBase));
2463
+ const hasMediaChild = childKinds.has('image') ||
2464
+ childRoles.has('image') ||
2465
+ childRoles.has('media-panel-card') ||
2466
+ childClassBases.some((base) => /^(?:aspect-|object-|h-\[|min-h-\[)/.test(base));
2467
+ const hasCardChildren = children.filter((child) => child.kind === 'card').length >= 2;
2468
+ const hasTextChildren = children.some((child) => ['heading', 'text', 'badge'].includes(child.kind) ||
2469
+ (child.kind === 'layout' &&
2470
+ child.classes.some((className) => /^(?:max-w-|space-y-|prose|flex|grid)/.test((0, utils_1.classBase)(className)))));
2471
+ const parentBaseSet = new Set(parentClasses.map(utils_1.classBase));
2472
+ if (baseSet.has('min-h-screen')) {
2473
+ return 'full-screen-hero-layout';
2474
+ }
2475
+ if (baseSet.has('absolute') &&
2476
+ (baseSet.has('bottom-3') || baseSet.has('bottom-4')) &&
2477
+ (baseSet.has('right-3') || baseSet.has('right-4'))) {
2478
+ return 'absolute-bottom-right-overlay';
2479
+ }
2480
+ if (isCompactMetricGrid(classes) ||
2481
+ isCompactMetricGrid(parentClasses)) {
2482
+ return 'compact-metric-grid-layout';
2483
+ }
2484
+ if ((baseSet.has('grid') || parentBaseSet.has('grid')) &&
2485
+ hasMediaChild &&
2486
+ hasTextChildren &&
2487
+ classes.some((className) => /(?:^|:)grid-cols-|(?:^|:)lg:grid-cols-/.test(className))) {
2488
+ return 'split-media-layout';
2489
+ }
2490
+ if ((baseSet.has('grid') || parentBaseSet.has('grid')) && hasCardChildren) {
2491
+ return 'card-grid-layout';
2492
+ }
2493
+ if (tagName === 'main' || (kind === 'layout' && baseSet.has('mx-auto'))) {
2494
+ return 'page-content-layout';
2495
+ }
2496
+ if (baseSet.has('flex') || baseSet.has('inline-flex')) {
2497
+ return 'flex-layout';
2498
+ }
2499
+ if (baseSet.has('grid')) {
2500
+ return 'grid-layout';
2501
+ }
2502
+ return 'layout-unspecified';
2503
+ }
2504
+ function isCompactHeroEyebrow(classes) {
2505
+ const bases = classes.map(utils_1.classBase);
2506
+ const baseSet = new Set(bases);
2507
+ return (baseSet.has('inline-flex') &&
2508
+ baseSet.has('rounded-full') &&
2509
+ baseSet.has('uppercase') &&
2510
+ classes.some((className) => /(?:^|:)text-xs$/.test(className)) &&
2511
+ classes.some((className) => /(?:^|:)sm:text-sm$/.test(className)) &&
2512
+ bases.some((base) => /^py-(?:1\.5|2)$/.test(base)) &&
2513
+ bases.some((base) => /^px-(?:3|4|5)$/.test(base)));
2514
+ }
2515
+ function isPlainTextEyebrow(classes, layoutRole) {
2516
+ const bases = classes.map(utils_1.classBase);
2517
+ const baseSet = new Set(bases);
2518
+ const hasPlainTextScale = classes.some((className) => /(?:^|:)text-(?:xs|sm|\[(?:10|11|12|13|14)px\])(?:\/\d+|\]\/\d+)?$/.test(className));
2519
+ const hasSurfaceTreatment = bases.some((base) => /^(?:bg-|border|rounded|shadow|ring|outline|backdrop)/.test(base));
2520
+ const hasPillSpacing = bases.some((base) => /^px-/.test(base)) || bases.some((base) => /^py-/.test(base));
2521
+ return (layoutRole === 'eyebrow-or-badge' &&
2522
+ baseSet.has('uppercase') &&
2523
+ hasPlainTextScale &&
2524
+ !hasSurfaceTreatment &&
2525
+ !hasPillSpacing);
2526
+ }
2527
+ function isOverlayPill(classes) {
2528
+ const bases = classes.map(utils_1.classBase);
2529
+ const baseSet = new Set(bases);
2530
+ return (baseSet.has('absolute') &&
2531
+ baseSet.has('rounded-full') &&
2532
+ bases.some((base) => /^bottom-/.test(base)) &&
2533
+ bases.some((base) => /^right-/.test(base)) &&
2534
+ (bases.some((base) => /^text-\[(?:10|11)px\]$/.test(base)) ||
2535
+ classes.some((className) => /(?:^|:)sm:text-\[(?:10|11)px\]$/.test(className))));
2536
+ }
2537
+ function isHeroHeadingScale(classes) {
2538
+ const classSet = new Set(classes);
2539
+ const bases = classes.map(utils_1.classBase);
2540
+ return (classSet.has('text-4xl') &&
2541
+ classSet.has('sm:text-6xl') &&
2542
+ classSet.has('lg:text-7xl') &&
2543
+ bases.some((base) => /^leading-/.test(base)));
2544
+ }
2545
+ function isOversizedHeadingScale(classes) {
2546
+ const classSet = new Set(classes);
2547
+ return (classSet.has('text-6xl') ||
2548
+ classSet.has('sm:text-7xl') ||
2549
+ classSet.has('lg:text-8xl') ||
2550
+ classSet.has('xl:text-9xl') ||
2551
+ classes.some((className) => /(?:^|:)text-\[(?:8|9|10)\dpx\]/.test(className)));
2552
+ }
2553
+ function isMetricValueScale(classes) {
2554
+ return classes.some((className) => /(?:^|:)text-(?:lg|xl|2xl|3xl|4xl)$/.test(className));
2555
+ }
2556
+ function isCompactStatValue(classes) {
2557
+ const classSet = new Set(classes);
2558
+ const bases = classes.map(utils_1.classBase);
2559
+ return (classSet.has('text-lg') &&
2560
+ bases.some((base) => /^font-(?:bold|extrabold|black)$/.test(base)) &&
2561
+ bases.some((base) => /^text-\[|^text-(?:brand|accent|yellow|amber|gold)/.test(base)));
2562
+ }
2563
+ function isLargeMetricValue(classes) {
2564
+ const classSet = new Set(classes);
2565
+ return (classSet.has('text-2xl') ||
2566
+ classSet.has('sm:text-3xl') ||
2567
+ classSet.has('text-3xl') ||
2568
+ classSet.has('text-4xl'));
2569
+ }
2570
+ function isCompactStatCard(classes, childRoles) {
2571
+ const bases = classes.map(utils_1.classBase);
2572
+ const baseSet = new Set(bases);
2573
+ return (childRoles.includes('compact-stat-value') ||
2574
+ (baseSet.has('rounded-lg') &&
2575
+ baseSet.has('border') &&
2576
+ bases.some((base) => /^p-(?:4|5)$/.test(base)) &&
2577
+ !baseSet.has('group')));
2578
+ }
2579
+ function isCompactMetricGrid(classes) {
2580
+ const bases = classes.map(utils_1.classBase);
2581
+ const baseSet = new Set(bases);
2582
+ const hasCompactGap = bases.some((base) => /^gap-(?:2|3|4)$/.test(base));
2583
+ const hasConstrainedWidth = classes.some((className) => /(?:^|:)(?:max-w-(?:2xl|3xl|4xl)|lg:max-w-(?:2xl|3xl|4xl)|max-w-\[[^\]]+\])$/.test(className));
2584
+ const hasDenseColumns = classes.some((className) => /(?:^|:)(?:grid-cols-[34]|grid-cols-\[[^\]]+\]|lg:grid-cols-[34]|md:grid-cols-[34])$/.test(className));
2585
+ return (baseSet.has('grid') &&
2586
+ (baseSet.has('auto-rows-fr') || baseSet.has('items-stretch')) &&
2587
+ hasCompactGap &&
2588
+ hasDenseColumns &&
2589
+ hasConstrainedWidth);
2590
+ }
2591
+ function isCtaPanelShell(classes, childRoles = []) {
2592
+ const bases = classes.map(utils_1.classBase);
2593
+ const baseSet = new Set(bases);
2594
+ const hasLargePanelShape = bases.some((base) => /^rounded-\[(?:2[4-9]|3\d)px\]$/.test(base)) ||
2595
+ bases.some((base) => /^rounded-(?:2xl|3xl)$/.test(base));
2596
+ const hasSpaciousPadding = bases.some((base) => /^p-(?:8|10|12|14|16)$/.test(base)) ||
2597
+ classes.some((className) => /(?:^|:)p-(?:8|10|12|14|16)$/.test(className));
2598
+ const hasCalloutSurface = bases.some((base) => /^bg-/.test(base)) &&
2599
+ bases.some((base) => /^border/.test(base)) &&
2600
+ (baseSet.has('overflow-hidden') || bases.some((base) => /^shadow/.test(base)));
2601
+ const hasTextHierarchy = childRoles.includes('section-heading') ||
2602
+ childRoles.includes('section-eyebrow-text') ||
2603
+ childRoles.includes('body-copy') ||
2604
+ childRoles.includes('action-button');
2605
+ return hasLargePanelShape && hasSpaciousPadding && hasCalloutSurface && hasTextHierarchy;
2606
+ }
2607
+ function isInteractiveServiceCard(classes) {
2608
+ const bases = classes.map(utils_1.classBase);
2609
+ const baseSet = new Set(bases);
2610
+ return (baseSet.has('group') ||
2611
+ classes.some((className) => className.includes('hover:')) ||
2612
+ (baseSet.has('transition') && bases.includes('shadow-2xl')));
2613
+ }
2614
+ function isStaticArticleCard(classes) {
2615
+ const bases = classes.map(utils_1.classBase);
2616
+ const baseSet = new Set(bases);
2617
+ return (baseSet.has('flex') &&
2618
+ baseSet.has('h-full') &&
2619
+ baseSet.has('flex-col') &&
2620
+ baseSet.has('rounded-lg') &&
2621
+ baseSet.has('border') &&
2622
+ bases.includes('p-6') &&
2623
+ !baseSet.has('group') &&
2624
+ !classes.some((className) => className.includes('hover:')));
2625
+ }
2626
+ function isMediaPanelCard(classes, childKinds, childRoles) {
2627
+ const bases = classes.map(utils_1.classBase);
2628
+ return (childKinds.includes('image') ||
2629
+ childRoles.includes('image') ||
2630
+ bases.some((base) => /^(?:aspect-|object-|overflow-hidden)$/.test(base)));
2631
+ }
2632
+ function isQuietAction(classes) {
2633
+ const bases = classes.map(utils_1.classBase);
2634
+ return (bases.includes('bg-transparent') &&
2635
+ (bases.includes('border-transparent') ||
2636
+ bases.some((base) => /^text-(?:muted|secondary|slate|gray)/.test(base))));
2637
+ }
2638
+ function isCompactAction(classes) {
2639
+ const bases = classes.map(utils_1.classBase);
2640
+ return (bases.some((base) => /^h-(?:9|10|11|12)$/.test(base)) &&
2641
+ bases.some((base) => /^px-(?:4|5)$/.test(base)) &&
2642
+ classes.some((className) => /(?:^|:)text-sm$/.test(className)));
2643
+ }
2644
+ function roleClassSort(a, b) {
2645
+ const priority = (value) => {
2646
+ const base = (0, utils_1.classBase)(value);
2647
+ if (/^(?:max-w-|min-w-|w-|h-|min-h-|max-h-)/.test(base))
2648
+ return 0;
2649
+ if (/^(?:text-|font-|leading-|tracking-)/.test(base))
2650
+ return 1;
2651
+ if (/^(?:p|m|gap|space)-/.test(base))
2652
+ return 2;
2653
+ if (/^(?:bg-|border|rounded|shadow|backdrop)/.test(base))
2654
+ return 3;
2655
+ return 4;
2656
+ };
2657
+ return priority(a) - priority(b) || a.localeCompare(b);
2658
+ }
2659
+ function deriveUiChildSemanticRole(tag, kind, classes, label) {
2660
+ const tagName = tag.toLowerCase();
2661
+ const tagBase = tag.split('.')[0].split(':').pop() ?? tag;
2662
+ const lowerBase = tagBase.toLowerCase();
2663
+ const classBases = classes.map(utils_1.classBase);
2664
+ const classHaystack = [tagName, lowerBase, ...classBases].join(' ').toLowerCase();
2665
+ const labelHaystack = (label ?? '').toLowerCase();
2666
+ if (kind === 'icon') {
2667
+ return 'icon';
2668
+ }
2669
+ if (kind === 'image') {
2670
+ return 'image';
2671
+ }
2672
+ if (kind === 'divider') {
2673
+ return 'divider';
2674
+ }
2675
+ if (tagName === 'img' ||
2676
+ tagName === 'picture' ||
2677
+ tagName === 'svg' ||
2678
+ /(?:icon|logo|mark|glyph|avatar|image|img)$/.test(lowerBase) ||
2679
+ /\b(?:icon|logo|mark|glyph|avatar)\b/.test(classHaystack)) {
2680
+ return tagName === 'img' || tagName === 'picture' ? 'image' : 'icon';
2681
+ }
2682
+ if (classes.some((className) => /^(?:w-px|h-px|border-l|border-r|border-t|border-b|divide-x|divide-y)$/.test((0, utils_1.classBase)(className))) ||
2683
+ /\b(?:divider|separator|sep|rule)\b/.test(classHaystack)) {
2684
+ return 'divider';
2685
+ }
2686
+ if (kind === 'text' || kind === 'heading' || kind === 'badge') {
2687
+ if (classes.some((className) => /^(?:text-muted|text-muted-foreground|text-secondary|opacity-|font-normal)/.test((0, utils_1.classBase)(className)))) {
2688
+ return 'secondary-text';
2689
+ }
2690
+ if (classes.some((className) => /^(?:font-bold|font-semibold|font-black|text-primary|text-brand|text-accent|text-yellow|text-amber|text-gold|text-\[)/.test((0, utils_1.classBase)(className))) ||
2691
+ (labelHaystack.length > 0 && labelHaystack.length <= 32)) {
2692
+ return 'primary-text';
2693
+ }
2694
+ return 'text-segment';
2695
+ }
2696
+ if (/^[A-Z]/.test(tagBase) && !classes.length && !label) {
2697
+ return 'component-icon';
2698
+ }
2699
+ return undefined;
2700
+ }
1796
2701
  function deriveLayoutRole(tag, kind, classes, label) {
1797
2702
  const tagName = tag.toLowerCase();
1798
2703
  const tagBase = tag.split('.')[0].split(':').pop() ?? tag;
@@ -1857,7 +2762,8 @@ function deriveLayoutRole(tag, kind, classes, label) {
1857
2762
  return undefined;
1858
2763
  }
1859
2764
  function isStateClass(className) {
1860
- return /^(?:hover|focus|active|disabled|visited|dark|group|peer|aria|data):/.test(className);
2765
+ const prefixes = className.split(':').slice(0, -1);
2766
+ return prefixes.some((prefix) => /^(?:hover|focus|active|disabled|visited|dark|group|peer|aria|data)/.test(prefix));
1861
2767
  }
1862
2768
  function extractApiCalls(content) {
1863
2769
  const calls = new Set();
@@ -2314,7 +3220,8 @@ function isTemplateFile(relPath) {
2314
3220
  relPath.endsWith('.blade.php'));
2315
3221
  }
2316
3222
  function isTestFile(relPath) {
2317
- return (/(?:^|\/)(?:__tests__|tests?|spec)\//i.test(relPath) ||
3223
+ return (/(?:^|\/)(?:app|pages|src\/app|src\/pages)\/test\//i.test(relPath) ||
3224
+ /(?:^|\/)(?:__tests__|tests?|spec)\//i.test(relPath) ||
2318
3225
  /\.(test|spec)\.[A-Za-z0-9]+$/.test(relPath));
2319
3226
  }
2320
3227
  function isConfigFile(relPath) {
@@ -2527,6 +3434,8 @@ function classifyUiElement(tag, attrs, classes, role) {
2527
3434
  const tagBase = originalTag.split('.')[0].split(':').pop() ?? originalTag;
2528
3435
  const lowerBase = tagBase.toLowerCase();
2529
3436
  const classHaystack = [tagName, role ?? '', ...classes].join(' ').toLowerCase();
3437
+ const classHelper = readPrimaryClassHelperUsage(readClassSource(attrs) ?? '');
3438
+ const classHelperName = classHelper?.name.toLowerCase() ?? '';
2530
3439
  const propHaystack = [
2531
3440
  tagName,
2532
3441
  role ?? '',
@@ -2541,12 +3450,30 @@ function classifyUiElement(tag, attrs, classes, role) {
2541
3450
  if (/^h[1-6]$/.test(tagName) || /^(heading|title|headline)$/i.test(tagBase)) {
2542
3451
  return 'heading';
2543
3452
  }
3453
+ if (tagName === 'img' ||
3454
+ tagName === 'picture' ||
3455
+ /^(image|img|avatar)$/i.test(tagBase) ||
3456
+ /(?:image|img|avatar)$/.test(lowerBase)) {
3457
+ return 'image';
3458
+ }
3459
+ if (tagName === 'svg' ||
3460
+ /^(icon|logo|mark|glyph)$/i.test(tagBase) ||
3461
+ /(?:icon|logo|mark|glyph)$/.test(lowerBase) ||
3462
+ /\b(icon|logo|mark|glyph)\b/.test(classHaystack)) {
3463
+ return 'icon';
3464
+ }
3465
+ if (/^(divider|separator|rule)$/i.test(tagBase) ||
3466
+ /\b(divider|separator|rule)\b/.test(classHaystack) ||
3467
+ classes.some((className) => /^(?:w-px|h-px|border-l|border-r|border-t|border-b|divide-x|divide-y)$/.test((0, utils_1.classBase)(className)))) {
3468
+ return 'divider';
3469
+ }
2544
3470
  if (['input', 'textarea', 'select', 'option', 'label', 'fieldset'].includes(tagName) ||
2545
3471
  /^(input|textarea|select|field|fieldlabel|label|checkbox|switch|slider|radio|combobox)$/i.test(tagBase) ||
2546
3472
  /\b(field|input|textarea|select|checkbox|switch|slider|radio)\b/.test(classHaystack)) {
2547
3473
  return 'input';
2548
3474
  }
2549
- if (tagName === 'button' ||
3475
+ if (/(?:button|cta|action)/.test(classHelperName) ||
3476
+ tagName === 'button' ||
2550
3477
  role === 'button' ||
2551
3478
  /^(button|iconbutton|submitbutton|cta)$/i.test(tagBase) ||
2552
3479
  /\b(btn|button|cta|submit)\b/.test(classHaystack) ||
@@ -2574,12 +3501,15 @@ function classifyUiElement(tag, attrs, classes, role) {
2574
3501
  /\b(dialog|modal|popover|drawer|sheet)\b/.test(classHaystack)) {
2575
3502
  return 'dialog';
2576
3503
  }
2577
- if (/^(badge|chip|pill|tag|status|eyebrow)$/i.test(tagBase) ||
2578
- /\b(badge|chip|pill|tag|status|eyebrow)\b/.test(classHaystack)) {
3504
+ if (/(?:badge|chip|pill|status)/.test(classHelperName) ||
3505
+ /^(badge|chip|pill|tag|status|eyebrow)$/i.test(tagBase) ||
3506
+ /\b(badge|chip|pill|tag|status|eyebrow)\b/.test(classHaystack) ||
3507
+ looksLikePillBadge(tagName, classes)) {
2579
3508
  return 'badge';
2580
3509
  }
2581
3510
  if (/^(card|panel|tile|surface|sectionframe|frame|callout)$/i.test(tagBase) ||
2582
- /\b(card|panel|tile|surface|section-frame|frame|callout)\b/.test(classHaystack)) {
3511
+ /\b(card|panel|tile|section-frame|context-surface|frame|callout)\b/.test(classHaystack) ||
3512
+ looksLikeSurfaceContainer(tagName, tagBase, classes)) {
2583
3513
  return 'card';
2584
3514
  }
2585
3515
  if (tagName === 'section' || lowerBase === 'section') {
@@ -2594,6 +3524,49 @@ function classifyUiElement(tag, attrs, classes, role) {
2594
3524
  }
2595
3525
  return 'unknown';
2596
3526
  }
3527
+ function looksLikeSurfaceContainer(tagName, tagBase, classes) {
3528
+ const bases = classes.map(utils_1.classBase);
3529
+ const isContainerTag = ['div', 'article', 'section', 'aside', 'figure', 'li', 'main', 'header', 'footer'].includes(tagName) ||
3530
+ /^[A-Z]/.test(tagBase);
3531
+ const isTinyIndicator = tagName === 'span' &&
3532
+ bases.includes('rounded-full') &&
3533
+ bases.some((base) => /^(?:h|size)-(?:px|0\.5|1|1\.5|2|2\.5|3)$/.test(base)) &&
3534
+ bases.some((base) => /^(?:w|size)-(?:px|0\.5|1|1\.5|2|2\.5|3)$/.test(base));
3535
+ if (!isContainerTag || isTinyIndicator) {
3536
+ return false;
3537
+ }
3538
+ const hasSurfaceBackground = bases.some((base) => base === 'bg-card' ||
3539
+ base === 'section-frame' ||
3540
+ base === 'context-surface' ||
3541
+ /^bg-/.test(base) ||
3542
+ /^bg-\[var\(--(?:surface|card)-/.test(base));
3543
+ const hasContainerShape = bases.some((base) => /^(?:border|rounded|shadow|ring|outline|overflow-hidden)/.test(base));
3544
+ const hasContainerStructure = bases.some((base) => /^(?:p[trblxy]?|gap-|space-|min-h-|max-w-|w-full|flex|grid|relative)/.test(base));
3545
+ return hasSurfaceBackground && hasContainerShape && hasContainerStructure;
3546
+ }
3547
+ function looksLikePillBadge(tagName, classes) {
3548
+ const bases = classes.map(utils_1.classBase);
3549
+ const baseSet = new Set(bases);
3550
+ const allowedTag = ['div', 'span', 'small', 'p', 'li'].includes(tagName);
3551
+ const hasPillShape = baseSet.has('rounded-full') || bases.some((base) => /^rounded-\[/.test(base));
3552
+ const hasCompactSpacing = bases.some((base) => /^px-(?:2|2\.5|3|4|5|6)$/.test(base)) &&
3553
+ bases.some((base) => /^py-(?:1|1\.5|2|2\.5|3)$/.test(base));
3554
+ const hasBadgeText = baseSet.has('uppercase') ||
3555
+ classes.some((className) => /(?:^|:)text-(?:xs|sm|\[(?:10|11|12|13|14)px\])$/.test(className));
3556
+ const hasCompoundPillLayout = bases.some((base) => /^gap-/.test(base));
3557
+ const hasBadgeSurface = bases.some((base) => /^bg-/.test(base)) ||
3558
+ bases.some((base) => /^border/.test(base)) ||
3559
+ bases.some((base) => /^shadow/.test(base));
3560
+ const hasInlineLayout = baseSet.has('inline-flex') ||
3561
+ baseSet.has('inline-grid') ||
3562
+ (baseSet.has('absolute') && bases.some((base) => /^bottom-|^top-/.test(base)));
3563
+ return (allowedTag &&
3564
+ hasPillShape &&
3565
+ hasCompactSpacing &&
3566
+ (hasBadgeText || hasCompoundPillLayout) &&
3567
+ hasBadgeSurface &&
3568
+ hasInlineLayout);
3569
+ }
2597
3570
  function stripTags(value) {
2598
3571
  return value.replace(/<[^>]+>/g, ' ').replace(/\{[{%][\s\S]*?[}%]\}/g, ' ');
2599
3572
  }