chaincss 2.0.7 → 2.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.
Files changed (159) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/CODE_OF_CONDUCT.md +21 -0
  3. package/CONTRIBUTING.md +28 -0
  4. package/README.md +455 -226
  5. package/demo/demo/node_modules/caniuse-db/fulldata-json/data-2.0.json +1 -0
  6. package/demo/index.html +16 -0
  7. package/demo/package.json +20 -0
  8. package/demo/src/App.tsx +117 -0
  9. package/demo/src/chaincss-barrel.ts +9 -0
  10. package/demo/src/main.tsx +8 -0
  11. package/demo/src/styles.chain.ts +300 -0
  12. package/demo/vite.config.ts +46 -0
  13. package/dist/cli/commands/build.d.ts +0 -1
  14. package/dist/cli/commands/cache.d.ts +1 -0
  15. package/dist/cli/commands/init.d.ts +6 -3
  16. package/dist/cli/commands/timeline.d.ts +0 -1
  17. package/dist/cli/commands/watch.d.ts +0 -1
  18. package/dist/cli/index.d.ts +0 -1
  19. package/dist/cli/index.js +3213 -5296
  20. package/dist/cli/types.d.ts +51 -20
  21. package/dist/cli/utils/config-loader.d.ts +0 -1
  22. package/dist/cli/utils/file-utils.d.ts +27 -3
  23. package/dist/cli/utils/logger.d.ts +0 -1
  24. package/dist/compiler/Chain.d.ts +215 -0
  25. package/dist/compiler/animations.d.ts +76 -0
  26. package/dist/compiler/atomic-optimizer.d.ts +47 -12
  27. package/dist/compiler/breakpoints.d.ts +46 -0
  28. package/dist/compiler/btt.d.ts +36 -60
  29. package/dist/compiler/cache-manager.d.ts +58 -4
  30. package/dist/compiler/commonProps.d.ts +0 -1
  31. package/dist/compiler/content-addressable-cache.d.ts +78 -0
  32. package/dist/compiler/helpers.d.ts +54 -0
  33. package/dist/compiler/index.d.ts +16 -9
  34. package/dist/compiler/index.js +4450 -4316
  35. package/dist/compiler/prefixer.d.ts +17 -1
  36. package/dist/compiler/shorthands.d.ts +28 -0
  37. package/dist/compiler/suggestions.d.ts +43 -0
  38. package/dist/compiler/theme-contract.d.ts +16 -27
  39. package/dist/compiler/token-resolver.d.ts +69 -0
  40. package/dist/compiler/tokens.d.ts +33 -8
  41. package/dist/core/auto-detector.d.ts +34 -0
  42. package/dist/core/common-utils.d.ts +97 -0
  43. package/dist/core/compiler.d.ts +63 -23
  44. package/dist/core/constants.d.ts +137 -36
  45. package/dist/core/smart-chain.d.ts +3 -0
  46. package/dist/core/types.d.ts +122 -15
  47. package/dist/core/utils.d.ts +134 -17
  48. package/dist/index.d.ts +52 -8
  49. package/dist/index.js +7090 -5578
  50. package/dist/plugins/vite.d.ts +7 -5
  51. package/dist/plugins/vite.js +2964 -25641
  52. package/dist/plugins/webpack.d.ts +24 -1
  53. package/dist/plugins/webpack.js +209 -72
  54. package/dist/runtime/Chain.d.ts +32 -0
  55. package/dist/runtime/auto-hooks.d.ts +11 -0
  56. package/dist/runtime/hmr.d.ts +22 -2
  57. package/dist/runtime/index.d.ts +3 -2
  58. package/dist/runtime/index.js +3648 -301
  59. package/dist/runtime/injector.d.ts +39 -72
  60. package/dist/runtime/react.d.ts +17 -12
  61. package/dist/runtime/svelte.d.ts +15 -0
  62. package/dist/runtime/types.d.ts +126 -4
  63. package/dist/runtime/utils.d.ts +0 -1
  64. package/dist/runtime/vue.d.ts +34 -14
  65. package/package.json +59 -66
  66. package/src/cli/commands/build.ts +133 -0
  67. package/src/cli/commands/cache.ts +371 -0
  68. package/src/cli/commands/init.ts +230 -0
  69. package/src/cli/commands/timeline.ts +435 -0
  70. package/src/cli/commands/watch.ts +211 -0
  71. package/src/cli/index.ts +226 -0
  72. package/src/cli/types.ts +100 -0
  73. package/src/cli/utils/config-loader.ts +174 -0
  74. package/src/cli/utils/file-utils.ts +139 -0
  75. package/src/cli/utils/logger.ts +74 -0
  76. package/src/compiler/Chain.ts +831 -0
  77. package/src/compiler/animations.ts +517 -0
  78. package/src/compiler/atomic-optimizer.ts +786 -0
  79. package/src/compiler/breakpoints.ts +347 -0
  80. package/src/compiler/btt.ts +1147 -0
  81. package/src/compiler/cache-manager.ts +446 -0
  82. package/src/compiler/commonProps.ts +18 -0
  83. package/src/compiler/content-addressable-cache.ts +478 -0
  84. package/src/compiler/helpers.ts +407 -0
  85. package/src/compiler/index.ts +72 -0
  86. package/src/compiler/prefixer.ts +720 -0
  87. package/src/compiler/shorthands.ts +558 -0
  88. package/src/compiler/suggestions.ts +436 -0
  89. package/src/compiler/theme-contract.ts +197 -0
  90. package/src/compiler/token-resolver.ts +241 -0
  91. package/src/compiler/tokens.ts +612 -0
  92. package/src/core/auto-detector.ts +187 -0
  93. package/src/core/common-utils.ts +423 -0
  94. package/src/core/compiler.ts +835 -0
  95. package/src/core/constants.ts +424 -0
  96. package/src/core/index.ts +107 -0
  97. package/src/core/smart-chain.ts +163 -0
  98. package/src/core/types.ts +257 -0
  99. package/src/core/utils.ts +598 -0
  100. package/src/index.ts +208 -0
  101. package/src/plugins/vite.d.ts +316 -0
  102. package/src/plugins/vite.ts +424 -0
  103. package/src/plugins/webpack.d.ts +289 -0
  104. package/src/plugins/webpack.ts +416 -0
  105. package/src/runtime/Chain.ts +242 -0
  106. package/src/runtime/auto-hooks.tsx +127 -0
  107. package/src/runtime/auto-vue.ts +72 -0
  108. package/src/runtime/hmr.ts +212 -0
  109. package/src/runtime/index.ts +82 -0
  110. package/src/runtime/injector.ts +273 -0
  111. package/src/runtime/react.tsx +269 -0
  112. package/src/runtime/svelte.ts +15 -0
  113. package/src/runtime/types.ts +256 -0
  114. package/src/runtime/utils.ts +128 -0
  115. package/src/runtime/vite-env.d.ts +120 -0
  116. package/src/runtime/vue.ts +231 -0
  117. package/tsconfig.build.json +41 -0
  118. package/tsconfig.json +25 -0
  119. package/tsconfig.runtimes.json +18 -0
  120. package/dist/cli/cli.cjs +0 -7
  121. package/dist/cli/commands/build.d.ts.map +0 -1
  122. package/dist/cli/commands/compile.d.ts +0 -3
  123. package/dist/cli/commands/compile.d.ts.map +0 -1
  124. package/dist/cli/commands/init.d.ts.map +0 -1
  125. package/dist/cli/commands/timeline.d.ts.map +0 -1
  126. package/dist/cli/commands/watch.d.ts.map +0 -1
  127. package/dist/cli/index.d.ts.map +0 -1
  128. package/dist/cli/types.d.ts.map +0 -1
  129. package/dist/cli/utils/config-loader.d.ts.map +0 -1
  130. package/dist/cli/utils/file-utils.d.ts.map +0 -1
  131. package/dist/cli/utils/logger.d.ts.map +0 -1
  132. package/dist/compiler/atomic-optimizer.d.ts.map +0 -1
  133. package/dist/compiler/btt.d.ts.map +0 -1
  134. package/dist/compiler/cache-manager.d.ts.map +0 -1
  135. package/dist/compiler/commonProps.d.ts.map +0 -1
  136. package/dist/compiler/index.d.ts.map +0 -1
  137. package/dist/compiler/prefixer.d.ts.map +0 -1
  138. package/dist/compiler/theme-contract.d.ts.map +0 -1
  139. package/dist/compiler/tokens.d.ts.map +0 -1
  140. package/dist/compiler/types.d.ts +0 -57
  141. package/dist/compiler/types.d.ts.map +0 -1
  142. package/dist/core/compiler.d.ts.map +0 -1
  143. package/dist/core/constants.d.ts.map +0 -1
  144. package/dist/core/index.d.ts +0 -4
  145. package/dist/core/index.d.ts.map +0 -1
  146. package/dist/core/types.d.ts.map +0 -1
  147. package/dist/core/utils.d.ts.map +0 -1
  148. package/dist/index.d.ts.map +0 -1
  149. package/dist/plugins/vite.d.ts.map +0 -1
  150. package/dist/plugins/webpack.d.ts.map +0 -1
  151. package/dist/runtime/hmr.d.ts.map +0 -1
  152. package/dist/runtime/index.d.ts.map +0 -1
  153. package/dist/runtime/injector.d.ts.map +0 -1
  154. package/dist/runtime/react.d.ts.map +0 -1
  155. package/dist/runtime/react.js +0 -324
  156. package/dist/runtime/types.d.ts.map +0 -1
  157. package/dist/runtime/utils.d.ts.map +0 -1
  158. package/dist/runtime/vue.d.ts.map +0 -1
  159. package/dist/runtime/vue.js +0 -286
@@ -0,0 +1,436 @@
1
+ // src/compiler/suggestions.ts
2
+ // Types
3
+ export interface SuggestionMatch {
4
+ name: string;
5
+ distance: number;
6
+ type: 'shorthand' | 'css-property' | 'macro' | 'animation' | 'breakpoint';
7
+ }
8
+
9
+ // Known shorthands (from shorthandMap)
10
+ export const KNOWN_SHORTHANDS: string[] = [
11
+ // Spacing
12
+ 'm', 'mt', 'mr', 'mb', 'ml',
13
+ 'p', 'pt', 'pr', 'pb', 'pl',
14
+ 'mx', 'my', 'px', 'py',
15
+ 'inset', 'insetX', 'insetY',
16
+
17
+ // Sizing
18
+ 'w', 'h', 'minW', 'maxW', 'minH', 'maxH',
19
+ 'size', 'aspect',
20
+
21
+ // Display & Layout
22
+ 'd', 'pos', 'flex', 'grid', 'inlineFlex', 'inlineGrid',
23
+ 'flexDir', 'flexWrap', 'justify', 'items', 'align', 'content', 'self',
24
+ 'center', 'flexCenter', 'gridCenter', 'stack', 'gridTable',
25
+ 'cols', 'rows', 'gap', 'gapX', 'gapY', 'grow', 'shrink', 'basis', 'order',
26
+
27
+ // Colors & Backgrounds
28
+ 'bg', 'c', 'text', 'op',
29
+
30
+ // Borders
31
+ 'border', 'borderW', 'borderC', 'borderS',
32
+ 'borderT', 'borderR', 'borderB', 'borderL',
33
+ 'borderX', 'borderY',
34
+ 'rounded', 'br', 'radius',
35
+ 'roundedTL', 'roundedTR', 'roundedBR', 'roundedBL',
36
+
37
+ // Typography
38
+ 'fontF', 'fs', 'fw', 'lh', 'ls', 'align',
39
+
40
+ // Effects
41
+ 'shadow', 'truncate', 'hide', 'show', 'unselectable',
42
+ 'scrollable', 'glass', 'glow', 'textGradient', 'meshGradient', 'noise',
43
+
44
+ // Positioning
45
+ 'absolute', 'fixed', 'sticky', 'relative',
46
+
47
+ // Utilities
48
+ 'pill', 'container', 'fullScreen', 'shimmer', 'bento',
49
+ 'pressable', 'focusRing', 'outlineDebug', 'skeleton',
50
+ 'safeArea', 'clickScale', 'onInteracting', 'children',
51
+ 'dark', 'light', 'fluidText'
52
+ ];
53
+
54
+ // Common CSS properties for suggestions
55
+ export const COMMON_CSS_PROPERTIES: string[] = [
56
+ 'display', 'position', 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
57
+ 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
58
+ 'color', 'background', 'background-color', 'background-image', 'background-size', 'background-position',
59
+ 'border', 'border-width', 'border-style', 'border-color', 'border-radius',
60
+ 'width', 'height', 'max-width', 'max-height', 'min-width', 'min-height',
61
+ 'font-size', 'font-weight', 'font-family', 'line-height', 'letter-spacing', 'text-align',
62
+ 'cursor', 'opacity', 'z-index', 'overflow', 'overflow-x', 'overflow-y',
63
+ 'flex', 'flex-direction', 'flex-wrap', 'justify-content', 'align-items', 'align-self', 'gap',
64
+ 'grid', 'grid-template-columns', 'grid-template-rows', 'grid-column', 'grid-row',
65
+ 'transition', 'transform', 'animation', 'box-shadow', 'text-shadow',
66
+ 'filter', 'backdrop-filter', 'clip-path', 'mask',
67
+ 'pointer-events', 'user-select', 'resize', 'appearance'
68
+ ];
69
+
70
+ // Animation presets (from animations.ts)
71
+ export const ANIMATION_PRESETS: string[] = [
72
+ 'fadeIn', 'fadeOut', 'fadeInUp', 'fadeInDown', 'fadeInLeft', 'fadeInRight',
73
+ 'fadeOutUp', 'fadeOutDown', 'slideInUp', 'slideInDown', 'slideInLeft', 'slideInRight',
74
+ 'slideOutUp', 'slideOutDown', 'zoomIn', 'zoomOut', 'zoomInUp', 'zoomInDown',
75
+ 'bounce', 'bounceIn', 'bounceOut', 'pulse', 'pulseGlow', 'shake', 'shakeX', 'shakeY',
76
+ 'spin', 'spinReverse', 'wiggle', 'wobble', 'flip', 'flipX', 'blink', 'typing',
77
+ 'cursor', 'shimmer', 'ripple', 'float', 'sink', 'swing', 'flash',
78
+ 'textReveal', 'textGlitch'
79
+ ];
80
+
81
+ // Breakpoint names (from breakpoints.ts)
82
+ export const BREAKPOINTS: string[] = [
83
+ 'sm', 'md', 'lg', 'xl', '2xl',
84
+ 'mobile', 'tablet', 'desktop',
85
+ 'mobile-sm', 'mobile-md', 'tablet-sm', 'tablet-lg',
86
+ 'desktop-sm', 'desktop-md', 'desktop-lg',
87
+ 'portrait', 'landscape',
88
+ 'dark', 'light', 'reducedMotion', 'highContrast',
89
+ 'print', 'hover', 'no-hover', 'fine', 'coarse'
90
+ ];
91
+
92
+ // Simple Levenshtein distance for finding closest matches
93
+ function levenshteinDistance(a: string, b: string): number {
94
+ if (a.length === 0) return b.length;
95
+ if (b.length === 0) return a.length;
96
+
97
+ const matrix: number[][] = [];
98
+
99
+ for (let i = 0; i <= b.length; i++) {
100
+ matrix[i] = [i];
101
+ }
102
+ for (let j = 0; j <= a.length; j++) {
103
+ matrix[0][j] = j;
104
+ }
105
+
106
+ for (let i = 1; i <= b.length; i++) {
107
+ for (let j = 1; j <= a.length; j++) {
108
+ const cost = a[j - 1] === b[i - 1] ? 0 : 1;
109
+ matrix[i][j] = Math.min(
110
+ matrix[i - 1][j] + 1, // deletion
111
+ matrix[i][j - 1] + 1, // insertion
112
+ matrix[i - 1][j - 1] + cost // substitution
113
+ );
114
+ }
115
+ }
116
+
117
+ return matrix[b.length][a.length];
118
+ }
119
+
120
+ // Calculate similarity score (0-1, higher is better)
121
+ function similarityScore(a: string, b: string): number {
122
+ const distance = levenshteinDistance(a.toLowerCase(), b.toLowerCase());
123
+ const maxLen = Math.max(a.length, b.length);
124
+ return maxLen === 0 ? 1 : 1 - (distance / maxLen);
125
+ }
126
+
127
+ // Find best matches with scores
128
+ function findBestMatches(
129
+ query: string,
130
+ candidates: string[],
131
+ maxResults: number = 3,
132
+ maxDistance: number = 3
133
+ ): SuggestionMatch[] {
134
+ const matches: SuggestionMatch[] = [];
135
+
136
+ for (const candidate of candidates) {
137
+ const distance = levenshteinDistance(query.toLowerCase(), candidate.toLowerCase());
138
+ if (distance <= maxDistance) {
139
+ matches.push({
140
+ name: candidate,
141
+ distance,
142
+ type: getTypeForCandidate(candidate)
143
+ });
144
+ }
145
+ }
146
+
147
+ // Sort by distance (lower is better) then by name
148
+ matches.sort((a, b) => {
149
+ if (a.distance !== b.distance) return a.distance - b.distance;
150
+ return a.name.localeCompare(b.name);
151
+ });
152
+
153
+ return matches.slice(0, maxResults);
154
+ }
155
+
156
+ // Get type for a candidate
157
+ function getTypeForCandidate(candidate: string): 'shorthand' | 'css-property' | 'macro' | 'animation' | 'breakpoint' {
158
+ if (KNOWN_SHORTHANDS.includes(candidate)) return 'shorthand';
159
+ if (COMMON_CSS_PROPERTIES.includes(candidate)) return 'css-property';
160
+ if (ANIMATION_PRESETS.includes(candidate)) return 'animation';
161
+ if (BREAKPOINTS.includes(candidate)) return 'breakpoint';
162
+ return 'macro';
163
+ }
164
+
165
+ // Get suggestion for invalid shorthand or property
166
+ export function getSuggestion(
167
+ prop: string,
168
+ validProperties: string[] = [],
169
+ type: 'shorthand' | 'css-property' | 'all' = 'all'
170
+ ): string | SuggestionMatch | null {
171
+ // Build candidate list based on type
172
+ let candidates: string[] = [];
173
+
174
+ if (type === 'shorthand') {
175
+ candidates = KNOWN_SHORTHANDS;
176
+ } else if (type === 'css-property') {
177
+ candidates = [...COMMON_CSS_PROPERTIES, ...validProperties];
178
+ } else {
179
+ candidates = [
180
+ ...KNOWN_SHORTHANDS,
181
+ ...COMMON_CSS_PROPERTIES,
182
+ ...validProperties,
183
+ ...ANIMATION_PRESETS,
184
+ ...BREAKPOINTS
185
+ ];
186
+ }
187
+
188
+ // Remove duplicates
189
+ candidates = [...new Set(candidates)];
190
+
191
+ // Find best matches
192
+ const matches = findBestMatches(prop, candidates, 1, 3);
193
+
194
+ if (matches.length > 0) {
195
+ return matches[0];
196
+ }
197
+
198
+ return null;
199
+ }
200
+
201
+ // Get multiple suggestions
202
+ export function getSuggestions(
203
+ prop: string,
204
+ validProperties: string[] = [],
205
+ maxResults: number = 3
206
+ ): SuggestionMatch[] {
207
+ const candidates = [...new Set([
208
+ ...KNOWN_SHORTHANDS,
209
+ ...COMMON_CSS_PROPERTIES,
210
+ ...validProperties,
211
+ ...ANIMATION_PRESETS,
212
+ ...BREAKPOINTS
213
+ ])];
214
+
215
+ return findBestMatches(prop, candidates, maxResults, 4);
216
+ }
217
+
218
+ // Get suggestion for CSS property with context
219
+ export function getPropertySuggestion(
220
+ prop: string,
221
+ context?: 'spacing' | 'color' | 'typography' | 'layout' | 'animation'
222
+ ): string | null {
223
+ const contextProperties: Record<string, string[]> = {
224
+ spacing: [
225
+ 'margin', 'padding', 'gap', 'width', 'height', 'top', 'right', 'bottom', 'left',
226
+ 'inset', 'position', 'translate', 'scale', 'rotate'
227
+ ],
228
+ color: [
229
+ 'color', 'background-color', 'border-color', 'outline-color', 'fill', 'stroke',
230
+ 'box-shadow', 'text-shadow'
231
+ ],
232
+ typography: [
233
+ 'font-family', 'font-size', 'font-weight', 'line-height', 'letter-spacing',
234
+ 'text-align', 'text-decoration', 'text-transform', 'word-spacing'
235
+ ],
236
+ layout: [
237
+ 'display', 'position', 'flex', 'grid', 'justify-content', 'align-items',
238
+ 'flex-direction', 'flex-wrap', 'order', 'z-index', 'overflow'
239
+ ],
240
+ animation: [
241
+ 'animation', 'transition', 'transform', 'opacity', 'filter', 'backdrop-filter',
242
+ 'transform-origin', 'transition-property', 'transition-duration'
243
+ ]
244
+ };
245
+
246
+ const candidates = context && contextProperties[context]
247
+ ? contextProperties[context]
248
+ : COMMON_CSS_PROPERTIES;
249
+
250
+ const matches = findBestMatches(prop, candidates, 1, 2);
251
+
252
+ return matches.length > 0 ? matches[0].name : null;
253
+ }
254
+
255
+ // Get shorthand suggestion with explanation
256
+ export function getShorthandSuggestion(shorthand: string): { suggestion: string; explanation: string } | null {
257
+ const shorthandMap: Record<string, { property: string; description: string }> = {
258
+ 'm': { property: 'margin', description: 'Sets margin on all sides' },
259
+ 'mt': { property: 'margin-top', description: 'Sets top margin' },
260
+ 'mr': { property: 'margin-right', description: 'Sets right margin' },
261
+ 'mb': { property: 'margin-bottom', description: 'Sets bottom margin' },
262
+ 'ml': { property: 'margin-left', description: 'Sets left margin' },
263
+ 'p': { property: 'padding', description: 'Sets padding on all sides' },
264
+ 'pt': { property: 'padding-top', description: 'Sets top padding' },
265
+ 'pr': { property: 'padding-right', description: 'Sets right padding' },
266
+ 'pb': { property: 'padding-bottom', description: 'Sets bottom padding' },
267
+ 'pl': { property: 'padding-left', description: 'Sets left padding' },
268
+ 'mx': { property: 'margin-left/right', description: 'Sets horizontal margins' },
269
+ 'my': { property: 'margin-top/bottom', description: 'Sets vertical margins' },
270
+ 'px': { property: 'padding-left/right', description: 'Sets horizontal padding' },
271
+ 'py': { property: 'padding-top/bottom', description: 'Sets vertical padding' },
272
+ 'd': { property: 'display', description: 'Sets display property' },
273
+ 'pos': { property: 'position', description: 'Sets position property' },
274
+ 'w': { property: 'width', description: 'Sets width' },
275
+ 'h': { property: 'height', description: 'Sets height' },
276
+ 'bg': { property: 'background', description: 'Sets background color/image' },
277
+ 'c': { property: 'color', description: 'Sets text color' },
278
+ 'fs': { property: 'font-size', description: 'Sets font size' },
279
+ 'fw': { property: 'font-weight', description: 'Sets font weight' },
280
+ 'flex': { property: 'display: flex', description: 'Creates a flex container' },
281
+ 'grid': { property: 'display: grid', description: 'Creates a grid container' }
282
+ };
283
+
284
+ const match = shorthandMap[shorthand];
285
+ if (match) {
286
+ return {
287
+ suggestion: match.property,
288
+ explanation: match.description
289
+ };
290
+ }
291
+
292
+ // Try fuzzy match
293
+ const matches = findBestMatches(shorthand, Object.keys(shorthandMap), 1, 2);
294
+ if (matches.length > 0) {
295
+ const best = matches[0];
296
+ const matchInfo = shorthandMap[best.name];
297
+ if (matchInfo) {
298
+ return {
299
+ suggestion: `${best.name} → ${matchInfo.property}`,
300
+ explanation: matchInfo.description
301
+ };
302
+ }
303
+ }
304
+
305
+ return null;
306
+ }
307
+
308
+ // Validate and suggest fix for CSS value
309
+ export function getValueSuggestion(
310
+ property: string,
311
+ value: string
312
+ ): { suggested: string; confidence: number } | null {
313
+ // Common value corrections
314
+ const corrections: Record<string, Record<string, string>> = {
315
+ 'display': {
316
+ 'flexbox': 'flex',
317
+ 'inline-flexbox': 'inline-flex',
318
+ 'gridbox': 'grid',
319
+ 'block-level': 'block',
320
+ 'inline-level': 'inline'
321
+ },
322
+ 'position': {
323
+ 'static': 'static',
324
+ 'relative': 'relative',
325
+ 'absolute': 'absolute',
326
+ 'fixed': 'fixed',
327
+ 'sticky': 'sticky'
328
+ },
329
+ 'text-align': {
330
+ 'center': 'center',
331
+ 'left': 'left',
332
+ 'right': 'right',
333
+ 'justify': 'justify'
334
+ }
335
+ };
336
+
337
+ const propertyCorrections = corrections[property];
338
+ if (propertyCorrections) {
339
+ const lowerValue = value.toLowerCase();
340
+ for (const [wrong, correct] of Object.entries(propertyCorrections)) {
341
+ if (lowerValue === wrong) {
342
+ return { suggested: correct, confidence: 0.95 };
343
+ }
344
+ if (lowerValue.includes(wrong)) {
345
+ return { suggested: correct, confidence: 0.7 };
346
+ }
347
+ }
348
+ }
349
+
350
+ return null;
351
+ }
352
+
353
+ // Get all available suggestions for autocomplete
354
+ export function getAutocompleteSuggestions(prefix: string = '', limit: number = 10): SuggestionMatch[] {
355
+ const allSuggestions = [
356
+ ...KNOWN_SHORTHANDS.map(s => ({ name: s, type: 'shorthand' as const, distance: 0 })),
357
+ ...COMMON_CSS_PROPERTIES.map(s => ({ name: s, type: 'css-property' as const, distance: 0 })),
358
+ ...ANIMATION_PRESETS.map(s => ({ name: s, type: 'animation' as const, distance: 0 })),
359
+ ...BREAKPOINTS.map(s => ({ name: s, type: 'breakpoint' as const, distance: 0 }))
360
+ ];
361
+
362
+ if (!prefix) {
363
+ return allSuggestions.slice(0, limit);
364
+ }
365
+
366
+ const lowerPrefix = prefix.toLowerCase();
367
+ const matches = allSuggestions
368
+ .filter(s => s.name.toLowerCase().startsWith(lowerPrefix))
369
+ .slice(0, limit);
370
+
371
+ if (matches.length < limit) {
372
+ // Also include fuzzy matches
373
+ const fuzzyMatches = findBestMatches(prefix, allSuggestions.map(s => s.name), limit - matches.length);
374
+ for (const match of fuzzyMatches) {
375
+ if (!matches.some(m => m.name === match.name)) {
376
+ matches.push(match as any);
377
+ }
378
+ }
379
+ }
380
+
381
+ return matches;
382
+ }
383
+
384
+ // Format suggestion for console output
385
+ export function formatSuggestion(suggestion: SuggestionMatch): string {
386
+ const typeColors: Record<string, string> = {
387
+ 'shorthand': '🟢',
388
+ 'css-property': '🔵',
389
+ 'macro': '🟣',
390
+ 'animation': '🎬',
391
+ 'breakpoint': '📱'
392
+ };
393
+
394
+ const icon = typeColors[suggestion.type] || '⚪';
395
+ return `${icon} ${suggestion.name} (${suggestion.type}, distance: ${suggestion.distance})`;
396
+ }
397
+
398
+ // Get suggestion with full details
399
+ export function getDetailedSuggestion(prop: string, validProperties: string[] = []): {
400
+ suggestion: string | null;
401
+ alternatives: SuggestionMatch[];
402
+ type: string;
403
+ confidence: number;
404
+ } | null {
405
+ const suggestion = getSuggestion(prop, validProperties);
406
+
407
+ if (!suggestion) {
408
+ return null;
409
+ }
410
+
411
+ const match = typeof suggestion === 'object' ? suggestion : { name: suggestion, distance: 1, type: getTypeForCandidate(suggestion) };
412
+ const confidence = 1 - (match.distance / Math.max(prop.length, match.name.length));
413
+
414
+ return {
415
+ suggestion: match.name,
416
+ alternatives: getSuggestions(prop, validProperties, 3),
417
+ type: match.type,
418
+ confidence: Math.max(0, Math.min(1, confidence))
419
+ };
420
+ }
421
+
422
+ // Export default
423
+ export default {
424
+ getSuggestion,
425
+ getSuggestions,
426
+ getPropertySuggestion,
427
+ getShorthandSuggestion,
428
+ getValueSuggestion,
429
+ getAutocompleteSuggestions,
430
+ formatSuggestion,
431
+ getDetailedSuggestion,
432
+ KNOWN_SHORTHANDS,
433
+ COMMON_CSS_PROPERTIES,
434
+ ANIMATION_PRESETS,
435
+ BREAKPOINTS
436
+ };
@@ -0,0 +1,197 @@
1
+ // src/compiler/theme-contract.ts
2
+ /**
3
+ * Theme Contract System for ChainCSS
4
+ * Validates that themes match the expected shape
5
+ */
6
+
7
+ export interface ThemeContract {
8
+ [key: string]: ThemeContract | string;
9
+ }
10
+
11
+ export interface ThemeTokens {
12
+ [key: string]: string | ThemeTokens;
13
+ }
14
+
15
+ /**
16
+ * Theme class with getter method
17
+ */
18
+ export class Theme {
19
+ private tokens: ThemeTokens;
20
+
21
+ constructor(tokens: ThemeTokens) {
22
+ this.tokens = tokens;
23
+ }
24
+
25
+ get(path: string): string | undefined {
26
+ const parts = path.split('.');
27
+ let current: any = this.tokens;
28
+ for (const part of parts) {
29
+ if (current === undefined || current === null) return undefined;
30
+ current = current[part];
31
+ }
32
+ return typeof current === 'string' ? current : undefined;
33
+ }
34
+
35
+ set(path: string, value: string): void {
36
+ const parts = path.split('.');
37
+ let current: any = this.tokens;
38
+ for (let i = 0; i < parts.length - 1; i++) {
39
+ if (!current[parts[i]]) {
40
+ current[parts[i]] = {};
41
+ }
42
+ current = current[parts[i]];
43
+ }
44
+ current[parts[parts.length - 1]] = value;
45
+ }
46
+
47
+ toObject(): ThemeTokens {
48
+ return this.tokens;
49
+ }
50
+
51
+ toCSSVariables(prefix: string = 'theme'): string {
52
+ let css = '';
53
+ const flatten = (obj: ThemeTokens, path: string = '') => {
54
+ for (const [key, value] of Object.entries(obj)) {
55
+ const newPath = path ? `${path}-${key}` : key;
56
+ if (typeof value === 'object' && value !== null) {
57
+ flatten(value, newPath);
58
+ } else {
59
+ css += ` --${prefix}-${newPath}: ${value};\n`;
60
+ }
61
+ }
62
+ };
63
+ flatten(this.tokens);
64
+ return `:root {\n${css}}\n`;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Create a theme contract that defines the expected shape of themes
70
+ */
71
+ export function createThemeContract<T extends ThemeContract>(contractShape: T): T & {
72
+ __isContract: true;
73
+ __validate: (theme: ThemeTokens) => boolean;
74
+ } {
75
+ const contract = contractShape;
76
+
77
+ const contractProxy = Object.assign({}, contract, {
78
+ __isContract: true as const,
79
+ __validate: (theme: ThemeTokens) => validateTheme(contract, theme)
80
+ });
81
+
82
+ return contractProxy as T & {
83
+ __isContract: true;
84
+ __validate: (theme: ThemeTokens) => boolean;
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Validate that a theme matches the contract
90
+ * @returns true if valid, throws error otherwise
91
+ */
92
+ export function validateTheme(
93
+ contract: ThemeContract,
94
+ theme: ThemeTokens = {},
95
+ path: string = ''
96
+ ): boolean {
97
+ const errors: string[] = [];
98
+
99
+ function validate(
100
+ contractPart: ThemeContract,
101
+ themePart: ThemeTokens | undefined,
102
+ currentPath: string
103
+ ): void {
104
+ if (typeof contractPart === 'object' && contractPart !== null) {
105
+ const requiredKeys = Object.keys(contractPart);
106
+ const themeKeys = Object.keys(themePart || {});
107
+
108
+ requiredKeys.forEach(key => {
109
+ const newPath = currentPath ? `${currentPath}.${key}` : key;
110
+
111
+ if (!themePart || !(key in themePart)) {
112
+ errors.push(` ✗ Missing required token: "${newPath}"`);
113
+ } else {
114
+ validate(
115
+ contractPart[key] as ThemeContract,
116
+ themePart[key] as ThemeTokens | undefined,
117
+ newPath
118
+ );
119
+ }
120
+ });
121
+
122
+ themeKeys.forEach(key => {
123
+ if (!(key in contractPart)) {
124
+ const newPath = currentPath ? `${currentPath}.${key}` : key;
125
+ console.warn(`⚠️ Extra token not in contract: "${newPath}"`);
126
+ }
127
+ });
128
+ } else {
129
+ if (themePart !== undefined && typeof themePart !== 'string') {
130
+ errors.push(` ✗ Token "${currentPath}" must be a string, got ${typeof themePart}`);
131
+ }
132
+ }
133
+ }
134
+
135
+ validate(contract, theme, path);
136
+
137
+ if (errors.length > 0) {
138
+ throw new Error(`Theme Contract Validation Failed:\n${errors.join('\n')}`);
139
+ }
140
+
141
+ return true;
142
+ }
143
+
144
+ /**
145
+ * Create an actual theme from a contract and values
146
+ */
147
+ export function createTheme<T extends ThemeContract>(
148
+ contract: T | (T & { __isContract: boolean }),
149
+ themeValues: ThemeTokens
150
+ ): Theme {
151
+ // Validate if contract has validation method
152
+ if (typeof (contract as any).__validate === 'function') {
153
+ (contract as any).__validate(themeValues);
154
+ } else {
155
+ validateTheme(contract as T, themeValues);
156
+ }
157
+
158
+ const tokens: ThemeTokens = {};
159
+
160
+ function buildTokens(
161
+ contractPart: T,
162
+ themePart: ThemeTokens | undefined,
163
+ target: ThemeTokens,
164
+ _path: string = ''
165
+ ): void {
166
+ Object.keys(contractPart).forEach(key => {
167
+ if (typeof contractPart[key] === 'object' && contractPart[key] !== null) {
168
+ target[key] = {};
169
+ buildTokens(
170
+ contractPart[key] as T,
171
+ (themePart?.[key] as ThemeTokens) || {},
172
+ target[key] as ThemeTokens,
173
+ _path
174
+ );
175
+ } else {
176
+ target[key] = themePart?.[key] as string;
177
+ }
178
+ });
179
+ }
180
+
181
+ buildTokens(contract as T, themeValues, tokens);
182
+
183
+ return new Theme(tokens);
184
+ }
185
+
186
+ // Type guard to check if an object is a theme contract
187
+ export function isThemeContract(obj: any): obj is ThemeContract & { __isContract: true } {
188
+ return obj && typeof obj === 'object' && obj.__isContract === true;
189
+ }
190
+
191
+ export default {
192
+ Theme,
193
+ createThemeContract,
194
+ validateTheme,
195
+ createTheme,
196
+ isThemeContract
197
+ };